2021-05-17 21:10:42 +00:00
# frozen_string_literal: true
module Integrations
class Datadog < Integration
2021-07-14 18:08:31 +00:00
include HasWebHook
extend Gitlab :: Utils :: Override
2021-07-14 15:09:57 +00:00
DEFAULT_DOMAIN = 'datadoghq.com'
2021-09-07 15:11:06 +00:00
URL_TEMPLATE = 'https://webhook-intake.%{datadog_domain}/api/v2/webhook'
2021-07-14 15:09:57 +00:00
URL_API_KEYS_DOCS = " https://docs. #{ DEFAULT_DOMAIN } /account_management/api-app-keys/ "
2021-05-17 21:10:42 +00:00
SUPPORTED_EVENTS = %w[
pipeline job
] . freeze
2022-02-17 12:12:30 +00:00
TAG_KEY_VALUE_RE = %r{ \ A [ \ w-]+ : .* \ S.* \ z }x . freeze
prop_accessor :datadog_site , :api_url , :api_key , :datadog_service , :datadog_env , :datadog_tags
before_validation :strip_properties
2021-05-17 21:10:42 +00:00
with_options if : :activated? do
validates :api_key , presence : true , format : { with : / \ A \ w+ \ z / }
validates :datadog_site , format : { with : / \ A[ \ w \ .]+ \ z / , allow_blank : true }
validates :api_url , public_url : { allow_blank : true }
validates :datadog_site , presence : true , unless : - > ( obj ) { obj . api_url . present? }
validates :api_url , presence : true , unless : - > ( obj ) { obj . datadog_site . present? }
2022-02-17 12:12:30 +00:00
validate :datadog_tags_are_valid
2021-05-17 21:10:42 +00:00
end
def initialize_properties
super
2021-07-14 15:09:57 +00:00
self . datadog_site || = DEFAULT_DOMAIN
2021-05-17 21:10:42 +00:00
end
def self . supported_events
SUPPORTED_EVENTS
end
2022-01-19 00:12:04 +00:00
def supported_events
events = super
return events + [ 'archive_trace' ] if Feature . enabled? ( :datadog_integration_logs_collection , parent )
events
end
2021-05-17 21:10:42 +00:00
def self . default_test_event
'pipeline'
end
def configurable_events
[ ] # do not allow to opt out of required hooks
2022-01-19 00:12:04 +00:00
# archive_trace is opt-in but we handle it with a more detailed field below
2021-05-17 21:10:42 +00:00
end
def title
'Datadog'
end
def description
2021-08-09 15:09:13 +00:00
s_ ( 'DatadogIntegration|Trace your GitLab pipelines with Datadog.' )
2021-05-17 21:10:42 +00:00
end
def help
2022-01-19 00:12:04 +00:00
docs_link = ActionController :: Base . helpers . link_to (
s_ ( 'DatadogIntegration|How do I set up this integration?' ) ,
Rails . application . routes . url_helpers . help_page_url ( 'integration/datadog' ) ,
target : '_blank' , rel : 'noopener noreferrer'
)
2021-08-09 15:09:13 +00:00
s_ ( 'DatadogIntegration|Send CI/CD pipeline information to Datadog to monitor for job failures and troubleshoot performance issues. %{docs_link}' ) . html_safe % { docs_link : docs_link . html_safe }
2021-05-17 21:10:42 +00:00
end
def self . to_param
'datadog'
end
def fields
2022-01-19 00:12:04 +00:00
f = [
2021-05-17 21:10:42 +00:00
{
type : 'text' ,
name : 'datadog_site' ,
2021-07-14 15:09:57 +00:00
placeholder : DEFAULT_DOMAIN ,
2021-08-09 15:09:13 +00:00
help : ERB :: Util . html_escape (
s_ ( 'DatadogIntegration|The Datadog site to send data to. To send data to the EU site, use %{codeOpen}datadoghq.eu%{codeClose}.' )
) % {
codeOpen : '<code>' . html_safe ,
codeClose : '</code>' . html_safe
} ,
2021-05-17 21:10:42 +00:00
required : false
} ,
{
type : 'text' ,
name : 'api_url' ,
2021-08-09 15:09:13 +00:00
title : s_ ( 'DatadogIntegration|API URL' ) ,
help : s_ ( 'DatadogIntegration|(Advanced) The full URL for your Datadog site.' ) ,
2021-05-17 21:10:42 +00:00
required : false
} ,
{
type : 'password' ,
name : 'api_key' ,
title : _ ( 'API key' ) ,
non_empty_password_title : s_ ( 'ProjectService|Enter new API key' ) ,
non_empty_password_help : s_ ( 'ProjectService|Leave blank to use your current API key' ) ,
2021-08-09 15:09:13 +00:00
help : ERB :: Util . html_escape (
s_ ( 'DatadogIntegration|%{linkOpen}API key%{linkClose} used for authentication with Datadog.' )
) % {
2021-08-31 21:10:43 +00:00
linkOpen : %Q{ <a href=" #{ URL_API_KEYS_DOCS } " target="_blank" rel="noopener noreferrer"> } . html_safe ,
2021-08-09 15:09:13 +00:00
linkClose : '</a>' . html_safe
} ,
2021-05-17 21:10:42 +00:00
required : true
2022-01-19 00:12:04 +00:00
}
]
if Feature . enabled? ( :datadog_integration_logs_collection , parent )
f . append ( {
type : 'checkbox' ,
name : 'archive_trace_events' ,
title : s_ ( 'Logs' ) ,
checkbox_label : s_ ( 'Enable logs collection' ) ,
help : s_ ( 'When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces.' ) ,
required : false
} )
end
f += [
2021-05-17 21:10:42 +00:00
{
type : 'text' ,
name : 'datadog_service' ,
2021-08-09 15:09:13 +00:00
title : s_ ( 'DatadogIntegration|Service' ) ,
2021-05-17 21:10:42 +00:00
placeholder : 'gitlab-ci' ,
2021-08-09 15:09:13 +00:00
help : s_ ( 'DatadogIntegration|Tag all data from this GitLab instance in Datadog. Useful when managing several self-managed deployments.' )
2021-05-17 21:10:42 +00:00
} ,
{
type : 'text' ,
name : 'datadog_env' ,
2021-08-09 15:09:13 +00:00
title : s_ ( 'DatadogIntegration|Environment' ) ,
placeholder : 'ci' ,
help : ERB :: Util . html_escape (
s_ ( 'DatadogIntegration|For self-managed deployments, set the %{codeOpen}env%{codeClose} tag for all the data sent to Datadog. %{linkOpen}How do I use tags?%{linkClose}' )
) % {
codeOpen : '<code>' . html_safe ,
codeClose : '</code>' . html_safe ,
linkOpen : '<a href="https://docs.datadoghq.com/getting_started/tagging/#using-tags" target="_blank" rel="noopener noreferrer">' . html_safe ,
linkClose : '</a>' . html_safe
}
2022-02-17 12:12:30 +00:00
} ,
{
type : 'textarea' ,
name : 'datadog_tags' ,
title : s_ ( 'DatadogIntegration|Tags' ) ,
placeholder : " tag:value \n another_tag:value " ,
help : ERB :: Util . html_escape (
s_ ( 'DatadogIntegration|Custom tags in Datadog. Enter one tag per line in the %{codeOpen}key:value%{codeClose} format. %{linkOpen}How do I use tags?%{linkClose}' )
) % {
codeOpen : '<code>' . html_safe ,
codeClose : '</code>' . html_safe ,
linkOpen : '<a href="https://docs.datadoghq.com/getting_started/tagging/#using-tags" target="_blank" rel="noopener noreferrer">' . html_safe ,
linkClose : '</a>' . html_safe
}
2021-05-17 21:10:42 +00:00
}
]
2022-01-19 00:12:04 +00:00
f
2021-05-17 21:10:42 +00:00
end
2021-07-14 18:08:31 +00:00
override :hook_url
2021-05-17 21:10:42 +00:00
def hook_url
2021-07-14 15:09:57 +00:00
url = api_url . presence || sprintf ( URL_TEMPLATE , datadog_domain : datadog_domain )
2021-05-17 21:10:42 +00:00
url = URI . parse ( url )
2021-07-14 15:09:57 +00:00
query = {
" dd-api-key " = > api_key ,
service : datadog_service . presence ,
2022-02-17 12:12:30 +00:00
env : datadog_env . presence ,
tags : datadog_tags_query_param . presence
2021-07-14 15:09:57 +00:00
} . compact
url . query = query . to_query
2021-05-17 21:10:42 +00:00
url . to_s
end
def execute ( data )
object_kind = data [ :object_kind ]
object_kind = 'job' if object_kind == 'build'
return unless supported_events . include? ( object_kind )
2022-01-19 00:12:04 +00:00
data = hook_data ( data , object_kind )
2021-07-14 18:08:31 +00:00
execute_web_hook! ( data , " #{ object_kind } hook " )
2021-05-17 21:10:42 +00:00
end
def test ( data )
2021-08-06 12:10:15 +00:00
result = execute ( data )
{
success : ( 200 .. 299 ) . cover? ( result [ :http_status ] ) ,
result : result [ :message ]
}
2021-05-17 21:10:42 +00:00
end
2021-07-14 15:09:57 +00:00
private
def datadog_domain
# Transparently ignore "app" prefix from datadog_site as the official docs table in
# https://docs.datadoghq.com/getting_started/site/ is confusing for internal URLs.
# US3 needs to keep a prefix but other datacenters cannot have the listed "app" prefix
datadog_site . delete_prefix ( " app. " )
end
2022-01-19 00:12:04 +00:00
def hook_data ( data , object_kind )
if object_kind == 'pipeline' && data . respond_to? ( :with_retried_builds )
return data . with_retried_builds
end
data
end
2022-02-17 12:12:30 +00:00
def strip_properties
datadog_service . strip! if datadog_service && ! datadog_service . frozen?
datadog_env . strip! if datadog_env && ! datadog_env . frozen?
datadog_tags . strip! if datadog_tags && ! datadog_tags . frozen?
end
def datadog_tags_are_valid
return unless datadog_tags
unless datadog_tags . split ( " \n " ) . select ( & :present? ) . all? { _1 =~ TAG_KEY_VALUE_RE }
errors . add ( :datadog_tags , s_ ( " DatadogIntegration|have an invalid format " ) )
end
end
def datadog_tags_query_param
return unless datadog_tags
datadog_tags . split ( " \n " ) . filter_map do | tag |
tag . strip!
next if tag . blank?
if tag . include? ( ',' )
" \" #{ tag } \" "
else
tag
end
end . join ( ',' )
end
2021-05-17 21:10:42 +00:00
end
end