Commit 694a0512 authored by Adrián López Calvo's avatar Adrián López Calvo Committed by Alex Kalderimis

Add custom tags to the Datadog integration

Users can now provide custom tags in a new input in the settings.
These are set in Datadog in all the spans for Gitlab pipelines.

Changelog: added
parent 3ae908d3
...@@ -30,6 +30,7 @@ module Integrations ...@@ -30,6 +30,7 @@ module Integrations
:datadog_site, :datadog_site,
:datadog_env, :datadog_env,
:datadog_service, :datadog_service,
:datadog_tags,
:default_irc_uri, :default_irc_uri,
:device, :device,
:disable_diffs, :disable_diffs,
......
...@@ -13,7 +13,11 @@ module Integrations ...@@ -13,7 +13,11 @@ module Integrations
pipeline job pipeline job
].freeze ].freeze
prop_accessor :datadog_site, :api_url, :api_key, :datadog_service, :datadog_env 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
with_options if: :activated? do with_options if: :activated? do
validates :api_key, presence: true, format: { with: /\A\w+\z/ } validates :api_key, presence: true, format: { with: /\A\w+\z/ }
...@@ -21,6 +25,7 @@ module Integrations ...@@ -21,6 +25,7 @@ module Integrations
validates :api_url, public_url: { allow_blank: true } validates :api_url, public_url: { allow_blank: true }
validates :datadog_site, presence: true, unless: -> (obj) { obj.api_url.present? } validates :datadog_site, presence: true, unless: -> (obj) { obj.api_url.present? }
validates :api_url, presence: true, unless: -> (obj) { obj.datadog_site.present? } validates :api_url, presence: true, unless: -> (obj) { obj.datadog_site.present? }
validate :datadog_tags_are_valid
end end
def initialize_properties def initialize_properties
...@@ -140,6 +145,20 @@ module Integrations ...@@ -140,6 +145,20 @@ module Integrations
linkOpen: '<a href="https://docs.datadoghq.com/getting_started/tagging/#using-tags" target="_blank" rel="noopener noreferrer">'.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 linkClose: '</a>'.html_safe
} }
},
{
type: 'textarea',
name: 'datadog_tags',
title: s_('DatadogIntegration|Tags'),
placeholder: "tag:value\nanother_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
}
} }
] ]
...@@ -153,7 +172,8 @@ module Integrations ...@@ -153,7 +172,8 @@ module Integrations
query = { query = {
"dd-api-key" => api_key, "dd-api-key" => api_key,
service: datadog_service.presence, service: datadog_service.presence,
env: datadog_env.presence env: datadog_env.presence,
tags: datadog_tags_query_param.presence
}.compact }.compact
url.query = query.to_query url.query = query.to_query
url.to_s url.to_s
...@@ -193,5 +213,35 @@ module Integrations ...@@ -193,5 +213,35 @@ module Integrations
data data
end end
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
end end
end end
...@@ -322,6 +322,7 @@ Parameters: ...@@ -322,6 +322,7 @@ Parameters:
| `datadog_env` | string | false | For self-managed deployments, set the env% tag for all the data sent to Datadog. | | `datadog_env` | string | false | For self-managed deployments, set the env% tag for all the data sent to Datadog. |
| `datadog_service` | string | false | Tag all data from this GitLab instance in Datadog. Useful when managing several self-managed deployments | | `datadog_service` | string | false | Tag all data from this GitLab instance in Datadog. Useful when managing several self-managed deployments |
| `datadog_site` | string | false | The Datadog site to send data to. To send data to the EU site, use `datadoghq.eu` | | `datadog_site` | string | false | The Datadog site to send data to. To send data to the EU site, use `datadoghq.eu` |
| `datadog_tags` | string | false | Custom tags in Datadog. Specify one tag per line in the format: `key:value\nkey2:value2` ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79665) in GitLab 14.8.) |
<!-- | `archive_trace_events` | boolean | false | When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346339) in GitLab 14.7) | --> <!-- | `archive_trace_events` | boolean | false | When enabled, job logs are collected by Datadog and displayed along with pipeline execution traces ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/346339) in GitLab 14.7) | -->
<!-- TODO: uncomment the archive_trace_events field once :datadog_integration_logs_collection is rolled out. Rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/346339 --> <!-- TODO: uncomment the archive_trace_events field once :datadog_integration_logs_collection is rolled out. Rollout issue: https://gitlab.com/gitlab-org/gitlab/-/issues/346339 -->
......
...@@ -42,6 +42,8 @@ project, group, or instance level: ...@@ -42,6 +42,8 @@ project, group, or instance level:
1. Optional. If you use groups of GitLab instances (such as staging and production 1. Optional. If you use groups of GitLab instances (such as staging and production
environments), provide an **Env** name. This value is attached to each span environments), provide an **Env** name. This value is attached to each span
the integration generates. the integration generates.
1. Optional. To define any custom tags for all spans at which the integration is being configured,
enter one tag per line in **Tags**. Each line must be in the format `key:value`. ([Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/79665) in GitLab 14.8.)
1. Optional. Select **Test settings** to test your integration. 1. Optional. Select **Test settings** to test your integration.
1. Select **Save changes**. 1. Select **Save changes**.
......
...@@ -346,7 +346,13 @@ module API ...@@ -346,7 +346,13 @@ module API
required: false, required: false,
name: :datadog_env, name: :datadog_env,
type: String, type: String,
desc: 'For self-managed deployments, set the env tag for all the data sent to Datadog. How do I use tags?' desc: 'For self-managed deployments, set the env tag for all the data sent to Datadog'
},
{
required: false,
name: :datadog_tags,
type: String,
desc: 'Custom tags in Datadog. Specify one tag per line in the format: "key:value\nkey2:value2"'
} }
], ],
'discord' => [ 'discord' => [
......
...@@ -11375,6 +11375,9 @@ msgstr "" ...@@ -11375,6 +11375,9 @@ msgstr ""
msgid "DatadogIntegration|API URL" msgid "DatadogIntegration|API URL"
msgstr "" msgstr ""
msgid "DatadogIntegration|Custom tags in Datadog. Enter one tag per line in the %{codeOpen}key:value%{codeClose} format. %{linkOpen}How do I use tags?%{linkClose}"
msgstr ""
msgid "DatadogIntegration|Environment" msgid "DatadogIntegration|Environment"
msgstr "" msgstr ""
...@@ -11393,12 +11396,18 @@ msgstr "" ...@@ -11393,12 +11396,18 @@ msgstr ""
msgid "DatadogIntegration|Tag all data from this GitLab instance in Datadog. Useful when managing several self-managed deployments." msgid "DatadogIntegration|Tag all data from this GitLab instance in Datadog. Useful when managing several self-managed deployments."
msgstr "" msgstr ""
msgid "DatadogIntegration|Tags"
msgstr ""
msgid "DatadogIntegration|The Datadog site to send data to. To send data to the EU site, use %{codeOpen}datadoghq.eu%{codeClose}." msgid "DatadogIntegration|The Datadog site to send data to. To send data to the EU site, use %{codeOpen}datadoghq.eu%{codeClose}."
msgstr "" msgstr ""
msgid "DatadogIntegration|Trace your GitLab pipelines with Datadog." msgid "DatadogIntegration|Trace your GitLab pipelines with Datadog."
msgstr "" msgstr ""
msgid "DatadogIntegration|have an invalid format"
msgstr ""
msgid "Datasource name not found" msgid "Datasource name not found"
msgstr "" msgstr ""
......
...@@ -16,6 +16,7 @@ RSpec.describe Integrations::Datadog do ...@@ -16,6 +16,7 @@ RSpec.describe Integrations::Datadog do
let(:api_key) { SecureRandom.hex(32) } let(:api_key) { SecureRandom.hex(32) }
let(:dd_env) { 'ci' } let(:dd_env) { 'ci' }
let(:dd_service) { 'awesome-gitlab' } let(:dd_service) { 'awesome-gitlab' }
let(:dd_tags) { '' }
let(:expected_hook_url) { default_url + "?dd-api-key=#{api_key}&env=#{dd_env}&service=#{dd_service}" } let(:expected_hook_url) { default_url + "?dd-api-key=#{api_key}&env=#{dd_env}&service=#{dd_service}" }
...@@ -27,7 +28,8 @@ RSpec.describe Integrations::Datadog do ...@@ -27,7 +28,8 @@ RSpec.describe Integrations::Datadog do
api_url: api_url, api_url: api_url,
api_key: api_key, api_key: api_key,
datadog_env: dd_env, datadog_env: dd_env,
datadog_service: dd_service datadog_service: dd_service,
datadog_tags: dd_tags
) )
end end
...@@ -95,6 +97,20 @@ RSpec.describe Integrations::Datadog do ...@@ -95,6 +97,20 @@ RSpec.describe Integrations::Datadog do
it { is_expected.not_to allow_value('datadog hq.com').for(:datadog_site) } it { is_expected.not_to allow_value('datadog hq.com').for(:datadog_site) }
it { is_expected.not_to allow_value('example.com').for(:api_url) } it { is_expected.not_to allow_value('example.com').for(:api_url) }
end end
context 'with custom tags' do
it { is_expected.to allow_value('').for(:datadog_tags) }
it { is_expected.to allow_value('key:value').for(:datadog_tags) }
it { is_expected.to allow_value("key:value\nkey2:value2").for(:datadog_tags) }
it { is_expected.to allow_value("key:value\nkey2:value with spaces and 123?&$").for(:datadog_tags) }
it { is_expected.to allow_value("key:value\n\n\n\nkey2:value2\n").for(:datadog_tags) }
it { is_expected.not_to allow_value('value').for(:datadog_tags) }
it { is_expected.not_to allow_value('key:').for(:datadog_tags) }
it { is_expected.not_to allow_value('key: ').for(:datadog_tags) }
it { is_expected.not_to allow_value(':value').for(:datadog_tags) }
it { is_expected.not_to allow_value("key:value\nINVALID").for(:datadog_tags) }
end
end end
context 'when integration is not active' do context 'when integration is not active' do
...@@ -134,9 +150,23 @@ RSpec.describe Integrations::Datadog do ...@@ -134,9 +150,23 @@ RSpec.describe Integrations::Datadog do
context 'without optional params' do context 'without optional params' do
let(:dd_service) { '' } let(:dd_service) { '' }
let(:dd_env) { '' } let(:dd_env) { '' }
let(:dd_tags) { '' }
it { is_expected.to eq(default_url + "?dd-api-key=#{api_key}") } it { is_expected.to eq(default_url + "?dd-api-key=#{api_key}") }
end end
context 'with custom tags' do
let(:dd_tags) { "key:value\nkey2:value, 2" }
let(:escaped_tags) { CGI.escape("key:value,\"key2:value, 2\"") }
it { is_expected.to eq(expected_hook_url + "&tags=#{escaped_tags}") }
context 'and empty lines' do
let(:dd_tags) { "key:value\r\n\n\n\nkey2:value, 2\n" }
it { is_expected.to eq(expected_hook_url + "&tags=#{escaped_tags}") }
end
end
end end
describe '#test' do describe '#test' do
......
...@@ -18,6 +18,8 @@ Integration.available_integration_names.each do |integration| ...@@ -18,6 +18,8 @@ Integration.available_integration_names.each do |integration|
hash.merge!(k => 'https://example.atlassian.net/wiki') hash.merge!(k => 'https://example.atlassian.net/wiki')
elsif integration == 'datadog' && k == :datadog_site elsif integration == 'datadog' && k == :datadog_site
hash.merge!(k => 'datadoghq.com') hash.merge!(k => 'datadoghq.com')
elsif integration == 'datadog' && k == :datadog_tags
hash.merge!(k => 'key:value')
elsif integration == 'packagist' && k == :server elsif integration == 'packagist' && k == :server
hash.merge!(k => 'https://packagist.example.com') hash.merge!(k => 'https://packagist.example.com')
elsif k =~ /^(.*_url|url|webhook)/ elsif k =~ /^(.*_url|url|webhook)/
......
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