diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/ci/gitlab_ci_yaml_processor.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/devise_failure.rb | 23 | ||||
-rw-r--r-- | lib/gitlab/exclusive_lease.rb | 41 | ||||
-rw-r--r-- | lib/gitlab/github_import/importer.rb | 11 | ||||
-rw-r--r-- | lib/gitlab/github_import/pull_request_formatter.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/middleware/go.rb | 50 | ||||
-rw-r--r-- | lib/gitlab/project_search_results.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/push_data_builder.rb | 2 | ||||
-rw-r--r-- | lib/gitlab/redis_config.rb | 30 | ||||
-rw-r--r-- | lib/gitlab/search_results.rb | 25 | ||||
-rw-r--r-- | lib/gitlab/snippet_search_results.rb | 10 | ||||
-rw-r--r-- | lib/gitlab/user_access.rb | 2 | ||||
-rw-r--r-- | lib/tasks/cache.rake | 6 | ||||
-rw-r--r-- | lib/tasks/gitlab/web_hook.rake | 14 | ||||
-rw-r--r-- | lib/tasks/spec.rake | 13 |
15 files changed, 197 insertions, 52 deletions
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb index 1a3f662811a..28e074cd289 100644 --- a/lib/ci/gitlab_ci_yaml_processor.rb +++ b/lib/ci/gitlab_ci_yaml_processor.rb @@ -10,7 +10,7 @@ module Ci attr_reader :before_script, :image, :services, :variables, :path, :cache def initialize(config, path = nil) - @config = YAML.safe_load(config, [Symbol]) + @config = YAML.safe_load(config, [Symbol], [], true) @path = path unless @config.is_a? Hash diff --git a/lib/gitlab/devise_failure.rb b/lib/gitlab/devise_failure.rb new file mode 100644 index 00000000000..a78fde9d782 --- /dev/null +++ b/lib/gitlab/devise_failure.rb @@ -0,0 +1,23 @@ +module Gitlab + class DeviseFailure < Devise::FailureApp + protected + + # Override `Devise::FailureApp#request_format` to handle a special case + # + # This tells Devise to handle an unauthenticated `.zip` request as an HTML + # request (i.e., redirect to sign in). + # + # Otherwise, Devise would respond with a 401 Unauthorized with + # `Content-Type: application/zip` and a response body in plaintext, and the + # browser would freak out. + # + # See https://gitlab.com/gitlab-org/gitlab-ce/issues/12944 + def request_format + if request.format == :zip + Mime::Type.lookup_by_extension(:html).ref + else + super + end + end + end +end diff --git a/lib/gitlab/exclusive_lease.rb b/lib/gitlab/exclusive_lease.rb new file mode 100644 index 00000000000..2ef50286b1d --- /dev/null +++ b/lib/gitlab/exclusive_lease.rb @@ -0,0 +1,41 @@ +module Gitlab + # This class implements an 'exclusive lease'. We call it a 'lease' + # because it has a set expiry time. We call it 'exclusive' because only + # one caller may obtain a lease for a given key at a time. The + # implementation is intended to work across GitLab processes and across + # servers. It is a 'cheap' alternative to using SQL queries and updates: + # you do not need to change the SQL schema to start using + # ExclusiveLease. + # + # It is important to choose the timeout wisely. If the timeout is very + # high (1 hour) then the throughput of your operation gets very low (at + # most once an hour). If the timeout is lower than how long your + # operation may take then you cannot count on exclusivity. For example, + # if the timeout is 10 seconds and you do an operation which may take 20 + # seconds then two overlapping operations may hold a lease for the same + # key at the same time. + # + class ExclusiveLease + def initialize(key, timeout:) + @key, @timeout = key, timeout + end + + # Try to obtain the lease. Return true on success, + # false if the lease is already taken. + def try_obtain + # Performing a single SET is atomic + !!redis.set(redis_key, '1', nx: true, ex: @timeout) + end + + private + + def redis + # Maybe someday we want to use a connection pool... + @redis ||= Redis.new(url: Gitlab::RedisConfig.url) + end + + def redis_key + "gitlab:exclusive_lease:#{@key}" + end + end +end diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb index e2a85f29825..172c5441e36 100644 --- a/lib/gitlab/github_import/importer.rb +++ b/lib/gitlab/github_import/importer.rb @@ -45,10 +45,13 @@ module Gitlab direction: :asc).each do |raw_data| pull_request = PullRequestFormatter.new(project, raw_data) - if !pull_request.cross_project? && pull_request.valid? - merge_request = MergeRequest.create!(pull_request.attributes) - import_comments(pull_request.number, merge_request) - import_comments_on_diff(pull_request.number, merge_request) + if pull_request.valid? + merge_request = MergeRequest.new(pull_request.attributes) + + if merge_request.save + import_comments(pull_request.number, merge_request) + import_comments_on_diff(pull_request.number, merge_request) + end end end diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb index f96fed0f5cf..4e507b090e8 100644 --- a/lib/gitlab/github_import/pull_request_formatter.rb +++ b/lib/gitlab/github_import/pull_request_formatter.rb @@ -17,16 +17,12 @@ module Gitlab } end - def cross_project? - source_repo.id != target_repo.id - end - def number raw_data.number end def valid? - source_branch.present? && target_branch.present? + !cross_project? && source_branch.present? && target_branch.present? end private @@ -53,6 +49,10 @@ module Gitlab raw_data.body || "" end + def cross_project? + source_repo.present? && target_repo.present? && source_repo.id != target_repo.id + end + def description formatter.author_line(author) + body end diff --git a/lib/gitlab/middleware/go.rb b/lib/gitlab/middleware/go.rb new file mode 100644 index 00000000000..50b0dd32380 --- /dev/null +++ b/lib/gitlab/middleware/go.rb @@ -0,0 +1,50 @@ +# A dumb middleware that returns a Go HTML document if the go-get=1 query string +# is used irrespective if the namespace/project exists +module Gitlab + module Middleware + class Go + def initialize(app) + @app = app + end + + def call(env) + request = Rack::Request.new(env) + + if go_request?(request) + render_go_doc(request) + else + @app.call(env) + end + end + + private + + def render_go_doc(request) + body = go_body(request) + response = Rack::Response.new(body, 200, { 'Content-Type' => 'text/html' }) + response.finish + end + + def go_request?(request) + request["go-get"].to_i == 1 && request.env["PATH_INFO"].present? + end + + def go_body(request) + base_url = Gitlab.config.gitlab.url + # Go subpackages may be in the form of namespace/project/path1/path2/../pathN + # We can just ignore the paths and leave the namespace/project + path_info = request.env["PATH_INFO"] + path_info.sub!(/^\//, '') + project_path = path_info.split('/').first(2).join('/') + request_url = URI.join(base_url, project_path) + domain_path = strip_url(request_url.to_s) + + "<!DOCTYPE html><html><head><meta content='#{domain_path} git #{request_url}.git' name='go-import'></head></html>\n"; + end + + def strip_url(url) + url.gsub(/\Ahttps?:\/\//, '') + end + end + end +end diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index 70de6a74e76..0607a8b9592 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -2,8 +2,8 @@ module Gitlab class ProjectSearchResults < SearchResults attr_reader :project, :repository_ref - def initialize(project_id, query, repository_ref = nil) - @project = Project.find(project_id) + def initialize(project, query, repository_ref = nil) + @project = project @repository_ref = if repository_ref.present? repository_ref else @@ -73,7 +73,7 @@ module Gitlab end def notes - Note.where(project_id: limit_project_ids).user.search(query).order('updated_at DESC') + project.notes.user.search(query).order('updated_at DESC') end def commits @@ -84,8 +84,8 @@ module Gitlab end end - def limit_project_ids - [project.id] + def project_ids_relation + project end end end diff --git a/lib/gitlab/push_data_builder.rb b/lib/gitlab/push_data_builder.rb index da1c15fef61..97d1edab9c1 100644 --- a/lib/gitlab/push_data_builder.rb +++ b/lib/gitlab/push_data_builder.rb @@ -63,7 +63,7 @@ module Gitlab end # This method provide a sample data generated with - # existing project and commits to test web hooks + # existing project and commits to test webhooks def build_sample(project, user) commits = project.repository.commits(project.default_branch, nil, 3) ref = "#{Gitlab::Git::BRANCH_REF_PREFIX}#{project.default_branch}" diff --git a/lib/gitlab/redis_config.rb b/lib/gitlab/redis_config.rb new file mode 100644 index 00000000000..4949c6db539 --- /dev/null +++ b/lib/gitlab/redis_config.rb @@ -0,0 +1,30 @@ +module Gitlab + class RedisConfig + attr_reader :url + + def self.url + new.url + end + + def self.redis_store_options + url = new.url + redis_config_hash = Redis::Store::Factory.extract_host_options_from_uri(url) + # Redis::Store does not handle Unix sockets well, so let's do it for them + redis_uri = URI.parse(url) + if redis_uri.scheme == 'unix' + redis_config_hash[:path] = redis_uri.path + end + redis_config_hash + end + + def initialize(rails_env=nil) + rails_env ||= Rails.env + config_file = File.expand_path('../../../config/resque.yml', __FILE__) + + @url = "redis://localhost:6379" + if File.exists?(config_file) + @url =YAML.load_file(config_file)[rails_env] + end + end + end +end diff --git a/lib/gitlab/search_results.rb b/lib/gitlab/search_results.rb index 2ab2d4af797..f13528a2eea 100644 --- a/lib/gitlab/search_results.rb +++ b/lib/gitlab/search_results.rb @@ -2,12 +2,12 @@ module Gitlab class SearchResults attr_reader :query - # Limit search results by passed project ids + # Limit search results by passed projects # It allows us to search only for projects user has access to - attr_reader :limit_project_ids + attr_reader :limit_projects - def initialize(limit_project_ids, query) - @limit_project_ids = limit_project_ids || Project.all + def initialize(limit_projects, query) + @limit_projects = limit_projects || Project.all @query = Shellwords.shellescape(query) if query.present? end @@ -27,7 +27,8 @@ module Gitlab end def total_count - @total_count ||= projects_count + issues_count + merge_requests_count + milestones_count + @total_count ||= projects_count + issues_count + merge_requests_count + + milestones_count end def projects_count @@ -53,27 +54,29 @@ module Gitlab private def projects - Project.where(id: limit_project_ids).search(query) + limit_projects.search(query) end def issues - issues = Issue.where(project_id: limit_project_ids) + issues = Issue.where(project_id: project_ids_relation) + if query =~ /#(\d+)\z/ issues = issues.where(iid: $1) else issues = issues.full_search(query) end + issues.order('updated_at DESC') end def milestones - milestones = Milestone.where(project_id: limit_project_ids) + milestones = Milestone.where(project_id: project_ids_relation) milestones = milestones.search(query) milestones.order('updated_at DESC') end def merge_requests - merge_requests = MergeRequest.in_projects(limit_project_ids) + merge_requests = MergeRequest.in_projects(project_ids_relation) if query =~ /[#!](\d+)\z/ merge_requests = merge_requests.where(iid: $1) else @@ -89,5 +92,9 @@ module Gitlab def per_page 20 end + + def project_ids_relation + limit_projects.select(:id).reorder(nil) + end end end diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb index addda95be2b..e0e74ff8359 100644 --- a/lib/gitlab/snippet_search_results.rb +++ b/lib/gitlab/snippet_search_results.rb @@ -2,10 +2,10 @@ module Gitlab class SnippetSearchResults < SearchResults include SnippetsHelper - attr_reader :limit_snippet_ids + attr_reader :limit_snippets - def initialize(limit_snippet_ids, query) - @limit_snippet_ids = limit_snippet_ids + def initialize(limit_snippets, query) + @limit_snippets = limit_snippets @query = query end @@ -35,11 +35,11 @@ module Gitlab private def snippet_titles - Snippet.where(id: limit_snippet_ids).search(query).order('updated_at DESC') + limit_snippets.search(query).order('updated_at DESC') end def snippet_blobs - Snippet.where(id: limit_snippet_ids).search_code(query).order('updated_at DESC') + limit_snippets.search_code(query).order('updated_at DESC') end def default_scope diff --git a/lib/gitlab/user_access.rb b/lib/gitlab/user_access.rb index 4885baf9526..d1b42c1f9b9 100644 --- a/lib/gitlab/user_access.rb +++ b/lib/gitlab/user_access.rb @@ -3,7 +3,7 @@ module Gitlab def self.allowed?(user) return false if user.blocked? - if user.requires_ldap_check? + if user.requires_ldap_check? && user.try_obtain_ldap_lease return false unless Gitlab::LDAP::Access.allowed?(user) end diff --git a/lib/tasks/cache.rake b/lib/tasks/cache.rake index f221afcf73a..51e746ef923 100644 --- a/lib/tasks/cache.rake +++ b/lib/tasks/cache.rake @@ -4,16 +4,16 @@ namespace :cache do desc "GitLab | Clear redis cache" task :clear => :environment do - redis_store = Rails.cache.instance_variable_get(:@data) + redis = Redis.new(url: Gitlab::RedisConfig.url) cursor = REDIS_SCAN_START_STOP loop do - cursor, keys = redis_store.scan( + cursor, keys = redis.scan( cursor, match: "#{Gitlab::REDIS_CACHE_NAMESPACE}*", count: CLEAR_BATCH_SIZE ) - redis_store.del(*keys) if keys.any? + redis.del(*keys) if keys.any? break if cursor == REDIS_SCAN_START_STOP end diff --git a/lib/tasks/gitlab/web_hook.rake b/lib/tasks/gitlab/web_hook.rake index 76e443e55ee..cc0f668474e 100644 --- a/lib/tasks/gitlab/web_hook.rake +++ b/lib/tasks/gitlab/web_hook.rake @@ -1,13 +1,13 @@ namespace :gitlab do namespace :web_hook do - desc "GitLab | Adds a web hook to the projects" + desc "GitLab | Adds a webhook to the projects" task :add => :environment do web_hook_url = ENV['URL'] namespace_path = ENV['NAMESPACE'] projects = find_projects(namespace_path) - puts "Adding web hook '#{web_hook_url}' to:" + puts "Adding webhook '#{web_hook_url}' to:" projects.find_each(batch_size: 1000) do |project| print "- #{project.name} ... " web_hook = project.hooks.new(url: web_hook_url) @@ -20,7 +20,7 @@ namespace :gitlab do end end - desc "GitLab | Remove a web hook from the projects" + desc "GitLab | Remove a webhook from the projects" task :rm => :environment do web_hook_url = ENV['URL'] namespace_path = ENV['NAMESPACE'] @@ -28,12 +28,12 @@ namespace :gitlab do projects = find_projects(namespace_path) projects_ids = projects.pluck(:id) - puts "Removing web hooks with the url '#{web_hook_url}' ... " + puts "Removing webhooks with the url '#{web_hook_url}' ... " count = WebHook.where(url: web_hook_url, project_id: projects_ids, type: 'ProjectHook').delete_all - puts "#{count} web hooks were removed." + puts "#{count} webhooks were removed." end - desc "GitLab | List web hooks" + desc "GitLab | List webhooks" task :list => :environment do namespace_path = ENV['NAMESPACE'] @@ -43,7 +43,7 @@ namespace :gitlab do puts "#{hook.project.name.truncate(20).ljust(20)} -> #{hook.url}" end - puts "\n#{web_hooks.size} web hooks found." + puts "\n#{web_hooks.size} webhooks found." end end diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 0985ef3a669..2cf7a25a0fd 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -46,20 +46,11 @@ namespace :spec do run_commands(cmds) end - desc 'GitLab | Rspec | Run benchmark specs' - task :benchmark do - cmds = [ - %W(rake gitlab:setup), - %W(rspec spec --tag @benchmark) - ] - run_commands(cmds) - end - desc 'GitLab | Rspec | Run other specs' task :other do cmds = [ %W(rake gitlab:setup), - %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services --tag ~@benchmark) + %W(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services) ] run_commands(cmds) end @@ -69,7 +60,7 @@ desc "GitLab | Run specs" task :spec do cmds = [ %W(rake gitlab:setup), - %W(rspec spec --tag ~@benchmark), + %W(rspec spec), ] run_commands(cmds) end |