Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2021-07-01 06:07:35 +00:00
parent 07be35d058
commit 0537e77587
19 changed files with 178 additions and 93 deletions

View File

@ -47,8 +47,7 @@ module Ci
# rubocop: disable CodeReuse/ActiveRecord
def pipelines_using_cte
sha_relation = merge_request.all_commits.select(:sha)
sha_relation = sha_relation.distinct if Feature.enabled?(:use_distinct_in_shas_cte, default_enabled: :yaml)
sha_relation = merge_request.all_commits.select(:sha).distinct
cte = Gitlab::SQL::CTE.new(:shas, sha_relation)

View File

@ -6,6 +6,10 @@ module Types
graphql_name 'CiRunner'
authorize :read_runner
JOB_COUNT_LIMIT = 1000
alias_method :runner, :object
field :id, ::Types::GlobalIDType[::Ci::Runner], null: false,
description: 'ID of the runner.'
field :description, GraphQL::STRING_TYPE, null: true,
@ -37,6 +41,30 @@ module Types
description: 'Type of the runner.'
field :tag_list, [GraphQL::STRING_TYPE], null: true,
description: 'Tags associated with the runner.'
field :project_count, GraphQL::INT_TYPE, null: true,
description: 'Number of projects that the runner is associated with.'
field :job_count, GraphQL::INT_TYPE, null: true,
description: "Number of jobs processed by the runner (limited to #{JOB_COUNT_LIMIT}, plus one to indicate that more items exist)."
def job_count
# We limit to 1 above the JOB_COUNT_LIMIT to indicate that more items exist after JOB_COUNT_LIMIT
runner.builds.limit(JOB_COUNT_LIMIT + 1).count
end
# rubocop: disable CodeReuse/ActiveRecord
def project_count
BatchLoader::GraphQL.for(runner.id).batch(key: :runner_project_count) do |ids, loader, args|
counts = ::Ci::RunnerProject.select(:runner_id, 'COUNT(*) as count')
.where(runner_id: ids)
.group(:runner_id)
.index_by(&:runner_id)
ids.each do |id|
loader.call(id, counts[id]&.count)
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
end
end
end

View File

@ -1,8 +0,0 @@
---
name: ci_quota_check_on_retries
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/62702
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/333765
milestone: '14.0'
type: development
group: group::pipeline execution
default_enabled: false

View File

@ -1,8 +0,0 @@
---
name: use_distinct_in_shas_cte
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/61454
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/330586
milestone: '13.12'
type: development
group: group::optimize
default_enabled: true

View File

@ -5,7 +5,7 @@ const argumentsParser = require('commander');
const glob = require('glob');
const webpack = require('webpack');
const IS_EE = require('./helpers/is_ee_env');
const webpackConfig = require('./webpack.config.js');
const webpackConfig = require('./webpack.config');
const ROOT_PATH = path.resolve(__dirname, '..');
const SPECS_PATH = /^(?:\.[\\/])?(ee[\\/])?spec[\\/]javascripts[\\/]/;

View File

@ -422,7 +422,7 @@ module.exports = {
);
// eslint-disable-next-line global-require
const dllConfig = require('./webpack.vendor.config.js');
const dllConfig = require('./webpack.vendor.config');
const dllCompiler = webpack(dllConfig);
dllCompiler.run((err, stats) => {

View File

@ -7710,9 +7710,11 @@ Represents the total number of issues and their weights for a particular day.
| <a id="cirunnerdescription"></a>`description` | [`String`](#string) | Description of the runner. |
| <a id="cirunnerid"></a>`id` | [`CiRunnerID!`](#cirunnerid) | ID of the runner. |
| <a id="cirunneripaddress"></a>`ipAddress` | [`String!`](#string) | IP address of the runner. |
| <a id="cirunnerjobcount"></a>`jobCount` | [`Int`](#int) | Number of jobs processed by the runner (limited to 1000, plus one to indicate that more items exist). |
| <a id="cirunnerlocked"></a>`locked` | [`Boolean`](#boolean) | Indicates the runner is locked. |
| <a id="cirunnermaximumtimeout"></a>`maximumTimeout` | [`Int`](#int) | Maximum timeout (in seconds) for jobs processed by the runner. |
| <a id="cirunnerprivateprojectsminutescostfactor"></a>`privateProjectsMinutesCostFactor` | [`Float`](#float) | Private projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerprojectcount"></a>`projectCount` | [`Int`](#int) | Number of projects that the runner is associated with. |
| <a id="cirunnerpublicprojectsminutescostfactor"></a>`publicProjectsMinutesCostFactor` | [`Float`](#float) | Public projects' "minutes cost factor" associated with the runner (GitLab.com only). |
| <a id="cirunnerrevision"></a>`revision` | [`String!`](#string) | Revision of the runner. |
| <a id="cirunnerrununtagged"></a>`runUntagged` | [`Boolean!`](#boolean) | Indicates the runner is able to run untagged jobs. |

View File

@ -18,7 +18,7 @@
# batch_count(::Clusters::Cluster.aws_installed.enabled, :cluster_id)
# batch_count(Namespace.group(:type))
# batch_distinct_count(::Project, :creator_id)
# batch_distinct_count(::Project.with_active_integrations.service_desk_enabled.where(time_period), start: ::User.minimum(:id), finish: ::User.maximum(:id))
# batch_distinct_count(::Project.aimed_for_deletion.service_desk_enabled.where(time_period), start: ::User.minimum(:id), finish: ::User.maximum(:id))
# batch_distinct_count(Project.group(:visibility_level), :creator_id)
# batch_sum(User, :sign_in_count)
# batch_sum(Issue.group(:state_id), :weight))

View File

@ -11,17 +11,17 @@ module Gitlab
# In order to not use a possible complex time consuming query when calculating min and max values,
# the start and finish can be sent specifically, start and finish should contain max and min values for PRIMARY KEY of
# relation (most cases `id` column) rather than counted attribute eg:
# estimate_distinct_count(start: ::Project.with_active_integrations.minimum(:id), finish: ::Project.with_active_integrations.maximum(:id))
# estimate_distinct_count(start: ::Project.aimed_for_deletion.minimum(:id), finish: ::Project.aimed_for_deletion.maximum(:id))
#
# Grouped relations are NOT supported yet.
#
# @example Usage
# ::Gitlab::Database::PostgresHllBatchDistinctCount.new(::Project, :creator_id).execute
# ::Gitlab::Database::PostgresHllBatchDistinctCount.new(::Project.with_active_integrations.service_desk_enabled.where(time_period))
# ::Gitlab::Database::PostgresHllBatchDistinctCount.new(::Project.aimed_for_deletion.service_desk_enabled.where(time_period))
# .execute(
# batch_size: 1_000,
# start: ::Project.with_active_integrations.service_desk_enabled.where(time_period).minimum(:id),
# finish: ::Project.with_active_integrations.service_desk_enabled.where(time_period).maximum(:id)
# start: ::Project.aimed_for_deletion.service_desk_enabled.where(time_period).minimum(:id),
# finish: ::Project.aimed_for_deletion.service_desk_enabled.where(time_period).maximum(:id)
# )
#
# @note HyperLogLog is an PROBABILISTIC algorithm that ESTIMATES distinct count of given attribute value for supplied relation

View File

@ -6,7 +6,7 @@ const {
decorateExtractorWithHelpers,
} = require('gettext-extractor-vue');
const vue2TemplateCompiler = require('vue-template-compiler');
const ensureSingleLine = require('../../app/assets/javascripts/locale/ensure_single_line.js');
const ensureSingleLine = require('../../app/assets/javascripts/locale/ensure_single_line');
const args = argumentsParser
.option('-f, --file <file>', 'Extract message from one single file')

View File

@ -1,6 +1,6 @@
const stylelint = require('stylelint');
const utils = require('./stylelint-utils');
const utilityClasses = require('./utility-classes-map.js');
const utilityClasses = require('./utility-classes-map');
const ruleName = 'stylelint-gitlab/utility-classes';

View File

@ -313,27 +313,18 @@ module Trigger
comment = "<!-- #{IDENTIFIABLE_NOTE_TAG} --> \nStarted database testing [pipeline](https://ops.gitlab.net/#{downstream_project_path}/-/pipelines/#{pipeline.id}) " \
"(limited access). This comment will be updated once the pipeline has finished running."
# Look for a note to update
# Look for an existing note
db_testing_notes = gitlab.merge_request_notes(project_path, merge_request_id).auto_paginate.select do |note|
note.body.include?(IDENTIFIABLE_NOTE_TAG)
end
note = db_testing_notes.max_by { |note| Time.parse(note.created_at) }
if note && note.type != 'DiscussionNote'
# The latest note has not led to a discussion. Update it.
gitlab.edit_merge_request_note(project_path, merge_request_id, note.id, comment)
puts "Updated comment:\n"
else
# This is the first note or the latest note has been discussed on the MR.
# Don't update, create new note instead.
if db_testing_notes.empty?
# This is the first note
note = gitlab.create_merge_request_note(project_path, merge_request_id, comment)
puts "Posted comment to:\n"
puts "https://gitlab.com/#{project_path}/-/merge_requests/#{merge_request_id}#note_#{note.id}"
end
puts "https://gitlab.com/#{project_path}/-/merge_requests/#{merge_request_id}#note_#{note.id}"
end
private

View File

@ -11,6 +11,7 @@ RSpec.describe GitlabSchema.types['CiRunner'] do
expected_fields = %w[
id description contacted_at maximum_timeout access_level active status
version short_sha revision locked run_untagged ip_address runner_type tag_list
project_count job_count
]
expect(described_class).to include_graphql_fields(*expected_fields)

View File

@ -1 +1 @@
export * from '../../../frontend/lib/utils/mock_data.js';
export * from '../../../frontend/lib/utils/mock_data';

View File

@ -10,7 +10,7 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
let_it_be(:project) { create(:project) }
subject(:service) do
subject(:integration) do
described_class.create!(
project: project,
properties: {
@ -28,47 +28,47 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
end
describe 'Validations' do
context 'when service is active' do
context 'when active' do
before do
subject.active = true
integration.active = true
end
it { is_expected.to validate_presence_of(:build_key) }
it { is_expected.to validate_presence_of(:bamboo_url) }
it_behaves_like 'issue tracker service URL attribute', :bamboo_url
it_behaves_like 'issue tracker integration URL attribute', :bamboo_url
describe '#username' do
it 'does not validate the presence of username if password is nil' do
subject.password = nil
integration.password = nil
expect(subject).not_to validate_presence_of(:username)
expect(integration).not_to validate_presence_of(:username)
end
it 'validates the presence of username if password is present' do
subject.password = 'secret'
integration.password = 'secret'
expect(subject).to validate_presence_of(:username)
expect(integration).to validate_presence_of(:username)
end
end
describe '#password' do
it 'does not validate the presence of password if username is nil' do
subject.username = nil
integration.username = nil
expect(subject).not_to validate_presence_of(:password)
expect(integration).not_to validate_presence_of(:password)
end
it 'validates the presence of password if username is present' do
subject.username = 'john'
integration.username = 'john'
expect(subject).to validate_presence_of(:password)
expect(integration).to validate_presence_of(:password)
end
end
end
context 'when service is inactive' do
context 'when inactive' do
before do
subject.active = false
integration.active = false
end
it { is_expected.not_to validate_presence_of(:build_key) }
@ -82,45 +82,38 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
describe 'before_update :reset_password' do
context 'when a password was previously set' do
it 'resets password if url changed' do
bamboo_integration = service
integration.bamboo_url = 'http://gitlab1.com'
integration.save!
bamboo_integration.bamboo_url = 'http://gitlab1.com'
bamboo_integration.save!
expect(bamboo_integration.password).to be_nil
expect(integration.password).to be_nil
end
it 'does not reset password if username changed' do
bamboo_integration = service
integration.username = 'some_name'
integration.save!
bamboo_integration.username = 'some_name'
bamboo_integration.save!
expect(bamboo_integration.password).to eq('password')
expect(integration.password).to eq('password')
end
it "does not reset password if new url is set together with password, even if it's the same password" do
bamboo_integration = service
integration.bamboo_url = 'http://gitlab_edited.com'
integration.password = 'password'
integration.save!
bamboo_integration.bamboo_url = 'http://gitlab_edited.com'
bamboo_integration.password = 'password'
bamboo_integration.save!
expect(bamboo_integration.password).to eq('password')
expect(bamboo_integration.bamboo_url).to eq('http://gitlab_edited.com')
expect(integration.password).to eq('password')
expect(integration.bamboo_url).to eq('http://gitlab_edited.com')
end
end
it 'saves password if new url is set together with password when no password was previously set' do
bamboo_integration = service
bamboo_integration.password = nil
integration.password = nil
bamboo_integration.bamboo_url = 'http://gitlab_edited.com'
bamboo_integration.password = 'password'
bamboo_integration.save!
integration.bamboo_url = 'http://gitlab_edited.com'
integration.password = 'password'
integration.save!
expect(bamboo_integration.password).to eq('password')
expect(bamboo_integration.bamboo_url).to eq('http://gitlab_edited.com')
expect(integration.password).to eq('password')
expect(integration.bamboo_url).to eq('http://gitlab_edited.com')
end
end
end
@ -129,29 +122,29 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
it 'runs update and build action' do
stub_update_and_build_request
subject.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
integration.execute(Gitlab::DataBuilder::Push::SAMPLE_DATA)
end
end
describe '#build_page' do
it 'returns the contents of the reactive cache' do
stub_reactive_cache(service, { build_page: 'foo' }, 'sha', 'ref')
stub_reactive_cache(integration, { build_page: 'foo' }, 'sha', 'ref')
expect(service.build_page('sha', 'ref')).to eq('foo')
expect(integration.build_page('sha', 'ref')).to eq('foo')
end
end
describe '#commit_status' do
it 'returns the contents of the reactive cache' do
stub_reactive_cache(service, { commit_status: 'foo' }, 'sha', 'ref')
stub_reactive_cache(integration, { commit_status: 'foo' }, 'sha', 'ref')
expect(service.commit_status('sha', 'ref')).to eq('foo')
expect(integration.commit_status('sha', 'ref')).to eq('foo')
end
end
shared_examples 'reactive cache calculation' do
describe '#build_page' do
subject { service.calculate_reactive_cache('123', 'unused')[:build_page] }
subject { integration.calculate_reactive_cache('123', 'unused')[:build_page] }
it 'returns a specific URL when status is 500' do
stub_request(status: 500)
@ -183,7 +176,7 @@ RSpec.describe Integrations::Bamboo, :use_clean_rails_memory_store_caching do
end
describe '#commit_status' do
subject { service.calculate_reactive_cache('123', 'unused')[:commit_status] }
subject { integration.calculate_reactive_cache('123', 'unused')[:commit_status] }
it 'sets commit status to :error when status is 500' do
stub_request(status: 500)

View File

@ -59,7 +59,9 @@ RSpec.describe 'Query.runner(id)' do
'accessLevel' => runner.access_level.to_s.upcase,
'runUntagged' => runner.run_untagged,
'ipAddress' => runner.ip_address,
'runnerType' => 'INSTANCE_TYPE'
'runnerType' => 'INSTANCE_TYPE',
'jobCount' => 0,
'projectCount' => nil
)
expect(runner_data['tagList']).to match_array runner.tag_list
end
@ -111,6 +113,51 @@ RSpec.describe 'Query.runner(id)' do
it_behaves_like 'runner details fetch', :inactive_runner
end
describe 'for multiple runners' do
let_it_be(:project1) { create(:project, :test_repo) }
let_it_be(:project2) { create(:project, :test_repo) }
let_it_be(:project_runner) do
create(:ci_runner, :project, projects: [project1, project2], description: 'Runner 1', contacted_at: 2.hours.ago,
active: true, version: 'adfe156', revision: 'a', locked: true, ip_address: '127.0.0.1', maximum_timeout: 600,
access_level: 0, run_untagged: true)
end
let!(:job) { create(:ci_build, runner: project_runner) }
context 'requesting project and job counts' do
let(:query) do
%(
query {
projectRunner: runner(id: "#{project_runner.to_global_id}") {
projectCount
jobCount
}
activeRunner: runner(id: "#{active_runner.to_global_id}") {
projectCount
jobCount
}
}
)
end
before do
post_graphql(query, current_user: user)
end
it 'retrieves expected fields' do
runner1_data = graphql_data_at(:project_runner)
runner2_data = graphql_data_at(:active_runner)
expect(runner1_data).to match a_hash_including(
'jobCount' => 1,
'projectCount' => 2)
expect(runner2_data).to match a_hash_including(
'jobCount' => 0,
'projectCount' => nil)
end
end
end
describe 'by regular user' do
let(:user) { create_default(:user) }
@ -127,11 +174,14 @@ RSpec.describe 'Query.runner(id)' do
def runner_query(runner)
<<~SINGLE
runner(id: "#{runner.to_global_id}") {
#{all_graphql_fields_for('CiRunner')}
#{all_graphql_fields_for('CiRunner', excluded: excluded_fields)}
}
SINGLE
end
# Currently excluding a known N+1 issue, see https://gitlab.com/gitlab-org/gitlab/-/issues/334759
let(:excluded_fields) { %w[jobCount] }
let(:single_query) do
<<~QUERY
{

View File

@ -5,7 +5,7 @@ const path = require('path');
const sass = require('node-sass'); // eslint-disable-line import/no-unresolved
const { buildIncludePaths, resolveGlobUrl } = require('node-sass-magic-importer/dist/toolbox'); // eslint-disable-line import/no-unresolved
const webpack = require('webpack');
const gitlabWebpackConfig = require('../../config/webpack.config.js');
const gitlabWebpackConfig = require('../../config/webpack.config');
const ROOT = path.resolve(__dirname, '../../');
const TRANSPARENT_1X1_PNG =

View File

@ -101,6 +101,38 @@ func TestConfigDefaults(t *testing.T) {
require.Equal(t, expectedCfg, cfg)
}
func TestCableConfigDefault(t *testing.T) {
backendURL, err := url.Parse("http://localhost:1234")
require.NoError(t, err)
args := []string{
"-authBackend", backendURL.String(),
}
boot, cfg, err := buildConfig("test", args)
require.NoError(t, err, "build config")
expectedBoot := &bootConfig{
secretPath: "./.gitlab_workhorse_secret",
listenAddr: "localhost:8181",
listenNetwork: "tcp",
logFormat: "text",
}
require.Equal(t, expectedBoot, boot)
expectedCfg := &config.Config{
Backend: backendURL,
CableBackend: backendURL,
Version: "(unknown version)",
DocumentRoot: "public",
ProxyHeadersTimeout: 5 * time.Minute,
APIQueueTimeout: queueing.DefaultTimeout,
APICILongPollingDuration: 50 * time.Nanosecond,
ImageResizerConfig: config.DefaultImageResizerConfig,
}
require.Equal(t, expectedCfg, cfg)
}
func TestConfigFlagParsing(t *testing.T) {
backendURL, err := url.Parse("http://localhost:1234")
require.NoError(t, err)

View File

@ -95,7 +95,7 @@ func buildConfig(arg0 string, args []string) (*bootConfig, *config.Config, error
fset.StringVar(&cfg.Socket, "authSocket", "", "Optional: Unix domain socket to dial authBackend at")
// actioncable backend
cableBackend := fset.String("cableBackend", upstream.DefaultBackend.String(), "ActionCable backend")
cableBackend := fset.String("cableBackend", "", "ActionCable backend")
fset.StringVar(&cfg.CableSocket, "cableSocket", "", "Optional: Unix domain socket to dial cableBackend at")
fset.StringVar(&cfg.DocumentRoot, "documentRoot", "public", "Path to static files content")
@ -123,9 +123,14 @@ func buildConfig(arg0 string, args []string) (*bootConfig, *config.Config, error
return nil, nil, fmt.Errorf("authBackend: %v", err)
}
cfg.CableBackend, err = parseAuthBackend(*cableBackend)
if err != nil {
return nil, nil, fmt.Errorf("cableBackend: %v", err)
if *cableBackend != "" {
// A custom -cableBackend has been specified
cfg.CableBackend, err = parseAuthBackend(*cableBackend)
if err != nil {
return nil, nil, fmt.Errorf("cableBackend: %v", err)
}
} else {
cfg.CableBackend = cfg.Backend
}
tomlData := ""