datadog-exporter 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e95a0191b86630b9e7cf948cc9fffe4051329c001354d98a095bd3b3b65f107c
4
+ data.tar.gz: c528a1c2c768998bf8e618bf3afd70d384e6981c6ea6c7af94c26d588228b948
5
+ SHA512:
6
+ metadata.gz: 42f8f7d7f0e538ed54350ef007fa7c07ce3312b5630113a4b9626c004771e82bb148bbf28e2411199291d0d39fa37df989f1a4c851e93ca34cbad8c8b8b1dc52
7
+ data.tar.gz: 03d1452fcf7b0fca965180340e251c8f3b342a3849ea61f250e01e133de1dc986476af75bb93e0d0363b067d0c4955c37cdd19bc3289d3ec0bf623c5b1b8ed27
@@ -0,0 +1,63 @@
1
+ require "datadog_api_client"
2
+
3
+ module DatadogExporter
4
+ class Client
5
+ ##
6
+ # The configuration for the DatadogExporter::Client.
7
+ class Config
8
+ DEFAULT_ORGANIZATIONS_CONFIGURATIONS = {
9
+ monitors: {
10
+ export_tag: "",
11
+ template_keys: [],
12
+ placeholders: {
13
+ base: {
14
+ },
15
+ },
16
+ },
17
+ }.freeze
18
+
19
+ attr_reader :logger
20
+
21
+ # NOTE: See DatadogExporter::Configurations to see the available options
22
+ def initialize(**options) # rubocop:disable Metrics/AbcSize
23
+ @site = options[:site] || DatadogExporter.configuration.site
24
+ @api_key = options[:api_key] || DatadogExporter.configuration.api_key
25
+ @application_key =
26
+ options[:application_key] || DatadogExporter.configuration.application_key
27
+ @logger = options[:logger] || DatadogExporter.configuration.logger
28
+ @base_path = options[:base_path] || DatadogExporter.configuration.base_path
29
+ @organizations_config_filename =
30
+ options[:org_config_filename] ||
31
+ DatadogExporter.configuration.organizations_config_filename
32
+ end
33
+
34
+ def base_path
35
+ Pathname.new(@base_path)
36
+ end
37
+
38
+ # Creates the Datadog API global configuration
39
+ #
40
+ # See https://github.com/DataDog/datadog-api-client-ruby/blob/master/lib/datadog_api_client/configuration.rb
41
+ def datadog_api_configuration
42
+ DatadogAPIClient::Configuration.new do |client_config|
43
+ client_config.server_variables[:site] = @site
44
+ client_config.api_key = @api_key
45
+ client_config.application_key = @application_key
46
+ end
47
+ end
48
+
49
+ def organizations_config
50
+ organizations_config_file = base_path.join(@organizations_config_filename)
51
+
52
+ unless File.exist?(organizations_config_file)
53
+ warn(
54
+ "WARNING: Organizations configuration file not found: #{organizations_config_file}. Using default configuration.",
55
+ )
56
+ return DEFAULT_ORGANIZATIONS_CONFIGURATIONS
57
+ end
58
+
59
+ DEFAULT_ORGANIZATIONS_CONFIGURATIONS.merge(YAML.load_file(organizations_config_file))
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ require_relative "client/config"
2
+
3
+ module DatadogExporter
4
+ ##
5
+ # The client that makes the actual requests to the DatadogAPIClient.
6
+ # `config` is an instance of `DatadogApi::Client::Config::Base`
7
+ class Client
8
+ def initialize(config: DatadogExporter::Client::Config.new)
9
+ @datadog_config = config.datadog_api_configuration
10
+ end
11
+
12
+ # Creates the Datadog API client
13
+ #
14
+ # See https://github.com/DataDog/datadog-api-client-ruby/blob/master/lib/datadog_api_client/api_client.rb
15
+ #
16
+ # @return [DatadogAPIClient::APIClient]
17
+ def datadog_client
18
+ @datadog_client ||= DatadogAPIClient::APIClient.new(@datadog_config)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,40 @@
1
+ require "logger"
2
+
3
+ module DatadogExporter
4
+ ##
5
+ # The global configuration of the DatadogExporter client.
6
+ #
7
+ # @see DatadogExporter.configure
8
+ class Configuration
9
+ def initialize
10
+ @config = {}
11
+ end
12
+
13
+ ##
14
+ # Create a getter and a setter for a setting.
15
+ #
16
+ # If the default value is a Proc, it will be called on initialize.
17
+ #
18
+ # @param name: Symbol
19
+ # @param default: Proc | Object
20
+ def self.add_setting(name, default)
21
+ define_method name do
22
+ @config[name] ||= default.is_a?(Proc) ? default.call : default
23
+ end
24
+
25
+ define_method :"#{name}=" do |value|
26
+ @config[name] = value
27
+ end
28
+ end
29
+
30
+ DEFAULT_ORG_CONFIG_FILENAME = "organizations_config.yml".freeze
31
+
32
+ add_setting :logger, -> { Logger.new($stdout) }
33
+ add_setting :base_path, -> { Dir.pwd }
34
+ add_setting :site, -> { ENV.fetch("DATADOG_API_SITE", nil) }
35
+ add_setting :api_key, -> { ENV.fetch("DATADOG_API_KEY", nil) }
36
+ add_setting :application_key, -> { ENV.fetch("DATADOG_APPLICATION_KEY", nil) }
37
+ add_setting :organizations_config_filename,
38
+ -> { ENV.fetch("DATADOG_ORG_CONFIG_FILENAME", DEFAULT_ORG_CONFIG_FILENAME) }
39
+ end
40
+ end
@@ -0,0 +1,56 @@
1
+ module DatadogExporter
2
+ module DatadogApiRequests
3
+ # The class that contains all the Datadog API client requests for Monitors
4
+ class Monitors
5
+ attr_reader :monitors_api
6
+
7
+ def initialize(
8
+ config: DatadogExporter::Client::Config.new,
9
+ client: DatadogExporter::Client.new(config:),
10
+ monitors_api: DatadogAPIClient::V1::MonitorsAPI.new(client.datadog_client)
11
+ )
12
+ @client = client
13
+ @monitors_api = monitors_api
14
+ end
15
+
16
+ ##
17
+ # Returns a list of Hashes containing Datadog monitor configuration.
18
+ # If no tag is provided, it returns all the monitors.
19
+ #
20
+ # @param [String] tag (optional) A tag defined in the Datadog monitors
21
+ #
22
+ # @return [Array<Hash>] A list of monitor configurations
23
+ def list_monitors(tag: nil)
24
+ monitors =
25
+ @monitors_api
26
+ .list_monitors # NOTE: Unfortunately, the filter provided by the API is not working https://github.com/DataDog/datadog-api-client-ruby/issues/2063
27
+ .select { |monitor| tag.nil? || monitor.tags.include?(tag) }
28
+
29
+ monitors.map(&:to_hash)
30
+ end
31
+
32
+ ##
33
+ # Returns a Datadog monitor configuration.
34
+ #
35
+ # @param [Integer] monitor_id The monitor ID in Datadog
36
+ #
37
+ # @return [Hash] The monitor configuration
38
+ def find(monitor_id)
39
+ monitor = @monitors_api.get_monitor(monitor_id)
40
+ monitor.to_hash
41
+ end
42
+
43
+ ##
44
+ # Creates a monitor in Datadog
45
+ #
46
+ # @param [Hash] monitor_hash The monitor configuration
47
+ #
48
+ # @return [Integer] The monitor ID
49
+ def create(monitor_hash)
50
+ monitor = @monitors_api.create_monitor(monitor_hash)
51
+
52
+ monitor.id
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ require_relative "datadog_api_requests/monitors"
2
+
3
+ module DatadogExporter
4
+ ##
5
+ # The module that contains all the Datadog API client requests
6
+ # See https://github.com/DataDog/datadog-api-client-ruby
7
+ module DatadogApiRequests
8
+ end
9
+ end
@@ -0,0 +1,90 @@
1
+ module DatadogExporter
2
+ module Monitors
3
+ ##
4
+ # This class provide export tools for Datadog Monitors
5
+ class Export
6
+ attr_reader :config
7
+
8
+ def initialize(
9
+ config: DatadogExporter::Client::Config.new,
10
+ client: DatadogExporter::Client.new(config:),
11
+ request: DatadogExporter::DatadogApiRequests::Monitors.new(config:, client:),
12
+ name_transformer: Utilities::NameTransformer.new,
13
+ template_creator: Utilities::TemplateManager.new(config:),
14
+ file_class: File
15
+ )
16
+ @config = config
17
+ @request = request
18
+ @monitors_base_path = config.base_path.join(DatadogExporter::Monitors::EXPORT_FOLDER)
19
+ @name_transformer = name_transformer
20
+ @template_creator = template_creator
21
+ @file_class = file_class
22
+ end
23
+
24
+ ##
25
+ # Exports Datadog monitors configuration in YAML files.
26
+ # If no tag is provided, it exports all the monitors.
27
+ #
28
+ # @param [String] tag (optional) A tag defined in the Datadog monitors
29
+ #
30
+ # @return [String] Output message
31
+ def export(tag: nil)
32
+ reset_monitors_dir
33
+
34
+ monitors(tag:).each { |exported_datadog_hash| save_original(exported_datadog_hash) }
35
+
36
+ config.logger.info("Exported #{@monitors.count} monitors to #{@monitors_base_path}")
37
+ end
38
+
39
+ ##
40
+ # Exports Datadog monitors configuration in YAML files as templates.
41
+ # If no tag is provided, it exports all the monitors.
42
+ #
43
+ # See TemplateManager for more information about the transformation.
44
+ #
45
+ # @param [String] tag (optional) A tag defined in the Datadog monitors
46
+ #
47
+ # @return [String] Output message
48
+ def export_as_template(tag: nil)
49
+ reset_monitors_dir
50
+
51
+ monitors(tag:).each { |exported_datadog_hash| save_template(exported_datadog_hash) }
52
+
53
+ config.logger.info(
54
+ "Exported #{@monitors.count} monitor templates to #{@monitors_base_path}",
55
+ )
56
+ end
57
+
58
+ private
59
+
60
+ def monitors(tag:)
61
+ @monitors ||= @request.list_monitors(tag:)
62
+ end
63
+
64
+ def reset_monitors_dir
65
+ @monitors_base_path.mkpath unless @file_class.exist?(@monitors_base_path)
66
+
67
+ @monitors_base_path.glob("*.yml").each(&:delete)
68
+ end
69
+
70
+ def save_template(exported_datadog_hash)
71
+ save!(
72
+ "template.#{@name_transformer.transform(exported_datadog_hash[:name])}",
73
+ @template_creator.create_template(exported_datadog_hash).to_yaml,
74
+ )
75
+ end
76
+
77
+ def save_original(exported_datadog_hash)
78
+ save!(
79
+ @name_transformer.transform(exported_datadog_hash[:name]),
80
+ exported_datadog_hash.to_yaml,
81
+ )
82
+ end
83
+
84
+ def save!(name, yaml_data)
85
+ filepath = @monitors_base_path.join("#{name}.yml")
86
+ @file_class.write(filepath, yaml_data)
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,63 @@
1
+ module DatadogExporter
2
+ module Monitors
3
+ ##
4
+ # This class provide import tools for Datadog Monitors
5
+ #
6
+ # Whether they are templates or original monitors, it imports them from the base_path/monitors folder.
7
+ class Import
8
+ def initialize(
9
+ config: DatadogExporter::Client::Config.new,
10
+ client: DatadogExporter::Client.new(config:),
11
+ request: DatadogExporter::DatadogApiRequests::Monitors.new(config:, client:),
12
+ template_manager: Utilities::TemplateManager.new(config:),
13
+ file_class: File
14
+ )
15
+ @config = config
16
+ @request = request
17
+ @monitors_base_path = config.base_path.join(DatadogExporter::Monitors::EXPORT_FOLDER)
18
+ @template_manager = template_manager
19
+ @file_class = file_class
20
+ end
21
+
22
+ ##
23
+ # Imports Datadog monitors configuration from YAML files.
24
+ #
25
+ # * Loops all exported monitors in the base_path/monitors folder.
26
+ # * Transforms the monitor into a template with placeholders.
27
+ # * Replace the placeholders with the environment values
28
+ # * Creates the monitor in the Datadog environment.
29
+ #
30
+ # If a tag is provided, it imports only the monitors with that tag.
31
+ #
32
+ # @param [Symbol] to The environment (defined in your organizations_config_filename) where the monitors will be imported
33
+ # @param [String] tag (optional) A tag defined in the Datadog monitors
34
+ def import(to:, tag: nil)
35
+ monitors
36
+ .select { |monitor| tag.nil? || monitor[:tags].include?(tag) }
37
+ .each do |monitor|
38
+ template = @template_manager.create_template(monitor)
39
+ target_monitor = @template_manager.create_monitor(template, environment: to)
40
+ @request.create(target_monitor) if accepted?(target_monitor, to)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def monitors
47
+ @monitors_base_path
48
+ .glob("*.yml")
49
+ .map do |file|
50
+ YAML.load_file(file, permitted_classes: [Symbol, Time, Date, DateTime, Object])
51
+ end
52
+ end
53
+
54
+ def accepted?(monitor, to)
55
+ puts monitor.to_yaml
56
+
57
+ print "Save to #{to} (y/n): "
58
+ input = gets.chomp.downcase
59
+ input == "y"
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ module DatadogExporter
2
+ module Monitors
3
+ module Utilities
4
+ ##
5
+ # This class transform the monitor name to a valid file name
6
+ class NameTransformer
7
+ # Transforms the monitor name to a valid file name
8
+ #
9
+ # @param [String] name The monitor name
10
+ #
11
+ # @return [String] The transformed name
12
+ def transform(name)
13
+ name.downcase.gsub(/\s*\|\s*/, "_").tr("/", "_").gsub(/\s+/, "_")
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,126 @@
1
+ module DatadogExporter
2
+ module Monitors
3
+ module Utilities
4
+ ##
5
+ # This class transform the monitor into a template
6
+ class TemplateManager
7
+ ##
8
+ # This class replaces the matching text with a placeholder
9
+ class StringToPlaceholder
10
+ attr_reader :original_string, :placeholders
11
+
12
+ def initialize(original_string, placeholders)
13
+ @original_string = original_string
14
+ @placeholders = placeholders
15
+ end
16
+
17
+ def replace
18
+ replaced_string = original_string.dup
19
+
20
+ placeholders.each do |placeholder_name, matching_text|
21
+ placeholder = "#{placeholder_name}_placeholder"
22
+ original_string.include?(matching_text) &&
23
+ replaced_string.gsub!(matching_text, placeholder)
24
+ end
25
+
26
+ replaced_string
27
+ end
28
+ end
29
+
30
+ ##
31
+ # This class replaces the matching placeholder with text
32
+ class PlaceholderToString
33
+ attr_reader :target_string, :placeholders
34
+
35
+ def initialize(placeholders, target_string)
36
+ @placeholders = placeholders
37
+ @target_string = target_string
38
+ end
39
+
40
+ def replace
41
+ replaced_string = target_string.dup
42
+
43
+ placeholders.each do |placeholder_name, target_text|
44
+ placeholder = "#{placeholder_name}_placeholder"
45
+ target_string.include?(placeholder) && replaced_string.gsub!(placeholder, target_text)
46
+ end
47
+
48
+ replaced_string
49
+ end
50
+ end
51
+
52
+ def initialize(config: DatadogExporter::Client::Config.new)
53
+ @config = config.organizations_config[:monitors]
54
+ end
55
+
56
+ # Transforms the monitor to a template
57
+ #
58
+ # @param [Hash] datadog_hash The monitor configuration
59
+ #
60
+ # @param [Symbol] environment (optional) The env tag name where the placeholders are defined
61
+ # Placeholders can be defined in a "organizations_config.yml" file
62
+ # See "organizations_config.example.yml" to see an example
63
+ #
64
+ # @return [Hash] The transformed monitor into a template with placeholders
65
+ # If no placeholder is defined, it returns the monitor as it is
66
+ def create_template(datadog_hash, environment: :base)
67
+ placeholders = @config[:placeholders][environment]
68
+
69
+ replace_values_with_placeholders(filter_by_template_keys(datadog_hash), placeholders)
70
+ end
71
+
72
+ def create_monitor(template, environment: :base)
73
+ placeholders = @config[:placeholders][environment]
74
+
75
+ replace_placeholders_with_values(placeholders, filter_by_template_keys(template))
76
+ end
77
+
78
+ private
79
+
80
+ def filter_by_template_keys(datadog_hash)
81
+ return datadog_hash if template_keys.empty?
82
+
83
+ datadog_hash.slice(*template_keys)
84
+ end
85
+
86
+ def replace_values_with_placeholders(hash_data, placeholders)
87
+ return hash_data if placeholders.nil?
88
+
89
+ hash_data.each do |key, value|
90
+ case value
91
+ when Hash
92
+ replace_values_with_placeholders(value, placeholders)
93
+ when String
94
+ hash_data[key] = StringToPlaceholder.new(value, placeholders).replace
95
+ when Array
96
+ hash_data[key] = value.map do |value|
97
+ StringToPlaceholder.new(value, placeholders).replace
98
+ end
99
+ end
100
+ end
101
+ end
102
+
103
+ def replace_placeholders_with_values(placeholders, hash_data)
104
+ return hash_data if placeholders.nil?
105
+
106
+ hash_data.each do |key, value|
107
+ case value
108
+ when Hash
109
+ replace_placeholders_with_values(placeholders, value)
110
+ when String
111
+ hash_data[key] = PlaceholderToString.new(placeholders, value).replace
112
+ when Array
113
+ hash_data[key] = value.map do |value|
114
+ PlaceholderToString.new(placeholders, value).replace
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ def template_keys
121
+ @config[:template_keys]
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,12 @@
1
+ require_relative "monitors/utilities/name_transformer"
2
+ require_relative "monitors/utilities/template_manager"
3
+ require_relative "monitors/export"
4
+ require_relative "monitors/import"
5
+
6
+ module DatadogExporter
7
+ ##
8
+ # The module that contains all the Datadog Monitors related classes
9
+ module Monitors
10
+ EXPORT_FOLDER = "monitors".freeze
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module DatadogExporter
2
+ VERSION = "0.2.0".freeze
3
+ end
@@ -0,0 +1,34 @@
1
+ require "datadog_exporter/configuration"
2
+ require "datadog_exporter/client"
3
+ require "datadog_exporter/datadog_api_requests"
4
+ require "datadog_exporter/monitors"
5
+
6
+ ##
7
+ # The DatadogExporter tool
8
+ module DatadogExporter
9
+ # Returns the global `DatadogExporter::Configuration` object. While
10
+ # you _can_ use this method to access the configuration, the more common
11
+ # convention is to use `DatadogExporter.configure``
12
+ #
13
+ # @example
14
+ # DatadogExporter.configuration.logger = Logger.new($stdout)
15
+ # @see DatadogExporter.configure
16
+ # @see DatadogExporter::Configuration
17
+ def self.configuration
18
+ @configuration ||= DatadogExporter::Configuration.new
19
+ end
20
+
21
+ # Yields the global configuration to a block.
22
+ # @yield [Configuration] global configuration
23
+ #
24
+ # @example
25
+ # DatadogExporter.configure do |config|
26
+ # config.logger = Logger.new($stdout)
27
+ # end
28
+ # @see DatadogExporter::Configuration
29
+ def self.configure
30
+ raise ArgumentError, "Please provide a block to configure" unless block_given?
31
+
32
+ yield configuration
33
+ end
34
+ end
@@ -0,0 +1,12 @@
1
+ module DatadogAPIClient
2
+ ##
3
+ # Stub for the Datadog API client to help Solargraph
4
+ #
5
+ # The reason of this stub is to help Solargraph to understand the code because
6
+ # it is rasing next error:
7
+ #
8
+ # .../gems/3.3.0/doc/datadog_api_client-2.27.1/.yardoc is too large to process (26824811 bytes)
9
+ # .../datadog_exporter/client.rb:17 - Unresolved return type DatadogAPIClient::APIClient for DatadogExporter::Client#datadog_client
10
+ class APIClient # rubocop:disable Lint/EmptyClass
11
+ end
12
+ end
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: datadog-exporter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Fran Martinez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2024-12-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 2.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 2.0.0
27
+ description:
28
+ email:
29
+ - martinezcoder@gmail.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - lib/datadog_exporter.rb
35
+ - lib/datadog_exporter/client.rb
36
+ - lib/datadog_exporter/client/config.rb
37
+ - lib/datadog_exporter/configuration.rb
38
+ - lib/datadog_exporter/datadog_api_requests.rb
39
+ - lib/datadog_exporter/datadog_api_requests/monitors.rb
40
+ - lib/datadog_exporter/monitors.rb
41
+ - lib/datadog_exporter/monitors/export.rb
42
+ - lib/datadog_exporter/monitors/import.rb
43
+ - lib/datadog_exporter/monitors/utilities/name_transformer.rb
44
+ - lib/datadog_exporter/monitors/utilities/template_manager.rb
45
+ - lib/datadog_exporter/version.rb
46
+ - lib/stubs/datadog_stub.rb
47
+ homepage: https://github.com/datadog-exporter/datadog-exporter
48
+ licenses:
49
+ - MIT
50
+ metadata:
51
+ rubygems_mfa_required: 'true'
52
+ post_install_message:
53
+ rdoc_options: []
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '3.3'
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ requirements:
63
+ - - ">="
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubygems_version: 3.5.16
68
+ signing_key:
69
+ specification_version: 4
70
+ summary: Exporting tools for Datadog
71
+ test_files: []