Split gitolite backend. Use gitolite_config methods moved to separate class

This commit is contained in:
Dmitriy Zaporozhets 2012-09-07 08:16:29 +03:00
parent 137594dd08
commit 79021e674b
7 changed files with 241 additions and 186 deletions

View File

@ -14,10 +14,6 @@ class ApplicationController < ActionController::Base
render "errors/gitolite", layout: "error"
end
rescue_from Gitlab::Gitolite::InvalidKey do |exception|
render "errors/invalid_ssh_key", layout: "error"
end
rescue_from Encoding::CompatibilityError do |exception|
render "errors/encoding", layout: "error", status: 404
end

View File

@ -1,3 +0,0 @@
%h1 Git Error
%hr
%p Seems like SSH Key you provided is not a valid SSH key.

View File

@ -1,202 +1,43 @@
require 'gitolite'
require 'timeout'
require 'fileutils'
require_relative 'gitolite_config'
# TODO: refactor & cleanup
module Gitlab
class Gitolite
class AccessDenied < StandardError; end
class InvalidKey < StandardError; end
def config
@config ||= Gitlab::GitoliteConfig.new
end
def set_key key_id, key_content, projects
configure do |c|
c.update_keys(key_id, key_content)
c.update_projects(projects)
config.apply do |config|
config.write_key(key_id, key_content)
config.update_projects(projects)
end
end
def remove_key key_id, projects
configure do |c|
c.delete_key(key_id)
c.update_projects(projects)
config.apply do |config|
config.rm_key(key_id)
config.update_projects(projects)
end
end
def update_repository project
configure do |c|
c.update_project(project.path, project)
end
config.update_project!(project.path, project)
end
alias_method :create_repository, :update_repository
def remove_repository project
configure do |c|
c.destroy_project(project)
end
config.destroy_project!(project)
end
def url_to_repo path
Gitlab.config.ssh_path + "#{path}.git"
end
def initialize
# create tmp dir
@local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
end
def enable_automerge
configure do |git|
git.admin_all_repo
end
config.admin_all_repo!(project)
end
protected
def destroy_project(project)
FileUtils.rm_rf(project.path_to_repo)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
conf.rm_repo(project.path)
ga_repo.save
end
#update or create
def update_keys(user, key)
File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) }
end
def delete_key(user)
File.unlink(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"))
`cd #{File.join(@local_dir,'gitolite')} ; git rm keydir/#{user}.pub`
end
# update or create
def update_project(repo_name, project)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
ga_repo.save
end
# Updates many projects and uses project.path as the repo path
# An order of magnitude faster than update_project
def update_projects(projects)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
projects.each do |project|
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
end
ga_repo.save
end
def update_project_config(project, conf)
repo_name = project.path
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
name_readers = project.repository_readers
name_writers = project.repository_writers
name_masters = project.repository_masters
pr_br = project.protected_branches.map(&:name).join("$ ")
repo.clean_permissions
# Deny access to protected branches for writers
unless name_writers.blank? || pr_br.blank?
repo.add_permission("-", pr_br.strip + "$ ", name_writers)
end
# Add read permissions
repo.add_permission("R", "", name_readers) unless name_readers.blank?
# Add write permissions
repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
repo
end
def admin_all_repo
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))
conf = ga_repo.config
owner_name = ""
# Read gitolite-admin user
#
begin
repo = conf.get_repo("gitolite-admin")
owner_name = repo.permissions[0]["RW+"][""][0]
raise StandardError if owner_name.blank?
rescue => ex
puts "Can't determine gitolite-admin owner".red
raise StandardError
end
# @ALL repos premission for gitolite owner
repo_name = "@all"
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
repo.add_permission("RW+", "", owner_name)
conf.add_repo(repo, true)
ga_repo.save
end
private
def pull
# create tmp dir
@local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
Dir.mkdir @local_dir
`git clone #{Gitlab.config.gitolite_admin_uri} #{@local_dir}/gitolite`
end
def push
Dir.chdir(File.join(@local_dir, "gitolite"))
`git add -A`
`git commit -am "GitLab"`
`git push`
Dir.chdir(Rails.root)
FileUtils.rm_rf(@local_dir)
end
def configure
Timeout::timeout(30) do
File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
begin
f.flock(File::LOCK_EX)
pull
yield(self)
push
ensure
f.flock(File::LOCK_UN)
end
end
end
rescue Exception => ex
if ex.message =~ /is not a valid SSH key string/
raise Gitolite::InvalidKey.new("ssh key is not valid")
else
Gitlab::Logger.error(ex.message)
raise Gitolite::AccessDenied.new("gitolite timeout")
end
end
alias_method :create_repository, :update_repository
end
end

View File

@ -0,0 +1,168 @@
require 'gitolite'
require 'timeout'
require 'fileutils'
module Gitlab
class GitoliteConfig
def config_tmp_dir
@config_tmp_dir ||= File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
end
def apply
Timeout::timeout(30) do
File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
begin
f.flock(File::LOCK_EX)
pull
yield(self)
push
ensure
f.flock(File::LOCK_UN)
end
end
end
rescue Exception => ex
Gitlab::Logger.error(ex.message)
raise Gitolite::AccessDenied.new("gitolite timeout")
end
def destroy_project(project)
FileUtils.rm_rf(project.path_to_repo)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
conf = ga_repo.config
conf.rm_repo(project.path)
ga_repo.save
end
def destroy_project!(project)
apply do |config|
config.destroy_project(project)
end
end
def write_key(id, key)
File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f|
f.write(key.gsub(/\n/,''))
end
end
def rm_key(user)
File.unlink(File.join(config_tmp_dir, 'gitolite/keydir',"#{user}.pub"))
`cd #{File.join(config_tmp_dir,'gitolite')} ; git rm keydir/#{user}.pub`
end
# update or create
def update_project(repo_name, project)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
conf = ga_repo.config
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
ga_repo.save
end
def update_project!(repo_name, project)
apply do |config|
config.update_project(repo_name, project)
end
end
# Updates many projects and uses project.path as the repo path
# An order of magnitude faster than update_project
def update_projects(projects)
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
conf = ga_repo.config
projects.each do |project|
repo = update_project_config(project, conf)
conf.add_repo(repo, true)
end
ga_repo.save
end
def update_project_config(project, conf)
repo_name = project.path
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
name_readers = project.repository_readers
name_writers = project.repository_writers
name_masters = project.repository_masters
pr_br = project.protected_branches.map(&:name).join("$ ")
repo.clean_permissions
# Deny access to protected branches for writers
unless name_writers.blank? || pr_br.blank?
repo.add_permission("-", pr_br.strip + "$ ", name_writers)
end
# Add read permissions
repo.add_permission("R", "", name_readers) unless name_readers.blank?
# Add write permissions
repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
repo
end
def admin_all_repo
ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
conf = ga_repo.config
owner_name = ""
# Read gitolite-admin user
#
begin
repo = conf.get_repo("gitolite-admin")
owner_name = repo.permissions[0]["RW+"][""][0]
raise StandardError if owner_name.blank?
rescue => ex
puts "Can't determine gitolite-admin owner".red
raise StandardError
end
# @ALL repos premission for gitolite owner
repo_name = "@all"
repo = if conf.has_repo?(repo_name)
conf.get_repo(repo_name)
else
::Gitolite::Config::Repo.new(repo_name)
end
repo.add_permission("RW+", "", owner_name)
conf.add_repo(repo, true)
ga_repo.save
end
def admin_all_repo!
apply { |config| config.admin_all_repo }
end
private
def pull
Dir.mkdir config_tmp_dir
`git clone #{Gitlab.config.gitolite_admin_uri} #{config_tmp_dir}/gitolite`
end
def push
Dir.chdir(File.join(config_tmp_dir, "gitolite"))
`git add -A`
`git commit -am "GitLab"`
`git push`
Dir.chdir(Rails.root)
FileUtils.rm_rf(config_tmp_dir)
end
end
end

View File

@ -0,0 +1,16 @@
require 'spec_helper'
describe Gitlab::GitoliteConfig do
let(:gitolite) { Gitlab::GitoliteConfig.new }
it { should respond_to :write_key }
it { should respond_to :rm_key }
it { should respond_to :update_project }
it { should respond_to :update_project! }
it { should respond_to :update_projects }
it { should respond_to :destroy_project }
it { should respond_to :destroy_project! }
it { should respond_to :apply }
it { should respond_to :admin_all_repo }
it { should respond_to :admin_all_repo! }
end

25
spec/lib/gitolite_spec.rb Normal file
View File

@ -0,0 +1,25 @@
require 'spec_helper'
describe Gitlab::Gitolite do
let(:project) { double('Project', path: 'diaspora') }
let(:gitolite_config) { double('Gitlab::GitoliteConfig') }
let(:gitolite) { Gitlab::Gitolite.new }
before do
gitolite.stub(config: gitolite_config)
end
it { should respond_to :set_key }
it { should respond_to :remove_key }
it { should respond_to :update_repository }
it { should respond_to :create_repository }
it { should respond_to :remove_repository }
it { gitolite.url_to_repo('diaspora').should == Gitlab.config.ssh_path + "diaspora.git" }
it "should call config update" do
gitolite_config.should_receive(:update_project!)
gitolite.update_repository project
end
end

View File

@ -17,7 +17,7 @@ module GitoliteStub
)
gitolite_admin = double(
'Gitolite::GitoliteAdmin',
'Gitolite::GitoliteAdmin',
config: gitolite_config,
save: true,
)
@ -27,9 +27,21 @@ module GitoliteStub
end
def stub_gitlab_gitolite
gitlab_gitolite = Gitlab::Gitolite.new
Gitlab::Gitolite.stub(new: gitlab_gitolite)
gitlab_gitolite.stub(configure: ->() { yield(self) })
gitlab_gitolite.stub(update_keys: true)
gitolite_config = double('Gitlab::GitoliteConfig')
gitolite_config.stub(
apply: ->() { yield(self) },
write_key: true,
rm_key: true,
update_projects: true,
update_project: true,
update_project!: true,
destroy_project: true,
destroy_project!: true,
admin_all_repo: true,
admin_all_repo!: true,
)
Gitlab::GitoliteConfig.stub(new: gitolite_config)
end
end