Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
bcc77054
Commit
bcc77054
authored
Feb 11, 2020
by
GitLab Bot
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add latest changes from gitlab-org/gitlab@master
parent
05b5c609
Changes
24
Show whitespace changes
Inline
Side-by-side
Showing
24 changed files
with
849 additions
and
83 deletions
+849
-83
app/assets/javascripts/blob/file_template_mediator.js
app/assets/javascripts/blob/file_template_mediator.js
+1
-5
app/assets/javascripts/code_navigation/store/actions.js
app/assets/javascripts/code_navigation/store/actions.js
+1
-1
app/models/container_expiration_policy.rb
app/models/container_expiration_policy.rb
+1
-1
app/models/incident_management/project_incident_management_setting.rb
...ncident_management/project_incident_management_setting.rb
+34
-0
app/models/project.rb
app/models/project.rb
+2
-0
app/services/incident_management/create_issue_service.rb
app/services/incident_management/create_issue_service.rb
+126
-0
app/views/projects/blob/_blob.html.haml
app/views/projects/blob/_blob.html.haml
+1
-1
app/workers/all_queues.yml
app/workers/all_queues.yml
+6
-0
app/workers/concerns/worker_context.rb
app/workers/concerns/worker_context.rb
+1
-1
app/workers/container_expiration_policy_worker.rb
app/workers/container_expiration_policy_worker.rb
+6
-4
app/workers/incident_management/process_alert_worker.rb
app/workers/incident_management/process_alert_worker.rb
+29
-0
changelogs/unreleased/show_selected_template_type.yml
changelogs/unreleased/show_selected_template_type.yml
+5
-0
config/prometheus/self_monitoring_default.yml
config/prometheus/self_monitoring_default.yml
+39
-0
doc/ci/environments.md
doc/ci/environments.md
+8
-5
doc/user/project/deploy_boards.md
doc/user/project/deploy_boards.md
+3
-3
spec/factories/incident_management/project_incident_management_settings.rb
...cident_management/project_incident_management_settings.rb
+10
-0
spec/features/issues/issue_detail_spec.rb
spec/features/issues/issue_detail_spec.rb
+5
-3
spec/features/projects/files/template_type_dropdown_spec.rb
spec/features/projects/files/template_type_dropdown_spec.rb
+5
-0
spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb
.../lib/gitlab/graphql/connections/keyset/connection_spec.rb
+93
-57
spec/models/container_expiration_policy_spec.rb
spec/models/container_expiration_policy_spec.rb
+2
-2
spec/models/incident_management/project_incident_management_setting_spec.rb
...nt_management/project_incident_management_setting_spec.rb
+111
-0
spec/services/incident_management/create_issue_service_spec.rb
...services/incident_management/create_issue_service_spec.rb
+311
-0
spec/workers/concerns/worker_context_spec.rb
spec/workers/concerns/worker_context_spec.rb
+10
-0
spec/workers/incident_management/process_alert_worker_spec.rb
.../workers/incident_management/process_alert_worker_spec.rb
+39
-0
No files found.
app/assets/javascripts/blob/file_template_mediator.js
View file @
bcc77054
...
...
@@ -117,11 +117,7 @@ export default class FileTemplateMediator {
selector
.
hide
();
}
});
if
(
this
.
editor
.
getValue
()
!==
''
)
{
this
.
setTypeSelectorToggleText
(
item
.
name
);
}
this
.
cacheToggleText
();
}
...
...
app/assets/javascripts/code_navigation/store/actions.js
View file @
bcc77054
...
...
@@ -16,7 +16,7 @@ export default {
commit
(
types
.
REQUEST_DATA
);
api
.
lsifData
(
state
.
projectPath
,
state
.
commitId
,
state
.
p
ath
)
.
lsifData
(
state
.
projectPath
,
state
.
commitId
,
state
.
blobP
ath
)
.
then
(({
data
})
=>
{
const
normalizedData
=
data
.
reduce
((
acc
,
d
)
=>
{
if
(
d
.
hover
)
{
...
...
app/models/container_expiration_policy.rb
View file @
bcc77054
...
...
@@ -14,7 +14,7 @@ class ContainerExpirationPolicy < ApplicationRecord
validates
:keep_n
,
inclusion:
{
in:
->
(
_
)
{
self
.
keep_n_options
.
keys
}
},
allow_nil:
true
scope
:active
,
->
{
where
(
enabled:
true
)
}
scope
:preloaded
,
->
{
preload
(
:project
)
}
scope
:preloaded
,
->
{
preload
(
project:
[
:route
]
)
}
def
self
.
keep_n_options
{
...
...
app/models/incident_management/project_incident_management_setting.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
module
IncidentManagement
class
ProjectIncidentManagementSetting
<
ApplicationRecord
include
Gitlab
::
Utils
::
StrongMemoize
belongs_to
:project
validate
:issue_template_exists
,
if: :create_issue?
def
available_issue_templates
Gitlab
::
Template
::
IssueTemplate
.
all
(
project
)
end
def
issue_template_content
strong_memoize
(
:issue_template_content
)
do
issue_template
&
.
content
if
issue_template_key
.
present?
end
end
private
def
issue_template_exists
return
unless
issue_template_key
.
present?
errors
.
add
(
:issue_template_key
,
'not found'
)
unless
issue_template
end
def
issue_template
Gitlab
::
Template
::
IssueTemplate
.
find
(
issue_template_key
,
project
)
rescue
Gitlab
::
Template
::
Finders
::
RepoTemplateFinder
::
FileNotFoundError
end
end
end
app/models/project.rb
View file @
bcc77054
...
...
@@ -187,6 +187,7 @@ class Project < ApplicationRecord
has_one
:import_state
,
autosave:
true
,
class_name:
'ProjectImportState'
,
inverse_of: :project
has_one
:import_export_upload
,
dependent: :destroy
# rubocop:disable Cop/ActiveRecordDependent
has_one
:project_repository
,
inverse_of: :project
has_one
:incident_management_setting
,
inverse_of: :project
,
class_name:
'IncidentManagement::ProjectIncidentManagementSetting'
has_one
:error_tracking_setting
,
inverse_of: :project
,
class_name:
'ErrorTracking::ProjectErrorTrackingSetting'
has_one
:metrics_setting
,
inverse_of: :project
,
class_name:
'ProjectMetricsSetting'
has_one
:grafana_integration
,
inverse_of: :project
...
...
@@ -316,6 +317,7 @@ class Project < ApplicationRecord
allow_destroy:
true
,
reject_if:
->
(
attrs
)
{
attrs
[
:id
].
blank?
&&
attrs
[
:url
].
blank?
}
accepts_nested_attributes_for
:incident_management_setting
,
update_only:
true
accepts_nested_attributes_for
:error_tracking_setting
,
update_only:
true
accepts_nested_attributes_for
:metrics_setting
,
update_only:
true
,
allow_destroy:
true
accepts_nested_attributes_for
:grafana_integration
,
update_only:
true
,
allow_destroy:
true
...
...
app/services/incident_management/create_issue_service.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
module
IncidentManagement
class
CreateIssueService
<
BaseService
include
Gitlab
::
Utils
::
StrongMemoize
INCIDENT_LABEL
=
{
title:
'incident'
,
color:
'#CC0033'
,
description:
<<~
DESCRIPTION
.
chomp
Denotes a disruption to IT services and \
the associated issues require immediate attention
DESCRIPTION
}.
freeze
def
initialize
(
project
,
params
)
super
(
project
,
User
.
alert_bot
,
params
)
end
def
execute
return
error_with
(
'setting disabled'
)
unless
incident_management_setting
.
create_issue?
return
error_with
(
'invalid alert'
)
unless
alert
.
valid?
issue
=
create_issue
return
error_with
(
issue_errors
(
issue
))
unless
issue
.
valid?
success
(
issue:
issue
)
end
private
def
create_issue
issue
=
do_create_issue
(
label_ids:
issue_label_ids
)
# Create an unlabelled issue if we couldn't create the issue
# due to labels errors.
# See https://gitlab.com/gitlab-org/gitlab-foss/issues/65042
if
issue
.
errors
.
include?
(
:labels
)
log_label_error
(
issue
)
issue
=
do_create_issue
end
issue
end
def
do_create_issue
(
**
params
)
Issues
::
CreateService
.
new
(
project
,
current_user
,
title:
issue_title
,
description:
issue_description
,
**
params
).
execute
end
def
issue_title
alert
.
full_title
end
def
issue_description
horizontal_line
=
"
\n
---
\n\n
"
[
alert_summary
,
alert_markdown
,
issue_template_content
].
compact
.
join
(
horizontal_line
)
end
def
issue_label_ids
[
find_or_create_label
(
**
INCIDENT_LABEL
)
].
compact
.
map
(
&
:id
)
end
def
find_or_create_label
(
**
params
)
Labels
::
FindOrCreateService
.
new
(
current_user
,
project
,
**
params
)
.
execute
end
def
alert_summary
alert
.
issue_summary_markdown
end
def
alert_markdown
alert
.
alert_markdown
end
def
alert
strong_memoize
(
:alert
)
do
Gitlab
::
Alerting
::
Alert
.
new
(
project:
project
,
payload:
params
).
present
end
end
def
issue_template_content
incident_management_setting
.
issue_template_content
end
def
incident_management_setting
strong_memoize
(
:incident_management_setting
)
do
project
.
incident_management_setting
||
project
.
build_incident_management_setting
end
end
def
issue_errors
(
issue
)
issue
.
errors
.
full_messages
.
to_sentence
end
def
log_label_error
(
issue
)
log_info
<<~
TEXT
.
chomp
Cannot create incident issue with labels \
#{
issue
.
labels
.
map
(
&
:title
).
inspect
}
\
for "
#{
project
.
full_name
}
":
#{
issue
.
errors
.
full_messages
.
to_sentence
}
.
Retrying without labels.
TEXT
end
def
error_with
(
message
)
log_error
(
%{Cannot create incident issue for "#{project.full_name}": #{message}}
)
error
(
message
)
end
end
end
app/views/projects/blob/_blob.html.haml
View file @
bcc77054
...
...
@@ -10,7 +10,7 @@
#blob-content-holder
.blob-content-holder
-
if
native_code_navigation_enabled?
(
@project
)
#js-code-navigation
{
data:
{
commit_id:
blob
.
commit_id
,
path:
blob
.
path
,
project_path:
@project
.
full_path
}
}
#js-code-navigation
{
data:
{
commit_id:
blob
.
commit_id
,
blob_
path:
blob
.
path
,
project_path:
@project
.
full_path
}
}
%article
.file-holder
=
render
'projects/blob/header'
,
blob:
blob
=
render
'projects/blob/content'
,
blob:
blob
app/workers/all_queues.yml
View file @
bcc77054
...
...
@@ -429,6 +429,12 @@
:latency_sensitive:
:resource_boundary: :unknown
:weight:
1
-
:name: incident_management:incident_management_process_alert
:feature_category: :incident_management
:has_external_dependencies:
:latency_sensitive:
:resource_boundary: :unknown
:weight:
2
-
:name: mail_scheduler:mail_scheduler_issue_due
:feature_category: :issue_tracking
:has_external_dependencies:
...
...
app/workers/concerns/worker_context.rb
View file @
bcc77054
...
...
@@ -60,6 +60,6 @@ module WorkerContext
end
def
with_context
(
context
,
&
block
)
Gitlab
::
ApplicationContext
.
new
(
context
).
use
(
&
block
)
Gitlab
::
ApplicationContext
.
new
(
context
).
use
{
yield
(
**
context
)
}
end
end
app/workers/container_expiration_policy_worker.rb
View file @
bcc77054
...
...
@@ -2,15 +2,17 @@
class
ContainerExpirationPolicyWorker
include
ApplicationWorker
include
CronjobQueue
# rubocop:disable Scalability/CronWorkerContext
include
CronjobQueue
feature_category
:container_registry
def
perform
ContainerExpirationPolicy
.
runnable_schedules
.
preloaded
.
find_each
do
|
container_expiration_policy
|
ContainerExpirationPolicyService
.
new
(
container_expiration_policy
.
project
,
container_expiration_policy
.
project
.
owner
).
execute
(
container_expiration_policy
)
with_context
(
project:
container_expiration_policy
.
project
,
user:
container_expiration_policy
.
project
.
owner
)
do
|
project
:,
user
:|
ContainerExpirationPolicyService
.
new
(
project
,
user
)
.
execute
(
container_expiration_policy
)
end
end
end
end
app/workers/incident_management/process_alert_worker.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
module
IncidentManagement
class
ProcessAlertWorker
include
ApplicationWorker
queue_namespace
:incident_management
feature_category
:incident_management
def
perform
(
project_id
,
alert
)
project
=
find_project
(
project_id
)
return
unless
project
create_issue
(
project
,
alert
)
end
private
def
find_project
(
project_id
)
Project
.
find_by_id
(
project_id
)
end
def
create_issue
(
project
,
alert
)
IncidentManagement
::
CreateIssueService
.
new
(
project
,
alert
)
.
execute
end
end
end
changelogs/unreleased/show_selected_template_type.yml
0 → 100644
View file @
bcc77054
---
title
:
Show selected template type when clicked
merge_request
:
24596
author
:
type
:
fixed
config/prometheus/self_monitoring_default.yml
0 → 100644
View file @
bcc77054
dashboard
:
'
Default
dashboard'
priority
:
1
panel_groups
:
-
group
:
Web Service
panels
:
-
title
:
Web Service - Error Ratio
type
:
line-chart
y_label
:
"
Unhandled
Exceptions
(%)"
metrics
:
-
id
:
wser_web_service
query_range
:
'
max(max_over_time(gitlab_service_errors:ratio{environment="{{ci_environment_slug}}",
type="web",
stage="main"}[1m]))
by
(type)
*
100'
unit
:
"
%"
label
:
"
Error
Ratio"
-
id
:
wser_degradation_slo
query_range
:
'
avg(slo:max:gitlab_service_errors:ratio{environment="{{ci_environment_slug}}",
type="web",
stage="main"})
or
avg(slo:max:gitlab_service_errors:ratio{type="web"})
*
100'
unit
:
"
%"
label
:
"
Degradation
SLO"
-
id
:
wser_outage_slo
query_range
:
'
2
*
(avg(slo:max:gitlab_service_errors:ratio{environment="{{ci_environment_slug}}",
type="web",
stage="main"})
or
avg(slo:max:gitlab_service_errors:ratio{type="web"}))
*
100'
unit
:
"
%"
label
:
"
Outage
SLO"
-
group
:
API Service
panels
:
-
title
:
API Service - Error Ratio
type
:
line-chart
y_label
:
"
Unhandled
Exceptions
(%)"
metrics
:
-
id
:
aser_web_service
query_range
:
'
max(max_over_time(gitlab_service_errors:ratio{environment="{{ci_environment_slug}}",
type="api",
stage="main"}[1m]))
by
(type)
*
100'
unit
:
"
%"
label
:
"
Error
Ratio"
-
id
:
aser_degradation_slo
query_range
:
'
avg(slo:max:gitlab_service_errors:ratio{environment="{{ci_environment_slug}}",
type="api",
stage="main"})
or
avg(slo:max:gitlab_service_errors:ratio{type="web"})
*
100'
unit
:
"
%"
label
:
"
Degradation
SLO"
-
id
:
aser_outage_slo
query_range
:
'
2
*
(avg(slo:max:gitlab_service_errors:ratio{environment="{{ci_environment_slug}}",
type="api",
stage="main"})
or
avg(slo:max:gitlab_service_errors:ratio{type="web"}))
*
100'
unit
:
"
%"
label
:
"
Outage
SLO"
doc/ci/environments.md
View file @
bcc77054
...
...
@@ -115,11 +115,14 @@ With this configuration, we:
-
Lastly we deploy to the staging server.
NOTE:
**Note:**
The
`environment`
keyword is just a hint for GitLab that this job actually
deploys to the
`name`
environment. It can also have a
`url`
that is
exposed in various places within GitLab. Each time a job that
has an environment specified succeeds, a deployment is recorded, storing
the Git SHA and environment name.
The
`environment`
keyword defines where the app is deployed.
The environment
`name`
and
`url`
is exposed in various places
within GitLab. Each time a job that has an environment specified
succeeds, a deployment is recorded, along with the Git SHA and environment name.
CAUTION:
**Caution**
:
Some characters are not allowed in environment names. Use only letters,
numbers, spaces, and
`-`
,
`_`
,
`/`
,
`{`
,
`}`
, or
`.`
. Also, it must not start nor end with
`/`
.
In summary, with the above
`.gitlab-ci.yml`
we have achieved the following:
...
...
doc/user/project/deploy_boards.md
View file @
bcc77054
...
...
@@ -27,7 +27,7 @@ deploy rolling out. The percentage is the percent of the pods that are updated
to the latest release.
Since Deploy Boards are tightly coupled with Kubernetes, there is some required
knowledge. In particular you should be familiar with:
knowledge. In particular
,
you should be familiar with:
-
[
Kubernetes pods
](
https://kubernetes.io/docs/concepts/workloads/pods/pod/
)
-
[
Kubernetes labels
](
https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/
)
...
...
@@ -37,7 +37,7 @@ knowledge. In particular you should be familiar with:
## Use cases
Since the Deploy Board is a visual representation of the Kubernetes pods for a
specific environment, there are
lot of uses
cases. To name a few:
specific environment, there are
a lot of use
cases. To name a few:
-
You want to promote what's running in staging, to production. You go to the
environments list, verify that what's running in staging is what you think is
...
...
@@ -65,7 +65,7 @@ To display the Deploy Boards for a specific [environment] you should:
NOTE:
**Running on OpenShift:**
If you are using OpenShift, ensure that you're using the
`Deployment`
resource
instead of
`DeploymentConfiguration`
, otherwise
the Deploy Boards won't render
instead of
`DeploymentConfiguration`
. Otherwise,
the Deploy Boards won't render
correctly. For more information, read the
[
OpenShift docs
](
https://docs.openshift.com/container-platform/3.7/dev_guide/deployments/kubernetes_deployments.html#kubernetes-deployments-vs-deployment-configurations
)
and
[
GitLab issue #4584
](
https://gitlab.com/gitlab-org/gitlab/issues/4584
)
.
...
...
spec/factories/incident_management/project_incident_management_settings.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
FactoryBot
.
define
do
factory
:project_incident_management_setting
,
class:
'IncidentManagement::ProjectIncidentManagementSetting'
do
project
create_issue
{
false
}
issue_template_key
{
nil
}
send_email
{
false
}
end
end
spec/features/issues/issue_detail_spec.rb
View file @
bcc77054
...
...
@@ -23,16 +23,18 @@ describe 'Issue Detail', :js do
context
'when issue description has xss snippet'
do
before
do
issue
.
update!
(
description:
'![xss" onload=alert(1);//](a)'
)
sign_in
(
user
)
visit
project_issue_path
(
project
,
issue
)
wait_for_requests
end
it
'encodes the description to prevent xss issues'
do
page
.
within
(
'.issuable-details .detail-page-description'
)
do
image
=
find
(
'img.js-lazy-loaded'
)
expect
(
page
).
to
have_selector
(
'img'
,
count:
1
)
expect
(
find
(
'img'
)
[
'onerror'
]).
to
be_nil
expect
(
find
(
'img'
)
[
'src'
]).
to
end_with
(
'/a'
)
expect
(
image
[
'onerror'
]).
to
be_nil
expect
(
image
[
'src'
]).
to
end_with
(
'/a'
)
end
end
end
...
...
spec/features/projects/files/template_type_dropdown_spec.rb
View file @
bcc77054
...
...
@@ -75,6 +75,11 @@ describe 'Projects > Files > Template type dropdown selector', :js do
check_type_selector_toggle_text
(
'.gitignore'
)
end
it
'sets the toggle text when selecting the template type'
do
select_template_type
(
'.gitignore'
)
check_type_selector_toggle_text
(
'.gitignore'
)
end
it
'selects every template type correctly'
do
try_selecting_all_types
end
...
...
spec/lib/gitlab/graphql/connections/keyset/connection_spec.rb
View file @
bcc77054
...
...
@@ -103,93 +103,119 @@ describe Gitlab::Graphql::Connections::Keyset::Connection do
end
end
context
'when multiple orders are defined'
do
let!
(
:project1
)
{
create
(
:project
,
last_repository_check_at:
10
.
days
.
ago
)
}
# Asc: project5 Desc: project3
let!
(
:project2
)
{
create
(
:project
,
last_repository_check_at:
nil
)
}
# Asc: project1 Desc: project1
let!
(
:project3
)
{
create
(
:project
,
last_repository_check_at:
5
.
days
.
ago
)
}
# Asc: project3 Desc: project5
let!
(
:project4
)
{
create
(
:project
,
last_repository_check_at:
nil
)
}
# Asc: project2 Desc: project2
let!
(
:project5
)
{
create
(
:project
,
last_repository_check_at:
20
.
days
.
ago
)
}
# Asc: project4 Desc: project4
context
'when ascending'
do
let
(
:nodes
)
do
Project
.
order
(
Arel
.
sql
(
'projects.last_repository_check_at IS NULL'
)).
order
(
last_repository_check_at: :asc
).
order
(
id: :asc
)
end
shared_examples
'nodes are in ascending order'
do
context
'when no cursor is passed'
do
let
(
:arguments
)
{
{}
}
it
'returns projects in ascending order'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project5
,
project1
,
project3
,
project2
,
project4
]
)
expect
(
subject
.
sliced_nodes
).
to
eq
(
ascending_nodes
)
end
end
context
'when before cursor value is
NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project4
)
}
}
context
'when before cursor value is not
NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
ascending_nodes
[
2
]
)
}
}
it
'returns all projects before the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project5
,
project1
,
project3
,
project2
]
)
expect
(
subject
.
sliced_nodes
).
to
eq
(
ascending_nodes
.
first
(
2
)
)
end
end
context
'when before
cursor value is not NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project3
)
}
}
context
'when after
cursor value is not NULL'
do
let
(
:arguments
)
{
{
after:
encoded_cursor
(
ascending_nodes
[
1
]
)
}
}
it
'returns all projects before
the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project5
,
project1
]
)
it
'returns all projects after
the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
(
ascending_nodes
.
last
(
3
)
)
end
end
context
'when after cursor value is NULL
'
do
let
(
:arguments
)
{
{
after:
encoded_cursor
(
project2
)
}
}
context
'when before and after cursor
'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
ascending_nodes
.
last
),
after:
encoded_cursor
(
ascending_nodes
.
first
)
}
}
it
'returns all projects after the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project4
])
expect
(
subject
.
sliced_nodes
).
to
eq
(
ascending_nodes
[
1
..
3
])
end
end
end
shared_examples
'nodes are in descending order'
do
context
'when no cursor is passed'
do
let
(
:arguments
)
{
{}
}
it
'only returns projects in descending order'
do
expect
(
subject
.
sliced_nodes
).
to
eq
(
descending_nodes
)
end
end
context
'when before cursor value is not NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
descending_nodes
[
2
])
}
}
it
'returns all projects before the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
(
descending_nodes
.
first
(
2
))
end
end
context
'when after cursor value is not NULL'
do
let
(
:arguments
)
{
{
after:
encoded_cursor
(
project1
)
}
}
let
(
:arguments
)
{
{
after:
encoded_cursor
(
descending_nodes
[
1
]
)
}
}
it
'returns all projects after the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project3
,
project2
,
project4
]
)
expect
(
subject
.
sliced_nodes
).
to
eq
(
descending_nodes
.
last
(
3
)
)
end
end
context
'when before and after cursor'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project4
),
after:
encoded_cursor
(
project5
)
}
}
let
(
:arguments
)
{
{
before:
encoded_cursor
(
descending_nodes
.
last
),
after:
encoded_cursor
(
descending_nodes
.
first
)
}
}
it
'returns all projects after the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project1
,
project3
,
project2
])
expect
(
subject
.
sliced_nodes
).
to
eq
(
descending_nodes
[
1
..
3
])
end
end
end
context
'when descending'
do
context
'when multiple orders with nil values are defined'
do
let!
(
:project1
)
{
create
(
:project
,
last_repository_check_at:
10
.
days
.
ago
)
}
# Asc: project5 Desc: project3
let!
(
:project2
)
{
create
(
:project
,
last_repository_check_at:
nil
)
}
# Asc: project1 Desc: project1
let!
(
:project3
)
{
create
(
:project
,
last_repository_check_at:
5
.
days
.
ago
)
}
# Asc: project3 Desc: project5
let!
(
:project4
)
{
create
(
:project
,
last_repository_check_at:
nil
)
}
# Asc: project2 Desc: project2
let!
(
:project5
)
{
create
(
:project
,
last_repository_check_at:
20
.
days
.
ago
)
}
# Asc: project4 Desc: project4
context
'when ascending'
do
let
(
:nodes
)
do
Project
.
order
(
Arel
.
sql
(
'projects.last_repository_check_at IS NULL'
)).
order
(
last_repository_check_at: :
de
sc
).
order
(
id: :asc
)
Project
.
order
(
Arel
.
sql
(
'projects.last_repository_check_at IS NULL'
)).
order
(
last_repository_check_at: :
a
sc
).
order
(
id: :asc
)
end
let
(
:ascending_nodes
)
{
[
project5
,
project1
,
project3
,
project2
,
project4
]
}
context
'when no cursor is passed'
do
let
(
:arguments
)
{
{}
}
it
'only returns projects in descending order'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project3
,
project1
,
project5
,
project2
,
project4
])
end
end
it_behaves_like
'nodes are in ascending order'
context
'when before cursor value is NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project4
)
}
}
it
'returns all projects before the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project
3
,
project1
,
project5
,
project2
])
expect
(
subject
.
sliced_nodes
).
to
eq
([
project
5
,
project1
,
project3
,
project2
])
end
end
context
'when before cursor value is not NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project5
)
}
}
context
'when after cursor value is NULL'
do
let
(
:arguments
)
{
{
after:
encoded_cursor
(
project2
)
}
}
it
'returns all projects after the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project4
])
end
end
end
context
'when descending'
do
let
(
:nodes
)
do
Project
.
order
(
Arel
.
sql
(
'projects.last_repository_check_at IS NULL'
)).
order
(
last_repository_check_at: :desc
).
order
(
id: :asc
)
end
let
(
:descending_nodes
)
{
[
project3
,
project1
,
project5
,
project2
,
project4
]
}
it_behaves_like
'nodes are in descending order'
context
'when before cursor value is NULL'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project4
)
}
}
it
'returns all projects before the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project3
,
project1
])
expect
(
subject
.
sliced_nodes
).
to
eq
([
project3
,
project1
,
project5
,
project2
])
end
end
...
...
@@ -200,22 +226,32 @@ describe Gitlab::Graphql::Connections::Keyset::Connection do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project4
])
end
end
context
'when after cursor value is not NULL'
do
let
(
:arguments
)
{
{
after:
encoded_cursor
(
project1
)
}
}
it
'returns all projects after the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project5
,
project2
,
project4
])
end
end
context
'when before and after cursor'
do
let
(
:arguments
)
{
{
before:
encoded_cursor
(
project4
),
after:
encoded_cursor
(
project3
)
}
}
context
'when ordering uses LOWER'
do
let!
(
:project1
)
{
create
(
:project
,
name:
'A'
)
}
# Asc: project1 Desc: project4
let!
(
:project2
)
{
create
(
:project
,
name:
'c'
)
}
# Asc: project5 Desc: project2
let!
(
:project3
)
{
create
(
:project
,
name:
'b'
)
}
# Asc: project3 Desc: project3
let!
(
:project4
)
{
create
(
:project
,
name:
'd'
)
}
# Asc: project2 Desc: project5
let!
(
:project5
)
{
create
(
:project
,
name:
'a'
)
}
# Asc: project4 Desc: project1
it
'returns all projects after the cursor'
do
expect
(
subject
.
sliced_nodes
).
to
eq
([
project1
,
project5
,
project2
])
context
'when ascending'
do
let
(
:nodes
)
do
Project
.
order
(
Arel
::
Table
.
new
(
:projects
)[
'name'
].
lower
.
asc
).
order
(
id: :asc
)
end
let
(
:ascending_nodes
)
{
[
project1
,
project5
,
project3
,
project2
,
project4
]
}
it_behaves_like
'nodes are in ascending order'
end
context
'when descending'
do
let
(
:nodes
)
do
Project
.
order
(
Arel
::
Table
.
new
(
:projects
)[
'name'
].
lower
.
desc
).
order
(
id: :desc
)
end
let
(
:descending_nodes
)
{
[
project4
,
project2
,
project3
,
project5
,
project1
]
}
it_behaves_like
'nodes are in descending order'
end
end
...
...
spec/models/container_expiration_policy_spec.rb
View file @
bcc77054
...
...
@@ -49,9 +49,9 @@ RSpec.describe ContainerExpirationPolicy, type: :model do
it
'preloads the associations'
do
subject
query
=
ActiveRecord
::
QueryRecorder
.
new
{
subject
.
each
(
&
:project
)
}
query
=
ActiveRecord
::
QueryRecorder
.
new
{
subject
.
map
(
&
:project
).
map
(
&
:full_path
)
}
expect
(
query
.
count
).
to
eq
(
2
)
expect
(
query
.
count
).
to
eq
(
3
)
end
end
...
...
spec/models/incident_management/project_incident_management_setting_spec.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
require
'spec_helper'
describe
IncidentManagement
::
ProjectIncidentManagementSetting
do
let_it_be
(
:project
)
{
create
(
:project
,
:repository
,
create_templates: :issue
)
}
describe
'Associations'
do
it
{
is_expected
.
to
belong_to
(
:project
)
}
end
describe
'Validations'
do
describe
'validate issue_template_exists'
do
subject
{
build
(
:project_incident_management_setting
,
project:
project
)
}
context
'with create_issue enabled'
do
before
do
subject
.
create_issue
=
true
end
context
'with valid issue_template_key'
do
before
do
subject
.
issue_template_key
=
'bug'
end
it
{
is_expected
.
to
be_valid
}
end
context
'with empty issue_template_key'
do
before
do
subject
.
issue_template_key
=
''
end
it
{
is_expected
.
to
be_valid
}
end
context
'with nil issue_template_key'
do
before
do
subject
.
issue_template_key
=
nil
end
it
{
is_expected
.
to
be_valid
}
end
context
'with invalid issue_template_key'
do
before
do
subject
.
issue_template_key
=
'unknown'
end
it
{
is_expected
.
to
be_invalid
}
it
'returns error'
do
subject
.
valid?
expect
(
subject
.
errors
[
:issue_template_key
]).
to
eq
([
'not found'
])
end
end
end
context
'with create_issue disabled'
do
before
do
subject
.
create_issue
=
false
end
context
'with unknown issue_template_key'
do
before
do
subject
.
issue_template_key
=
'unknown'
end
it
{
is_expected
.
to
be_valid
}
end
end
end
end
describe
'#issue_template_content'
do
subject
{
build
(
:project_incident_management_setting
,
project:
project
)
}
shared_examples
'no content'
do
it
'returns no content'
do
expect
(
subject
.
issue_template_content
).
to
be_nil
end
end
context
'with valid issue_template_key'
do
before
do
subject
.
issue_template_key
=
'bug'
end
it
'returns issue content'
do
expect
(
subject
.
issue_template_content
).
to
eq
(
'something valid'
)
end
end
context
'with unknown issue_template_key'
do
before
do
subject
.
issue_template_key
=
'unknown'
end
it_behaves_like
'no content'
end
context
'without issue_template_key'
do
before
do
subject
.
issue_template_key
=
nil
end
it_behaves_like
'no content'
end
end
end
spec/services/incident_management/create_issue_service_spec.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
require
'spec_helper'
describe
IncidentManagement
::
CreateIssueService
do
let
(
:project
)
{
create
(
:project
,
:repository
,
:private
)
}
let
(
:user
)
{
User
.
alert_bot
}
let
(
:service
)
{
described_class
.
new
(
project
,
alert_payload
)
}
let
(
:alert_starts_at
)
{
Time
.
now
}
let
(
:alert_title
)
{
'TITLE'
}
let
(
:alert_annotations
)
{
{
title:
alert_title
}
}
let
(
:alert_payload
)
do
build_alert_payload
(
annotations:
alert_annotations
,
starts_at:
alert_starts_at
)
end
let
(
:alert_presenter
)
do
Gitlab
::
Alerting
::
Alert
.
new
(
project:
project
,
payload:
alert_payload
).
present
end
let!
(
:setting
)
do
create
(
:project_incident_management_setting
,
project:
project
)
end
subject
{
service
.
execute
}
context
'when create_issue enabled'
do
let
(
:issue
)
{
subject
[
:issue
]
}
let
(
:summary_separator
)
{
"
\n
---
\n\n
"
}
before
do
setting
.
update!
(
create_issue:
true
)
end
context
'without issue_template_content'
do
it
'creates an issue with alert summary only'
do
expect
(
subject
).
to
include
(
status: :success
)
expect
(
issue
.
author
).
to
eq
(
user
)
expect
(
issue
.
title
).
to
eq
(
alert_title
)
expect
(
issue
.
description
).
to
include
(
alert_presenter
.
issue_summary_markdown
.
strip
)
expect
(
separator_count
(
issue
.
description
)).
to
eq
0
end
end
context
'with erroneous issue service'
do
let
(
:invalid_issue
)
do
build
(
:issue
,
project:
project
,
title:
nil
).
tap
(
&
:valid?
)
end
let
(
:issue_error
)
{
invalid_issue
.
errors
.
full_messages
.
to_sentence
}
it
'returns and logs the issue error'
do
expect_next_instance_of
(
Issues
::
CreateService
)
do
|
issue_service
|
expect
(
issue_service
).
to
receive
(
:execute
).
and_return
(
invalid_issue
)
end
expect
(
service
)
.
to
receive
(
:log_error
)
.
with
(
error_message
(
issue_error
))
expect
(
subject
).
to
include
(
status: :error
,
message:
issue_error
)
end
end
shared_examples
'GFM template'
do
context
'plain content'
do
let
(
:template_content
)
{
'some content'
}
it
'creates an issue appending issue template'
do
expect
(
subject
).
to
include
(
status: :success
)
expect
(
issue
.
description
).
to
include
(
alert_presenter
.
issue_summary_markdown
)
expect
(
separator_count
(
issue
.
description
)).
to
eq
1
expect
(
issue
.
description
).
to
include
(
template_content
)
end
end
context
'quick actions'
do
let
(
:user
)
{
create
(
:user
)
}
let
(
:plain_text
)
{
'some content'
}
let
(
:template_content
)
do
<<~
CONTENT
#{
plain_text
}
/due tomorrow
/assign @
#{
user
.
username
}
CONTENT
end
before
do
project
.
add_maintainer
(
user
)
end
it
'creates an issue interpreting quick actions'
do
expect
(
subject
).
to
include
(
status: :success
)
expect
(
issue
.
description
).
to
include
(
plain_text
)
expect
(
issue
.
due_date
).
to
be_present
expect
(
issue
.
assignees
).
to
eq
([
user
])
end
end
end
context
'with gitlab_incident_markdown'
do
let
(
:alert_annotations
)
do
{
title:
alert_title
,
gitlab_incident_markdown:
template_content
}
end
it_behaves_like
'GFM template'
end
context
'with issue_template_content'
do
before
do
create_issue_template
(
'bug'
,
template_content
)
setting
.
update!
(
issue_template_key:
'bug'
)
end
it_behaves_like
'GFM template'
context
'and gitlab_incident_markdown'
do
let
(
:template_content
)
{
'plain text'
}
let
(
:alt_template
)
{
'alternate text'
}
let
(
:alert_annotations
)
do
{
title:
alert_title
,
gitlab_incident_markdown:
alt_template
}
end
it
'includes both templates'
do
expect
(
subject
).
to
include
(
status: :success
)
expect
(
issue
.
description
).
to
include
(
alert_presenter
.
issue_summary_markdown
)
expect
(
issue
.
description
).
to
include
(
template_content
)
expect
(
issue
.
description
).
to
include
(
alt_template
)
expect
(
separator_count
(
issue
.
description
)).
to
eq
2
end
end
private
def
create_issue_template
(
name
,
content
)
project
.
repository
.
create_file
(
project
.
creator
,
".gitlab/issue_templates/
#{
name
}
.md"
,
content
,
message:
'message'
,
branch_name:
'master'
)
end
end
context
'with gitlab alert'
do
let
(
:gitlab_alert
)
{
create
(
:prometheus_alert
,
project:
project
)
}
before
do
alert_payload
[
'labels'
]
=
{
'gitlab_alert_id'
=>
gitlab_alert
.
prometheus_metric_id
.
to_s
}
end
it
'creates an issue'
do
query_title
=
"
#{
gitlab_alert
.
title
}
#{
gitlab_alert
.
computed_operator
}
#{
gitlab_alert
.
threshold
}
"
expect
(
subject
).
to
include
(
status: :success
)
expect
(
issue
.
author
).
to
eq
(
user
)
expect
(
issue
.
title
).
to
eq
(
alert_presenter
.
full_title
)
expect
(
issue
.
title
).
to
include
(
gitlab_alert
.
environment
.
name
)
expect
(
issue
.
title
).
to
include
(
query_title
)
expect
(
issue
.
title
).
to
include
(
'for 5 minutes'
)
expect
(
issue
.
description
).
to
include
(
alert_presenter
.
issue_summary_markdown
.
strip
)
expect
(
separator_count
(
issue
.
description
)).
to
eq
0
end
end
describe
'with invalid alert payload'
do
shared_examples
'invalid alert'
do
it
'does not create an issue'
do
expect
(
service
)
.
to
receive
(
:log_error
)
.
with
(
error_message
(
'invalid alert'
))
expect
(
subject
).
to
eq
(
status: :error
,
message:
'invalid alert'
)
end
end
context
'without title'
do
let
(
:alert_annotations
)
{
{}
}
it_behaves_like
'invalid alert'
end
context
'without startsAt'
do
let
(
:alert_starts_at
)
{
nil
}
it_behaves_like
'invalid alert'
end
end
describe
"label `incident`"
do
let
(
:title
)
{
'incident'
}
let
(
:color
)
{
'#CC0033'
}
let
(
:description
)
do
<<~
DESCRIPTION
.
chomp
Denotes a disruption to IT services and \
the associated issues require immediate attention
DESCRIPTION
end
shared_examples
'existing label'
do
it
'adds the existing label'
do
expect
{
subject
}.
not_to
change
(
Label
,
:count
)
expect
(
issue
.
labels
).
to
eq
([
label
])
end
end
shared_examples
'new label'
do
it
'adds newly created label'
do
expect
{
subject
}.
to
change
(
Label
,
:count
).
by
(
1
)
label
=
project
.
reload
.
labels
.
last
expect
(
issue
.
labels
).
to
eq
([
label
])
expect
(
label
.
title
).
to
eq
(
title
)
expect
(
label
.
color
).
to
eq
(
color
)
expect
(
label
.
description
).
to
eq
(
description
)
end
end
context
'with predefined project label'
do
it_behaves_like
'existing label'
do
let!
(
:label
)
{
create
(
:label
,
project:
project
,
title:
title
)
}
end
end
context
'with predefined group label'
do
let
(
:project
)
{
create
(
:project
,
group:
group
)
}
let
(
:group
)
{
create
(
:group
)
}
it_behaves_like
'existing label'
do
let!
(
:label
)
{
create
(
:group_label
,
group:
group
,
title:
title
)
}
end
end
context
'without label'
do
it_behaves_like
'new label'
end
context
'with duplicate labels'
,
issue:
'https://gitlab.com/gitlab-org/gitlab-foss/issues/65042'
do
before
do
# Replicate race condition to create duplicates
build
(
:label
,
project:
project
,
title:
title
).
save!
(
validate:
false
)
build
(
:label
,
project:
project
,
title:
title
).
save!
(
validate:
false
)
end
it
'create an issue without labels'
do
# Verify we have duplicates
expect
(
project
.
labels
.
size
).
to
eq
(
2
)
expect
(
project
.
labels
.
map
(
&
:title
)).
to
all
(
eq
(
title
))
message
=
<<~
MESSAGE
.
chomp
Cannot create incident issue with labels ["
#{
title
}
"] for \
"
#{
project
.
full_name
}
": Labels is invalid.
Retrying without labels.
MESSAGE
expect
(
service
)
.
to
receive
(
:log_info
)
.
with
(
message
)
expect
(
subject
).
to
include
(
status: :success
)
expect
(
issue
.
labels
).
to
be_empty
end
end
end
end
context
'when create_issue disabled'
do
before
do
setting
.
update!
(
create_issue:
false
)
end
it
'returns an error'
do
expect
(
service
)
.
to
receive
(
:log_error
)
.
with
(
error_message
(
'setting disabled'
))
expect
(
subject
).
to
eq
(
status: :error
,
message:
'setting disabled'
)
end
end
private
def
build_alert_payload
(
annotations:
{},
starts_at:
Time
.
now
)
{
'annotations'
=>
annotations
.
stringify_keys
}.
tap
do
|
payload
|
payload
[
'startsAt'
]
=
starts_at
.
rfc3339
if
starts_at
end
end
def
error_message
(
message
)
%{Cannot create incident issue for "#{project.full_name}": #{message}}
end
def
separator_count
(
text
)
text
.
scan
(
summary_separator
).
size
end
end
spec/workers/concerns/worker_context_spec.rb
View file @
bcc77054
...
...
@@ -106,5 +106,15 @@ describe WorkerContext do
expect
(
Labkit
::
Context
.
current
.
to_h
).
to
include
(
'meta.user'
=>
'jane-doe'
)
end
end
it
'yields the arguments to the block'
do
a_user
=
build_stubbed
(
:user
)
a_project
=
build_stubbed
(
:project
)
worker
.
new
.
with_context
(
user:
a_user
,
project:
a_project
)
do
|
user
:,
project
:|
expect
(
user
).
to
eq
(
a_user
)
expect
(
project
).
to
eq
(
a_project
)
end
end
end
end
spec/workers/incident_management/process_alert_worker_spec.rb
0 → 100644
View file @
bcc77054
# frozen_string_literal: true
require
'spec_helper'
describe
IncidentManagement
::
ProcessAlertWorker
do
let_it_be
(
:project
)
{
create
(
:project
)
}
describe
'#perform'
do
let
(
:alert
)
{
:alert
}
let
(
:create_issue_service
)
{
spy
(
:create_issue_service
)
}
subject
{
described_class
.
new
.
perform
(
project
.
id
,
alert
)
}
it
'calls create issue service'
do
expect
(
Project
).
to
receive
(
:find_by_id
).
and_call_original
expect
(
IncidentManagement
::
CreateIssueService
)
.
to
receive
(
:new
).
with
(
project
,
:alert
)
.
and_return
(
create_issue_service
)
expect
(
create_issue_service
).
to
receive
(
:execute
)
subject
end
context
'with invalid project'
do
let
(
:invalid_project_id
)
{
0
}
subject
{
described_class
.
new
.
perform
(
invalid_project_id
,
alert
)
}
it
'does not create issues'
do
expect
(
Project
).
to
receive
(
:find_by_id
).
and_call_original
expect
(
IncidentManagement
::
CreateIssueService
).
not_to
receive
(
:new
)
subject
end
end
end
end
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment