Add latest changes from gitlab-org/gitlab@master
This commit is contained in:
parent
82b4999167
commit
79b32f05d4
|
|
@ -538,3 +538,8 @@ Migration/ReferToIndexByName:
|
||||||
- !ruby/regexp /\Adb\/(post_)?migrate\/201.*\.rb\z/
|
- !ruby/regexp /\Adb\/(post_)?migrate\/201.*\.rb\z/
|
||||||
- !ruby/regexp /\Adb\/(post_)?migrate\/20200[1-7].*\.rb\z/
|
- !ruby/regexp /\Adb\/(post_)?migrate\/20200[1-7].*\.rb\z/
|
||||||
- !ruby/regexp /\Aee\/db\/geo\/(post_)?migrate\/201.*\.rb\z/
|
- !ruby/regexp /\Aee\/db\/geo\/(post_)?migrate\/201.*\.rb\z/
|
||||||
|
|
||||||
|
Migration/CreateTableWithForeignKeys:
|
||||||
|
# Disable this cop for all the existing migrations
|
||||||
|
Exclude:
|
||||||
|
- !ruby/regexp /\Adb\/(?:post_)?migrate\/(?:201[0-9]\d+|20200[0-8][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9])_.+\.rb\z/
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ module AlertManagement
|
||||||
create_alert_management_alert
|
create_alert_management_alert
|
||||||
end
|
end
|
||||||
|
|
||||||
process_incident_alert
|
process_incident_issues if process_issues?
|
||||||
end
|
end
|
||||||
|
|
||||||
def reset_alert_management_alert_status
|
def reset_alert_management_alert_status
|
||||||
|
|
@ -84,7 +84,7 @@ module AlertManagement
|
||||||
SystemNoteService.auto_resolve_prometheus_alert(issue, project, User.alert_bot) if issue.reset.closed?
|
SystemNoteService.auto_resolve_prometheus_alert(issue, project, User.alert_bot) if issue.reset.closed?
|
||||||
end
|
end
|
||||||
|
|
||||||
def process_incident_alert
|
def process_incident_issues
|
||||||
return unless alert.persisted?
|
return unless alert.persisted?
|
||||||
return if alert.issue
|
return if alert.issue
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,17 +17,21 @@ class IssuePlacementWorker
|
||||||
issue = Issue.id_in(issue_id).first
|
issue = Issue.id_in(issue_id).first
|
||||||
return unless issue
|
return unless issue
|
||||||
|
|
||||||
# Move the most recent 100 unpositioned items to the end.
|
# Move the oldest 100 unpositioned items to the end.
|
||||||
# This is to deal with out-of-order execution of the worker,
|
# This is to deal with out-of-order execution of the worker,
|
||||||
# while preserving creation order.
|
# while preserving creation order.
|
||||||
to_place = Issue
|
to_place = Issue
|
||||||
.relative_positioning_query_base(issue)
|
.relative_positioning_query_base(issue)
|
||||||
.where(relative_position: nil)
|
.where(relative_position: nil)
|
||||||
.order({ created_at: :desc }, { id: :desc })
|
.order({ created_at: :asc }, { id: :asc })
|
||||||
.limit(QUERY_LIMIT)
|
.limit(QUERY_LIMIT + 1)
|
||||||
|
.to_a
|
||||||
|
|
||||||
Issue.move_nulls_to_end(to_place.to_a.reverse)
|
leftover = to_place.pop if to_place.count > QUERY_LIMIT
|
||||||
|
|
||||||
|
Issue.move_nulls_to_end(to_place)
|
||||||
Issues::BaseService.new(nil).rebalance_if_needed(to_place.max_by(&:relative_position))
|
Issues::BaseService.new(nil).rebalance_if_needed(to_place.max_by(&:relative_position))
|
||||||
|
IssuePlacementWorker.perform_async(leftover.id) if leftover.present?
|
||||||
rescue RelativePositioning::NoSpaceLeft => e
|
rescue RelativePositioning::NoSpaceLeft => e
|
||||||
Gitlab::ErrorTracking.log_exception(e, issue_id: issue_id)
|
Gitlab::ErrorTracking.log_exception(e, issue_id: issue_id)
|
||||||
IssueRebalancingWorker.perform_async(nil, issue.project_id)
|
IssueRebalancingWorker.perform_async(nil, issue.project_id)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Place older issues before more recent ones
|
||||||
|
merge_request: 41602
|
||||||
|
author:
|
||||||
|
type: changed
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
title: Only create issues if supposed to for Prometheus alerts
|
||||||
|
merge_request: 41468
|
||||||
|
author:
|
||||||
|
type: fixed
|
||||||
|
|
@ -14596,6 +14596,11 @@ type Requirement {
|
||||||
"""
|
"""
|
||||||
iid: ID!
|
iid: ID!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Latest requirement test report state
|
||||||
|
"""
|
||||||
|
lastTestReportState: TestReportState
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Project to which the requirement belongs
|
Project to which the requirement belongs
|
||||||
"""
|
"""
|
||||||
|
|
@ -17656,6 +17661,11 @@ input UpdateRequirementInput {
|
||||||
"""
|
"""
|
||||||
iid: String!
|
iid: String!
|
||||||
|
|
||||||
|
"""
|
||||||
|
Creates a test report for the requirement with the given state
|
||||||
|
"""
|
||||||
|
lastTestReportState: TestReportState
|
||||||
|
|
||||||
"""
|
"""
|
||||||
The project full path the requirement is associated with
|
The project full path the requirement is associated with
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -42461,6 +42461,20 @@
|
||||||
"isDeprecated": false,
|
"isDeprecated": false,
|
||||||
"deprecationReason": null
|
"deprecationReason": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "lastTestReportState",
|
||||||
|
"description": "Latest requirement test report state",
|
||||||
|
"args": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"type": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "TestReportState",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"isDeprecated": false,
|
||||||
|
"deprecationReason": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "project",
|
"name": "project",
|
||||||
"description": "Project to which the requirement belongs",
|
"description": "Project to which the requirement belongs",
|
||||||
|
|
@ -51680,6 +51694,16 @@
|
||||||
},
|
},
|
||||||
"defaultValue": null
|
"defaultValue": null
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "lastTestReportState",
|
||||||
|
"description": "Creates a test report for the requirement with the given state",
|
||||||
|
"type": {
|
||||||
|
"kind": "ENUM",
|
||||||
|
"name": "TestReportState",
|
||||||
|
"ofType": null
|
||||||
|
},
|
||||||
|
"defaultValue": null
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "clientMutationId",
|
"name": "clientMutationId",
|
||||||
"description": "A unique identifier for the client performing the mutation.",
|
"description": "A unique identifier for the client performing the mutation.",
|
||||||
|
|
|
||||||
|
|
@ -2009,6 +2009,7 @@ Represents a requirement
|
||||||
| `createdAt` | Time! | Timestamp of when the requirement was created |
|
| `createdAt` | Time! | Timestamp of when the requirement was created |
|
||||||
| `id` | ID! | ID of the requirement |
|
| `id` | ID! | ID of the requirement |
|
||||||
| `iid` | ID! | Internal ID of the requirement |
|
| `iid` | ID! | Internal ID of the requirement |
|
||||||
|
| `lastTestReportState` | TestReportState | Latest requirement test report state |
|
||||||
| `project` | Project! | Project to which the requirement belongs |
|
| `project` | Project! | Project to which the requirement belongs |
|
||||||
| `state` | RequirementState! | State of the requirement |
|
| `state` | RequirementState! | State of the requirement |
|
||||||
| `title` | String | Title of the requirement |
|
| `title` | String | Title of the requirement |
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ ruby:
|
||||||
- bundle install
|
- bundle install
|
||||||
- bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
- bundle exec rspec --format progress --format RspecJunitFormatter --out rspec.xml
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
paths:
|
paths:
|
||||||
- rspec.xml
|
- rspec.xml
|
||||||
reports:
|
reports:
|
||||||
|
|
@ -116,6 +117,7 @@ golang:
|
||||||
- go get -u github.com/jstemmer/go-junit-report
|
- go get -u github.com/jstemmer/go-junit-report
|
||||||
- go test -v 2>&1 | go-junit-report -set-exit-code > report.xml
|
- go test -v 2>&1 | go-junit-report -set-exit-code > report.xml
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: report.xml
|
junit: report.xml
|
||||||
```
|
```
|
||||||
|
|
@ -137,6 +139,7 @@ java:
|
||||||
script:
|
script:
|
||||||
- gradle test
|
- gradle test
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: build/test-results/test/**/TEST-*.xml
|
junit: build/test-results/test/**/TEST-*.xml
|
||||||
```
|
```
|
||||||
|
|
@ -156,6 +159,7 @@ java:
|
||||||
script:
|
script:
|
||||||
- mvn verify
|
- mvn verify
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit:
|
junit:
|
||||||
- target/surefire-reports/TEST-*.xml
|
- target/surefire-reports/TEST-*.xml
|
||||||
|
|
@ -173,6 +177,7 @@ pytest:
|
||||||
script:
|
script:
|
||||||
- pytest --junitxml=report.xml
|
- pytest --junitxml=report.xml
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: report.xml
|
junit: report.xml
|
||||||
```
|
```
|
||||||
|
|
@ -194,6 +199,7 @@ cpp:
|
||||||
script:
|
script:
|
||||||
- gtest.exe --gtest_output="xml:report.xml"
|
- gtest.exe --gtest_output="xml:report.xml"
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: report.xml
|
junit: report.xml
|
||||||
```
|
```
|
||||||
|
|
@ -208,6 +214,7 @@ cunit:
|
||||||
script:
|
script:
|
||||||
- ./my-cunit-test
|
- ./my-cunit-test
|
||||||
artifacts:
|
artifacts:
|
||||||
|
when: always
|
||||||
reports:
|
reports:
|
||||||
junit: ./my-cunit-test.xml
|
junit: ./my-cunit-test.xml
|
||||||
```
|
```
|
||||||
|
|
|
||||||
|
|
@ -23,9 +23,9 @@ For more information on the database review process, check the [database review
|
||||||
Team members are encouraged to self-identify as database domain experts and add it to their [team profile](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/team.yml)
|
Team members are encouraged to self-identify as database domain experts and add it to their [team profile](https://gitlab.com/gitlab-com/www-gitlab-com/-/blob/master/data/team.yml)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
projects:
|
projects:
|
||||||
gitlab:
|
gitlab:
|
||||||
- reviewer database
|
- reviewer database
|
||||||
```
|
```
|
||||||
|
|
||||||
Assign the MR which adds your expertise to the `team.yml` file to a database maintainer
|
Assign the MR which adds your expertise to the `team.yml` file to a database maintainer
|
||||||
|
|
@ -70,9 +70,9 @@ they can update their [team profile](https://gitlab.com/gitlab-com/www-gitlab-co
|
||||||
to a `trainee_maintainer database`:
|
to a `trainee_maintainer database`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
projects:
|
projects:
|
||||||
gitlab:
|
gitlab:
|
||||||
- trainee_maintainer database
|
- trainee_maintainer database
|
||||||
```
|
```
|
||||||
|
|
||||||
The first step is to a create a [Trainee Database Maintainer Issue](https://gitlab.com/gitlab-com/www-gitlab-com/-/issues/new?issuable_template=trainee-database-maintainer).
|
The first step is to a create a [Trainee Database Maintainer Issue](https://gitlab.com/gitlab-com/www-gitlab-com/-/issues/new?issuable_template=trainee-database-maintainer).
|
||||||
|
|
|
||||||
|
|
@ -2155,7 +2155,7 @@ We store our Table of Contents in the `default-nav.yaml` file, in the
|
||||||
following line:
|
following line:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- category_title: GraphQL
|
- category_title: GraphQL
|
||||||
```
|
```
|
||||||
|
|
||||||
Be aware that CI tests for that second MR will fail with a bad link until the
|
Be aware that CI tests for that second MR will fail with a bad link until the
|
||||||
|
|
|
||||||
|
|
@ -147,7 +147,7 @@ request must also include the SHA256 sum of the file. An example JWT
|
||||||
payload looks like:
|
payload looks like:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
{ "data": { sha256: "31806bb23580caab78040f8c45d329f5016b0115" }, iat: "1234567890" }
|
{"data": {sha256: "31806bb23580caab78040f8c45d329f5016b0115"}, iat: "1234567890"}
|
||||||
```
|
```
|
||||||
|
|
||||||
If the requested file matches the requested SHA256 sum, then the Geo
|
If the requested file matches the requested SHA256 sum, then the Geo
|
||||||
|
|
|
||||||
|
|
@ -244,7 +244,7 @@ project_tree:
|
||||||
- :push_event_payload
|
- :push_event_payload
|
||||||
- issues:
|
- issues:
|
||||||
- events:
|
- events:
|
||||||
- ...
|
# ...
|
||||||
```
|
```
|
||||||
|
|
||||||
Only include the following attributes for the models specified:
|
Only include the following attributes for the models specified:
|
||||||
|
|
@ -254,8 +254,7 @@ included_attributes:
|
||||||
user:
|
user:
|
||||||
- :id
|
- :id
|
||||||
- :email
|
- :email
|
||||||
...
|
# ...
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Do not include the following attributes for the models specified:
|
Do not include the following attributes for the models specified:
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ shell check:
|
||||||
before_script:
|
before_script:
|
||||||
- shellcheck --version
|
- shellcheck --version
|
||||||
script:
|
script:
|
||||||
- shellcheck scripts/**/*.sh # path to your shell scripts
|
- shellcheck scripts/**/*.sh # path to your shell scripts
|
||||||
```
|
```
|
||||||
|
|
||||||
TIP: **Tip:**
|
TIP: **Tip:**
|
||||||
|
|
@ -93,7 +93,7 @@ shfmt:
|
||||||
before_script:
|
before_script:
|
||||||
- shfmt -version
|
- shfmt -version
|
||||||
script:
|
script:
|
||||||
- shfmt -i 2 -ci -d scripts # path to your shell scripts
|
- shfmt -i 2 -ci -d scripts # path to your shell scripts
|
||||||
```
|
```
|
||||||
|
|
||||||
TIP: **Tip:**
|
TIP: **Tip:**
|
||||||
|
|
|
||||||
|
|
@ -236,7 +236,7 @@ Implemented using Redis methods [PFADD](https://redis.io/commands/pfadd) and [PF
|
||||||
- name: i_compliance_credential_inventory
|
- name: i_compliance_credential_inventory
|
||||||
category: compliance
|
category: compliance
|
||||||
redis_slot: compliance
|
redis_slot: compliance
|
||||||
expiry: 42 # 6 weeks
|
expiry: 42 # 6 weeks
|
||||||
aggregation: weekly
|
aggregation: weekly
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative '../../migration_helpers'
|
||||||
|
|
||||||
|
module RuboCop
|
||||||
|
module Cop
|
||||||
|
module Migration
|
||||||
|
class CreateTableWithForeignKeys < RuboCop::Cop::Cop
|
||||||
|
include MigrationHelpers
|
||||||
|
|
||||||
|
MSG = 'Creating a table with more than one foreign key at once violates our migration style guide. ' \
|
||||||
|
'For more details check the https://docs.gitlab.com/ce/development/migration_style_guide.html#examples'
|
||||||
|
|
||||||
|
def_node_matcher :create_table_with_block?, <<~PATTERN
|
||||||
|
(block
|
||||||
|
(send nil? :create_table ...)
|
||||||
|
(args (arg _var))
|
||||||
|
_)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_search :belongs_to_and_references, <<~PATTERN
|
||||||
|
(send _var {:references :belongs_to} $...)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_search :foreign_key_options, <<~PATTERN
|
||||||
|
(_pair
|
||||||
|
{(sym :foreign_key) (str "foreign_key")}
|
||||||
|
{(hash _) (true)}
|
||||||
|
)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_search :to_table, <<~PATTERN
|
||||||
|
(_pair
|
||||||
|
{(sym :to_table) (str "to_table")} {(sym $...) (str $...)}
|
||||||
|
)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_search :argument_name, <<~PATTERN
|
||||||
|
{(sym $...) (str $...)}
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def_node_search :standalone_foreign_keys, <<~PATTERN
|
||||||
|
(send _var :foreign_key $...)
|
||||||
|
PATTERN
|
||||||
|
|
||||||
|
def on_send(node)
|
||||||
|
return unless in_migration?(node)
|
||||||
|
return unless node.command?(:create_table)
|
||||||
|
return unless create_table_with_block?(node.parent)
|
||||||
|
|
||||||
|
add_offense(node) if violates?(node.parent)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def violates?(node)
|
||||||
|
tables = all_target_tables(node).uniq
|
||||||
|
|
||||||
|
tables.length > 1 && !(tables & high_traffic_tables).empty?
|
||||||
|
end
|
||||||
|
|
||||||
|
def all_target_tables(node)
|
||||||
|
belongs_to_and_references_foreign_key_targets(node) + standalone_foreign_key_targets(node)
|
||||||
|
end
|
||||||
|
|
||||||
|
def belongs_to_and_references_foreign_key_targets(node)
|
||||||
|
belongs_to_and_references(node).select { |candidate| has_fk_option?(candidate) }
|
||||||
|
.flat_map { |definition| definition_to_table_names(definition) }
|
||||||
|
.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def standalone_foreign_key_targets(node)
|
||||||
|
standalone_foreign_keys(node).flat_map { |definition| definition_to_table_names(definition) }
|
||||||
|
.compact
|
||||||
|
end
|
||||||
|
|
||||||
|
def has_fk_option?(candidate)
|
||||||
|
foreign_key_options(candidate.last).first
|
||||||
|
end
|
||||||
|
|
||||||
|
def definition_to_table_names(definition)
|
||||||
|
table_name_from_options(definition.last) || arguments_to_table_names(definition)
|
||||||
|
end
|
||||||
|
|
||||||
|
def table_name_from_options(options)
|
||||||
|
to_table(options).to_a.first&.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def arguments_to_table_names(arguments)
|
||||||
|
arguments.flat_map { |argument| argument_name(argument).to_a.flatten.first.to_s.pluralize.to_sym }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -20,6 +20,10 @@ module RuboCop
|
||||||
|
|
||||||
TABLE_METHODS = %i(create_table create_table_if_not_exists change_table).freeze
|
TABLE_METHODS = %i(create_table create_table_if_not_exists change_table).freeze
|
||||||
|
|
||||||
|
def high_traffic_tables
|
||||||
|
@high_traffic_tables ||= rubocop_migrations_config.dig('Migration/UpdateLargeTable', 'DeniedTables')
|
||||||
|
end
|
||||||
|
|
||||||
# Returns true if the given node originated from the db/migrate directory.
|
# Returns true if the given node originated from the db/migrate directory.
|
||||||
def in_migration?(node)
|
def in_migration?(node)
|
||||||
in_deployment_migration?(node) || in_post_deployment_migration?(node)
|
in_deployment_migration?(node) || in_post_deployment_migration?(node)
|
||||||
|
|
@ -52,5 +56,13 @@ module RuboCop
|
||||||
def dirname(node)
|
def dirname(node)
|
||||||
File.dirname(node.location.expression.source_buffer.name)
|
File.dirname(node.location.expression.source_buffer.name)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def rubocop_migrations_config
|
||||||
|
@rubocop_migrations_config ||= YAML.load_file(File.join(rubocop_path, 'rubocop-migrations.yml'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def rubocop_path
|
||||||
|
File.expand_path(__dir__)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -8,20 +8,19 @@ RSpec.describe Gitlab::Metrics::Dashboard::Stages::TrackPanelType do
|
||||||
let(:project) { build_stubbed(:project) }
|
let(:project) { build_stubbed(:project) }
|
||||||
let(:environment) { build_stubbed(:environment, project: project) }
|
let(:environment) { build_stubbed(:environment, project: project) }
|
||||||
|
|
||||||
describe '#transform!' do
|
describe '#transform!', :snowplow do
|
||||||
subject { described_class.new(project, dashboard, environment: environment) }
|
subject { described_class.new(project, dashboard, environment: environment) }
|
||||||
|
|
||||||
let(:dashboard) { load_sample_dashboard.deep_symbolize_keys }
|
let(:dashboard) { load_sample_dashboard.deep_symbolize_keys }
|
||||||
|
|
||||||
it 'creates tracking event' do
|
it 'creates tracking event' do
|
||||||
stub_application_setting(snowplow_enabled: true, snowplow_collector_hostname: 'localhost')
|
|
||||||
allow(Gitlab::Tracking).to receive(:event).and_call_original
|
|
||||||
|
|
||||||
subject.transform!
|
subject.transform!
|
||||||
|
|
||||||
expect(Gitlab::Tracking).to have_received(:event)
|
expect_snowplow_event(
|
||||||
.with('MetricsDashboard::Chart', 'chart_rendered', { label: 'area-chart' })
|
category: 'MetricsDashboard::Chart',
|
||||||
.at_least(:once)
|
action: 'chart_rendered',
|
||||||
|
label: 'area-chart'
|
||||||
|
)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,191 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'fast_spec_helper'
|
||||||
|
require 'rubocop'
|
||||||
|
require_relative '../../../../rubocop/cop/migration/create_table_with_foreign_keys'
|
||||||
|
|
||||||
|
RSpec.describe RuboCop::Cop::Migration::CreateTableWithForeignKeys, type: :rubocop do
|
||||||
|
include CopHelper
|
||||||
|
|
||||||
|
let(:cop) { described_class.new }
|
||||||
|
|
||||||
|
context 'outside of a migration' do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
t.references :bar, foreign_key: { on_delete: 'cascade' }
|
||||||
|
t.references :zoo, foreign_key: { on_delete: 'cascade' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'in a migration' do
|
||||||
|
before do
|
||||||
|
allow(cop).to receive(:in_migration?).and_return(true)
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'without foreign key' do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
t.references :bar
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with foreign key' do
|
||||||
|
context 'with just one foreign key' do
|
||||||
|
context 'when the foreign_key targets a high traffic table' do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
t.references :project, "foreign_key" => { on_delete: 'cascade', to_table: 'projects' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the foreign_key does not target a high traffic table' do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
t.references :bar, foreign_key: { on_delete: 'cascade' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with more than one foreign keys' do
|
||||||
|
let(:offense) do
|
||||||
|
'Creating a table with more than one foreign key at once violates our migration style guide. ' \
|
||||||
|
'For more details check the https://docs.gitlab.com/ce/development/migration_style_guide.html#examples'
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples 'target to high traffic table' do |dsl_method, table_name|
|
||||||
|
context 'when the target is defined as option' do
|
||||||
|
it 'registers an offense ' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
^^^^^^^^^^^^^^^^^^ #{offense}
|
||||||
|
t.#{dsl_method} :#{table_name.singularize} #{explicit_target_opts}
|
||||||
|
t.#{dsl_method} :zoo #{implicit_target_opts}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the target has implicit definition' do
|
||||||
|
it 'registers an offense ' do
|
||||||
|
expect_offense(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
^^^^^^^^^^^^^^^^^^ #{offense}
|
||||||
|
t.#{dsl_method} :#{table_name.singularize} #{implicit_target_opts}
|
||||||
|
t.#{dsl_method} :zoo #{implicit_target_opts}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_context 'when there is a target to a high traffic table' do |dsl_method|
|
||||||
|
%w[
|
||||||
|
audit_events
|
||||||
|
ci_build_trace_sections
|
||||||
|
ci_builds
|
||||||
|
ci_builds_metadata
|
||||||
|
ci_job_artifacts
|
||||||
|
ci_pipeline_variables
|
||||||
|
ci_pipelines
|
||||||
|
ci_stages
|
||||||
|
deployments
|
||||||
|
events
|
||||||
|
gitlab_subscriptions
|
||||||
|
issues
|
||||||
|
merge_request_diff_commits
|
||||||
|
merge_request_diff_files
|
||||||
|
merge_request_diffs
|
||||||
|
merge_request_metrics
|
||||||
|
merge_requests
|
||||||
|
namespaces
|
||||||
|
note_diff_files
|
||||||
|
notes
|
||||||
|
project_authorizations
|
||||||
|
projects
|
||||||
|
project_ci_cd_settings
|
||||||
|
project_features
|
||||||
|
push_event_payloads
|
||||||
|
resource_label_events
|
||||||
|
routes
|
||||||
|
sent_notifications
|
||||||
|
system_note_metadata
|
||||||
|
taggings
|
||||||
|
todos
|
||||||
|
users
|
||||||
|
web_hook_logs
|
||||||
|
].each do |table|
|
||||||
|
let(:table_name) { table }
|
||||||
|
|
||||||
|
it_behaves_like 'target to high traffic table', dsl_method, table
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the foreign keys are defined as options' do
|
||||||
|
context 'when there is no target to a high traffic table' do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
t.references :bar, foreign_key: { on_delete: 'cascade', to_table: :bars }
|
||||||
|
t.references :zoo, 'foreign_key' => { on_delete: 'cascade' }
|
||||||
|
t.references :john, 'foreign_key' => { on_delete: 'cascade' }
|
||||||
|
t.references :doe, 'foreign_key' => { on_delete: 'cascade', to_table: 'doe' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
include_context 'when there is a target to a high traffic table', :references do
|
||||||
|
let(:explicit_target_opts) { ", foreign_key: { to_table: :#{table_name} }" }
|
||||||
|
let(:implicit_target_opts) { ", foreign_key: true" }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the foreign keys are defined by standlone migration helper' do
|
||||||
|
context 'when there is no target to a high traffic table' do
|
||||||
|
it 'does not register any offenses' do
|
||||||
|
expect_no_offenses(<<~RUBY)
|
||||||
|
def up
|
||||||
|
create_table(:foo) do |t|
|
||||||
|
t.foreign_key :bar
|
||||||
|
t.foreign_key :zoo, to_table: 'zoos'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
RUBY
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
include_context 'when there is a target to a high traffic table', :foreign_key do
|
||||||
|
let(:explicit_target_opts) { ", to_table: :#{table_name}" }
|
||||||
|
let(:implicit_target_opts) { }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
@ -10,7 +10,18 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#execute' do
|
describe '#execute' do
|
||||||
subject(:execute) { described_class.new(project, nil, payload).execute }
|
let(:service) { described_class.new(project, nil, payload) }
|
||||||
|
let(:incident_management_setting) { double(auto_close_incident?: auto_close_incident, create_issue?: create_issue) }
|
||||||
|
let(:auto_close_incident) { true }
|
||||||
|
let(:create_issue) { true }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow(service)
|
||||||
|
.to receive(:incident_management_setting)
|
||||||
|
.and_return(incident_management_setting)
|
||||||
|
end
|
||||||
|
|
||||||
|
subject(:execute) { service.execute }
|
||||||
|
|
||||||
context 'when alert payload is valid' do
|
context 'when alert payload is valid' do
|
||||||
let(:parsed_payload) { Gitlab::AlertManagement::Payload.parse(project, payload, monitoring_tool: 'Prometheus') }
|
let(:parsed_payload) { Gitlab::AlertManagement::Payload.parse(project, payload, monitoring_tool: 'Prometheus') }
|
||||||
|
|
@ -43,6 +54,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
||||||
|
|
||||||
it_behaves_like 'adds an alert management alert event'
|
it_behaves_like 'adds an alert management alert event'
|
||||||
|
it_behaves_like 'processes incident issues'
|
||||||
|
|
||||||
context 'existing alert is resolved' do
|
context 'existing alert is resolved' do
|
||||||
let!(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) }
|
let!(:alert) { create(:alert_management_alert, :resolved, project: project, fingerprint: fingerprint) }
|
||||||
|
|
@ -79,6 +91,12 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
execute
|
execute
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when auto-alert creation is disabled' do
|
||||||
|
let(:create_issue) { false }
|
||||||
|
|
||||||
|
it_behaves_like 'does not process incident issues'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when alert does not exist' do
|
context 'when alert does not exist' do
|
||||||
|
|
@ -89,13 +107,12 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
expect { subject }.to change(Note, :count).by(1)
|
expect { subject }.to change(Note, :count).by(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'processes the incident alert' do
|
it_behaves_like 'processes incident issues'
|
||||||
expect(IncidentManagement::ProcessAlertWorker)
|
|
||||||
.to receive(:perform_async)
|
|
||||||
.with(nil, nil, kind_of(Integer))
|
|
||||||
.once
|
|
||||||
|
|
||||||
expect(subject).to be_success
|
context 'when auto-alert creation is disabled' do
|
||||||
|
let(:create_issue) { false }
|
||||||
|
|
||||||
|
it_behaves_like 'does not process incident issues'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -114,12 +131,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
execute
|
execute
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not create incident issue' do
|
it_behaves_like 'does not process incident issues'
|
||||||
expect(IncidentManagement::ProcessAlertWorker)
|
|
||||||
.not_to receive(:perform_async)
|
|
||||||
|
|
||||||
expect(subject).to be_success
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it { is_expected.to be_success }
|
it { is_expected.to be_success }
|
||||||
|
|
@ -131,8 +143,6 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
let!(:alert) { create(:alert_management_alert, project: project, fingerprint: fingerprint) }
|
||||||
|
|
||||||
context 'when auto_resolve_incident set to true' do
|
context 'when auto_resolve_incident set to true' do
|
||||||
let_it_be(:operations_settings) { create(:project_incident_management_setting, project: project, auto_close_incident: true) }
|
|
||||||
|
|
||||||
context 'when status can be changed' do
|
context 'when status can be changed' do
|
||||||
it 'resolves an existing alert' do
|
it 'resolves an existing alert' do
|
||||||
expect { execute }.to change { alert.reload.resolved? }.to(true)
|
expect { execute }.to change { alert.reload.resolved? }.to(true)
|
||||||
|
|
@ -185,7 +195,7 @@ RSpec.describe AlertManagement::ProcessPrometheusAlertService do
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when auto_resolve_incident set to false' do
|
context 'when auto_resolve_incident set to false' do
|
||||||
let_it_be(:operations_settings) { create(:project_incident_management_setting, project: project, auto_close_incident: false) }
|
let(:auto_close_incident) { false }
|
||||||
|
|
||||||
it 'does not resolve an existing alert' do
|
it 'does not resolve an existing alert' do
|
||||||
expect { execute }.not_to change { alert.reload.resolved? }
|
expect { execute }.not_to change { alert.reload.resolved? }
|
||||||
|
|
|
||||||
|
|
@ -9,44 +9,6 @@ RSpec.describe Projects::Alerting::NotifyService do
|
||||||
allow(ProjectServiceWorker).to receive(:perform_async)
|
allow(ProjectServiceWorker).to receive(:perform_async)
|
||||||
end
|
end
|
||||||
|
|
||||||
shared_examples 'processes incident issues' do
|
|
||||||
let(:create_incident_service) { spy }
|
|
||||||
|
|
||||||
before do
|
|
||||||
allow_any_instance_of(AlertManagement::Alert).to receive(:execute_services)
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'processes issues' do
|
|
||||||
expect(IncidentManagement::ProcessAlertWorker)
|
|
||||||
.to receive(:perform_async)
|
|
||||||
.with(nil, nil, kind_of(Integer))
|
|
||||||
.once
|
|
||||||
|
|
||||||
Sidekiq::Testing.inline! do
|
|
||||||
expect(subject).to be_success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'does not process incident issues' do
|
|
||||||
it 'does not process issues' do
|
|
||||||
expect(IncidentManagement::ProcessAlertWorker)
|
|
||||||
.not_to receive(:perform_async)
|
|
||||||
|
|
||||||
expect(subject).to be_success
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
shared_examples 'does not process incident issues due to error' do |http_status:|
|
|
||||||
it 'does not process issues' do
|
|
||||||
expect(IncidentManagement::ProcessAlertWorker)
|
|
||||||
.not_to receive(:perform_async)
|
|
||||||
|
|
||||||
expect(subject).to be_error
|
|
||||||
expect(subject.http_status).to eq(http_status)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#execute' do
|
describe '#execute' do
|
||||||
let(:token) { 'invalid-token' }
|
let(:token) { 'invalid-token' }
|
||||||
let(:starts_at) { Time.current.change(usec: 0) }
|
let(:starts_at) { Time.current.change(usec: 0) }
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ module SnowplowHelpers
|
||||||
# end
|
# end
|
||||||
def expect_snowplow_event(category:, action:, **kwargs)
|
def expect_snowplow_event(category:, action:, **kwargs)
|
||||||
expect(Gitlab::Tracking).to have_received(:event)
|
expect(Gitlab::Tracking).to have_received(:event)
|
||||||
.with(category, action, **kwargs)
|
.with(category, action, **kwargs).at_least(:once)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Asserts that no call to `Gitlab::Tracking#event` was made.
|
# Asserts that no call to `Gitlab::Tracking#event` was made.
|
||||||
|
|
|
||||||
|
|
@ -39,3 +39,41 @@ RSpec.shared_examples 'adds an alert management alert event' do
|
||||||
subject
|
subject
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
RSpec.shared_examples 'processes incident issues' do
|
||||||
|
let(:create_incident_service) { spy }
|
||||||
|
|
||||||
|
before do
|
||||||
|
allow_any_instance_of(AlertManagement::Alert).to receive(:execute_services)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'processes issues' do
|
||||||
|
expect(IncidentManagement::ProcessAlertWorker)
|
||||||
|
.to receive(:perform_async)
|
||||||
|
.with(nil, nil, kind_of(Integer))
|
||||||
|
.once
|
||||||
|
|
||||||
|
Sidekiq::Testing.inline! do
|
||||||
|
expect(subject).to be_success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.shared_examples 'does not process incident issues' do
|
||||||
|
it 'does not process issues' do
|
||||||
|
expect(IncidentManagement::ProcessAlertWorker)
|
||||||
|
.not_to receive(:perform_async)
|
||||||
|
|
||||||
|
expect(subject).to be_success
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
RSpec.shared_examples 'does not process incident issues due to error' do |http_status:|
|
||||||
|
it 'does not process issues' do
|
||||||
|
expect(IncidentManagement::ProcessAlertWorker)
|
||||||
|
.not_to receive(:perform_async)
|
||||||
|
|
||||||
|
expect(subject).to be_error
|
||||||
|
expect(subject.http_status).to eq(http_status)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
||||||
|
|
@ -37,16 +37,38 @@ RSpec.describe IssuePlacementWorker do
|
||||||
described_class.new.perform(issue.id)
|
described_class.new.perform(issue.id)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'limits the sweep to QUERY_LIMIT records' do
|
context 'there are more than QUERY_LIMIT unplaced issues' do
|
||||||
# Ensure there are more than N issues in this set
|
before_all do
|
||||||
n = described_class::QUERY_LIMIT
|
# Ensure there are more than N issues in this set
|
||||||
create_list(:issue, n - 5, **unplaced)
|
n = described_class::QUERY_LIMIT
|
||||||
|
create_list(:issue, n - 5, **unplaced)
|
||||||
|
end
|
||||||
|
|
||||||
expect(Issue).to receive(:move_nulls_to_end).with(have_attributes(count: n)).and_call_original
|
it 'limits the sweep to QUERY_LIMIT records, and reschedules placement' do
|
||||||
|
expect(Issue).to receive(:move_nulls_to_end)
|
||||||
|
.with(have_attributes(count: described_class::QUERY_LIMIT))
|
||||||
|
.and_call_original
|
||||||
|
|
||||||
described_class.new.perform(issue.id)
|
expect(described_class).to receive(:perform_async).with(issue_d.id)
|
||||||
|
|
||||||
expect(project.issues.where(relative_position: nil)).to exist
|
described_class.new.perform(issue.id)
|
||||||
|
|
||||||
|
expect(project.issues.where(relative_position: nil)).to exist
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'is eventually correct' do
|
||||||
|
prefix = project.issues.where.not(relative_position: nil).order(:relative_position).to_a
|
||||||
|
moved = project.issues.where.not(id: prefix.map(&:id))
|
||||||
|
|
||||||
|
described_class.new.perform(issue.id)
|
||||||
|
|
||||||
|
expect(project.issues.where(relative_position: nil)).to exist
|
||||||
|
|
||||||
|
described_class.new.perform(issue.id)
|
||||||
|
|
||||||
|
expect(project.issues.where(relative_position: nil)).not_to exist
|
||||||
|
expect(project.issues.order(:relative_position)).to eq(prefix + moved.order(:created_at, :id))
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'anticipates the failure to find the issue' do
|
it 'anticipates the failure to find the issue' do
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue