Commit 79021e674bab1d34609228343fbf3403d9bd9bc6

Authored by Dmitriy Zaporozhets
1 parent 137594dd

Split gitolite backend. Use gitolite_config methods moved to separate class

app/controllers/application_controller.rb
@@ -14,10 +14,6 @@ class ApplicationController < ActionController::Base @@ -14,10 +14,6 @@ class ApplicationController < ActionController::Base
14 render "errors/gitolite", layout: "error" 14 render "errors/gitolite", layout: "error"
15 end 15 end
16 16
17 - rescue_from Gitlab::Gitolite::InvalidKey do |exception|  
18 - render "errors/invalid_ssh_key", layout: "error"  
19 - end  
20 -  
21 rescue_from Encoding::CompatibilityError do |exception| 17 rescue_from Encoding::CompatibilityError do |exception|
22 render "errors/encoding", layout: "error", status: 404 18 render "errors/encoding", layout: "error", status: 404
23 end 19 end
app/views/errors/invalid_ssh_key.html.haml
@@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
1 -%h1 Git Error  
2 -%hr  
3 -%p Seems like SSH Key you provided is not a valid SSH key.  
lib/gitlab/backend/gitolite.rb
1 -require 'gitolite'  
2 -require 'timeout'  
3 -require 'fileutils' 1 +require_relative 'gitolite_config'
4 2
5 -# TODO: refactor & cleanup  
6 module Gitlab 3 module Gitlab
7 class Gitolite 4 class Gitolite
8 class AccessDenied < StandardError; end 5 class AccessDenied < StandardError; end
9 - class InvalidKey < StandardError; end 6 +
  7 + def config
  8 + @config ||= Gitlab::GitoliteConfig.new
  9 + end
10 10
11 def set_key key_id, key_content, projects 11 def set_key key_id, key_content, projects
12 - configure do |c|  
13 - c.update_keys(key_id, key_content)  
14 - c.update_projects(projects) 12 + config.apply do |config|
  13 + config.write_key(key_id, key_content)
  14 + config.update_projects(projects)
15 end 15 end
16 end 16 end
17 17
18 def remove_key key_id, projects 18 def remove_key key_id, projects
19 - configure do |c|  
20 - c.delete_key(key_id)  
21 - c.update_projects(projects) 19 + config.apply do |config|
  20 + config.rm_key(key_id)
  21 + config.update_projects(projects)
22 end 22 end
23 end 23 end
24 24
25 def update_repository project 25 def update_repository project
26 - configure do |c|  
27 - c.update_project(project.path, project)  
28 - end 26 + config.update_project!(project.path, project)
29 end 27 end
30 28
31 - alias_method :create_repository, :update_repository  
32 -  
33 def remove_repository project 29 def remove_repository project
34 - configure do |c|  
35 - c.destroy_project(project)  
36 - end 30 + config.destroy_project!(project)
37 end 31 end
38 32
39 def url_to_repo path 33 def url_to_repo path
40 Gitlab.config.ssh_path + "#{path}.git" 34 Gitlab.config.ssh_path + "#{path}.git"
41 end 35 end
42 36
43 - def initialize  
44 - # create tmp dir  
45 - @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")  
46 - end  
47 -  
48 def enable_automerge 37 def enable_automerge
49 - configure do |git|  
50 - git.admin_all_repo  
51 - end  
52 - end  
53 -  
54 - protected  
55 -  
56 - def destroy_project(project)  
57 - FileUtils.rm_rf(project.path_to_repo)  
58 -  
59 - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))  
60 - conf = ga_repo.config  
61 - conf.rm_repo(project.path)  
62 - ga_repo.save  
63 - end  
64 -  
65 - #update or create  
66 - def update_keys(user, key)  
67 - File.open(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"), 'w') {|f| f.write(key.gsub(/\n/,'')) }  
68 - end  
69 -  
70 - def delete_key(user)  
71 - File.unlink(File.join(@local_dir, 'gitolite/keydir',"#{user}.pub"))  
72 - `cd #{File.join(@local_dir,'gitolite')} ; git rm keydir/#{user}.pub`  
73 - end  
74 -  
75 - # update or create  
76 - def update_project(repo_name, project)  
77 - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))  
78 - conf = ga_repo.config  
79 - repo = update_project_config(project, conf)  
80 - conf.add_repo(repo, true)  
81 -  
82 - ga_repo.save  
83 - end  
84 -  
85 - # Updates many projects and uses project.path as the repo path  
86 - # An order of magnitude faster than update_project  
87 - def update_projects(projects)  
88 - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))  
89 - conf = ga_repo.config  
90 -  
91 - projects.each do |project|  
92 - repo = update_project_config(project, conf)  
93 - conf.add_repo(repo, true)  
94 - end  
95 -  
96 - ga_repo.save  
97 - end  
98 -  
99 - def update_project_config(project, conf)  
100 - repo_name = project.path  
101 -  
102 - repo = if conf.has_repo?(repo_name)  
103 - conf.get_repo(repo_name)  
104 - else  
105 - ::Gitolite::Config::Repo.new(repo_name)  
106 - end  
107 -  
108 - name_readers = project.repository_readers  
109 - name_writers = project.repository_writers  
110 - name_masters = project.repository_masters  
111 -  
112 - pr_br = project.protected_branches.map(&:name).join("$ ")  
113 -  
114 - repo.clean_permissions  
115 -  
116 - # Deny access to protected branches for writers  
117 - unless name_writers.blank? || pr_br.blank?  
118 - repo.add_permission("-", pr_br.strip + "$ ", name_writers)  
119 - end  
120 -  
121 - # Add read permissions  
122 - repo.add_permission("R", "", name_readers) unless name_readers.blank?  
123 -  
124 - # Add write permissions  
125 - repo.add_permission("RW+", "", name_writers) unless name_writers.blank?  
126 - repo.add_permission("RW+", "", name_masters) unless name_masters.blank?  
127 -  
128 - repo  
129 - end  
130 -  
131 - def admin_all_repo  
132 - ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(@local_dir,'gitolite'))  
133 - conf = ga_repo.config  
134 - owner_name = ""  
135 -  
136 - # Read gitolite-admin user  
137 - #  
138 - begin  
139 - repo = conf.get_repo("gitolite-admin")  
140 - owner_name = repo.permissions[0]["RW+"][""][0]  
141 - raise StandardError if owner_name.blank?  
142 - rescue => ex  
143 - puts "Can't determine gitolite-admin owner".red  
144 - raise StandardError  
145 - end  
146 -  
147 - # @ALL repos premission for gitolite owner  
148 - repo_name = "@all"  
149 - repo = if conf.has_repo?(repo_name)  
150 - conf.get_repo(repo_name)  
151 - else  
152 - ::Gitolite::Config::Repo.new(repo_name)  
153 - end  
154 -  
155 - repo.add_permission("RW+", "", owner_name)  
156 - conf.add_repo(repo, true)  
157 - ga_repo.save 38 + config.admin_all_repo!(project)
158 end 39 end
159 40
160 - private  
161 -  
162 - def pull  
163 - # create tmp dir  
164 - @local_dir = File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")  
165 - Dir.mkdir @local_dir  
166 -  
167 - `git clone #{Gitlab.config.gitolite_admin_uri} #{@local_dir}/gitolite`  
168 - end  
169 -  
170 - def push  
171 - Dir.chdir(File.join(@local_dir, "gitolite"))  
172 - `git add -A`  
173 - `git commit -am "GitLab"`  
174 - `git push`  
175 - Dir.chdir(Rails.root)  
176 -  
177 - FileUtils.rm_rf(@local_dir)  
178 - end  
179 -  
180 - def configure  
181 - Timeout::timeout(30) do  
182 - File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|  
183 - begin  
184 - f.flock(File::LOCK_EX)  
185 - pull  
186 - yield(self)  
187 - push  
188 - ensure  
189 - f.flock(File::LOCK_UN)  
190 - end  
191 - end  
192 - end  
193 - rescue Exception => ex  
194 - if ex.message =~ /is not a valid SSH key string/  
195 - raise Gitolite::InvalidKey.new("ssh key is not valid")  
196 - else  
197 - Gitlab::Logger.error(ex.message)  
198 - raise Gitolite::AccessDenied.new("gitolite timeout")  
199 - end  
200 - end 41 + alias_method :create_repository, :update_repository
201 end 42 end
202 end 43 end
lib/gitlab/backend/gitolite_config.rb 0 → 100644
@@ -0,0 +1,168 @@ @@ -0,0 +1,168 @@
  1 +require 'gitolite'
  2 +require 'timeout'
  3 +require 'fileutils'
  4 +
  5 +module Gitlab
  6 + class GitoliteConfig
  7 + def config_tmp_dir
  8 + @config_tmp_dir ||= File.join(Rails.root, 'tmp',"gitlabhq-gitolite-#{Time.now.to_i}")
  9 + end
  10 +
  11 + def apply
  12 + Timeout::timeout(30) do
  13 + File.open(File.join(Rails.root, 'tmp', "gitlabhq-gitolite.lock"), "w+") do |f|
  14 + begin
  15 + f.flock(File::LOCK_EX)
  16 + pull
  17 + yield(self)
  18 + push
  19 + ensure
  20 + f.flock(File::LOCK_UN)
  21 + end
  22 + end
  23 + end
  24 + rescue Exception => ex
  25 + Gitlab::Logger.error(ex.message)
  26 + raise Gitolite::AccessDenied.new("gitolite timeout")
  27 + end
  28 +
  29 + def destroy_project(project)
  30 + FileUtils.rm_rf(project.path_to_repo)
  31 +
  32 + ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
  33 + conf = ga_repo.config
  34 + conf.rm_repo(project.path)
  35 + ga_repo.save
  36 + end
  37 +
  38 + def destroy_project!(project)
  39 + apply do |config|
  40 + config.destroy_project(project)
  41 + end
  42 + end
  43 +
  44 + def write_key(id, key)
  45 + File.open(File.join(config_tmp_dir, 'gitolite/keydir',"#{id}.pub"), 'w') do |f|
  46 + f.write(key.gsub(/\n/,''))
  47 + end
  48 + end
  49 +
  50 + def rm_key(user)
  51 + File.unlink(File.join(config_tmp_dir, 'gitolite/keydir',"#{user}.pub"))
  52 + `cd #{File.join(config_tmp_dir,'gitolite')} ; git rm keydir/#{user}.pub`
  53 + end
  54 +
  55 + # update or create
  56 + def update_project(repo_name, project)
  57 + ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
  58 + conf = ga_repo.config
  59 + repo = update_project_config(project, conf)
  60 + conf.add_repo(repo, true)
  61 +
  62 + ga_repo.save
  63 + end
  64 +
  65 + def update_project!(repo_name, project)
  66 + apply do |config|
  67 + config.update_project(repo_name, project)
  68 + end
  69 + end
  70 +
  71 + # Updates many projects and uses project.path as the repo path
  72 + # An order of magnitude faster than update_project
  73 + def update_projects(projects)
  74 + ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
  75 + conf = ga_repo.config
  76 +
  77 + projects.each do |project|
  78 + repo = update_project_config(project, conf)
  79 + conf.add_repo(repo, true)
  80 + end
  81 +
  82 + ga_repo.save
  83 + end
  84 +
  85 + def update_project_config(project, conf)
  86 + repo_name = project.path
  87 +
  88 + repo = if conf.has_repo?(repo_name)
  89 + conf.get_repo(repo_name)
  90 + else
  91 + ::Gitolite::Config::Repo.new(repo_name)
  92 + end
  93 +
  94 + name_readers = project.repository_readers
  95 + name_writers = project.repository_writers
  96 + name_masters = project.repository_masters
  97 +
  98 + pr_br = project.protected_branches.map(&:name).join("$ ")
  99 +
  100 + repo.clean_permissions
  101 +
  102 + # Deny access to protected branches for writers
  103 + unless name_writers.blank? || pr_br.blank?
  104 + repo.add_permission("-", pr_br.strip + "$ ", name_writers)
  105 + end
  106 +
  107 + # Add read permissions
  108 + repo.add_permission("R", "", name_readers) unless name_readers.blank?
  109 +
  110 + # Add write permissions
  111 + repo.add_permission("RW+", "", name_writers) unless name_writers.blank?
  112 + repo.add_permission("RW+", "", name_masters) unless name_masters.blank?
  113 +
  114 + repo
  115 + end
  116 +
  117 + def admin_all_repo
  118 + ga_repo = ::Gitolite::GitoliteAdmin.new(File.join(config_tmp_dir,'gitolite'))
  119 + conf = ga_repo.config
  120 + owner_name = ""
  121 +
  122 + # Read gitolite-admin user
  123 + #
  124 + begin
  125 + repo = conf.get_repo("gitolite-admin")
  126 + owner_name = repo.permissions[0]["RW+"][""][0]
  127 + raise StandardError if owner_name.blank?
  128 + rescue => ex
  129 + puts "Can't determine gitolite-admin owner".red
  130 + raise StandardError
  131 + end
  132 +
  133 + # @ALL repos premission for gitolite owner
  134 + repo_name = "@all"
  135 + repo = if conf.has_repo?(repo_name)
  136 + conf.get_repo(repo_name)
  137 + else
  138 + ::Gitolite::Config::Repo.new(repo_name)
  139 + end
  140 +
  141 + repo.add_permission("RW+", "", owner_name)
  142 + conf.add_repo(repo, true)
  143 + ga_repo.save
  144 + end
  145 +
  146 + def admin_all_repo!
  147 + apply { |config| config.admin_all_repo }
  148 + end
  149 +
  150 + private
  151 +
  152 + def pull
  153 + Dir.mkdir config_tmp_dir
  154 + `git clone #{Gitlab.config.gitolite_admin_uri} #{config_tmp_dir}/gitolite`
  155 + end
  156 +
  157 + def push
  158 + Dir.chdir(File.join(config_tmp_dir, "gitolite"))
  159 + `git add -A`
  160 + `git commit -am "GitLab"`
  161 + `git push`
  162 + Dir.chdir(Rails.root)
  163 +
  164 + FileUtils.rm_rf(config_tmp_dir)
  165 + end
  166 + end
  167 +end
  168 +
spec/lib/gitolite_config_spec.rb 0 → 100644
@@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::GitoliteConfig do
  4 + let(:gitolite) { Gitlab::GitoliteConfig.new }
  5 +
  6 + it { should respond_to :write_key }
  7 + it { should respond_to :rm_key }
  8 + it { should respond_to :update_project }
  9 + it { should respond_to :update_project! }
  10 + it { should respond_to :update_projects }
  11 + it { should respond_to :destroy_project }
  12 + it { should respond_to :destroy_project! }
  13 + it { should respond_to :apply }
  14 + it { should respond_to :admin_all_repo }
  15 + it { should respond_to :admin_all_repo! }
  16 +end
spec/lib/gitolite_spec.rb 0 → 100644
@@ -0,0 +1,25 @@ @@ -0,0 +1,25 @@
  1 +require 'spec_helper'
  2 +
  3 +describe Gitlab::Gitolite do
  4 + let(:project) { double('Project', path: 'diaspora') }
  5 + let(:gitolite_config) { double('Gitlab::GitoliteConfig') }
  6 + let(:gitolite) { Gitlab::Gitolite.new }
  7 +
  8 + before do
  9 + gitolite.stub(config: gitolite_config)
  10 + end
  11 +
  12 + it { should respond_to :set_key }
  13 + it { should respond_to :remove_key }
  14 +
  15 + it { should respond_to :update_repository }
  16 + it { should respond_to :create_repository }
  17 + it { should respond_to :remove_repository }
  18 +
  19 + it { gitolite.url_to_repo('diaspora').should == Gitlab.config.ssh_path + "diaspora.git" }
  20 +
  21 + it "should call config update" do
  22 + gitolite_config.should_receive(:update_project!)
  23 + gitolite.update_repository project
  24 + end
  25 +end
spec/support/gitolite_stub.rb
@@ -17,7 +17,7 @@ module GitoliteStub @@ -17,7 +17,7 @@ module GitoliteStub
17 ) 17 )
18 18
19 gitolite_admin = double( 19 gitolite_admin = double(
20 - 'Gitolite::GitoliteAdmin', 20 + 'Gitolite::GitoliteAdmin',
21 config: gitolite_config, 21 config: gitolite_config,
22 save: true, 22 save: true,
23 ) 23 )
@@ -27,9 +27,21 @@ module GitoliteStub @@ -27,9 +27,21 @@ module GitoliteStub
27 end 27 end
28 28
29 def stub_gitlab_gitolite 29 def stub_gitlab_gitolite
30 - gitlab_gitolite = Gitlab::Gitolite.new  
31 - Gitlab::Gitolite.stub(new: gitlab_gitolite)  
32 - gitlab_gitolite.stub(configure: ->() { yield(self) })  
33 - gitlab_gitolite.stub(update_keys: true) 30 + gitolite_config = double('Gitlab::GitoliteConfig')
  31 + gitolite_config.stub(
  32 + apply: ->() { yield(self) },
  33 + write_key: true,
  34 + rm_key: true,
  35 + update_projects: true,
  36 + update_project: true,
  37 + update_project!: true,
  38 + destroy_project: true,
  39 + destroy_project!: true,
  40 + admin_all_repo: true,
  41 + admin_all_repo!: true,
  42 +
  43 + )
  44 +
  45 + Gitlab::GitoliteConfig.stub(new: gitolite_config)
34 end 46 end
35 end 47 end