Commit e06ca006 authored by Vladimir Shushlin's avatar Vladimir Shushlin

Add service for migrating pages to zip storage

* we use exclusive lease to guard agains situations when
  new deployment happens at the same time as we try to
  zip pages content
* If project directory is absent(or `public` directory)
  then we mark pages as not deployed for this project
parent 12b637e6
......@@ -1829,6 +1829,15 @@ class Project < ApplicationRecord
ensure_pages_metadatum.update!(pages_deployment: deployment)
end
def set_first_pages_deployment!(deployment)
ensure_pages_metadatum
# where().update_all to perform update in the single transaction with check for null
ProjectPagesMetadatum
.where(project_id: id, pages_deployment_id: nil)
.update_all(pages_deployment_id: deployment.id)
end
def write_repository_config(gl_full_path: full_path)
# We'd need to keep track of project full path otherwise directory tree
# created with hashed storage enabled cannot be usefully imported using
......
# frozen_string_literal: true
module Pages
class MigrateLegacyStorageToDeploymentService
ExclusiveLeaseTaken = Class.new(StandardError)
include ::Pages::LegacyStorageLease
attr_reader :project
def initialize(project)
@project = project
end
def execute
migrated = try_obtain_lease do
execute_unsafe
true
end
raise ExclusiveLeaseTaken, "Can't migrate pages for project #{project.id}: exclusive lease taken" unless migrated
end
private
def execute_unsafe
archive_path, entries_count = ::Pages::ZipDirectoryService.new(project.pages_path).execute
deployment = nil
File.open(archive_path) do |file|
deployment = project.pages_deployments.create!(
file: file,
file_count: entries_count,
file_sha256: Digest::SHA256.file(archive_path).hexdigest
)
end
project.set_first_pages_deployment!(deployment)
rescue ::Pages::ZipDirectoryService::InvalidArchiveError => e
Gitlab::ErrorTracking.track_exception(e, project_id: project.id)
project.mark_pages_as_not_deployed if Feature.enabled?(:pages_migration_mark_as_not_deployed, project)
end
end
end
---
name: pages_migration_mark_as_not_deployed
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/49473
rollout_issue_url:
milestone: '13.7'
type: development
group: group::release
default_enabled: false
......@@ -6002,6 +6002,43 @@ RSpec.describe Project, factory_default: :keep do
end
end
describe '#set_first_pages_deployment!' do
let(:project) { create(:project) }
let(:deployment) { create(:pages_deployment, project: project) }
it "creates new metadata record if none exists yet and sets deployment" do
project.pages_metadatum.destroy!
project.reload
project.set_first_pages_deployment!(deployment)
expect(project.pages_metadatum.reload.pages_deployment).to eq(deployment)
end
it "updates the existing metadara record with deployment" do
expect do
project.set_first_pages_deployment!(deployment)
end.to change { project.pages_metadatum.reload.pages_deployment }.from(nil).to(deployment)
end
it 'only updates metadata for this project' do
other_project = create(:project)
expect do
project.set_first_pages_deployment!(deployment)
end.not_to change { other_project.pages_metadatum.reload.pages_deployment }.from(nil)
end
it 'does nothing if metadata already references some deployment' do
existing_deployment = create(:pages_deployment, project: project)
project.set_first_pages_deployment!(existing_deployment)
expect do
project.set_first_pages_deployment!(deployment)
end.not_to change { project.pages_metadatum.reload.pages_deployment }.from(existing_deployment)
end
end
describe '#has_pool_repsitory?' do
it 'returns false when it does not have a pool repository' do
subject = create(:project, :repository)
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Pages::MigrateLegacyStorageToDeploymentService do
let(:project) { create(:project, :repository) }
let(:service) { described_class.new(project) }
it 'marks pages as not deployed if public directory is absent' do
project.mark_pages_as_deployed
expect do
service.execute
end.to change { project.pages_metadatum.reload.deployed }.from(true).to(false)
end
it 'does not mark pages as not deployed if public directory is absent but feature is disabled' do
stub_feature_flags(pages_migration_mark_as_not_deployed: false)
project.mark_pages_as_deployed
expect do
service.execute
end.not_to change { project.pages_metadatum.reload.deployed }.from(true)
end
context 'when pages site is deployed to legacy storage' do
before do
FileUtils.mkdir_p File.join(project.pages_path, "public")
File.open(File.join(project.pages_path, "public/index.html"), "w") do |f|
f.write("Hello!")
end
end
it 'creates pages deployment' do
expect do
described_class.new(project).execute
end.to change { project.reload.pages_deployments.count }.by(1)
deployment = project.pages_metadatum.pages_deployment
Zip::File.open(deployment.file.path) do |zip_file|
expect(zip_file.glob("public").first.ftype).to eq(:directory)
expect(zip_file.glob("public/index.html").first.get_input_stream.read).to eq("Hello!")
end
expect(deployment.file_count).to eq(2)
expect(deployment.file_sha256).to eq(Digest::SHA256.file(deployment.file.path).hexdigest)
end
it 'does not change pages deployment if it is set' do
old_deployment = create(:pages_deployment, project: project)
project.update_pages_deployment!(old_deployment)
expect do
described_class.new(project).execute
end.not_to change { project.pages_metadatum.reload.pages_deployment_id }.from(old_deployment.id)
end
it 'raises exception if exclusive lease is taken' do
described_class.new(project).try_obtain_lease do
expect do
described_class.new(project).execute
end.to raise_error(described_class::ExclusiveLeaseTaken)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment