Commit bd83690e authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '227192-provide-pager-duty-data-to-frontend' into 'master'

Provide PagerDuty integration data to frontend

Closes #227192

See merge request gitlab-org/gitlab!36653
parents 784df1cb 28ec1471
...@@ -5,7 +5,12 @@ module Projects ...@@ -5,7 +5,12 @@ module Projects
class OperationsController < Projects::ApplicationController class OperationsController < Projects::ApplicationController
before_action :authorize_admin_operations! before_action :authorize_admin_operations!
before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token] before_action :authorize_read_prometheus_alerts!, only: [:reset_alerting_token]
respond_to :json, only: [:reset_alerting_token]
before_action do
push_frontend_feature_flag(:pagerduty_webhook, project)
end
respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token]
helper_method :error_tracking_setting helper_method :error_tracking_setting
...@@ -37,12 +42,31 @@ module Projects ...@@ -37,12 +42,31 @@ module Projects
end end
end end
def reset_pagerduty_token
result = ::Projects::Operations::UpdateService
.new(project, current_user, pagerduty_token_params)
.execute
if result[:status] == :success
pagerduty_token = project.incident_management_setting&.pagerduty_token
webhook_url = project_incidents_pagerduty_url(project, token: pagerduty_token)
render json: { pagerduty_webhook_url: webhook_url, pagerduty_token: pagerduty_token }
else
render json: {}, status: :unprocessable_entity
end
end
private private
def alerting_params def alerting_params
{ alerting_setting_attributes: { regenerate_token: true } } { alerting_setting_attributes: { regenerate_token: true } }
end end
def pagerduty_token_params
{ incident_management_setting_attributes: { regenerate_token: true } }
end
def render_update_response(result) def render_update_response(result)
respond_to do |format| respond_to do |format|
format.html do format.html do
......
...@@ -32,6 +32,23 @@ module OperationsHelper ...@@ -32,6 +32,23 @@ module OperationsHelper
'disabled' => disabled.to_s 'disabled' => disabled.to_s
} }
end end
def operations_settings_data
setting = project_incident_management_setting
templates = setting.available_issue_templates.map { |t| { key: t.key, name: t.name } }
{
operations_settings_endpoint: project_settings_operations_path(@project),
templates: templates.to_json,
create_issue: setting.create_issue.to_s,
issue_template_key: setting.issue_template_key.to_s,
send_email: setting.send_email.to_s,
pagerduty_active: setting.pagerduty_active.to_s,
pagerduty_token: setting.pagerduty_token.to_s,
pagerduty_webhook_url: project_incidents_pagerduty_url(@project, token: setting.pagerduty_token),
pagerduty_reset_key_path: reset_pagerduty_token_project_settings_operations_path(@project)
}
end
end end
OperationsHelper.prepend_if_ee('EE::OperationsHelper') OperationsHelper.prepend_if_ee('EE::OperationsHelper')
...@@ -8,6 +8,15 @@ module IncidentManagement ...@@ -8,6 +8,15 @@ module IncidentManagement
validate :issue_template_exists, if: :create_issue? validate :issue_template_exists, if: :create_issue?
before_validation :ensure_pagerduty_token
attr_encrypted :pagerduty_token,
mode: :per_attribute_iv,
key: Settings.attr_encrypted_db_key_base_truncated,
algorithm: 'aes-256-gcm',
encode: false, # No need to encode for binary column https://github.com/attr-encrypted/attr_encrypted#the-encode-encode_iv-encode_salt-and-default_encoding-options
encode_iv: false
def available_issue_templates def available_issue_templates
Gitlab::Template::IssueTemplate.all(project) Gitlab::Template::IssueTemplate.all(project)
end end
...@@ -30,5 +39,15 @@ module IncidentManagement ...@@ -30,5 +39,15 @@ module IncidentManagement
Gitlab::Template::IssueTemplate.find(issue_template_key, project) Gitlab::Template::IssueTemplate.find(issue_template_key, project)
rescue Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError rescue Gitlab::Template::Finders::RepoTemplateFinder::FileNotFoundError
end end
def ensure_pagerduty_token
return unless pagerduty_active
self.pagerduty_token ||= generate_pagerduty_token
end
def generate_pagerduty_token
SecureRandom.hex
end
end end
end end
...@@ -108,7 +108,18 @@ module Projects ...@@ -108,7 +108,18 @@ module Projects
end end
def incident_management_setting_params def incident_management_setting_params
params.slice(:incident_management_setting_attributes) attrs = params[:incident_management_setting_attributes]
return {} unless attrs
regenerate_token = attrs.delete(:regenerate_token)
if regenerate_token
attrs[:pagerduty_token] = nil
else
attrs = attrs.except(:pagerduty_token)
end
{ incident_management_setting_attributes: attrs }
end end
end end
end end
......
- setting = project_incident_management_setting .js-incidents-settings{ data: operations_settings_data }
- templates = setting.available_issue_templates.map { |t| { key: t.key, name: t.name } }
.js-incidents-settings{ data: { operations_settings_endpoint: project_settings_operations_path(@project),
templates: templates.to_json,
create_issue: setting.create_issue.to_s,
issue_template_key: setting.issue_template_key.to_s,
send_email: setting.send_email.to_s } }
...@@ -82,6 +82,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -82,6 +82,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
resource :operations, only: [:show, :update] do resource :operations, only: [:show, :update] do
member do member do
post :reset_alerting_token post :reset_alerting_token
post :reset_pagerduty_token
end end
end end
...@@ -406,7 +407,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -406,7 +407,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
post 'alerts/notify', to: 'alerting/notifications#create' post 'alerts/notify', to: 'alerting/notifications#create'
post 'incident_management/pager_duty', to: 'incident_management/pager_duty_incidents#create' post 'incidents/pagerduty', to: 'incident_management/pager_duty_incidents#create'
draw :legacy_builds draw :legacy_builds
......
...@@ -11,7 +11,6 @@ module EE ...@@ -11,7 +11,6 @@ module EE
def project_update_params def project_update_params
super super
.merge(tracing_setting_params) .merge(tracing_setting_params)
.merge(incident_management_setting_params)
.merge(status_page_setting_params) .merge(status_page_setting_params)
end end
...@@ -26,10 +25,6 @@ module EE ...@@ -26,10 +25,6 @@ module EE
{ tracing_setting_attributes: attr.merge(_destroy: destroy) } { tracing_setting_attributes: attr.merge(_destroy: destroy) }
end end
def incident_management_setting_params
params.slice(:incident_management_setting_attributes)
end
def status_page_setting_params def status_page_setting_params
return {} unless attrs = params[:status_page_setting_attributes] return {} unless attrs = params[:status_page_setting_attributes]
......
...@@ -32,6 +32,9 @@ module Gitlab ...@@ -32,6 +32,9 @@ module Gitlab
}, },
send_email: { send_email: {
name: 'sending_emails' name: 'sending_emails'
},
pagerduty_active: {
name: 'pagerduty_webhook'
} }
}.with_indifferent_access.freeze }.with_indifferent_access.freeze
end end
......
...@@ -151,7 +151,8 @@ RSpec.describe Projects::Settings::OperationsController do ...@@ -151,7 +151,8 @@ RSpec.describe Projects::Settings::OperationsController do
incident_management_setting_attributes: { incident_management_setting_attributes: {
create_issue: 'false', create_issue: 'false',
send_email: 'false', send_email: 'false',
issue_template_key: 'some-other-template' issue_template_key: 'some-other-template',
pagerduty_active: 'true'
} }
} }
end end
...@@ -159,7 +160,6 @@ RSpec.describe Projects::Settings::OperationsController do ...@@ -159,7 +160,6 @@ RSpec.describe Projects::Settings::OperationsController do
it_behaves_like 'PATCHable' it_behaves_like 'PATCHable'
context 'updating each incident management setting' do context 'updating each incident management setting' do
let(:project) { create(:project) }
let(:new_incident_management_settings) { {} } let(:new_incident_management_settings) { {} }
before do before do
...@@ -185,6 +185,98 @@ RSpec.describe Projects::Settings::OperationsController do ...@@ -185,6 +185,98 @@ RSpec.describe Projects::Settings::OperationsController do
it_behaves_like 'a gitlab tracking event', { issue_template_key: nil }, 'disabled_issue_template_on_alerts' it_behaves_like 'a gitlab tracking event', { issue_template_key: nil }, 'disabled_issue_template_on_alerts'
it_behaves_like 'a gitlab tracking event', { send_email: '1' }, 'enabled_sending_emails' it_behaves_like 'a gitlab tracking event', { send_email: '1' }, 'enabled_sending_emails'
it_behaves_like 'a gitlab tracking event', { send_email: '0' }, 'disabled_sending_emails' it_behaves_like 'a gitlab tracking event', { send_email: '0' }, 'disabled_sending_emails'
it_behaves_like 'a gitlab tracking event', { pagerduty_active: '1' }, 'enabled_pagerduty_webhook'
it_behaves_like 'a gitlab tracking event', { pagerduty_active: '0' }, 'disabled_pagerduty_webhook'
end
end
describe 'POST #reset_pagerduty_token' do
before do
project.add_maintainer(user)
end
context 'with existing incident management setting has active PagerDuty webhook' do
let!(:incident_management_setting) do
create(:project_incident_management_setting, project: project, pagerduty_active: true)
end
let!(:old_token) { incident_management_setting.pagerduty_token }
it 'returns newly reset token' do
reset_pagerduty_token
new_token = incident_management_setting.reload.pagerduty_token
new_webhook_url = project_incidents_pagerduty_url(project, token: new_token)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['pagerduty_webhook_url']).to eq(new_webhook_url)
expect(json_response['pagerduty_token']).to eq(new_token)
expect(old_token).not_to eq(new_token)
end
end
context 'without existing incident management setting' do
it 'does not reset a token' do
reset_pagerduty_token
new_webhook_url = project_incidents_pagerduty_url(project, token: nil)
expect(response).to have_gitlab_http_status(:ok)
expect(json_response['pagerduty_webhook_url']).to eq(new_webhook_url)
expect(project.incident_management_setting.pagerduty_token).to be_nil
end
end
context 'when update fails' do
let(:operations_update_service) { spy(:operations_update_service) }
let(:pagerduty_token_params) do
{ incident_management_setting_attributes: { regenerate_token: true } }
end
before do
expect(::Projects::Operations::UpdateService)
.to receive(:new).with(project, user, pagerduty_token_params)
.and_return(operations_update_service)
expect(operations_update_service).to receive(:execute)
.and_return(status: :error)
end
it 'returns unprocessable_entity' do
reset_pagerduty_token
expect(response).to have_gitlab_http_status(:unprocessable_entity)
expect(json_response).to be_empty
end
end
context 'with insufficient permissions' do
before do
project.add_reporter(user)
end
it 'returns 404' do
reset_pagerduty_token
expect(response).to have_gitlab_http_status(:not_found)
end
end
context 'as an anonymous user' do
before do
sign_out(user)
end
it 'returns a redirect' do
reset_pagerduty_token
expect(response).to have_gitlab_http_status(:redirect)
end
end
private
def reset_pagerduty_token
post :reset_pagerduty_token, params: project_params(project), format: :json
end end
end end
end end
...@@ -296,9 +388,7 @@ RSpec.describe Projects::Settings::OperationsController do ...@@ -296,9 +388,7 @@ RSpec.describe Projects::Settings::OperationsController do
end end
end end
describe 'POST reset_alerting_token' do describe 'POST #reset_alerting_token' do
let(:project) { create(:project) }
before do before do
project.add_maintainer(user) project.add_maintainer(user)
end end
......
...@@ -5,15 +5,18 @@ require 'spec_helper' ...@@ -5,15 +5,18 @@ require 'spec_helper'
RSpec.describe OperationsHelper do RSpec.describe OperationsHelper do
include Gitlab::Routing include Gitlab::Routing
describe '#alerts_settings_data' do let_it_be(:user) { create(:user) }
let_it_be(:user) { create(:user) } let_it_be(:project, reload: true) { create(:project) }
let_it_be(:project, reload: true) { create(:project) }
before do
helper.instance_variable_set(:@project, project)
allow(helper).to receive(:current_user) { user }
end
describe '#alerts_settings_data' do
subject { helper.alerts_settings_data } subject { helper.alerts_settings_data }
before do before do
helper.instance_variable_set(:@project, project)
allow(helper).to receive(:current_user) { user }
allow(helper).to receive(:can?).with(user, :admin_operations, project) { true } allow(helper).to receive(:can?).with(user, :admin_operations, project) { true }
end end
...@@ -127,4 +130,31 @@ RSpec.describe OperationsHelper do ...@@ -127,4 +130,31 @@ RSpec.describe OperationsHelper do
end end
end end
end end
describe '#operations_settings_data' do
let_it_be(:operations_settings) do
create(
:project_incident_management_setting,
project: project,
issue_template_key: 'template-key',
pagerduty_active: true
)
end
subject { helper.operations_settings_data }
it 'returns the correct set of data' do
is_expected.to eq(
operations_settings_endpoint: project_settings_operations_path(project),
templates: '[]',
create_issue: 'false',
issue_template_key: 'template-key',
send_email: 'false',
pagerduty_active: 'true',
pagerduty_token: operations_settings.pagerduty_token,
pagerduty_webhook_url: project_incidents_pagerduty_url(project, token: operations_settings.pagerduty_token),
pagerduty_reset_key_path: reset_pagerduty_token_project_settings_operations_path(project)
)
end
end
end end
...@@ -108,4 +108,42 @@ RSpec.describe IncidentManagement::ProjectIncidentManagementSetting do ...@@ -108,4 +108,42 @@ RSpec.describe IncidentManagement::ProjectIncidentManagementSetting do
it_behaves_like 'no content' it_behaves_like 'no content'
end end
end end
describe '#pagerduty_token' do
let(:active) { true }
subject do
create(:project_incident_management_setting, project: project, pagerduty_active: active, pagerduty_token: token)
end
context 'when token already set' do
let(:token) { SecureRandom.hex }
it 'reads the token' do
expect(subject.pagerduty_token).to eq(token)
expect(subject.encrypted_pagerduty_token).not_to be_nil
expect(subject.encrypted_pagerduty_token_iv).not_to be_nil
end
end
context 'when not set' do
let(:token) { nil }
context 'when PagerDuty webhook is active' do
it 'generates a token before validation' do
expect(subject).to be_valid
expect(subject.pagerduty_token).to match(/\A\h{32}\z/)
end
end
context 'when PagerDuty webhook is not active' do
let(:active) { false }
it 'does not generate a token before validation' do
expect(subject).to be_valid
expect(subject.pagerduty_token).to be_nil
end
end
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment