Add latest changes from gitlab-org/gitlab@master

This commit is contained in:
GitLab Bot 2020-11-23 09:09:13 +00:00
parent 45205f0db5
commit 8132e39e1b
29 changed files with 341 additions and 176 deletions

View File

@ -30,6 +30,7 @@ If applicable, any groups/projects that are happy to have this feature turned on
## Roll Out Steps
- [ ] Confirm that QA tests pass with the feature flag enabled (if you're unsure how, contact the relevant [stable counterpart in the Quality department](https://about.gitlab.com/handbook/engineering/quality/#individual-contributors))
- [ ] Enable on staging (`/chatops run feature set feature_name true --staging`)
- [ ] Test on staging
- [ ] Ensure that documentation has been updated

View File

@ -7,6 +7,7 @@ import {
GlSearchBoxByType,
GlSprintf,
GlButton,
GlAlert,
} from '@gitlab/ui';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import { __, s__ } from '~/locale';
@ -25,6 +26,7 @@ export default {
GlDropdownItem,
GlSearchBoxByType,
GlSprintf,
GlAlert,
},
props: {
knative: {
@ -106,12 +108,13 @@ export default {
<template>
<div class="row">
<div
<gl-alert
v-if="knative.updateFailed"
class="bs-callout bs-callout-danger cluster-application-banner col-12 mt-2 mb-2 js-cluster-knative-domain-name-failure-message"
class="gl-mb-5 col-12 js-cluster-knative-domain-name-failure-message"
variant="danger"
>
{{ s__('ClusterIntegration|Something went wrong while updating Knative domain name.') }}
</div>
</gl-alert>
<div
:class="{ 'col-md-6': knativeInstalled, 'col-12': !knativeInstalled }"

View File

@ -58,22 +58,6 @@
}
}
.cluster-application-banner {
height: 45px;
display: flex;
align-items: center;
justify-content: space-between;
}
.cluster-application-banner-close {
align-self: flex-start;
font-weight: 500;
font-size: 20px;
color: $orange-500;
opacity: 1;
margin: $gl-padding-8 14px 0 0;
}
.cluster-application-description {
flex: 1;
}

View File

@ -1,6 +1,15 @@
# frozen_string_literal: true
module OptimizedIssuableLabelFilter
extend ActiveSupport::Concern
prepended do
extend Gitlab::Cache::RequestCache
# Avoid repeating label queries times when the finder is instantiated multiple times during the request.
request_cache(:find_label_ids) { [root_namespace.id, params.label_names] }
end
def by_label(items)
return items unless params.labels?
@ -41,7 +50,7 @@ module OptimizedIssuableLabelFilter
def issuables_with_selected_labels(items, target_model)
if root_namespace
all_label_ids = find_label_ids(root_namespace)
all_label_ids = find_label_ids
# Found less labels in the DB than we were searching for. Return nothing.
return items.none if all_label_ids.size != params.label_names.size
@ -57,18 +66,20 @@ module OptimizedIssuableLabelFilter
items
end
def find_label_ids(root_namespace)
finder_params = {
include_subgroups: true,
include_ancestor_groups: true,
include_descendant_groups: true,
group: root_namespace,
title: params.label_names
}
def find_label_ids
group_labels = Label
.where(project_id: nil)
.where(title: params.label_names)
.where(group_id: root_namespace.self_and_descendants.select(:id))
LabelsFinder
.new(nil, finder_params)
.execute(skip_authorization: true)
project_labels = Label
.where(group_id: nil)
.where(title: params.label_names)
.where(project_id: Project.select(:id).where(namespace_id: root_namespace.self_and_descendants.select(:id)))
Label
.from_union([group_labels, project_labels], remove_duplicates: false)
.reorder(nil)
.pluck(:title, :id)
.group_by(&:first)
.values

View File

@ -149,7 +149,7 @@ class DiffNote < Note
end
def supported?
for_commit? || for_design? || self.noteable.has_complete_diff_refs?
for_commit? || for_design? || self.noteable&.has_complete_diff_refs?
end
def set_line_code

View File

@ -58,6 +58,8 @@
= render "projects/merge_requests/description"
= render "projects/merge_requests/widget"
= render "projects/merge_requests/awards_block"
- if mr_action === "show"
- add_page_startup_api_call discussions_path(@merge_request)
#js-vue-mr-discussions{ data: { notes_data: notes_data(@merge_request).to_json,
noteable_data: serialize_issuable(@merge_request, serializer: 'noteable'),
noteable_type: 'MergeRequest',

View File

@ -0,0 +1,5 @@
---
title: Convert knative error alert to glalert
merge_request: 47840
author:
type: changed

View File

@ -0,0 +1,5 @@
---
title: Repopulate historical vulnerability statistics
merge_request: 48128
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Avoid exception when validating diff_note support
merge_request: 48187
author:
type: fixed

View File

@ -0,0 +1,5 @@
---
title: Replace wrong index definition on labels (project_id, title)
merge_request: 48238
author:
type: other

View File

@ -250,6 +250,8 @@
- 1
- - project_service
- 1
- - project_template_export
- 1
- - project_update_repository_storage
- 1
- - prometheus_create_default_alerts

View File

@ -33,6 +33,8 @@ comparison_cmd = [
comment = `cat #{markdown_result}`
markdown(<<~MARKDOWN)
unless comment.strip.empty?
markdown(<<~MARKDOWN)
#{comment}
MARKDOWN
MARKDOWN
end

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
class ReplaceUnusedLabelsIndex < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
NEW_INDEX_NAME = 'index_labels_on_group_id_and_title_with_null_project_id'
OLD_INDEX_NAME = 'index_labels_on_group_id_and_title'
def up
add_concurrent_index :labels, [:group_id, :title], where: 'project_id IS NULL', name: NEW_INDEX_NAME
remove_concurrent_index_by_name :labels, OLD_INDEX_NAME
end
def down
add_concurrent_index :labels, [:group_id, :title], where: 'project_id = NULL::integer', name: OLD_INDEX_NAME
remove_concurrent_index_by_name :labels, NEW_INDEX_NAME
end
end

View File

@ -0,0 +1,31 @@
# frozen_string_literal: true
class ScheduleRepopulateHistoricalVulnerabilityStatistics < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
BATCH_SIZE = 50
DELAY_INTERVAL = 5.minutes
MIGRATION_CLASS = 'PopulateVulnerabilityHistoricalStatistics'
DAY_COUNT = 365
disable_ddl_transaction!
class ProjectSetting < ActiveRecord::Base
include EachBatch
self.table_name = 'project_settings'
scope :has_vulnerabilities, -> { where('has_vulnerabilities IS TRUE') }
end
def up
ProjectSetting.has_vulnerabilities.each_batch(of: BATCH_SIZE) do |batch, index|
migrate_in(index * DELAY_INTERVAL, MIGRATION_CLASS, [batch.pluck(:project_id), DAY_COUNT])
end
end
def down
# no-op
end
end

View File

@ -0,0 +1 @@
9ff8ddefff1df81f1eac2ccfc6f3019bb77a6129280e799c0abe54f51e09277a

View File

@ -0,0 +1 @@
8b60a6bc892f9700df81de9909595544f9f820621a210906a249428ddec9eefa

View File

@ -21202,7 +21202,7 @@ CREATE UNIQUE INDEX index_label_priorities_on_project_id_and_label_id ON label_p
CREATE UNIQUE INDEX index_labels_on_group_id_and_project_id_and_title ON labels USING btree (group_id, project_id, title);
CREATE INDEX index_labels_on_group_id_and_title ON labels USING btree (group_id, title) WHERE (project_id = NULL::integer);
CREATE INDEX index_labels_on_group_id_and_title_with_null_project_id ON labels USING btree (group_id, title) WHERE (project_id IS NULL);
CREATE INDEX index_labels_on_project_id ON labels USING btree (project_id);

View File

@ -49,7 +49,6 @@ addressed.
},
# Add your experiment here:
signup_flow: {
environment: ::Gitlab.dev_env_or_com?, # Target environment, defaults to enabled for development and GitLab.com
tracking_category: 'Growth::Activation::Experiment::SignUpFlow' # Used for providing the category when setting up tracking data
}
}.freeze

View File

@ -929,6 +929,13 @@ Gitlab::Elastic::Indexer::Error: time="2020-01-23T09:13:00Z" level=fatal msg="he
You probably have not used either `http://` or `https://` as part of your value in the **"URL"** field of the Elasticsearch Integration Menu. Please make sure you are using either `http://` or `https://` in this field as the [Elasticsearch client for Go](https://github.com/olivere/elastic) that we are using [needs the prefix for the URL to be accepted as valid](https://github.com/olivere/elastic/commit/a80af35aa41856dc2c986204e2b64eab81ccac3a).
Once you have corrected the formatting of the URL, delete the index (via the [dedicated Rake task](#gitlab-advanced-search-rake-tasks)) and [reindex the content of your instance](#enabling-advanced-search).
### My Elasticsearch cluster has a plugin and the integration is not working
Certain 3rd party plugins may introduce bugs in your cluster or for whatever
reason may be incompatible with our integration. You should try disabling
plugins so you can rule out the possibility that the plugin is causing the
problem.
### Low-level troubleshooting
There is a [more structured, lower-level troubleshooting document](../administration/troubleshooting/elasticsearch.md) for when you experience other issues, including poor performance.

View File

@ -4,7 +4,6 @@
#
# Utility module for A/B testing experimental features. Define your experiments in the `EXPERIMENTS` constant.
# Experiment options:
# - environment (optional, defaults to enabled for development and GitLab.com)
# - tracking_category (optional, used to set the category when tracking an experiment event)
# - use_backwards_compatible_subject_index (optional, set this to true if you need backwards compatibility)
#
@ -87,14 +86,13 @@ module Gitlab
class << self
def experiment(key)
Experiment.new(EXPERIMENTS[key].merge(key: key))
Gitlab::Experimentation::Experiment.new(key, **EXPERIMENTS[key])
end
def enabled?(experiment_key)
return false unless EXPERIMENTS.key?(experiment_key)
experiment = experiment(experiment_key)
experiment.enabled_for_environment? && experiment.enabled?
experiment(experiment_key).enabled?
end
def enabled_for_attribute?(experiment_key, attribute)
@ -106,36 +104,5 @@ module Gitlab
enabled?(experiment_key) && experiment(experiment_key).enabled_for_index?(value)
end
end
Experiment = Struct.new(
:key,
:environment,
:tracking_category,
:use_backwards_compatible_subject_index,
keyword_init: true
) do
def enabled?
experiment_percentage > 0
end
def enabled_for_environment?
return ::Gitlab.dev_env_or_com? if environment.nil?
environment
end
def enabled_for_index?(index)
return false if index.blank?
index <= experiment_percentage
end
private
# When a feature does not exist, the `percentage_of_time_value` method will return 0
def experiment_percentage
@experiment_percentage ||= Feature.get(:"#{key}_experiment_percentage").percentage_of_time_value # rubocop:disable Gitlab/AvoidFeatureGet
end
end
end
end

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
module Gitlab
module Experimentation
class Experiment
attr_reader :tracking_category, :use_backwards_compatible_subject_index
def initialize(key, **params)
@tracking_category = params[:tracking_category]
@use_backwards_compatible_subject_index = params[:use_backwards_compatible_subject_index]
@experiment_percentage = Feature.get(:"#{key}_experiment_percentage").percentage_of_time_value # rubocop:disable Gitlab/AvoidFeatureGet
end
def enabled?
::Gitlab.dev_env_or_com? && experiment_percentage > 0
end
def enabled_for_index?(index)
return false if index.blank?
index <= experiment_percentage
end
private
attr_reader :experiment_percentage
end
end
end

View File

@ -31,7 +31,8 @@ RSpec.describe 'Resolve an open thread in a merge request by creating an issue',
visit project_merge_request_path(project, merge_request)
end
it 'does not show a link to create a new issue' do
# https://gitlab.com/gitlab-org/gitlab/-/issues/285453
xit 'does not show a link to create a new issue' do
expect(page).not_to have_css resolve_discussion_selector
end
end

View File

@ -6,12 +6,10 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
backwards_compatible_test_experiment: {
environment: environment,
tracking_category: 'Team',
use_backwards_compatible_subject_index: true
},
test_experiment: {
environment: environment,
tracking_category: 'Team'
}
}
@ -21,7 +19,6 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
Feature.enable_percentage_of_time(:test_experiment_experiment_percentage, enabled_percentage)
end
let(:environment) { Rails.env.test? }
let(:enabled_percentage) { 10 }
controller(ApplicationController) do
@ -391,10 +388,8 @@ RSpec.describe Gitlab::Experimentation::ControllerConcern, type: :controller do
context 'do not track' do
before do
stub_experiment(test_experiment: true)
allow(controller).to receive(:current_user).and_return(user)
allow_next_instance_of(described_class) do |instance|
allow(instance).to receive(:experiment_enabled?).with(:test_experiment).and_return(false)
end
end
context 'is disabled' do

View File

@ -0,0 +1,55 @@
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::Experimentation::Experiment do
using RSpec::Parameterized::TableSyntax
let(:percentage) { 50 }
let(:params) do
{
tracking_category: 'Category1',
use_backwards_compatible_subject_index: true
}
end
before do
feature = double('FeatureFlag', percentage_of_time_value: percentage )
expect(Feature).to receive(:get).with(:experiment_key_experiment_percentage).and_return(feature)
end
subject(:experiment) { described_class.new(:experiment_key, **params) }
describe '#enabled?' do
before do
allow(Gitlab).to receive(:dev_env_or_com?).and_return(on_gitlab_com)
end
subject { experiment.enabled? }
where(:on_gitlab_com, :percentage, :is_enabled) do
true | 0 | false
true | 10 | true
false | 0 | false
false | 10 | false
end
with_them do
it { is_expected.to eq(is_enabled) }
end
end
describe '#enabled_for_index?' do
subject { experiment.enabled_for_index?(index) }
where(:index, :percentage, :is_enabled) do
50 | 40 | false
40 | 50 | true
nil | 50 | false
end
with_them do
it { is_expected.to eq(is_enabled) }
end
end
end

View File

@ -33,27 +33,25 @@ RSpec.describe Gitlab::Experimentation, :snowplow do
before do
stub_const('Gitlab::Experimentation::EXPERIMENTS', {
backwards_compatible_test_experiment: {
environment: environment,
tracking_category: 'Team',
use_backwards_compatible_subject_index: true
},
test_experiment: {
environment: environment,
tracking_category: 'Team'
}
})
Feature.enable_percentage_of_time(:backwards_compatible_test_experiment_experiment_percentage, enabled_percentage)
Feature.enable_percentage_of_time(:test_experiment_experiment_percentage, enabled_percentage)
allow(Gitlab).to receive(:com?).and_return(true)
end
let(:environment) { Rails.env.test? }
let(:enabled_percentage) { 10 }
describe '.enabled?' do
subject { described_class.enabled?(:test_experiment) }
context 'feature toggle is enabled, we are on the right environment and we are selected' do
context 'feature toggle is enabled and we are selected' do
it { is_expected.to be_truthy }
end
@ -68,20 +66,6 @@ RSpec.describe Gitlab::Experimentation, :snowplow do
it { is_expected.to be_falsey }
end
describe 'we are on the wrong environment' do
let(:environment) { ::Gitlab.com? }
it { is_expected.to be_falsey }
it 'ensures the typically less expensive environment is checked before the more expensive call to database for Feature' do
expect_next_instance_of(described_class::Experiment) do |experiment|
expect(experiment).not_to receive(:enabled?)
end
subject
end
end
end
describe '.enabled_for_value?' do

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe ScheduleRepopulateHistoricalVulnerabilityStatistics do
let(:namespaces) { table(:namespaces) }
let(:projects) { table(:projects) }
let(:project_settings) { table(:project_settings) }
let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
let!(:project_1) { projects.create!(namespace_id: namespace.id, name: 'foo_1') }
let!(:project_2) { projects.create!(namespace_id: namespace.id, name: 'foo_2') }
let!(:project_3) { projects.create!(namespace_id: namespace.id, name: 'foo_3') }
let!(:project_4) { projects.create!(namespace_id: namespace.id, name: 'foo_4') }
around do |example|
freeze_time { Sidekiq::Testing.fake! { example.run } }
end
before do
stub_const("#{described_class.name}::BATCH_SIZE", 1)
project_settings.create!(project_id: project_1.id, has_vulnerabilities: true)
project_settings.create!(project_id: project_2.id, has_vulnerabilities: false)
project_settings.create!(project_id: project_4.id, has_vulnerabilities: true)
end
it 'schedules the background jobs', :aggregate_failures do
migrate!
expect(BackgroundMigrationWorker.jobs.size).to be(2)
expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(described_class::DELAY_INTERVAL, [project_1.id], described_class::DAY_COUNT)
expect(described_class::MIGRATION_CLASS).to be_scheduled_delayed_migration(2 * described_class::DELAY_INTERVAL, [project_4.id], described_class::DAY_COUNT)
end
end

View File

@ -38,6 +38,14 @@ RSpec.describe DiffNote do
it_behaves_like 'a valid diff positionable note' do
subject { build(:diff_note_on_commit, project: project, commit_id: commit_id, position: position) }
end
it "is not valid when noteable is empty" do
note = build(:diff_note_on_merge_request, project: project, noteable: nil)
note.valid?
expect(note.errors[:noteable]).to include("doesn't support new-style diff notes")
end
end
describe "#position=" do

View File

@ -0,0 +1,82 @@
# frozen_string_literal: true
RSpec.shared_examples 'export worker' do
describe '#perform' do
let!(:user) { create(:user) }
let!(:project) { create(:project) }
before do
allow_next_instance_of(described_class) do |job|
allow(job).to receive(:jid).and_return(SecureRandom.hex(8))
end
end
context 'when it succeeds' do
it 'calls the ExportService' do
expect_next_instance_of(::Projects::ImportExport::ExportService) do |service|
expect(service).to receive(:execute)
end
subject.perform(user.id, project.id, { 'klass' => 'Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy' })
end
context 'export job' do
before do
allow_next_instance_of(::Projects::ImportExport::ExportService) do |service|
allow(service).to receive(:execute)
end
end
it 'creates an export job record for the project' do
expect { subject.perform(user.id, project.id, {}) }.to change { project.export_jobs.count }.from(0).to(1)
end
it 'sets the export job status to started' do
expect_next_instance_of(ProjectExportJob) do |job|
expect(job).to receive(:start)
end
subject.perform(user.id, project.id, {})
end
it 'sets the export job status to finished' do
expect_next_instance_of(ProjectExportJob) do |job|
expect(job).to receive(:finish)
end
subject.perform(user.id, project.id, {})
end
end
end
context 'when it fails' do
it 'does not raise an exception when strategy is invalid' do
expect(::Projects::ImportExport::ExportService).not_to receive(:new)
expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error
end
it 'does not raise error when project cannot be found' do
expect { subject.perform(user.id, non_existing_record_id, {}) }.not_to raise_error
end
it 'does not raise error when user cannot be found' do
expect { subject.perform(non_existing_record_id, project.id, {}) }.not_to raise_error
end
end
end
describe 'sidekiq options' do
it 'disables retry' do
expect(described_class.sidekiq_options['retry']).to eq(false)
end
it 'disables dead' do
expect(described_class.sidekiq_options['dead']).to eq(false)
end
it 'sets default status expiration' do
expect(described_class.sidekiq_options['status_expiration']).to eq(StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION)
end
end
end

View File

@ -3,84 +3,5 @@
require 'spec_helper'
RSpec.describe ProjectExportWorker do
let!(:user) { create(:user) }
let!(:project) { create(:project) }
subject { described_class.new }
describe '#perform' do
before do
allow_next_instance_of(described_class) do |job|
allow(job).to receive(:jid).and_return(SecureRandom.hex(8))
end
end
context 'when it succeeds' do
it 'calls the ExportService' do
expect_next_instance_of(::Projects::ImportExport::ExportService) do |service|
expect(service).to receive(:execute)
end
subject.perform(user.id, project.id, { 'klass' => 'Gitlab::ImportExport::AfterExportStrategies::DownloadNotificationStrategy' })
end
context 'export job' do
before do
allow_next_instance_of(::Projects::ImportExport::ExportService) do |service|
allow(service).to receive(:execute)
end
end
it 'creates an export job record for the project' do
expect { subject.perform(user.id, project.id, {}) }.to change { project.export_jobs.count }.from(0).to(1)
end
it 'sets the export job status to started' do
expect_next_instance_of(ProjectExportJob) do |job|
expect(job).to receive(:start)
end
subject.perform(user.id, project.id, {})
end
it 'sets the export job status to finished' do
expect_next_instance_of(ProjectExportJob) do |job|
expect(job).to receive(:finish)
end
subject.perform(user.id, project.id, {})
end
end
end
context 'when it fails' do
it 'does not raise an exception when strategy is invalid' do
expect(::Projects::ImportExport::ExportService).not_to receive(:new)
expect { subject.perform(user.id, project.id, { 'klass' => 'Whatever' }) }.not_to raise_error
end
it 'does not raise error when project cannot be found' do
expect { subject.perform(user.id, non_existing_record_id, {}) }.not_to raise_error
end
it 'does not raise error when user cannot be found' do
expect { subject.perform(non_existing_record_id, project.id, {}) }.not_to raise_error
end
end
end
describe 'sidekiq options' do
it 'disables retry' do
expect(described_class.sidekiq_options['retry']).to eq(false)
end
it 'disables dead' do
expect(described_class.sidekiq_options['dead']).to eq(false)
end
it 'sets default status expiration' do
expect(described_class.sidekiq_options['status_expiration']).to eq(StuckExportJobsWorker::EXPORT_JOBS_EXPIRATION)
end
end
it_behaves_like 'export worker'
end