Commit 37be2007 authored by Robert Speicher's avatar Robert Speicher

Merge branch 'workhorse-helpers' into 'master'

Add workhorse controller and API helpers

Adds `send_git_blob` and `send_git_archive` controller and API helpers to reduce duplication and make Workhorse easier for a developer to work with.

See merge request !4486
parents d6de8169 701e2df7
...@@ -55,6 +55,7 @@ v 8.9.0 (unreleased) ...@@ -55,6 +55,7 @@ v 8.9.0 (unreleased)
- Remove duplicated notification settings - Remove duplicated notification settings
- Put project Files and Commits tabs under Code tab - Put project Files and Commits tabs under Code tab
- Replace Colorize with Rainbow for coloring console output in Rake tasks. - Replace Colorize with Rainbow for coloring console output in Rake tasks.
- Add workhorse controller and API helpers
- An indicator is now displayed at the top of the comment field for confidential issues. - An indicator is now displayed at the top of the comment field for confidential issues.
- RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented - RepositoryCheck::SingleRepositoryWorker public and private methods are now instrumented
- Improve issuables APIs performance when accessing notes !4471 - Improve issuables APIs performance when accessing notes !4471
......
...@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base ...@@ -6,6 +6,7 @@ class ApplicationController < ActionController::Base
include Gitlab::GonHelper include Gitlab::GonHelper
include GitlabRoutingHelper include GitlabRoutingHelper
include PageLayoutHelper include PageLayoutHelper
include WorkhorseHelper
before_action :authenticate_user_from_token! before_action :authenticate_user_from_token!
before_action :authenticate_user! before_action :authenticate_user!
......
...@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController ...@@ -10,10 +10,7 @@ class Projects::AvatarsController < Projects::ApplicationController
return if cached_blob? return if cached_blob?
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) send_git_blob @repository, @blob
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type
else else
render_404 render_404
end end
......
...@@ -61,12 +61,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -61,12 +61,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
format.json { render json: @merge_request } format.json { render json: @merge_request }
format.patch { render text: @merge_request.to_patch } format.patch { render text: @merge_request.to_patch }
format.diff do format.diff do
headers.store(*Gitlab::Workhorse.send_git_diff(@project.repository, return render_404 unless @merge_request.diff_refs
@merge_request.diff_base_commit.id,
@merge_request.last_commit.id))
headers['Content-Disposition'] = 'inline'
head :ok send_git_diff @project.repository, @merge_request.diff_refs
end end
end end
end end
......
...@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController ...@@ -18,10 +18,7 @@ class Projects::RawController < Projects::ApplicationController
if @blob.lfs_pointer? if @blob.lfs_pointer?
send_lfs_object send_lfs_object
else else
headers.store(*Gitlab::Workhorse.send_git_blob(@repository, @blob)) send_git_blob @repository, @blob
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(@blob)
head :ok # 'render nothing: true' messes up the Content-Type
end end
else else
render_404 render_404
......
...@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController ...@@ -11,8 +11,7 @@ class Projects::RepositoriesController < Projects::ApplicationController
end end
def archive def archive
headers.store(*Gitlab::Workhorse.send_git_archive(@project, params[:ref], params[:format])) send_git_archive @repository, ref: params[:ref], format: params[:format]
head :ok
rescue => ex rescue => ex
logger.error("#{self.class.name}: #{ex}") logger.error("#{self.class.name}: #{ex}")
return git_not_found! return git_not_found!
......
# Helpers to send Git blobs, diffs or archives through Workhorse.
# Workhorse will also serve files when using `send_file`.
module WorkhorseHelper
# Send a Git blob through Workhorse
def send_git_blob(repository, blob)
headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob))
headers['Content-Disposition'] = 'inline'
headers['Content-Type'] = safe_content_type(blob)
head :ok # 'render nothing: true' messes up the Content-Type
end
# Send a Git diff through Workhorse
def send_git_diff(repository, diff_refs)
headers.store(*Gitlab::Workhorse.send_git_diff(repository, diff_refs))
headers['Content-Disposition'] = 'inline'
head :ok
end
# Archive a Git repository and send it through Workhorse
def send_git_archive(repository, ref:, format:)
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
head :ok
end
end
...@@ -408,5 +408,15 @@ module API ...@@ -408,5 +408,15 @@ module API
error!(errors[:access_level], 422) if errors[:access_level].any? error!(errors[:access_level], 422) if errors[:access_level].any?
not_found!(errors) not_found!(errors)
end end
def send_git_blob(repository, blob)
env['api.format'] = :txt
content_type 'text/plain'
header(*Gitlab::Workhorse.send_git_blob(repository, blob))
end
def send_git_archive(repository, ref:, format:)
header(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
end
end end
end end
...@@ -56,8 +56,7 @@ module API ...@@ -56,8 +56,7 @@ module API
blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath]) blob = Gitlab::Git::Blob.find(repo, commit.id, params[:filepath])
not_found! "File" unless blob not_found! "File" unless blob
content_type 'text/plain' send_git_blob repo, blob
header(*Gitlab::Workhorse.send_git_blob(repo, blob))
end end
# Get a raw blob contents by blob sha # Get a raw blob contents by blob sha
...@@ -80,10 +79,7 @@ module API ...@@ -80,10 +79,7 @@ module API
not_found! 'Blob' unless blob not_found! 'Blob' unless blob
env['api.format'] = :txt send_git_blob repo, blob
content_type blob.mime_type
header(*Gitlab::Workhorse.send_git_blob(repo, blob))
end end
# Get a an archive of the repository # Get a an archive of the repository
...@@ -98,7 +94,7 @@ module API ...@@ -98,7 +94,7 @@ module API
authorize! :download_code, user_project authorize! :download_code, user_project
begin begin
header(*Gitlab::Workhorse.send_git_archive(user_project, params[:sha], params[:format])) send_git_archive user_project.repository, ref: params[:sha], format: params[:format]
rescue rescue
not_found!('File') not_found!('File')
end end
......
...@@ -21,27 +21,29 @@ module Gitlab ...@@ -21,27 +21,29 @@ module Gitlab
[ [
SEND_DATA_HEADER, SEND_DATA_HEADER,
"git-blob:#{encode(params)}", "git-blob:#{encode(params)}"
] ]
end end
def send_git_archive(project, ref, format) def send_git_archive(repository, ref:, format:)
format ||= 'tar.gz' format ||= 'tar.gz'
format.downcase! format.downcase!
params = project.repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format) params = repository.archive_metadata(ref, Gitlab.config.gitlab.repository_downloads_path, format)
raise "Repository or ref not found" if params.empty? raise "Repository or ref not found" if params.empty?
[ [
SEND_DATA_HEADER, SEND_DATA_HEADER,
"git-archive:#{encode(params)}", "git-archive:#{encode(params)}"
] ]
end end
def send_git_diff(repository, from, to) def send_git_diff(repository, diff_refs)
from, to = diff_refs
params = { params = {
'RepoPath' => repository.path_to_repo, 'RepoPath' => repository.path_to_repo,
'ShaFrom' => from, 'ShaFrom' => from.sha,
'ShaTo' => to 'ShaTo' => to.sha
} }
[ [
......
...@@ -91,7 +91,7 @@ describe Projects::MergeRequestsController do ...@@ -91,7 +91,7 @@ describe Projects::MergeRequestsController do
id: merge_request.iid, id: merge_request.iid,
format: :diff) format: :diff)
expect(response.headers['Gitlab-Workhorse-Send-Data']).to start_with("git-diff:") expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-diff:")
end end
end end
......
...@@ -17,6 +17,7 @@ describe Projects::RawController do ...@@ -17,6 +17,7 @@ describe Projects::RawController do
expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8') expect(response.header['Content-Type']).to eq('text/plain; charset=utf-8')
expect(response.header['Content-Disposition']). expect(response.header['Content-Disposition']).
to eq("inline") to eq("inline")
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
end end
end end
...@@ -31,6 +32,7 @@ describe Projects::RawController do ...@@ -31,6 +32,7 @@ describe Projects::RawController do
expect(response.status).to eq(200) expect(response.status).to eq(200)
expect(response.header['Content-Type']).to eq('image/jpeg') expect(response.header['Content-Type']).to eq('image/jpeg')
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-blob:")
end end
end end
......
...@@ -20,10 +20,11 @@ describe Projects::RepositoriesController do ...@@ -20,10 +20,11 @@ describe Projects::RepositoriesController do
project.team << [user, :developer] project.team << [user, :developer]
sign_in(user) sign_in(user)
end end
it "uses Gitlab::Workhorse" do
expect(Gitlab::Workhorse).to receive(:send_git_archive).with(project, "master", "zip")
it "uses Gitlab::Workhorse" do
get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip" get :archive, namespace_id: project.namespace.path, project_id: project.path, ref: "master", format: "zip"
expect(response.header[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("git-archive:")
end end
context "when the service raises an error" do context "when the service raises an error" do
......
...@@ -11,7 +11,7 @@ describe Gitlab::Workhorse, lib: true do ...@@ -11,7 +11,7 @@ describe Gitlab::Workhorse, lib: true do
end end
it "raises an error" do it "raises an error" do
expect { subject.send_git_archive(project, "master", "zip") }.to raise_error(RuntimeError) expect { subject.send_git_archive(project.repository, ref: "master", format: "zip") }.to raise_error(RuntimeError)
end end
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment