Commit 5a439415 authored by GitLab Bot's avatar GitLab Bot

Merge remote-tracking branch 'upstream/master' into ce-to-ee-2018-05-28

# Conflicts:
#	app/assets/javascripts/jobs/components/sidebar_details_block.vue
#	app/workers/all_queues.yml
#	config/sidekiq_queues.yml
#	db/schema.rb
#	doc/administration/high_availability/database.md
#	doc/administration/high_availability/gitlab.md

[ci skip]
parents 44df8a18 71dea693
......@@ -172,7 +172,7 @@ gem 'acts-as-taggable-on', '~> 5.0'
# Background jobs
gem 'sidekiq', '~> 5.1'
gem 'sidekiq-cron', '~> 0.6.0'
gem 'redis-namespace', '~> 1.5.2'
gem 'redis-namespace', '~> 1.6.0'
gem 'sidekiq-limit_fetch', '~> 3.4', require: false
# Cron Parser
......@@ -427,7 +427,7 @@ group :ed25519 do
end
# Gitaly GRPC client
gem 'gitaly-proto', '~> 0.99.0', require: 'gitaly'
gem 'gitaly-proto', '~> 0.100.0', require: 'gitaly'
gem 'grpc', '~> 1.11.0'
# Locked until https://github.com/google/protobuf/issues/4210 is closed
......
......@@ -305,7 +305,7 @@ GEM
gettext_i18n_rails (>= 0.7.1)
po_to_json (>= 1.0.0)
rails (>= 3.2.0)
gitaly-proto (0.99.0)
gitaly-proto (0.100.0)
google-protobuf (~> 3.1)
grpc (~> 1.10)
github-linguist (5.3.3)
......@@ -738,8 +738,8 @@ GEM
redis-activesupport (5.0.4)
activesupport (>= 3, < 6)
redis-store (>= 1.3, < 2)
redis-namespace (1.5.2)
redis (~> 3.0, >= 3.0.4)
redis-namespace (1.6.0)
redis (>= 3.0.4)
redis-rack (2.0.4)
rack (>= 1.5, < 3)
redis-store (>= 1.2, < 2)
......@@ -1070,7 +1070,7 @@ DEPENDENCIES
gettext (~> 3.2.2)
gettext_i18n_rails (~> 1.8.0)
gettext_i18n_rails_js (~> 1.3)
gitaly-proto (~> 0.99.0)
gitaly-proto (~> 0.100.0)
github-linguist (~> 5.3.3)
gitlab-flowdock-git-hook (~> 1.0.1)
gitlab-gollum-lib (~> 4.2)
......@@ -1166,7 +1166,7 @@ DEPENDENCIES
recaptcha (~> 3.0)
redcarpet (~> 3.4)
redis (~> 3.2)
redis-namespace (~> 1.5.2)
redis-namespace (~> 1.6.0)
redis-rails (~> 5.0.2)
request_store (~> 1.3)
responders (~> 2.0)
......
......@@ -48,11 +48,14 @@ export default {
return `${this.job.runner.description} (#${this.job.runner.id})`;
},
retryButtonClass() {
<<<<<<< HEAD
let className = 'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
=======
let className =
'js-retry-button float-right btn btn-retry d-none d-md-block d-lg-block d-xl-block';
>>>>>>> upstream/master
className +=
this.job.status && this.job.recoverable
? ' btn-primary'
: ' btn-inverted-secondary';
this.job.status && this.job.recoverable ? ' btn-primary' : ' btn-inverted-secondary';
return className;
},
hasTimeout() {
......
......@@ -3,7 +3,11 @@ class Admin::DashboardController < Admin::ApplicationController
include CountHelper
COUNTED_ITEMS = [Project, User, Group, ForkedProjectLink, Issue, MergeRequest,
Note, Snippet, Key, Milestone].freeze
def index
@counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS)
@projects = Project.order_id_desc.without_deleted.with_route.limit(10)
@users = User.order_id_desc.limit(10)
@groups = Group.order_id_desc.with_route.limit(10)
......
module CountHelper
def approximate_count_with_delimiters(model)
number_with_delimiter(Gitlab::Database::Count.approximate_count(model))
def approximate_count_with_delimiters(count_data, model)
count = count_data[model]
raise "Missing model #{model} from count data" unless count
number_with_delimiter(count)
end
end
......@@ -12,7 +12,7 @@
= link_to admin_projects_path do
%h3.text-center
Projects:
= approximate_count_with_delimiters(Project)
= approximate_count_with_delimiters(@counts, Project)
%hr
= link_to('New project', new_project_path, class: "btn btn-new")
.col-sm-4
......@@ -21,7 +21,7 @@
= link_to admin_users_path do
%h3.text-center
Users:
= approximate_count_with_delimiters(User)
= approximate_count_with_delimiters(@counts, User)
= render_if_exists 'users_statistics'
%hr
= link_to 'New user', new_admin_user_path, class: "btn btn-new"
......@@ -31,7 +31,7 @@
= link_to admin_groups_path do
%h3.text-center
Groups:
= approximate_count_with_delimiters(Group)
= approximate_count_with_delimiters(@counts, Group)
%hr
= link_to 'New group', new_admin_group_path, class: "btn btn-new"
.row
......@@ -42,31 +42,31 @@
%p
Forks
%span.light.float-right
= approximate_count_with_delimiters(ForkedProjectLink)
= approximate_count_with_delimiters(@counts, ForkedProjectLink)
%p
Issues
%span.light.float-right
= approximate_count_with_delimiters(Issue)
= approximate_count_with_delimiters(@counts, Issue)
%p
Merge Requests
%span.light.float-right
= approximate_count_with_delimiters(MergeRequest)
= approximate_count_with_delimiters(@counts, MergeRequest)
%p
Notes
%span.light.float-right
= approximate_count_with_delimiters(Note)
= approximate_count_with_delimiters(@counts, Note)
%p
Snippets
%span.light.float-right
= approximate_count_with_delimiters(Snippet)
= approximate_count_with_delimiters(@counts, Snippet)
%p
SSH Keys
%span.light.float-right
= approximate_count_with_delimiters(Key)
= approximate_count_with_delimiters(@counts, Key)
%p
Milestones
%span.light.float-right
= approximate_count_with_delimiters(Milestone)
= approximate_count_with_delimiters(@counts, Milestone)
%p
Active Users
%span.light.float-right
......
......@@ -116,6 +116,7 @@
- web_hook
- repository_update_remote_mirror
- create_note_diff_file
<<<<<<< HEAD
# EE-specific queues
......@@ -176,3 +177,5 @@
- rebase
- repository_update_mirror
- chat_notification
=======
>>>>>>> upstream/master
---
title: Update redis-namespace to 1.6.0
merge_request: 19166
author: Takuya Noguchi
type: other
---
title: Add username to terms message in git and API calls
merge_request: 19126
author:
type: changed
---
title: Disallow updating job status if the job is not running
merge_request: 19101
author:
type: fixed
---
title: Fix admin counters not working when PostgreSQL has secondaries
merge_request:
author:
type: fixed
......@@ -76,6 +76,7 @@
- [repository_update_remote_mirror, 1]
- [repository_remove_remote, 1]
- [create_note_diff_file, 1]
<<<<<<< HEAD
# EE-specific queues
- [ldap_group_sync, 2]
......@@ -96,3 +97,6 @@
- geo_file_download
- geo_project_sync
- geo_repository_shard_sync
=======
>>>>>>> upstream/master
......@@ -2898,8 +2898,11 @@ ActiveRecord::Schema.define(version: 20180521171529) do
add_foreign_key "merge_requests_closing_issues", "merge_requests", on_delete: :cascade
add_foreign_key "milestones", "namespaces", column: "group_id", name: "fk_95650a40d4", on_delete: :cascade
add_foreign_key "milestones", "projects", name: "fk_9bd0a0c791", on_delete: :cascade
<<<<<<< HEAD
add_foreign_key "namespace_statistics", "namespaces", on_delete: :cascade
add_foreign_key "namespaces", "plans", name: "fk_fdd12e5b80", on_delete: :nullify
=======
>>>>>>> upstream/master
add_foreign_key "note_diff_files", "notes", column: "diff_note_id", on_delete: :cascade
add_foreign_key "notes", "projects", name: "fk_99e097b079", on_delete: :cascade
add_foreign_key "oauth_openid_requests", "oauth_access_grants", column: "access_grant_id", name: "fk_oauth_openid_requests_oauth_access_grants_access_grant_id"
......
......@@ -216,6 +216,7 @@ On each Consul node perform the following:
1. Edit `/etc/gitlab/gitlab.rb` replacing values noted in the `# START user configuration` section:
```ruby
<<<<<<< HEAD
# Disable all components except Consul
roles ['consul_role']
......@@ -270,6 +271,11 @@ On each database node perform the following:
```ruby
# Disable all components except PostgreSQL and Repmgr and Consul
=======
external_url 'https://gitlab.example.com'
# Disable all components except PostgreSQL
>>>>>>> upstream/master
roles ['postgres_role']
# PostgreSQL configuration
......
......@@ -76,44 +76,59 @@ for each GitLab application server in your environment.
servers should point to the external url that users will use to access GitLab.
In a typical HA setup, this will be the url of the load balancer which will
route traffic to all GitLab application servers in the HA cluster.
1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
> **Note:** When you specify `https` in the `external_url`, as in the example
above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If
certificates are not present, Nginx will fail to start. See
[Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
for more information.
## First GitLab application server
As a final step, run the setup rake task on the first GitLab application server.
It is not necessary to run this on additional application servers.
As a final step, run the setup rake task **only on** the first GitLab application server.
Do not run this on additional application servers.
1. Initialize the database by running `sudo gitlab-rake gitlab:setup`.
1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
> **WARNING:** Only run this setup task on **NEW** GitLab instances because it
will wipe any existing data.
> **Note:** When you specify `https` in the `external_url`, as in the example
above, GitLab assumes you have SSL certificates in `/etc/gitlab/ssl/`. If
certificates are not present, Nginx will fail to start. See
[Nginx documentation](http://docs.gitlab.com/omnibus/settings/nginx.html#enable-https)
for more information.
## Extra configuration for additional GitLab application servers
Additional GitLab servers (servers configured **after** the first GitLab server)
need some extra configuration.
1. Configure shared secrets. These values can be obtained from the primary
<<<<<<< HEAD
GitLab server in `/etc/gitlab/gitlab-secrets.json`. Copy this file to the
secondary servers **prior to** running the first `reconfigure` in the steps
above.
=======
GitLab server in `/etc/gitlab/gitlab-secrets.json`. Add these to
`/etc/gitlab/gitlab.rb` **prior to** running the first `reconfigure`.
```ruby
gitlab_shell['secret_token'] = 'fbfb19c355066a9afb030992231c4a363357f77345edd0f2e772359e5be59b02538e1fa6cae8f93f7d23355341cea2b93600dab6d6c3edcdced558fc6d739860'
gitlab_rails['otp_key_base'] = 'b719fe119132c7810908bba18315259ed12888d4f5ee5430c42a776d840a396799b0a5ef0a801348c8a357f07aa72bbd58e25a84b8f247a25c72f539c7a6c5fa'
gitlab_rails['secret_key_base'] = '6e657410d57c71b4fc3ed0d694e7842b1895a8b401d812c17fe61caf95b48a6d703cb53c112bc01ebd197a85da81b18e29682040e99b4f26594772a4a2c98c6d'
gitlab_rails['db_key_base'] = 'bf2e47b68d6cafaef1d767e628b619365becf27571e10f196f98dc85e7771042b9203199d39aff91fcb6837c8ed83f2a912b278da50999bb11a2fbc0fba52964'
```
>>>>>>> upstream/master
1. Run `touch /etc/gitlab/skip-auto-migrations` to prevent database migrations
from running on upgrade. Only the primary GitLab application server should
handle migrations.
<<<<<<< HEAD
1. **Optional** Configure host keys. Copy all contents(primary and public keys) inside `/etc/ssh/` on
the primary application server to `/ets/ssh` on all secondary servers. This
prevents false man-in-the-middle-attack alerts when accesing servers in your
High Availability cluster behind a load balancer.
=======
1. Run `sudo gitlab-ctl reconfigure` to compile the configuration.
>>>>>>> upstream/master
## Troubleshooting
......
......@@ -123,6 +123,7 @@ module API
end
put '/:id' do
job = authenticate_job!
forbidden!('Job is not running') unless job.running?
job.trace.set(params[:trace]) if params[:trace]
......@@ -131,9 +132,9 @@ module API
case params[:state].to_s
when 'success'
job.success
job.success!
when 'failed'
job.drop(params[:failure_reason] || :unknown_failure)
job.drop!(params[:failure_reason] || :unknown_failure)
end
end
......
......@@ -73,29 +73,40 @@ module Backup
end
def prepare_directories
# TODO: Need to find a way to do this for gitaly
# Gitaly discussion issue: https://gitlab.com/gitlab-org/gitaly/issues/1194
Gitlab.config.repositories.storages.each do |name, repository_storage|
path = repository_storage.legacy_disk_path
next unless File.exist?(path)
# Move all files in the existing repos directory except . and .. to
# repositories.old.<timestamp> directory
bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s)
FileUtils.mkdir_p(bk_repos_path, mode: 0700)
files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")]
begin
FileUtils.mv(files, bk_repos_path)
rescue Errno::EACCES
access_denied_error(path)
rescue Errno::EBUSY
resource_busy_error(path)
delete_all_repositories(name, repository_storage)
end
end
def delete_all_repositories(name, repository_storage)
gitaly_migrate(:delete_all_repositories) do |is_enabled|
if is_enabled
Gitlab::GitalyClient::StorageService.new(name).delete_all_repositories
else
local_delete_all_repositories(name, repository_storage)
end
end
end
def local_delete_all_repositories(name, repository_storage)
path = repository_storage.legacy_disk_path
return unless File.exist?(path)
# Move all files in the existing repos directory except . and .. to
# repositories.old.<timestamp> directory
bk_repos_path = File.join(Gitlab.config.backup.path, "tmp", "#{name}-repositories.old." + Time.now.to_i.to_s)
FileUtils.mkdir_p(bk_repos_path, mode: 0700)
files = Dir.glob(File.join(path, "*"), File::FNM_DOTMATCH) - [File.join(path, "."), File.join(path, "..")]
begin
FileUtils.mv(files, bk_repos_path)
rescue Errno::EACCES
access_denied_error(path)
rescue Errno::EBUSY
resource_busy_error(path)
end
end
def restore_custom_hooks(project)
# TODO: Need to find a way to do this for gitaly
# Gitaly migration issue: https://gitlab.com/gitlab-org/gitaly/issues/1195
......@@ -113,6 +124,7 @@ module Backup
def restore
prepare_directories
gitlab_shell = Gitlab::Shell.new
Project.find_each(batch_size: 1000) do |project|
progress.print " * #{project.full_path} ... "
path_to_project_bundle = path_to_bundle(project)
......@@ -121,7 +133,6 @@ module Backup
restore_repo_success = nil
if File.exist?(path_to_project_bundle)
begin
gitlab_shell.remove_repository(project.repository_storage, project.disk_path) if project.repository_exists?
project.repository.create_from_bundle path_to_project_bundle
restore_repo_success = true
rescue => e
......@@ -146,7 +157,6 @@ module Backup
if File.exist?(path_to_wiki_bundle)
progress.print " * #{wiki.full_path} ... "
begin
gitlab_shell.remove_repository(wiki.repository_storage, wiki.disk_path) if wiki.repository_exists?
wiki.repository.create_from_bundle(path_to_wiki_bundle)
progress.puts "[DONE]".color(:green)
rescue => e
......@@ -224,5 +234,11 @@ module Backup
def display_repo_path(project)
project.hashed_storage?(:repository) ? "#{project.full_path} (#{project.disk_path})" : project.full_path
end
def gitaly_migrate(method, status: Gitlab::GitalyClient::MigrationStatus::OPT_IN, &block)
Gitlab::GitalyClient.migrate(method, status: status, &block)
rescue GRPC::NotFound, GRPC::BadStatus => e
raise Error, e
end
end
end
......@@ -8,12 +8,12 @@ module Gitlab
def rejection_message
case rejection_type
when :internal
'This action cannot be performed by internal users'
"This action cannot be performed by internal users"
when :terms_not_accepted
'You must accept the Terms of Service in order to perform this action. '\
'Please access GitLab from a web browser to accept these terms.'
"You (#{@user.to_reference}) must accept the Terms of Service in order to perform this action. "\
"Please access GitLab from a web browser to accept these terms."
else
'Your account has been blocked.'
"Your account has been blocked."
end
end
......
......@@ -17,31 +17,69 @@ module Gitlab
].freeze
end
def self.approximate_count(model)
return model.count unless Gitlab::Database.postgresql?
# Takes in an array of models and returns a Hash for the approximate
# counts for them. If the model's table has not been vacuumed or
# analyzed recently, simply run the Model.count to get the data.
#
# @param [Array]
# @return [Hash] of Model -> count mapping
def self.approximate_counts(models)
table_to_model_map = models.each_with_object({}) do |model, hash|
hash[model.table_name] = model
end
execute_estimate_if_updated_recently(model) || model.count
end
table_names = table_to_model_map.keys
counts_by_table_name = Gitlab::Database.postgresql? ? reltuples_from_recently_updated(table_names) : {}
def self.execute_estimate_if_updated_recently(model)
ActiveRecord::Base.connection.select_value(postgresql_estimate_query(model)).to_i if reltuples_updated_recently?(model)
rescue *CONNECTION_ERRORS
# Convert table -> count to Model -> count
counts_by_model = counts_by_table_name.each_with_object({}) do |pair, hash|
model = table_to_model_map[pair.first]
hash[model] = pair.second
end
missing_tables = table_names - counts_by_table_name.keys
missing_tables.each do |table|
model = table_to_model_map[table]
counts_by_model[model] = model.count
end
counts_by_model
end
def self.reltuples_updated_recently?(model)
time = "to_timestamp(#{1.hour.ago.to_i})"
query = <<~SQL
SELECT 1 FROM pg_stat_user_tables WHERE relname = '#{model.table_name}' AND
(last_vacuum > #{time} OR last_autovacuum > #{time} OR last_analyze > #{time} OR last_autoanalyze > #{time})
SQL
# Returns a hash of the table names that have recently updated tuples.
#
# @param [Array] table names
# @returns [Hash] Table name to count mapping (e.g. { 'projects' => 5, 'users' => 100 })
def self.reltuples_from_recently_updated(table_names)
query = postgresql_estimate_query(table_names)
rows = []
ActiveRecord::Base.connection.select_all(query).count > 0
# Querying tuple stats only works on the primary. Due to load
# balancing, we need to ensure this query hits the load balancer. The
# easiest way to do this is to start a transaction.
ActiveRecord::Base.transaction do
rows = ActiveRecord::Base.connection.select_all(query)
end
rows.each_with_object({}) { |row, data| data[row['table_name']] = row['estimate'].to_i }
rescue *CONNECTION_ERRORS
false
{}
end
def self.postgresql_estimate_query(model)
"SELECT reltuples::bigint AS estimate FROM pg_class where relname = '#{model.table_name}'"
# Generates the PostgreSQL query to return the tuples for tables
# that have been vacuumed or analyzed in the last hour.
#
# @param [Array] table names
# @returns [Hash] Table name to count mapping (e.g. { 'projects' => 5, 'users' => 100 })
def self.postgresql_estimate_query(table_names)
time = "to_timestamp(#{1.hour.ago.to_i})"
<<~SQL
SELECT pg_class.relname AS table_name, reltuples::bigint AS estimate FROM pg_class
LEFT JOIN pg_stat_user_tables ON pg_class.relname = pg_stat_user_tables.relname
WHERE pg_class.relname IN (#{table_names.map { |table| "'#{table}'" }.join(',')})
AND (last_vacuum > #{time} OR last_autovacuum > #{time} OR last_analyze > #{time} OR last_autoanalyze > #{time})
SQL
end
end
end
......
module Gitlab
module GitalyClient
class StorageService
def initialize(storage)
@storage = storage
end
# Delete all repositories in the storage. This is a slow and VERY DESTRUCTIVE operation.
def delete_all_repositories
request = Gitaly::DeleteAllRepositoriesRequest.new(storage_name: @storage)
GitalyClient.call(@storage, :storage_service, :delete_all_repositories, request)
end
end
end
end
......@@ -71,6 +71,40 @@ describe Backup::Repository do
end
end
describe '#delete_all_repositories', :seed_helper do
shared_examples('delete_all_repositories') do
before do
allow(FileUtils).to receive(:mkdir_p).and_call_original
allow(FileUtils).to receive(:mv).and_call_original
end
after(:all) do
ensure_seeds
end
it 'removes all repositories' do
# Sanity check: there should be something for us to delete
expect(list_repositories).to include(File.join(SEED_STORAGE_PATH, TEST_REPO_PATH))
subject.delete_all_repositories('default', Gitlab.config.repositories.storages['default'])
expect(list_repositories).to be_empty
end
def list_repositories
Dir[SEED_STORAGE_PATH + '/*.git']
end
end
context 'with gitaly' do
it_behaves_like 'delete_all_repositories'
end
context 'without gitaly', :skip_gitaly_mock do
it_behaves_like 'delete_all_repositories'
end
end
describe '#empty_repo?' do
context 'for a wiki' do
let(:wiki) { create(:project_wiki) }
......
......@@ -22,7 +22,8 @@ describe Gitlab::Auth::UserAccessDeniedReason do
enforce_terms
end
it { is_expected.to match /You must accept the Terms of Service/ }
it { is_expected.to match /must accept the Terms of Service/ }
it { is_expected.to include(user.username) }
end
context 'when the user is internal' do
......
......@@ -3,59 +3,68 @@ require 'spec_helper'
describe Gitlab::Database::Count do
before do
create_list(:project, 3)
create(:identity)
end
describe '.execute_estimate_if_updated_recently', :postgresql do
context 'when reltuples have not been updated' do
before do
expect(described_class).to receive(:reltuples_updated_recently?).and_return(false)
end
let(:models) { [Project, Identity] }
it 'returns nil' do
expect(described_class.execute_estimate_if_updated_recently(Project)).to be nil
end
end
describe '.approximate_counts' do
context 'with MySQL' do
context 'when reltuples have not been updated' do
it 'counts all models the normal way' do
expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
context 'when reltuples have been updated' do
before do
ActiveRecord::Base.connection.execute('ANALYZE projects')
end
expect(Project).to receive(:count).and_call_original
expect(Identity).to receive(:count).and_call_original
it 'calls postgresql_estimate_query' do
expect(described_class).to receive(:postgresql_estimate_query).with(Project).and_call_original
expect(described_class.execute_estimate_if_updated_recently(Project)).to eq(3)
expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
end
end
end
end
describe '.approximate_count' do
context 'when reltuples have not been updated' do
it 'counts all projects the normal way' do
allow(described_class).to receive(:reltuples_updated_recently?).and_return(false)
context 'with PostgreSQL', :postgresql do
describe 'when reltuples have not been updated' do
it 'counts all models the normal way' do
expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({})
expect(Project).to receive(:count).and_call_original
expect(described_class.approximate_count(Project)).to eq(3)
expect(Project).to receive(:count).and_call_original
expect(Identity).to receive(:count).and_call_original
expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
end
end
end
context 'no permission' do
it 'falls back to standard query' do
allow(described_class).to receive(:reltuples_updated_recently?).and_raise(PG::InsufficientPrivilege)
describe 'no permission' do
it 'falls back to standard query' do
allow(described_class).to receive(:postgresql_estimate_query).and_raise(PG::InsufficientPrivilege)
expect(Project).to receive(:count).and_call_original
expect(described_class.approximate_count(Project)).to eq(3)
expect(Project).to receive(:count).and_call_original
expect(Identity).to receive(:count).and_call_original
expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
end
end
end
describe 'when reltuples have been updated', :postgresql do
before do
ActiveRecord::Base.connection.execute('ANALYZE projects')
describe 'when some reltuples have been updated' do
it 'counts projects in the fast way' do
expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({ 'projects' => 3 })
expect(Project).not_to receive(:count).and_call_original
expect(Identity).to receive(:count).and_call_original
expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
end
end
it 'counts all projects in the fast way' do
expect(described_class).to receive(:postgresql_estimate_query).with(Project).and_call_original
describe 'when all reltuples have been updated' do
before do
ActiveRecord::Base.connection.execute('ANALYZE projects')
ActiveRecord::Base.connection.execute('ANALYZE identities')
end
it 'counts models with the standard way' do
expect(Project).not_to receive(:count)
expect(Identity).not_to receive(:count)
expect(described_class.approximate_count(Project)).to eq(3)
expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 })
end
end
end
end
......
......@@ -1183,7 +1183,7 @@ describe Gitlab::GitAccess do
it 'blocks access when the user did not accept terms', :aggregate_failures do
actions.each do |action|
expect { action.call }.to raise_unauthorized(/You must accept the Terms of Service in order to perform this action/)
expect { action.call }.to raise_unauthorized(/must accept the Terms of Service in order to perform this action/)
end
end
......
......@@ -174,7 +174,7 @@ describe API::Helpers do
end
it 'returns a 403 when a user has not accepted the terms' do
expect { current_user }.to raise_error /You must accept the Terms of Service/
expect { current_user }.to raise_error /must accept the Terms of Service/
end
it 'sets the current user when the user accepted the terms' do
......
......@@ -832,6 +832,21 @@ describe API::Runner, :clean_gitlab_redis_shared_state do
end
end
context 'when job has already been finished' do
before do
job.trace.set('Job failed')
job.drop!(:script_failure)
end
it 'does not update job status and job trace' do
update_job(state: 'success', trace: 'BUILD TRACE UPDATED')
expect(response).to have_gitlab_http_status(403)
expect(job.trace.raw).to eq 'Job failed'
expect(job).to be_failed
end
end
def update_job(token = job.token, **params)
new_params = params.merge(token: token)
put api("/jobs/#{job.id}"), new_params
......
......@@ -4,6 +4,11 @@ describe 'admin/dashboard/index.html.haml' do
include Devise::Test::ControllerHelpers
before do
counts = Admin::DashboardController::COUNTED_ITEMS.each_with_object({}) do |item, hash|
hash[item] = 100
end
assign(:counts, counts)
assign(:projects, create_list(:project, 1))
assign(:users, create_list(:user, 1))
assign(:groups, create_list(:group, 1))
......
This diff is collapsed.
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