summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/stylesheets/page_bundles/oncall_schedules.scss75
-rw-r--r--app/finders/alert_management/http_integrations_finder.rb27
-rw-r--r--app/models/alert_management/http_integration.rb28
-rw-r--r--app/models/merge_request.rb4
-rw-r--r--app/models/merge_request_diff.rb2
-rw-r--r--app/services/alert_management/http_integrations/base_service.rb57
-rw-r--r--app/services/alert_management/http_integrations/create_service.rb64
-rw-r--r--app/services/alert_management/http_integrations/destroy_service.rb11
-rw-r--r--app/services/alert_management/http_integrations/update_service.rb49
-rw-r--r--app/services/projects/prometheus/alerts/notify_service.rb10
10 files changed, 151 insertions, 176 deletions
diff --git a/app/assets/stylesheets/page_bundles/oncall_schedules.scss b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
index 51bffd99dd0..10cc6cbd78e 100644
--- a/app/assets/stylesheets/page_bundles/oncall_schedules.scss
+++ b/app/assets/stylesheets/page_bundles/oncall_schedules.scss
@@ -4,17 +4,6 @@
box-shadow: inset 0 0 0 $gl-border-size-1 $red-500 if-important($important);
}
-.timezone-dropdown {
- .gl-dropdown-item-text-primary {
- @include gl-overflow-hidden;
- @include gl-text-overflow-ellipsis;
- }
-
- .btn-block {
- margin-bottom: 0;
- }
-}
-
.modal-footer {
@include gl-bg-gray-10;
}
@@ -52,65 +41,17 @@ $scroll-top-gradient: linear-gradient(to bottom, $gradient-dark-gray 0%, $gradie
$scroll-bottom-gradient: linear-gradient(to bottom, $gradient-gray 0%, $gradient-dark-gray 100%);
$column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradient-gray 100%);
-.schedule-shell {
- @include gl-relative;
- @include gl-h-full;
- @include gl-w-full;
- @include gl-overflow-x-auto;
-}
-
.timeline-section {
- @include gl-sticky;
- @include gl-top-0;
z-index: 20;
- .timeline-header-label,
- .timeline-header-item {
- @include gl-float-left;
- }
-
.timeline-header-label {
- @include gl-sticky;
- @include gl-top-0;
- @include gl-left-0;
width: $details-cell-width;
- z-index: 2;
}
.timeline-header-item {
- .item-sublabel .sublabel-value {
- color: var(--gray-700, $gray-700);
- @include gl-font-weight-normal;
-
- &.label-dark {
- color: var(--gray-900, $gray-900);
- }
-
- &.label-bold {
- @include gl-font-weight-bold;
- }
- }
-
- .item-sublabel {
- @include gl-relative;
- @include gl-display-flex;
-
- .sublabel-value {
- @include gl-flex-grow-1;
- @include gl-flex-basis-0;
-
- text-align: center;
- @include gl-font-base;
- }
- }
-
.current-day-indicator-header {
- @include gl-absolute;
- @include gl-bottom-0;
height: $grid-size;
width: $grid-size;
- background-color: var(--red-500, $red-500);
- @include gl-rounded-full;
transform: translate(-50%, 50%);
}
@@ -137,35 +78,19 @@ $column-right-gradient: linear-gradient(to right, $gradient-dark-gray 0%, $gradi
.details-cell,
.timeline-cell {
- @include gl-float-left;
height: $item-height;
}
.details-cell {
- @include gl-sticky;
- @include gl-left-0;
width: $details-cell-width;
- @include gl-font-base;
z-index: 10;
}
.timeline-cell {
- @include gl-relative;
- @include gl-bg-transparent;
- border-right: $border-style;
-
- &:last-child {
- @include gl-border-r-0;
- }
-
.current-day-indicator {
- @include gl-absolute;
top: -1px;
width: $gl-spacing-scale-1;
height: calc(100% + 1px);
- background-color: var(--red-500, $red-500);
- @include gl-pointer-events-none;
- transform: translateX(-50%);
}
}
diff --git a/app/finders/alert_management/http_integrations_finder.rb b/app/finders/alert_management/http_integrations_finder.rb
index e8e85da11b7..77a3824576f 100644
--- a/app/finders/alert_management/http_integrations_finder.rb
+++ b/app/finders/alert_management/http_integrations_finder.rb
@@ -2,7 +2,9 @@
module AlertManagement
class HttpIntegrationsFinder
- def initialize(project, params)
+ TYPE_IDENTIFIERS = ::AlertManagement::HttpIntegration.type_identifiers
+
+ def initialize(project, params = {})
@project = project
@params = params
end
@@ -13,6 +15,7 @@ module AlertManagement
filter_by_availability
filter_by_endpoint_identifier
filter_by_active
+ filter_by_type
collection
end
@@ -21,15 +24,13 @@ module AlertManagement
attr_reader :project, :params, :collection
+ # Overridden in EE
def filter_by_availability
- return if multiple_alert_http_integrations?
-
- first_id = project.alert_management_http_integrations
- .ordered_by_id
- .select(:id)
- .limit(1)
-
- @collection = collection.id_in(first_id)
+ # Re-find by id so subsequent filters don't expose unavailable records
+ @collection = collection.id_in(collection
+ .select('DISTINCT ON (type_identifier) id')
+ .ordered_by_type_and_id
+ .limit(TYPE_IDENTIFIERS.length))
end
def filter_by_endpoint_identifier
@@ -44,9 +45,11 @@ module AlertManagement
@collection = collection.active
end
- # Overridden in EE
- def multiple_alert_http_integrations?
- false
+ def filter_by_type
+ return unless params[:type_identifier]
+ return unless TYPE_IDENTIFIERS.include?(params[:type_identifier])
+
+ @collection = collection.for_type(params[:type_identifier])
end
end
end
diff --git a/app/models/alert_management/http_integration.rb b/app/models/alert_management/http_integration.rb
index 906855d6dfc..d5162865a79 100644
--- a/app/models/alert_management/http_integration.rb
+++ b/app/models/alert_management/http_integration.rb
@@ -3,8 +3,8 @@
module AlertManagement
class HttpIntegration < ApplicationRecord
include ::Gitlab::Routing
+
LEGACY_IDENTIFIER = 'legacy'
- DEFAULT_NAME_SLUG = 'http-endpoint'
belongs_to :project, inverse_of: :alert_management_http_integrations
@@ -19,6 +19,7 @@ module AlertManagement
validates :active, inclusion: { in: [true, false] }
validates :token, presence: true, format: { with: /\A\h{32}\z/ }
validates :name, presence: true, length: { maximum: 255 }
+ validates :type_identifier, presence: true
validates :endpoint_identifier, presence: true, length: { maximum: 255 }, format: { with: /\A[A-Za-z0-9]+\z/ }
validates :endpoint_identifier, uniqueness: { scope: [:project_id, :active] }, if: :active?
validates :payload_attribute_mapping, json_schema: { filename: 'http_integration_payload_attribute_mapping' }
@@ -29,15 +30,30 @@ module AlertManagement
before_validation :ensure_payload_example_not_nil
scope :for_endpoint_identifier, ->(endpoint_identifier) { where(endpoint_identifier: endpoint_identifier) }
+ scope :for_type, ->(type) { where(type_identifier: type) }
+ scope :for_project, ->(project_ids) { where(project: project_ids) }
scope :active, -> { where(active: true) }
- scope :ordered_by_id, -> { order(:id) }
+ scope :legacy, -> { for_endpoint_identifier(LEGACY_IDENTIFIER) }
+ scope :ordered_by_type_and_id, -> { order(:type_identifier, :id) }
+
+ enum type_identifier: {
+ http: 0,
+ prometheus: 1
+ }
def url
- return project_alerts_notify_url(project, format: :json) if legacy?
+ if legacy?
+ return project_alerts_notify_url(project, format: :json) if http?
+ return notify_project_prometheus_alerts_url(project, format: :json) if prometheus?
+ end
project_alert_http_integration_url(project, name_slug, endpoint_identifier, format: :json)
end
+ def legacy?
+ endpoint_identifier == LEGACY_IDENTIFIER
+ end
+
private
def self.generate_token
@@ -45,11 +61,7 @@ module AlertManagement
end
def name_slug
- (name && Gitlab::Utils.slugify(name)) || DEFAULT_NAME_SLUG
- end
-
- def legacy?
- endpoint_identifier == LEGACY_IDENTIFIER
+ (name && Gitlab::Utils.slugify(name)) || "#{type_identifier}-endpoint"
end
# Blank token assignment triggers token reset
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 7b1d4b97d3b..b7e39423e85 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -95,9 +95,9 @@ class MergeRequest < ApplicationRecord
dependent: :delete_all # rubocop:disable Cop/ActiveRecordDependent
has_many :cached_closes_issues, through: :merge_requests_closing_issues, source: :issue
- has_many :pipelines_for_merge_request, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline'
+ has_many :pipelines_for_merge_request, foreign_key: 'merge_request_id', class_name: 'Ci::Pipeline', inverse_of: :merge_request
has_many :suggestions, through: :notes
- has_many :unresolved_notes, -> { unresolved }, as: :noteable, class_name: 'Note'
+ has_many :unresolved_notes, -> { unresolved }, as: :noteable, class_name: 'Note', inverse_of: :noteable
has_many :merge_request_assignees
has_many :assignees, class_name: "User", through: :merge_request_assignees
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 0e699d7a81d..d36857fc94a 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -32,7 +32,7 @@ class MergeRequestDiff < ApplicationRecord
-> { order(:merge_request_diff_id, :relative_order) },
inverse_of: :merge_request_diff
- has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }
+ has_many :merge_request_diff_commits, -> { order(:merge_request_diff_id, :relative_order) }, inverse_of: :merge_request_diff
validates :base_commit_sha, :head_commit_sha, :start_commit_sha, sha: true
validates :merge_request_id, uniqueness: { scope: :diff_type }, if: :merge_head?
diff --git a/app/services/alert_management/http_integrations/base_service.rb b/app/services/alert_management/http_integrations/base_service.rb
new file mode 100644
index 00000000000..980f18631c0
--- /dev/null
+++ b/app/services/alert_management/http_integrations/base_service.rb
@@ -0,0 +1,57 @@
+# frozen_string_literal: true
+
+module AlertManagement
+ module HttpIntegrations
+ class BaseService < BaseProjectService
+ # @param project [Project]
+ # @param current_user [User]
+ # @param params [Hash]
+ def initialize(project, current_user, params)
+ @response = nil
+
+ super(project: project, current_user: current_user, params: params.with_indifferent_access)
+ end
+
+ private
+
+ def allowed?
+ current_user&.can?(:admin_operations, project)
+ end
+
+ def too_many_integrations?(integration)
+ AlertManagement::HttpIntegration
+ .for_project(integration.project_id)
+ .for_type(integration.type_identifier)
+ .id_not_in(integration.id)
+ .any?
+ end
+
+ def permitted_params
+ params.slice(*permitted_params_keys)
+ end
+
+ # overriden in EE
+ def permitted_params_keys
+ %i[name active type_identifier]
+ end
+
+ def error(message)
+ ServiceResponse.error(message: message)
+ end
+
+ def success(integration)
+ ServiceResponse.success(payload: { integration: integration.reset })
+ end
+
+ def error_multiple_integrations
+ error(_('Multiple integrations of a single type are not supported for this project'))
+ end
+
+ def error_on_save(integration)
+ error(integration.errors.full_messages.to_sentence)
+ end
+ end
+ end
+end
+
+::AlertManagement::HttpIntegrations::BaseService.prepend_mod
diff --git a/app/services/alert_management/http_integrations/create_service.rb b/app/services/alert_management/http_integrations/create_service.rb
index 1abe0548c45..17e39577c29 100644
--- a/app/services/alert_management/http_integrations/create_service.rb
+++ b/app/services/alert_management/http_integrations/create_service.rb
@@ -2,68 +2,34 @@
module AlertManagement
module HttpIntegrations
- class CreateService
- # @param project [Project]
- # @param current_user [User]
- # @param params [Hash]
- def initialize(project, current_user, params)
- @project = project
- @current_user = current_user
- @params = params.with_indifferent_access
- end
-
+ class CreateService < BaseService
def execute
return error_no_permissions unless allowed?
- return error_multiple_integrations unless creation_allowed?
-
- integration = project.alert_management_http_integrations.create(permitted_params)
- return error_in_create(integration) unless integration.valid?
-
- success(integration)
- end
- private
+ ::AlertManagement::HttpIntegration.transaction do
+ integration = project.alert_management_http_integrations.build(permitted_params)
- attr_reader :project, :current_user, :params
+ if integration.save
+ @response = success(integration)
- def allowed?
- current_user&.can?(:admin_operations, project)
- end
+ if too_many_integrations?(integration)
+ @response = error_multiple_integrations
- def creation_allowed?
- project.alert_management_http_integrations.empty?
- end
-
- def permitted_params
- params.slice(*permitted_params_keys)
- end
+ raise ActiveRecord::Rollback
+ end
+ else
+ @response = error_on_save(integration)
+ end
+ end
- # overriden in EE
- def permitted_params_keys
- %i[name active]
+ @response
end
- def error(message)
- ServiceResponse.error(message: message)
- end
-
- def success(integration)
- ServiceResponse.success(payload: { integration: integration })
- end
+ private
def error_no_permissions
error(_('You have insufficient permissions to create an HTTP integration for this project'))
end
-
- def error_multiple_integrations
- error(_('Multiple HTTP integrations are not supported for this project'))
- end
-
- def error_in_create(integration)
- error(integration.errors.full_messages.to_sentence)
- end
end
end
end
-
-::AlertManagement::HttpIntegrations::CreateService.prepend_mod_with('AlertManagement::HttpIntegrations::CreateService')
diff --git a/app/services/alert_management/http_integrations/destroy_service.rb b/app/services/alert_management/http_integrations/destroy_service.rb
index aeb3f6cb807..1bd73ca46e4 100644
--- a/app/services/alert_management/http_integrations/destroy_service.rb
+++ b/app/services/alert_management/http_integrations/destroy_service.rb
@@ -12,6 +12,7 @@ module AlertManagement
def execute
return error_no_permissions unless allowed?
+ return error_legacy_prometheus unless destroy_allowed?
if integration.destroy
success
@@ -28,6 +29,12 @@ module AlertManagement
current_user&.can?(:admin_operations, integration)
end
+ # Prevents downtime while migrating from Integrations::Prometheus.
+ # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/409734
+ def destroy_allowed?
+ !(integration.legacy? && integration.prometheus?)
+ end
+
def error(message)
ServiceResponse.error(message: message)
end
@@ -39,6 +46,10 @@ module AlertManagement
def error_no_permissions
error(_('You have insufficient permissions to remove this HTTP integration'))
end
+
+ def error_legacy_prometheus
+ error(_('Legacy Prometheus integrations cannot currently be removed'))
+ end
end
end
end
diff --git a/app/services/alert_management/http_integrations/update_service.rb b/app/services/alert_management/http_integrations/update_service.rb
index 8662f966a2e..f7a079576e4 100644
--- a/app/services/alert_management/http_integrations/update_service.rb
+++ b/app/services/alert_management/http_integrations/update_service.rb
@@ -2,51 +2,48 @@
module AlertManagement
module HttpIntegrations
- class UpdateService
+ class UpdateService < BaseService
# @param integration [AlertManagement::HttpIntegration]
# @param current_user [User]
# @param params [Hash]
def initialize(integration, current_user, params)
@integration = integration
- @current_user = current_user
- @params = params.with_indifferent_access
+
+ super(integration.project, current_user, params)
end
def execute
return error_no_permissions unless allowed?
- params[:token] = nil if params.delete(:regenerate_token)
+ integration.transaction do
+ if integration.update(permitted_params.merge(token_params))
+ @response = success(integration)
+
+ if type_update? && too_many_integrations?(integration)
+ @response = error_multiple_integrations
- if integration.update(permitted_params)
- success
- else
- error(integration.errors.full_messages.to_sentence)
+ raise ActiveRecord::Rollback
+ end
+ else
+ @response = error_on_save(integration)
+ end
end
+
+ @response
end
private
- attr_reader :integration, :current_user, :params
+ attr_reader :integration
- def allowed?
- current_user&.can?(:admin_operations, integration)
- end
+ def token_params
+ return {} unless params[:regenerate_token]
- def permitted_params
- params.slice(*permitted_params_keys)
+ { token: nil }
end
- # overriden in EE
- def permitted_params_keys
- %i[name active token]
- end
-
- def error(message)
- ServiceResponse.error(message: message)
- end
-
- def success
- ServiceResponse.success(payload: { integration: integration.reset })
+ def type_update?
+ params[:type_identifier].present?
end
def error_no_permissions
@@ -55,5 +52,3 @@ module AlertManagement
end
end
end
-
-::AlertManagement::HttpIntegrations::UpdateService.prepend_mod_with('AlertManagement::HttpIntegrations::UpdateService')
diff --git a/app/services/projects/prometheus/alerts/notify_service.rb b/app/services/projects/prometheus/alerts/notify_service.rb
index 1e084c0e5eb..1d24a113e05 100644
--- a/app/services/projects/prometheus/alerts/notify_service.rb
+++ b/app/services/projects/prometheus/alerts/notify_service.rb
@@ -79,12 +79,18 @@ module Projects
end
def valid_alert_manager_token?(token, integration)
- valid_for_manual?(token) ||
- valid_for_alerts_endpoint?(token, integration) ||
+ valid_for_alerts_endpoint?(token, integration) ||
+ valid_for_manual?(token) ||
valid_for_cluster?(token)
end
def valid_for_manual?(token)
+ # If migration from Integrations::Prometheus to
+ # AlertManagement::HttpIntegrations is complete,
+ # we should use use the HttpIntegration as SSOT.
+ # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/409734
+ return false if project.alert_management_http_integrations.legacy.prometheus.any?
+
prometheus = project.find_or_initialize_integration('prometheus')
return false unless prometheus.manual_configuration?