Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite of primary gitolite interface for better resilience and performance. #124

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.*.swp
bin/
selinux/tmp/
142 changes: 122 additions & 20 deletions README.mkd

Large diffs are not rendered by default.

114 changes: 63 additions & 51 deletions app/controllers/git_http_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,71 +10,83 @@ class GitHttpController < ApplicationController

before_filter :authenticate


def index
p1 = params[:p1]
p2 = params[:p2]
p3 = params[:p3]
proj_id = params[:id]


@git_http_repo_path = (params[:path]).gsub(/\.git$/, "")

reqfile = p2 == "" ? p1 : ( p3 == "" ? p1 + "/" + p2 : p1 + "/" + p2 + "/" + p3);

if p1 == "git-upload-pack"
service_rpc("upload-pack")
elsif p1 == "git-receive-pack"
service_rpc("receive-pack")
elsif p1 == "info" && p2 == "refs"
get_info_refs(reqfile)
elsif p1 == "HEAD"
get_text_file(reqfile)
elsif p1 == "objects" && p2 == "info"
if p3 != packs
get_text_file(reqfile)
else
get_info_packs(reqfile)
end
elsif p1 == "objects" && p2 != "pack"
get_loose_object(reqfile)
elsif p1 == "objects" && p2 == "pack" && p3.match(/\.pack$/)
get_pack_file(reqfile)
elsif p1 == "objects" && p2 == "pack" && p3.match(/\.idx$/)
get_idx_file(reqfile)
else
render_not_found
end

if proj_path_split = (params[:project_path].match(/^(.*?([^\/]+))\.git$/))
proj_id = proj_path_split[2]
project = Project.find_by_identifier(proj_id)
@git_http_repo_path = project_path = proj_path_split[1]

if GitHosting.http_access_url(project) == project_path
p1 = params[:path][0] || ""
p2 = params[:path][1] || ""
p3 = params[:path][2] || ""

# Full requested path from .git repo
reqfile = params[:path].join('/')

# git http protocol
if p1 == "git-upload-pack"
service_rpc("upload-pack")
elsif p1 == "git-receive-pack"
service_rpc("receive-pack")
elsif p1 == "info" && p2 == "refs"
get_info_refs(reqfile)
elsif p1 == "HEAD"
get_text_file(reqfile)
elsif p1 == "objects" && p2 == "info"
if p3 != "packs"
get_text_file(reqfile)
else
get_info_packs(reqfile)
end
elsif p1 == "objects" && p2 != "pack"
get_loose_object(reqfile)
elsif p1 == "objects" && p2 == "pack" && p3.match(/\.pack$/)
get_pack_file(reqfile)
elsif p1 == "objects" && p2 == "pack" && p3.match(/\.idx$/)
get_idx_file(reqfile)
else
render_not_found
end
else
# repository URL doesn't match
render_not_found
end
return
end
# Wrong prefix or :base doesn't end in .git or doesn't at least have one char in base name
render_not_found
end

private

def authenticate
is_push = params[:p1] == "git-receive-pack"
is_push = (params[:path][0] == "git-receive-pack")
query_valid = false
authentication_valid = true

project = Project.find(params[:id])
repository = project != nil ? project.repository : nil
if(project != nil && repository !=nil)
if repository.extra[:git_http] == 2 || (repository.extra[:git_http] == 1 && is_ssl?)
query_valid = true
allow_anonymous_read = project.is_public
if is_push || (!allow_anonymous_read)
authentication_valid = false
authenticate_or_request_with_http_basic do |login, password|
user = User.find_by_login(login);
if user.is_a?(User)
if user.allowed_to?( :commit_access, project ) || ((!is_push) && user.allowed_to?( :view_changesets, project ))
authentication_valid = user.check_password?(password)
if proj_path_split = (params[:project_path].match(/^(.*?([^\/]+))\.git$/))
project = Project.find_by_identifier(proj_path_split[2])
repository = project != nil ? project.repository : nil
if(project != nil && repository !=nil)
if repository.extra[:git_http] == 2 || (repository.extra[:git_http] == 1 && is_ssl?)
query_valid = true
allow_anonymous_read = project.is_public
if is_push || (!allow_anonymous_read)
authentication_valid = false
authenticate_or_request_with_http_basic do |login, password|
user = User.find_by_login(login);
if user.is_a?(User)
if user.allowed_to?( :commit_access, project ) || ((!is_push) && user.allowed_to?( :view_changesets, project ))
authentication_valid = user.check_password?(password)
end
end
authentication_valid
end
authentication_valid
end
end
end
end
end

