datadog-exporter 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []