Skip to content
Snippets Groups Projects
Commit 06589d92 authored by Shinya Maeda's avatar Shinya Maeda Committed by Adam Hegyi
Browse files

Expose alert information for environments

This commit extends GraphQL endpoint and Internal API
to expose alert information for environments.
parent e4695080
No related branches found
No related tags found
No related merge requests found
Showing
with 375 additions and 8 deletions
Loading
Loading
@@ -107,6 +107,16 @@ class AlertType < BaseObject
description: 'Todos of the current user for the alert',
resolver: Resolvers::TodoResolver
 
field :details_url,
GraphQL::STRING_TYPE,
null: false,
description: 'The URL of the alert detail page'
field :prometheus_alert,
Types::PrometheusAlertType,
null: true,
description: 'The alert condition for Prometheus'
def notes
object.ordered_notes
end
Loading
Loading
Loading
Loading
@@ -19,5 +19,10 @@ class EnvironmentType < BaseObject
field :metrics_dashboard, Types::Metrics::DashboardType, null: true,
description: 'Metrics dashboard schema for the environment',
resolver: Resolvers::Metrics::DashboardResolver
field :latest_opened_most_severe_alert,
Types::AlertManagement::AlertType,
null: true,
description: 'The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned.'
end
end
Loading
Loading
@@ -169,6 +169,12 @@ class ProjectType < BaseObject
description: 'Environments of the project',
resolver: Resolvers::EnvironmentsResolver
 
field :environment,
Types::EnvironmentType,
null: true,
description: 'A single environment of the project',
resolver: Resolvers::EnvironmentsResolver.single
field :sast_ci_configuration, ::Types::CiConfiguration::Sast::Type, null: true,
description: 'SAST CI configuration for the project',
resolver: ::Resolvers::CiConfiguration::SastResolver
Loading
Loading
# frozen_string_literal: true
module Types
class PrometheusAlertType < BaseObject
graphql_name 'PrometheusAlert'
description 'The alert condition for Prometheus'
authorize :read_prometheus_alerts
present_using PrometheusAlertPresenter
field :id, GraphQL::ID_TYPE, null: false,
description: 'ID of the alert condition'
field :humanized_text,
GraphQL::STRING_TYPE,
null: false,
description: 'The human-readable text of the alert condition'
end
end
Loading
Loading
@@ -118,7 +118,7 @@ class Alert < ApplicationRecord
end
 
delegate :iid, to: :issue, prefix: true, allow_nil: true
delegate :metrics_dashboard_url, :runbook, to: :present
delegate :metrics_dashboard_url, :runbook, :details_url, to: :present
 
scope :for_iid, -> (iid) { where(iid: iid) }
scope :for_status, -> (status) { where(status: status) }
Loading
Loading
@@ -137,6 +137,7 @@ class Alert < ApplicationRecord
# Descending sort order sorts severity from more critical to less critical.
# https://gitlab.com/gitlab-org/gitlab/-/issues/221242#what-is-the-expected-correct-behavior
scope :order_severity, -> (sort_order) { order(severity: sort_order == :asc ? :desc : :asc) }
scope :order_severity_with_open_prometheus_alert, -> { open.with_prometheus_alert.order(severity: :asc, started_at: :desc) }
 
# Ascending sort order sorts statuses: Ignored > Resolved > Acknowledged > Triggered
# Descending sort order sorts statuses: Triggered > Acknowledged > Resolved > Ignored
Loading
Loading
Loading
Loading
@@ -29,6 +29,7 @@ class Environment < ApplicationRecord
has_one :last_visible_deployment, -> { visible.distinct_on_environment }, inverse_of: :environment, class_name: 'Deployment'
has_one :last_visible_deployable, through: :last_visible_deployment, source: 'deployable', source_type: 'CommitStatus'
has_one :last_visible_pipeline, through: :last_visible_deployable, source: 'pipeline'
has_one :latest_opened_most_severe_alert, -> { order_severity_with_open_prometheus_alert }, class_name: 'AlertManagement::Alert', inverse_of: :environment
 
before_validation :nullify_external_url
before_validation :generate_slug, if: ->(env) { env.slug.blank? }
Loading
Loading
@@ -291,6 +292,10 @@ def has_sample_metrics?
!!ENV['USE_SAMPLE_METRICS']
end
 
def has_opened_alert?
latest_opened_most_severe_alert.present?
end
def metrics
prometheus_adapter.query(:environment, self) if has_metrics_and_can_query?
end
Loading
Loading
Loading
Loading
@@ -3,6 +3,7 @@
class PrometheusAlert < ApplicationRecord
include Sortable
include UsageStatistics
include Presentable
 
OPERATORS_MAP = {
lt: "<",
Loading
Loading
# frozen_string_literal: true
class PrometheusAlertPolicy < ::BasePolicy
delegate { @subject.project }
end
Loading
Loading
@@ -4,6 +4,7 @@ module AlertManagement
class AlertPresenter < Gitlab::View::Presenter::Delegated
include Gitlab::Utils::StrongMemoize
include IncidentManagement::Settings
include ActionView::Helpers::UrlHelper
 
MARKDOWN_LINE_BREAK = " \n".freeze
 
Loading
Loading
@@ -45,15 +46,12 @@ def runbook
 
def metrics_dashboard_url; end
 
private
def details_url
::Gitlab::Routing.url_helpers.details_project_alert_management_url(
project,
alert.iid
)
details_project_alert_management_url(project, alert.iid)
end
 
private
attr_reader :alert, :project
 
def alerting_alert
Loading
Loading
# frozen_string_literal: true
class PrometheusAlertPresenter < Gitlab::View::Presenter::Delegated
include ActionView::Helpers::UrlHelper
presents :prometheus_alert
def humanized_text
operator_text =
case prometheus_alert.operator
when 'lt' then s_('PrometheusAlerts|is less than')
when 'eq' then s_('PrometheusAlerts|is equal to')
when 'gt' then s_('PrometheusAlerts|exceeded')
end
"#{operator_text} #{prometheus_alert.threshold}#{prometheus_alert.prometheus_metric.unit}"
end
end
Loading
Loading
@@ -71,6 +71,8 @@ class EnvironmentEntity < Grape::Entity
can?(current_user, :destroy_environment, environment)
end
 
expose :has_opened_alert?, if: -> (*) { can_read_alert_management_alert? }, expose_nil: false, as: :has_opened_alert
private
 
alias_method :environment, :object
Loading
Loading
@@ -91,6 +93,10 @@ def can_read_pod_logs?
can?(current_user, :read_pod_logs, environment.project)
end
 
def can_read_alert_management_alert?
can?(current_user, :read_alert_management_alert, environment.project)
end
def cluster_platform_kubernetes?
deployment_platform && deployment_platform.is_a?(Clusters::Platforms::Kubernetes)
end
Loading
Loading
---
title: Expose alert information for environments
merge_request: 38881
author:
type: added
Loading
Loading
@@ -209,6 +209,11 @@ type AlertManagementAlert implements Noteable {
"""
details: JSON
 
"""
The URL of the alert detail page
"""
detailsUrl: String!
"""
All discussions on this noteable
"""
Loading
Loading
@@ -294,6 +299,11 @@ type AlertManagementAlert implements Noteable {
last: Int
): NoteConnection!
 
"""
The alert condition for Prometheus
"""
prometheusAlert: PrometheusAlert
"""
Runbook for the alert as defined in alert details
"""
Loading
Loading
@@ -4418,6 +4428,11 @@ type Environment {
"""
id: ID!
 
"""
The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned.
"""
latestOpenedMostSevereAlert: AlertManagementAlert
"""
Metrics dashboard schema for the environment
"""
Loading
Loading
@@ -10553,6 +10568,26 @@ type Project {
"""
descriptionHtml: String
 
"""
A single environment of the project
"""
environment(
"""
Name of the environment
"""
name: String
"""
Search query for environment name
"""
search: String
"""
States of environments that should be included in result
"""
states: [String!]
): Environment
"""
Environments of the project
"""
Loading
Loading
@@ -12116,6 +12151,21 @@ type ProjectStatistics {
wikiSize: Float
}
 
"""
The alert condition for Prometheus
"""
type PrometheusAlert {
"""
The human-readable text of the alert condition
"""
humanizedText: String!
"""
ID of the alert condition
"""
id: ID!
}
type Query {
"""
Get information about current user
Loading
Loading
Loading
Loading
@@ -577,6 +577,24 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "detailsUrl",
"description": "The URL of the alert detail page",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "discussions",
"description": "All discussions on this noteable",
Loading
Loading
@@ -801,6 +819,20 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "prometheusAlert",
"description": "The alert condition for Prometheus",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "PrometheusAlert",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "runbook",
"description": "Runbook for the alert as defined in alert details",
Loading
Loading
@@ -12338,6 +12370,20 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "latestOpenedMostSevereAlert",
"description": "The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned.",
"args": [
],
"type": {
"kind": "OBJECT",
"name": "AlertManagementAlert",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "metricsDashboard",
"description": "Metrics dashboard schema for the environment",
Loading
Loading
@@ -31457,6 +31503,57 @@
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "environment",
"description": "A single environment of the project",
"args": [
{
"name": "name",
"description": "Name of the environment",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "search",
"description": "Search query for environment name",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{
"name": "states",
"description": "States of environments that should be included in result",
"type": {
"kind": "LIST",
"name": null,
"ofType": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
}
},
"defaultValue": null
}
],
"type": {
"kind": "OBJECT",
"name": "Environment",
"ofType": null
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "environments",
"description": "Environments of the project",
Loading
Loading
@@ -35655,6 +35752,55 @@
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "PrometheusAlert",
"description": "The alert condition for Prometheus",
"fields": [
{
"name": "humanizedText",
"description": "The human-readable text of the alert condition",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "String",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
},
{
"name": "id",
"description": "ID of the alert condition",
"args": [
],
"type": {
"kind": "NON_NULL",
"name": null,
"ofType": {
"kind": "SCALAR",
"name": "ID",
"ofType": null
}
},
"isDeprecated": false,
"deprecationReason": null
}
],
"inputFields": null,
"interfaces": [
],
"enumValues": null,
"possibleTypes": null
},
{
"kind": "OBJECT",
"name": "Query",
Loading
Loading
@@ -64,6 +64,7 @@ Describes an alert from the project's Alert Management
| `createdAt` | Time | Timestamp the alert was created |
| `description` | String | Description of the alert |
| `details` | JSON | Alert details |
| `detailsUrl` | String! | The URL of the alert detail page |
| `endedAt` | Time | Timestamp the alert ended |
| `eventCount` | Int | Number of events of this alert |
| `hosts` | String! => Array | List of hosts the alert came from |
Loading
Loading
@@ -71,6 +72,7 @@ Describes an alert from the project's Alert Management
| `issueIid` | ID | Internal ID of the GitLab issue attached to the alert |
| `metricsDashboardUrl` | String | URL for metrics embed for the alert |
| `monitoringTool` | String | Monitoring tool the alert came from |
| `prometheusAlert` | PrometheusAlert | The alert condition for Prometheus |
| `runbook` | String | Runbook for the alert as defined in alert details |
| `service` | String | Service the alert came from |
| `severity` | AlertManagementSeverity | Severity of the alert |
Loading
Loading
@@ -739,6 +741,7 @@ Describes where code is deployed for a project
| Name | Type | Description |
| --- | ---- | ---------- |
| `id` | ID! | ID of the environment |
| `latestOpenedMostSevereAlert` | AlertManagementAlert | The most severe open alert for the environment. If multiple alerts have equal severity, the most recent is returned. |
| `metricsDashboard` | MetricsDashboard | Metrics dashboard schema for the environment |
| `name` | String! | Human-readable name of the environment |
| `state` | String! | State of the environment, for example: available/stopped |
Loading
Loading
@@ -1602,6 +1605,7 @@ Information about pagination in a connection.
| `createdAt` | Time | Timestamp of the project creation |
| `description` | String | Short description of the project |
| `descriptionHtml` | String | The GitLab Flavored Markdown rendering of `description` |
| `environment` | Environment | A single environment of the project |
| `forksCount` | Int! | Number of times the project has been forked |
| `fullPath` | ID! | Full path of the project |
| `grafanaIntegration` | GrafanaIntegration | Grafana integration details for the project |
Loading
Loading
@@ -1732,6 +1736,15 @@ Represents a Project Member
| `storageSize` | Float! | Storage size of the project |
| `wikiSize` | Float | Wiki size of the project |
 
## PrometheusAlert
The alert condition for Prometheus
| Name | Type | Description |
| --- | ---- | ---------- |
| `humanizedText` | String! | The human-readable text of the alert condition |
| `id` | ID! | ID of the alert condition |
## Release
 
Represents a release
Loading
Loading
Loading
Loading
@@ -75,6 +75,7 @@
"can_stop": {
"type": "boolean"
},
"has_opened_alert": { "type": "boolean" },
"cancel_auto_stop_path": { "type": "string" },
"auto_stop_at": { "type": "string", "format": "date-time" },
"can_delete": {
Loading
Loading
Loading
Loading
@@ -19432,9 +19432,18 @@ msgstr ""
msgid "PrometheusAlerts|Threshold"
msgstr ""
 
msgid "PrometheusAlerts|exceeded"
msgstr ""
msgid "PrometheusAlerts|https://gitlab.com/gitlab-com/runbooks"
msgstr ""
 
msgid "PrometheusAlerts|is equal to"
msgstr ""
msgid "PrometheusAlerts|is less than"
msgstr ""
msgid "PrometheusService|%{exporters} with %{metrics} were found"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -33,6 +33,7 @@
"updated_at": { "type": "string", "format": "date-time" },
"auto_stop_at": { "type": "string", "format": "date-time" },
"can_stop": { "type": "boolean" },
"has_opened_alert": { "type": "boolean" },
"cluster_type": { "type": "types/nullable_string.json" },
"terminal_path": { "type": "types/nullable_string.json" },
"last_deployment": {
Loading
Loading
Loading
Loading
@@ -30,6 +30,8 @@
metrics_dashboard_url
runbook
todos
details_url
prometheus_alert
]
 
expect(described_class).to have_graphql_fields(*expected_fields)
Loading
Loading
Loading
Loading
@@ -7,11 +7,76 @@
 
it 'has the expected fields' do
expected_fields = %w[
name id state metrics_dashboard
name id state metrics_dashboard latest_opened_most_severe_alert
]
 
expect(described_class).to have_graphql_fields(*expected_fields)
end
 
specify { expect(described_class).to require_graphql_authorizations(:read_environment) }
context 'when there is an environment' do
let_it_be(:project) { create(:project) }
let_it_be(:environment) { create(:environment, project: project) }
let_it_be(:user) { create(:user) }
subject { GitlabSchema.execute(query, context: { current_user: user }).as_json }
let(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
name
state
}
}
}
)
end
before do
project.add_developer(user)
end
it 'returns an environment' do
expect(subject['data']['project']['environment']['name']).to eq(environment.name)
end
context 'when query alert data for the environment' do
let_it_be(:query) do
%(
query {
project(fullPath: "#{project.full_path}") {
environment(name: "#{environment.name}") {
name
state
latestOpenedMostSevereAlert {
severity
title
detailsUrl
prometheusAlert {
humanizedText
}
}
}
}
}
)
end
it 'does not return alert information' do
expect(subject['data']['project']['environment']['latestOpenedMostSevereAlert']).to be_nil
end
context 'when alert is raised on the environment' do
let!(:prometheus_alert) { create(:prometheus_alert, project: project, environment: environment) }
let!(:alert) { create(:alert_management_alert, :triggered, :prometheus, project: project, environment: environment, prometheus_alert: prometheus_alert) }
it 'returns alert information' do
expect(subject['data']['project']['environment']['latestOpenedMostSevereAlert']['severity']).to eq(alert.severity.upcase)
end
end
end
end
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment