Commit 0b429028 authored by Stan Hu's avatar Stan Hu

Merge branch 'ce-to-ee-2018-04-27' into 'master'

CE upstream - 2018-04-27 18:24 UTC

Closes gitlab-development-kit#342, gitlab-ce#45045, and gitaly#314

See merge request gitlab-org/gitlab-ee!5504
parents 26ec59a1 2c910a71
...@@ -73,3 +73,4 @@ eslint-report.html ...@@ -73,3 +73,4 @@ eslint-report.html
/locale/**/*.time_stamp /locale/**/*.time_stamp
/.rspec /.rspec
/plugins/* /plugins/*
/.gitlab_pages_secret
...@@ -36,5 +36,6 @@ ...@@ -36,5 +36,6 @@
= form.text_field :domain, class: 'form-control', placeholder: 'domain.com' = form.text_field :domain, class: 'form-control', placeholder: 'domain.com'
.help-block .help-block
= s_('CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.') = s_('CICD|You need to specify a domain if you want to use Auto Review Apps and Auto Deploy stages.')
= link_to icon('question-circle'), help_page_path('topics/autodevops/index.md', anchor: 'auto-devops-base-domain'), target: '_blank'
= f.submit 'Save changes', class: "btn btn-success prepend-top-15" = f.submit 'Save changes', class: "btn btn-success prepend-top-15"
---
title: Repository#exists? is always executed through Gitaly
merge_request:
author:
type: performance
...@@ -184,18 +184,18 @@ production: &base ...@@ -184,18 +184,18 @@ production: &base
# base_dir: uploads/-/system # base_dir: uploads/-/system
object_store: object_store:
enabled: false enabled: false
# remote_directory: uploads # Bucket name remote_directory: uploads # Bucket name
# direct_upload: false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false) # direct_upload: false # Use Object Storage directly for uploads instead of background uploads if enabled (Default: false)
# background_upload: false # Temporary option to limit automatic upload (Default: true) # background_upload: false # Temporary option to limit automatic upload (Default: true)
# proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage # proxy_download: false # Passthrough all downloads via GitLab instead of using Redirects to Object Storage
connection: connection:
provider: AWS provider: AWS
aws_access_key_id: AWS_ACCESS_KEY_ID aws_access_key_id: AWS_ACCESS_KEY_ID
aws_secret_access_key: AWS_SECRET_ACCESS_KEY aws_secret_access_key: AWS_SECRET_ACCESS_KEY
region: us-east-1 region: us-east-1
# host: 'localhost' # default: s3.amazonaws.com # host: 'localhost' # default: s3.amazonaws.com
# endpoint: 'http://127.0.0.1:9000' # default: nil # endpoint: 'http://127.0.0.1:9000' # default: nil
# path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object' # path_style: true # Use 'host/bucket_name/object' instead of 'bucket_name.host/object'
## GitLab Pages ## GitLab Pages
pages: pages:
...@@ -212,6 +212,8 @@ production: &base ...@@ -212,6 +212,8 @@ production: &base
artifacts_server: true artifacts_server: true
# external_http: ["1.1.1.1:80", "[2001::1]:80"] # If defined, enables custom domain support in GitLab Pages # external_http: ["1.1.1.1:80", "[2001::1]:80"] # If defined, enables custom domain support in GitLab Pages
# external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages # external_https: ["1.1.1.1:443", "[2001::1]:443"] # If defined, enables custom domain and certificate support in GitLab Pages
admin:
address: unix:/home/git/gitlab/tmp/sockets/private/pages-admin.socket # TCP connections are supported too (e.g. tcp://host:port)
## Mattermost ## Mattermost
## For enabling Add to Mattermost button ## For enabling Add to Mattermost button
......
...@@ -232,6 +232,9 @@ Settings.pages['external_http'] ||= false unless Settings.pages['external_ht ...@@ -232,6 +232,9 @@ Settings.pages['external_http'] ||= false unless Settings.pages['external_ht
Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present? Settings.pages['external_https'] ||= false unless Settings.pages['external_https'].present?
Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pages['artifacts_server'].nil? Settings.pages['artifacts_server'] ||= Settings.pages['enabled'] if Settings.pages['artifacts_server'].nil?
Settings.pages['admin'] ||= Settingslogic.new({})
Settings.pages.admin['certificate'] ||= ''
# #
# Geo # Geo
# #
......
Gitlab::PagesClient.read_or_create_token
Gitlab::PagesClient.load_certificate
...@@ -135,6 +135,11 @@ and `1.2.3.4` is the IP address of your load balancer; generally NGINX ...@@ -135,6 +135,11 @@ and `1.2.3.4` is the IP address of your load balancer; generally NGINX
([see prerequisites](#prerequisites)). How to set up the DNS record is beyond ([see prerequisites](#prerequisites)). How to set up the DNS record is beyond
the scope of this document; you should check with your DNS provider. the scope of this document; you should check with your DNS provider.
Alternatively you can use free public services like [xip.io](http://xip.io) or
[nip.io](http://nip.io) which provide automatic wildcard DNS without any
configuration. Just set the Auto DevOps base domain to `1.2.3.4.xip.io` or
`1.2.3.4.nip.io`.
Once set up, all requests will hit the load balancer, which in turn will route Once set up, all requests will hit the load balancer, which in turn will route
them to the Kubernetes pods that run your application(s). them to the Kubernetes pods that run your application(s).
......
...@@ -19,6 +19,9 @@ describe Projects::UpdateRepositoryStorageService do ...@@ -19,6 +19,9 @@ describe Projects::UpdateRepositoryStorageService do
stub_storage_settings(storages) stub_storage_settings(storages)
allow(Time).to receive(:now).and_return(time) allow(Time).to receive(:now).and_return(time)
# This will force the creation of the repository and bypass Gitaly
allow_any_instance_of(Gitlab::Git::Repository).to receive(:exists?).and_return(false)
end end
after do after do
...@@ -64,7 +67,9 @@ describe Projects::UpdateRepositoryStorageService do ...@@ -64,7 +67,9 @@ describe Projects::UpdateRepositoryStorageService do
let(:wiki_repository_double) { double(:repository) } let(:wiki_repository_double) { double(:repository) }
before do before do
allow_any_instance_of(Gitlab::Git::Repository).to receive(:exists?).and_return(false)
project.create_wiki project.create_wiki
allow_any_instance_of(Gitlab::Git::Repository).to receive(:exists?).and_return(true)
# Default stub for non-specified params # Default stub for non-specified params
allow(Gitlab::Git::Repository).to receive(:new).and_call_original allow(Gitlab::Git::Repository).to receive(:new).and_call_original
......
...@@ -142,15 +142,7 @@ module Gitlab ...@@ -142,15 +142,7 @@ module Gitlab
end end
def exists? def exists?
Gitlab::GitalyClient.migrate(:repository_exists, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |enabled| gitaly_repository_client.exists?
if enabled
gitaly_repository_client.exists?
else
circuit_breaker.perform do
File.exist?(File.join(path, 'refs'))
end
end
end
end end
# Returns an Array of branch names # Returns an Array of branch names
......
module Gitlab
class PagesClient
class << self
attr_reader :certificate, :token
def call(service, rpc, request, timeout: nil)
kwargs = request_kwargs(timeout)
stub(service).__send__(rpc, request, kwargs) # rubocop:disable GitlabSecurity/PublicSend
end
# This function is not thread-safe. Call it from an initializer only.
def read_or_create_token
@token = read_token
rescue Errno::ENOENT
# TODO: uncomment this when omnibus knows how to write the token file for us
# https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2466
#
# write_token(SecureRandom.random_bytes(64))
#
# # Read from disk in case someone else won the race and wrote the file
# # before us. If this fails again let the exception bubble up.
# @token = read_token
end
# This function is not thread-safe. Call it from an initializer only.
def load_certificate
cert_path = config.certificate
return unless cert_path.present?
@certificate = File.read(cert_path)
end
def ping
request = Grpc::Health::V1::HealthCheckRequest.new
call(:health_check, :check, request, timeout: 5.seconds)
end
private
def request_kwargs(timeout)
encoded_token = Base64.strict_encode64(token.to_s)
metadata = {
'authorization' => "Bearer #{encoded_token}"
}
result = { metadata: metadata }
return result unless timeout
# Do not use `Time.now` for deadline calculation, since it
# will be affected by Timecop in some tests, but grpc's c-core
# uses system time instead of timecop's time, so tests will fail
# `Time.at(Process.clock_gettime(Process::CLOCK_REALTIME))` will
# circumvent timecop
deadline = Time.at(Process.clock_gettime(Process::CLOCK_REALTIME)) + timeout
result[:deadline] = deadline
result
end
def stub(name)
stub_class(name).new(address, grpc_creds)
end
def stub_class(name)
if name == :health_check
Grpc::Health::V1::Health::Stub
else
# TODO use pages namespace
Gitaly.const_get(name.to_s.camelcase.to_sym).const_get(:Stub)
end
end
def address
addr = config.address
addr = addr.sub(%r{^tcp://}, '') if URI(addr).scheme == 'tcp'
addr
end
def grpc_creds
if address.start_with?('unix:')
:this_channel_is_insecure
elsif @certificate
GRPC::Core::ChannelCredentials.new(@certificate)
else
# Use system certificate pool
GRPC::Core::ChannelCredentials.new
end
end
def config
Gitlab.config.pages.admin
end
def read_token
File.read(token_path)
end
def token_path
Rails.root.join('.gitlab_pages_secret').to_s
end
def write_token(new_token)
Tempfile.open(File.basename(token_path), File.dirname(token_path), encoding: 'ascii-8bit') do |f|
f.write(new_token)
f.close
File.link(f.path, token_path)
end
rescue Errno::EACCES => ex
# TODO stop rescuing this exception in GitLab 11.0 https://gitlab.com/gitlab-org/gitlab-ce/issues/45672
Rails.logger.error("Could not write pages admin token file: #{ex}")
rescue Errno::EEXIST
# Another process wrote the token file concurrently with us. Use their token, not ours.
end
end
end
end
namespace :gitlab do
namespace :pages do
desc 'Ping the pages admin API'
task admin_ping: :gitlab_environment do
Gitlab::PagesClient.ping
puts "OK: gitlab-pages admin API is reachable"
end
end
end
require 'spec_helper'
describe Gitlab::PagesClient do
subject { described_class }
describe '.token' do
it 'returns the token as it is on disk' do
pending 'add omnibus support for generating the secret file https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2466'
expect(subject.token).to eq(File.read('.gitlab_pages_secret'))
end
end
describe '.read_or_create_token' do
subject { described_class.read_or_create_token }
let(:token_path) { 'tmp/tests/gitlab-pages-secret' }
before do
allow(described_class).to receive(:token_path).and_return(token_path)
FileUtils.rm_f(token_path)
end
it 'uses the existing token file if it exists' do
secret = 'existing secret'
File.write(token_path, secret)
subject
expect(described_class.token).to eq(secret)
end
it 'creates one if none exists' do
pending 'add omnibus support for generating the secret file https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests/2466'
old_token = described_class.token
# sanity check
expect(File.exist?(token_path)).to eq(false)
subject
expect(described_class.token.bytesize).to eq(64)
expect(described_class.token).not_to eq(old_token)
end
end
describe '.write_token' do
let(:token_path) { 'tmp/tests/gitlab-pages-secret' }
before do
allow(described_class).to receive(:token_path).and_return(token_path)
FileUtils.rm_f(token_path)
end
it 'writes the secret' do
new_secret = 'hello new secret'
expect(File.exist?(token_path)).to eq(false)
described_class.send(:write_token, new_secret)
expect(File.read(token_path)).to eq(new_secret)
end
it 'does nothing if the file already exists' do
existing_secret = 'hello secret'
File.write(token_path, existing_secret)
described_class.send(:write_token, 'new secret')
expect(File.read(token_path)).to eq(existing_secret)
end
end
describe '.load_certificate' do
subject { described_class.load_certificate }
before do
allow(described_class).to receive(:config).and_return(config)
end
context 'with no certificate in the config' do
let(:config) { double(:config, certificate: '') }
it 'does not set @certificate' do
subject
expect(described_class.certificate).to be_nil
end
end
context 'with a certificate path in the config' do
let(:certificate_path) { 'tmp/tests/fake-certificate' }
let(:config) { double(:config, certificate: certificate_path) }
it 'sets @certificate' do
certificate_data = "--- BEGIN CERTIFICATE ---\nbla\n--- END CERTIFICATE ---\n"
File.write(certificate_path, certificate_data)
subject
expect(described_class.certificate).to eq(certificate_data)
end
end
end
describe '.request_kwargs' do
let(:token) { 'secret token' }
let(:auth_header) { 'Bearer c2VjcmV0IHRva2Vu' }
before do
allow(described_class).to receive(:token).and_return(token)
end
context 'without timeout' do
it { expect(subject.send(:request_kwargs, nil)[:metadata]['authorization']).to eq(auth_header) }
end
context 'with timeout' do
let(:timeout) { 1.second }
it 'still sets the authorization header' do
expect(subject.send(:request_kwargs, timeout)[:metadata]['authorization']).to eq(auth_header)
end
it 'sets a deadline value' do
now = Time.now
deadline = subject.send(:request_kwargs, timeout)[:deadline]
expect(deadline).to be_between(now, now + 2 * timeout)
end
end
end
describe '.stub' do
before do
allow(described_class).to receive(:address).and_return('unix:/foo/bar')
end
it { expect(subject.send(:stub, :health_check)).to be_a(Grpc::Health::V1::Health::Stub) }
end
describe '.address' do
subject { described_class.send(:address) }
before do
allow(described_class).to receive(:config).and_return(config)
end
context 'with a unix: address' do
let(:config) { double(:config, address: 'unix:/foo/bar') }
it { expect(subject).to eq('unix:/foo/bar') }
end
context 'with a tcp:// address' do
let(:config) { double(:config, address: 'tcp://localhost:1234') }
it { expect(subject).to eq('localhost:1234') }
end
end
describe '.grpc_creds' do
subject { described_class.send(:grpc_creds) }
before do
allow(described_class).to receive(:config).and_return(config)
end
context 'with a unix: address' do
let(:config) { double(:config, address: 'unix:/foo/bar') }
it { expect(subject).to eq(:this_channel_is_insecure) }
end
context 'with a tcp:// address' do
let(:config) { double(:config, address: 'tcp://localhost:1234') }
it { expect(subject).to be_a(GRPC::Core::ChannelCredentials) }
end
end
end
...@@ -1256,15 +1256,15 @@ describe Repository do ...@@ -1256,15 +1256,15 @@ describe Repository do
end end
end end
shared_examples 'repo exists check' do describe '#exists?' do
it 'returns true when a repository exists' do it 'returns true when a repository exists' do
expect(repository.exists?).to eq(true) expect(repository.exists?).to be(true)
end end
it 'returns false if no full path can be constructed' do it 'returns false if no full path can be constructed' do
allow(repository).to receive(:full_path).and_return(nil) allow(repository).to receive(:full_path).and_return(nil)
expect(repository.exists?).to eq(false) expect(repository.exists?).to be(false)
end end
context 'with broken storage', :broken_storage do context 'with broken storage', :broken_storage do
...@@ -1274,16 +1274,6 @@ describe Repository do ...@@ -1274,16 +1274,6 @@ describe Repository do
end end
end end
describe '#exists?' do
context 'when repository_exists is disabled' do
it_behaves_like 'repo exists check'
end
context 'when repository_exists is enabled', :skip_gitaly_mock do
it_behaves_like 'repo exists check'
end
end
describe '#has_visible_content?' do describe '#has_visible_content?' do
before do before do
# If raw_repository.has_visible_content? gets called more than once then # If raw_repository.has_visible_content? gets called more than once then
......
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