#if authentication failed, error already rendered
#so, just render case where user queried a project
Expand Down
6 changes: 6 additions & 0 deletions app/controllers/gitolite_public_keys_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,34 @@ def edit
end

def delete
GitHostingObserver.set_update_active(false)
@gitolite_public_key[:active] = 0
@gitolite_public_key.save
redirect_to url_for(:controller => 'my', :action => 'account')
GitHostingObserver.set_update_active(true)
end

def update
GitHostingObserver.set_update_active(false)
if @gitolite_public_key.update_attributes(params[:public_key])
flash[:notice] = l(:notice_public_key_updated)
redirect_to url_for(:controller => 'my', :action => 'account')
else
render :action => 'edit'
end
GitHostingObserver.set_update_active(true)
end

def create
GitHostingObserver.set_update_active(false)
@gitolite_public_key = GitolitePublicKey.new(params[:public_key].merge(:user => @user))
if @gitolite_public_key.save
flash[:notice] = l(:notice_public_key_added)
else
@gitolite_public_key = GitolitePublicKey.new(:user => @user)
end
redirect_to url_for(:controller => 'my', :action => 'account')
GitHostingObserver.set_update_active(true)
end

protected
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/repository_mirrors_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ def settings
def push
respond_to do |format|
format.html {
@mirror.push
(@push_failed,@shellout) = @mirror.push
}
end
end
Expand Down
70 changes: 43 additions & 27 deletions app/models/git_hosting_observer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ class GitHostingObserver < ActiveRecord::Observer
observe :project, :user, :gitolite_public_key, :member, :role, :repository

@@updating_active = true
@@updating_active_stack = 0
@@updating_active_flags = {}
@@cached_project_updates = []

def reload_this_observer
Expand All @@ -11,28 +13,40 @@ def reload_this_observer
end


def self.set_update_active(is_active)
@@updating_active = is_active
if is_active
if @@cached_project_updates.length > 0
def self.set_update_active(*is_active)
if !is_active || !is_active.first
@@updating_active_stack += 1
else
is_active.each do |item|
case item
when Symbol then @@updating_active_flags[item] = true
when Hash then @@updating_active_flags.merge!(item)
when Project then @@cached_project_updates |= [item]
end
end

# If about to transition to zero and have something to run, do it
if @@updating_active_stack == 1 && (@@cached_project_updates.length > 0 || !@@updating_active_flags.empty?)
@@cached_project_updates = @@cached_project_updates.flatten.uniq.compact
GitHosting::update_repositories(@@cached_project_updates, false)
end
end
@@cached_project_updates = []
end


def before_destroy(object)
if object.is_a?(Repository::Git)
if Setting.plugin_redmine_git_hosting['deleteGitRepositories'] == "true"
GitHosting::update_repositories(object.project, true)
%x[#{GitHosting::git_user_runner} 'rm -rf #{object.url}' ]
end
GitHosting::clear_cache_for_project(object.project)
GitHosting::update_repositories(@@cached_project_updates, @@updating_active_flags)
@@cached_project_updates = []
@@updating_active_flags = {}
end

# Wait until after running update_repositories before releasing
@@updating_active_stack -= 1
if @@updating_active_stack < 0
@@updating_active_stack = 0
end
end
@@updating_active = (@@updating_active_stack == 0)
end


# Register args for updating and then do it without allowing recursive calls
def self.bracketed_update_repositories(*args)
set_update_active(false)
set_update_active(*args)
end

def after_create(object)
if not object.is_a?(Project)
Expand All @@ -51,20 +65,21 @@ def before_save(object)
def after_save(object)
update_repositories(object)
end


def after_destroy(object)
if !object.is_a?(Repository::Git)
update_repositories(object)
if object.is_a?(Repository::Git)
update_repositories(object,:delete=>true)
GitHosting::clear_cache_for_project(object.project)
else
update_repositories(object)
end
end


protected


def update_repositories(object)

def update_repositories(object,*flags)
projects = []
case object
when Repository::Git then projects.push(object.project)
Expand All @@ -73,11 +88,12 @@ def update_repositories(object)
when Member then projects.push(object.project)
when Role then projects = object.members.map(&:project).flatten.uniq.compact
end
if(projects.length > 0)
if (projects.length > 0)
if (@@updating_active)
GitHosting::update_repositories(projects, false)
GitHosting::update_repositories(projects,*flags)
else
@@cached_project_updates.concat(projects)
@@updating_active_flags.merge!(*flags) unless flags.empty?
end
end
end
Expand Down
Loading