Split gitolite backend. Use gitolite_config methods moved to separate class
This commit is contained in:
		
							parent
							
								
									137594dd08
								
							
						
					
					
						commit
						79021e674b
					
				|  | @ -14,10 +14,6 @@ class ApplicationController < ActionController::Base | ||||||
|     render "errors/gitolite", layout: "error" |     render "errors/gitolite", layout: "error" | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   rescue_from Gitlab::Gitolite::InvalidKey do |exception| |  | ||||||
|     render "errors/invalid_ssh_key", layout: "error" |  | ||||||
|   end |  | ||||||
| 
 |  | ||||||
|   rescue_from Encoding::CompatibilityError do |exception| |   rescue_from Encoding::CompatibilityError do |exception| | ||||||
|     render "errors/encoding", layout: "error", status: 404 |     render "errors/encoding", layout: "error", status: 404 | ||||||
|   end |   end | ||||||
|  |  | ||||||
|  | @ -1,3 +0,0 @@ | ||||||
| %h1 Git Error |  | ||||||
| %hr |  | ||||||
| %p Seems like SSH Key you provided is not a valid SSH key. |  | ||||||
|  | @ -1,202 +1,43 @@ | ||||||
| require 'gitolite' | require_relative 'gitolite_config' | ||||||
| require 'timeout' |  | ||||||
| require 'fileutils' |  | ||||||
| 
 | 
 | ||||||
| # TODO: refactor & cleanup  |  | ||||||
| module Gitlab | module Gitlab | ||||||
|   class Gitolite |   class Gitolite | ||||||
|     class AccessDenied < StandardError; end |     class AccessDenied < StandardError; end | ||||||
|     class InvalidKey < StandardError; end | 
 | ||||||
|  |     def config | ||||||
|  |       @config ||= Gitlab::GitoliteConfig.new | ||||||
|  |     end | ||||||
| 
 | 
 | ||||||
|     def set_key key_id, key_content, projects |     def set_key key_id, key_content, projects | ||||||
|       configure do |c| |       config.apply do |config| | ||||||
|         c.update_keys(key_id, key_content) |         config.write_key(key_id, key_content) | ||||||
|         c.update_projects(projects) |         config.update_projects(projects) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def remove_key key_id, projects |     def remove_key key_id, projects | ||||||
|       configure do |c| |       config.apply do |config| | ||||||
|         c.delete_key(key_id) |         config.rm_key(key_id) | ||||||
|         c.update_projects(projects) |         config.update_projects(projects) | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def update_repository project |     def update_repository project | ||||||
|       configure do |c| |       config.update_project!(project.path, project) | ||||||
|         c.update_project(project.path, project) |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     alias_method :create_repository, :update_repository |  | ||||||
| 
 |  | ||||||
|     def remove_repository project |     def remove_repository project | ||||||
|       configure do |c| |       config.destroy_project!(project) | ||||||
|         c.destroy_project(project) |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def url_to_repo path |     def url_to_repo path | ||||||
|       Gitlab.config.ssh_path + "#{path}.git" |       Gitlab.config.ssh_path + "#{path}.git" | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     def initialize |  | ||||||
|       # create tmp dir |  | ||||||
|       @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}") |  | ||||||
|     end |  | ||||||
| 
 |  | ||||||
|     def enable_automerge |     def enable_automerge | ||||||
|       configure do |git| |       config.admin_all_repo!(project) | ||||||
|         git.admin_all_repo |  | ||||||
|       end |  | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     protected |     alias_method :create_repository, :update_repository | ||||||
| 
 |  | ||||||
|     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 |  | ||||||
|   end |   end | ||||||
| end | end | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  | 
 | ||||||
|  | @ -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 | ||||||
|  | @ -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 | ||||||
|  | @ -17,7 +17,7 @@ module GitoliteStub | ||||||
|     ) |     ) | ||||||
| 
 | 
 | ||||||
|     gitolite_admin = double( |     gitolite_admin = double( | ||||||
|       'Gitolite::GitoliteAdmin',  |       'Gitolite::GitoliteAdmin', | ||||||
|       config: gitolite_config, |       config: gitolite_config, | ||||||
|       save: true, |       save: true, | ||||||
|     ) |     ) | ||||||
|  | @ -27,9 +27,21 @@ module GitoliteStub | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def stub_gitlab_gitolite |   def stub_gitlab_gitolite | ||||||
|     gitlab_gitolite = Gitlab::Gitolite.new |     gitolite_config = double('Gitlab::GitoliteConfig') | ||||||
|     Gitlab::Gitolite.stub(new: gitlab_gitolite) |     gitolite_config.stub( | ||||||
|     gitlab_gitolite.stub(configure: ->() { yield(self) }) |       apply: ->() { yield(self) }, | ||||||
|     gitlab_gitolite.stub(update_keys: true) |       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 | ||||||
| end | end | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue