Commit cd1ea380 authored by Francisco Javier López's avatar Francisco Javier López Committed by Nick Thomas

Refactor gl_repository class

The repository disk path does't include any suffix
but GlRepository automatically adds it unless it is
a project one.

Since we're adding more repository types that won't
include any suffix and aren't a project type
either, we need a way to set this.
parent c1fc94ae
...@@ -2207,7 +2207,7 @@ class Project < ApplicationRecord ...@@ -2207,7 +2207,7 @@ class Project < ApplicationRecord
end end
def reference_counter(type: Gitlab::GlRepository::PROJECT) def reference_counter(type: Gitlab::GlRepository::PROJECT)
Gitlab::ReferenceCounter.new(type.identifier_for_repositorable(self)) Gitlab::ReferenceCounter.new(type.identifier_for_container(self))
end end
def badges def badges
......
...@@ -65,7 +65,7 @@ class ProjectWiki ...@@ -65,7 +65,7 @@ class ProjectWiki
# Returns the Gitlab::Git::Wiki object. # Returns the Gitlab::Git::Wiki object.
def wiki def wiki
@wiki ||= begin @wiki ||= begin
gl_repository = Gitlab::GlRepository::WIKI.identifier_for_repositorable(project) gl_repository = Gitlab::GlRepository::WIKI.identifier_for_container(project)
raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository, full_path) raw_repository = Gitlab::Git::Repository.new(project.repository_storage, disk_path + '.git', gl_repository, full_path)
create_repo!(raw_repository) unless raw_repository.exists? create_repo!(raw_repository) unless raw_repository.exists?
......
...@@ -1183,7 +1183,7 @@ class Repository ...@@ -1183,7 +1183,7 @@ class Repository
def initialize_raw_repository def initialize_raw_repository
Gitlab::Git::Repository.new(project.repository_storage, Gitlab::Git::Repository.new(project.repository_storage,
disk_path + '.git', disk_path + '.git',
repo_type.identifier_for_repositorable(project), repo_type.identifier_for_container(project),
project.full_path) project.full_path)
end end
end end
......
...@@ -20,7 +20,7 @@ module EE ...@@ -20,7 +20,7 @@ module EE
if ::Gitlab::Geo.primary? if ::Gitlab::Geo.primary?
# This ID is used by the /internal/post_receive API call # This ID is used by the /internal/post_receive API call
gl_id = ::Gitlab::GlId.gl_id(user) gl_id = ::Gitlab::GlId.gl_id(user)
gl_repository = repo_type.identifier_for_repositorable(project) gl_repository = repo_type.identifier_for_container(project)
node_id = params["geo_node_id"] node_id = params["geo_node_id"]
::Gitlab::Geo::GitPushHttp.new(gl_id, gl_repository).cache_referrer_node(node_id) ::Gitlab::Geo::GitPushHttp.new(gl_id, gl_repository).cache_referrer_node(node_id)
end end
......
...@@ -9,7 +9,8 @@ module EE ...@@ -9,7 +9,8 @@ module EE
DESIGN = ::Gitlab::GlRepository::RepoType.new( DESIGN = ::Gitlab::GlRepository::RepoType.new(
name: :design, name: :design,
access_checker_class: ::Gitlab::GitAccessDesign, access_checker_class: ::Gitlab::GitAccessDesign,
repository_accessor: -> (project) { ::DesignManagement::Repository.new(project) } repository_resolver: -> (project) { ::DesignManagement::Repository.new(project) },
suffix: :design
) )
EE_TYPES = { EE_TYPES = {
......
...@@ -8,7 +8,7 @@ describe ::EE::Gitlab::GlRepository do ...@@ -8,7 +8,7 @@ describe ::EE::Gitlab::GlRepository do
end end
it "builds a design repository" do it "builds a design repository" do
expect(described_class::DESIGN.repository_accessor.call(create(:project))) expect(described_class::DESIGN.repository_resolver.call(create(:project)))
.to be_a(::DesignManagement::Repository) .to be_a(::DesignManagement::Repository)
end end
end end
......
...@@ -2,19 +2,27 @@ ...@@ -2,19 +2,27 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::GlRepository::RepoType do describe Gitlab::GlRepository::RepoType do
set(:project) { create(:project) } let_it_be(:project) { create(:project) }
describe Gitlab::GlRepository::DESIGN do describe Gitlab::GlRepository::DESIGN do
it_behaves_like 'a repo type' do it_behaves_like 'a repo type' do
let(:expected_identifier) { "design-#{project.id}" } let(:expected_identifier) { "design-#{project.id}" }
let(:expected_id) { project.id.to_s } let(:expected_id) { project.id.to_s }
let(:expected_suffix) { ".design" } let(:expected_suffix) { '.design' }
let(:expected_repository) { project.design_repository } let(:expected_repository) { project.design_repository }
let(:expected_container) { project }
end end
it "knows its type" do it 'knows its type' do
expect(described_class).to be_design expect(described_class).to be_design
expect(described_class).not_to be_project expect(described_class).not_to be_project
expect(described_class).not_to be_wiki
end
it 'checks if repository path is valid' do
expect(described_class.valid?(project.design_repository.full_path)).to be_truthy
expect(described_class.valid?(project.repository.full_path)).to be_falsey
expect(described_class.valid?(project.wiki.repository.full_path)).to be_falsey
end end
end end
end end
...@@ -118,7 +118,7 @@ describe API::Internal::Base do ...@@ -118,7 +118,7 @@ describe API::Internal::Base do
context "for design repositories" do context "for design repositories" do
set(:project) { create(:project) } set(:project) { create(:project) }
let(:gl_repository) { EE::Gitlab::GlRepository::DESIGN.identifier_for_repositorable(project) } let(:gl_repository) { EE::Gitlab::GlRepository::DESIGN.identifier_for_container(project) }
it "does not allow access" do it "does not allow access" do
post(api("/internal/allowed"), post(api("/internal/allowed"),
......
...@@ -385,7 +385,7 @@ describe "Git HTTP requests (Geo)", :geo do ...@@ -385,7 +385,7 @@ describe "Git HTTP requests (Geo)", :geo do
it 'returns a 200' do it 'returns a 200' do
is_expected.to have_gitlab_http_status(:ok) is_expected.to have_gitlab_http_status(:ok)
expect(json_response['GL_ID']).to match("user-#{user.id}") expect(json_response['GL_ID']).to match("user-#{user.id}")
expect(json_response['GL_REPOSITORY']).to match(Gitlab::GlRepository::PROJECT.identifier_for_repositorable(project)) expect(json_response['GL_REPOSITORY']).to match(Gitlab::GlRepository::PROJECT.identifier_for_container(project))
end end
end end
end end
......
...@@ -96,7 +96,7 @@ describe DesignManagement::DeleteDesignsService do ...@@ -96,7 +96,7 @@ describe DesignManagement::DeleteDesignsService do
end end
it 'calls repository#log_geo_updated_event' do it 'calls repository#log_geo_updated_event' do
design_repository = EE::Gitlab::GlRepository::DESIGN.repository_accessor.call(project) design_repository = EE::Gitlab::GlRepository::DESIGN.repository_resolver.call(project)
allow_any_instance_of(described_class).to receive(:repository).and_return(design_repository) allow_any_instance_of(described_class).to receive(:repository).and_return(design_repository)
expect(design_repository).to receive(:log_geo_updated_event) expect(design_repository).to receive(:log_geo_updated_event)
......
...@@ -7,7 +7,7 @@ describe DesignManagement::SaveDesignsService do ...@@ -7,7 +7,7 @@ describe DesignManagement::SaveDesignsService do
let(:developer) { create(:user) } let(:developer) { create(:user) }
let(:user) { developer } let(:user) { developer }
let(:files) { [rails_sample] } let(:files) { [rails_sample] }
let(:design_repository) { EE::Gitlab::GlRepository::DESIGN.repository_accessor.call(project) } let(:design_repository) { EE::Gitlab::GlRepository::DESIGN.repository_resolver.call(project) }
let(:rails_sample_name) { 'rails_sample.jpg' } let(:rails_sample_name) { 'rails_sample.jpg' }
let(:rails_sample) { sample_image(rails_sample_name) } let(:rails_sample) { sample_image(rails_sample_name) }
let(:dk_png) { sample_image('dk.png') } let(:dk_png) { sample_image('dk.png') }
......
...@@ -116,7 +116,7 @@ module API ...@@ -116,7 +116,7 @@ module API
# Project id to pass between components that don't share/don't have # Project id to pass between components that don't share/don't have
# access to the same filesystem mounts # access to the same filesystem mounts
def gl_repository def gl_repository
repo_type.identifier_for_repositorable(project) repo_type.identifier_for_container(project)
end end
def gl_project_path def gl_project_path
......
...@@ -7,12 +7,13 @@ module Gitlab ...@@ -7,12 +7,13 @@ module Gitlab
PROJECT = RepoType.new( PROJECT = RepoType.new(
name: :project, name: :project,
access_checker_class: Gitlab::GitAccess, access_checker_class: Gitlab::GitAccess,
repository_accessor: -> (project) { project.repository } repository_resolver: -> (project) { project.repository }
).freeze ).freeze
WIKI = RepoType.new( WIKI = RepoType.new(
name: :wiki, name: :wiki,
access_checker_class: Gitlab::GitAccessWiki, access_checker_class: Gitlab::GitAccessWiki,
repository_accessor: -> (project) { project.wiki.repository } repository_resolver: -> (project) { project.wiki.repository },
suffix: :wiki
).freeze ).freeze
TYPES = { TYPES = {
...@@ -27,15 +28,14 @@ module Gitlab ...@@ -27,15 +28,14 @@ module Gitlab
def self.parse(gl_repository) def self.parse(gl_repository)
type_name, _id = gl_repository.split('-').first type_name, _id = gl_repository.split('-').first
type = types[type_name] type = types[type_name]
subject_id = type&.fetch_id(gl_repository)
unless subject_id unless type
raise ArgumentError, "Invalid GL Repository \"#{gl_repository}\"" raise ArgumentError, "Invalid GL Repository \"#{gl_repository}\""
end end
project = Project.find_by_id(subject_id) container = type.fetch_container!(gl_repository)
[project, type] [container, type]
end end
def self.default_type def self.default_type
......
...@@ -5,16 +5,25 @@ module Gitlab ...@@ -5,16 +5,25 @@ module Gitlab
class RepoType class RepoType
attr_reader :name, attr_reader :name,
:access_checker_class, :access_checker_class,
:repository_accessor :repository_resolver,
:container_resolver,
:suffix
def initialize(name:, access_checker_class:, repository_accessor:) def initialize(
name:,
access_checker_class:,
repository_resolver:,
container_resolver: default_container_resolver,
suffix: nil)
@name = name @name = name
@access_checker_class = access_checker_class @access_checker_class = access_checker_class
@repository_accessor = repository_accessor @repository_resolver = repository_resolver
@container_resolver = container_resolver
@suffix = suffix
end end
def identifier_for_repositorable(repositorable) def identifier_for_container(container)
"#{name}-#{repositorable.id}" "#{name}-#{container.id}"
end end
def fetch_id(identifier) def fetch_id(identifier)
...@@ -22,6 +31,14 @@ module Gitlab ...@@ -22,6 +31,14 @@ module Gitlab
match[:id] if match match[:id] if match
end end
def fetch_container!(identifier)
id = fetch_id(identifier)
raise ArgumentError, "Invalid GL Repository \"#{identifier}\"" unless id
container_resolver.call(id)
end
def wiki? def wiki?
self == WIKI self == WIKI
end end
...@@ -31,11 +48,21 @@ module Gitlab ...@@ -31,11 +48,21 @@ module Gitlab
end end
def path_suffix def path_suffix
project? ? "" : ".#{name}" suffix ? ".#{suffix}" : ''
end end
def repository_for(repositorable) def repository_for(container)
repository_accessor.call(repositorable) repository_resolver.call(container)
end
def valid?(repository_path)
repository_path.end_with?(path_suffix)
end
private
def default_container_resolver
-> (id) { Project.find_by_id(id) }
end end
end end
end end
......
...@@ -4,8 +4,9 @@ module Gitlab ...@@ -4,8 +4,9 @@ module Gitlab
module RepoPath module RepoPath
NotFoundError = Class.new(StandardError) NotFoundError = Class.new(StandardError)
def self.parse(repo_path) def self.parse(path)
project_path = repo_path.sub(/\.git\z/, '').sub(%r{\A/}, '') repo_path = path.sub(/\.git\z/, '').sub(%r{\A/}, '')
redirected_path = nil
# Detect the repo type based on the path, the first one tried is the project # Detect the repo type based on the path, the first one tried is the project
# type, which does not have a suffix. # type, which does not have a suffix.
...@@ -14,10 +15,13 @@ module Gitlab ...@@ -14,10 +15,13 @@ module Gitlab
# type. # type.
# We'll always try to find a project with an empty suffix (for the # We'll always try to find a project with an empty suffix (for the
# `Gitlab::GlRepository::PROJECT` type. # `Gitlab::GlRepository::PROJECT` type.
next unless project_path.end_with?(type.path_suffix) next unless type.valid?(repo_path)
project, was_redirected = find_project(project_path.chomp(type.path_suffix)) # Removing the suffix (.wiki, .design, ...) from the project path
redirected_path = project_path if was_redirected full_path = repo_path.chomp(type.path_suffix)
project, was_redirected = find_project(full_path)
redirected_path = repo_path if was_redirected
# If we found a matching project, then the type was matched, no need to # If we found a matching project, then the type was matched, no need to
# continue looking. # continue looking.
......
...@@ -24,7 +24,7 @@ module Gitlab ...@@ -24,7 +24,7 @@ module Gitlab
attrs = { attrs = {
GL_ID: Gitlab::GlId.gl_id(user), GL_ID: Gitlab::GlId.gl_id(user),
GL_REPOSITORY: repo_type.identifier_for_repositorable(repository.project), GL_REPOSITORY: repo_type.identifier_for_container(repository.project),
GL_USERNAME: user&.username, GL_USERNAME: user&.username,
ShowAllRefs: show_all_refs, ShowAllRefs: show_all_refs,
Repository: repository.gitaly_repository.to_h, Repository: repository.gitaly_repository.to_h,
......
...@@ -2,33 +2,45 @@ ...@@ -2,33 +2,45 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::GlRepository::RepoType do describe Gitlab::GlRepository::RepoType do
set(:project) { create(:project) } let_it_be(:project) { create(:project) }
describe Gitlab::GlRepository::PROJECT do describe Gitlab::GlRepository::PROJECT do
it_behaves_like 'a repo type' do it_behaves_like 'a repo type' do
let(:expected_identifier) { "project-#{project.id}" } let(:expected_identifier) { "project-#{project.id}" }
let(:expected_id) { project.id.to_s } let(:expected_id) { project.id.to_s }
let(:expected_suffix) { "" } let(:expected_suffix) { '' }
let(:expected_repository) { project.repository } let(:expected_repository) { project.repository }
let(:expected_container) { project }
end end
it "knows its type" do it 'knows its type' do
expect(described_class).not_to be_wiki expect(described_class).not_to be_wiki
expect(described_class).to be_project expect(described_class).to be_project
end end
it 'checks if repository path is valid' do
expect(described_class.valid?(project.repository.full_path)).to be_truthy
expect(described_class.valid?(project.wiki.repository.full_path)).to be_truthy
end
end end
describe Gitlab::GlRepository::WIKI do describe Gitlab::GlRepository::WIKI do
it_behaves_like 'a repo type' do it_behaves_like 'a repo type' do
let(:expected_identifier) { "wiki-#{project.id}" } let(:expected_identifier) { "wiki-#{project.id}" }
let(:expected_id) { project.id.to_s } let(:expected_id) { project.id.to_s }
let(:expected_suffix) { ".wiki" } let(:expected_suffix) { '.wiki' }
let(:expected_repository) { project.wiki.repository } let(:expected_repository) { project.wiki.repository }
let(:expected_container) { project }
end end
it "knows its type" do it 'knows its type' do
expect(described_class).to be_wiki expect(described_class).to be_wiki
expect(described_class).not_to be_project expect(described_class).not_to be_project
end end
it 'checks if repository path is valid' do
expect(described_class.valid?(project.repository.full_path)).to be_falsey
expect(described_class.valid?(project.wiki.repository.full_path)).to be_truthy
end
end end
end end
...@@ -4,7 +4,7 @@ require 'spec_helper' ...@@ -4,7 +4,7 @@ require 'spec_helper'
describe ::Gitlab::GlRepository do describe ::Gitlab::GlRepository do
describe '.parse' do describe '.parse' do
set(:project) { create(:project, :repository) } let_it_be(:project) { create(:project, :repository) }
it 'parses a project gl_repository' do it 'parses a project gl_repository' do
expect(described_class.parse("project-#{project.id}")).to eq([project, Gitlab::GlRepository::PROJECT]) expect(described_class.parse("project-#{project.id}")).to eq([project, Gitlab::GlRepository::PROJECT])
...@@ -14,8 +14,12 @@ describe ::Gitlab::GlRepository do ...@@ -14,8 +14,12 @@ describe ::Gitlab::GlRepository do
expect(described_class.parse("wiki-#{project.id}")).to eq([project, Gitlab::GlRepository::WIKI]) expect(described_class.parse("wiki-#{project.id}")).to eq([project, Gitlab::GlRepository::WIKI])
end end
it 'throws an argument error on an invalid gl_repository' do it 'throws an argument error on an invalid gl_repository type' do
expect { described_class.parse("badformat-#{project.id}") }.to raise_error(ArgumentError) expect { described_class.parse("badformat-#{project.id}") }.to raise_error(ArgumentError)
end end
it 'throws an argument error on an invalid gl_repository id' do
expect { described_class.parse("project-foo") }.to raise_error(ArgumentError)
end
end end
end end
...@@ -3992,7 +3992,7 @@ describe Project do ...@@ -3992,7 +3992,7 @@ describe Project do
end end
it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the project repo is in use' do it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the project repo is in use' do
Gitlab::ReferenceCounter.new(Gitlab::GlRepository::PROJECT.identifier_for_repositorable(project)).increase Gitlab::ReferenceCounter.new(Gitlab::GlRepository::PROJECT.identifier_for_container(project)).increase
expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in) expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
...@@ -4000,7 +4000,7 @@ describe Project do ...@@ -4000,7 +4000,7 @@ describe Project do
end end
it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the wiki repo is in use' do it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the wiki repo is in use' do
Gitlab::ReferenceCounter.new(Gitlab::GlRepository::WIKI.identifier_for_repositorable(project)).increase Gitlab::ReferenceCounter.new(Gitlab::GlRepository::WIKI.identifier_for_container(project)).increase
expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in) expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in)
......
...@@ -268,7 +268,7 @@ describe API::Internal::Base do ...@@ -268,7 +268,7 @@ describe API::Internal::Base do
end end
context 'with env passed as a JSON' do context 'with env passed as a JSON' do
let(:gl_repository) { Gitlab::GlRepository::WIKI.identifier_for_repositorable(project) } let(:gl_repository) { Gitlab::GlRepository::WIKI.identifier_for_container(project) }
it 'sets env in RequestStore' do it 'sets env in RequestStore' do
obj_dir_relative = './objects' obj_dir_relative = './objects'
...@@ -1054,9 +1054,9 @@ describe API::Internal::Base do ...@@ -1054,9 +1054,9 @@ describe API::Internal::Base do
def gl_repository_for(project_or_wiki) def gl_repository_for(project_or_wiki)
case project_or_wiki case project_or_wiki
when ProjectWiki when ProjectWiki
Gitlab::GlRepository::WIKI.identifier_for_repositorable(project_or_wiki.project) Gitlab::GlRepository::WIKI.identifier_for_container(project_or_wiki.project)
when Project when Project
Gitlab::GlRepository::PROJECT.identifier_for_repositorable(project_or_wiki) Gitlab::GlRepository::PROJECT.identifier_for_container(project_or_wiki)
else else
nil nil
end end
......
# frozen_string_literal: true # frozen_string_literal: true
RSpec.shared_examples 'a repo type' do RSpec.shared_examples 'a repo type' do
describe "#identifier_for_repositorable" do describe '#identifier_for_container' do
subject { described_class.identifier_for_repositorable(project) } subject { described_class.identifier_for_container(project) }
it { is_expected.to eq(expected_identifier) } it { is_expected.to eq(expected_identifier) }
end end
describe "#fetch_id" do describe '#fetch_id' do
it "finds an id match in the identifier" do it 'finds an id match in the identifier' do
expect(described_class.fetch_id(expected_identifier)).to eq(expected_id) expect(described_class.fetch_id(expected_identifier)).to eq(expected_id)
end end
it 'does not break on other identifiers' do it 'does not break on other identifiers' do
expect(described_class.fetch_id("wiki-noid")).to eq(nil) expect(described_class.fetch_id('wiki-noid')).to eq(nil)
end end
end end
describe "#path_suffix" do describe '#fetch_container!' do
it 'returns the container' do
expect(described_class.fetch_container!(expected_identifier)).to eq expected_container
end
it 'raises an exception if the identifier is invalid' do
expect { described_class.fetch_container!('project-noid') }.to raise_error ArgumentError
end
end
describe '#path_suffix' do
subject { described_class.path_suffix } subject { described_class.path_suffix }
it { is_expected.to eq(expected_suffix) } it { is_expected.to eq(expected_suffix) }
end end
describe "#repository_for" do describe '#repository_for' do
it "finds the repository for the repo type" do it 'finds the repository for the repo type' do
expect(described_class.repository_for(project)).to eq(expected_repository) expect(described_class.repository_for(project)).to eq(expected_repository)
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