kennel 1.122.0 → 1.124.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 +4 -4
 - data/lib/kennel/api.rb +4 -4
 - data/lib/kennel/file_cache.rb +11 -11
 - data/lib/kennel/importer.rb +3 -1
 - data/lib/kennel/models/dashboard.rb +8 -16
 - data/lib/kennel/models/monitor.rb +12 -14
 - data/lib/kennel/models/project.rb +1 -6
 - data/lib/kennel/models/record.rb +73 -13
 - data/lib/kennel/models/slo.rb +15 -16
 - data/lib/kennel/models/synthetic_test.rb +4 -11
 - data/lib/kennel/optional_validations.rb +58 -1
 - data/lib/kennel/syncer.rb +41 -30
 - data/lib/kennel/tasks.rb +16 -12
 - data/lib/kennel/utils.rb +6 -0
 - data/lib/kennel/version.rb +1 -1
 - data/lib/kennel.rb +30 -28
 - metadata +2 -3
 - data/lib/kennel/compatibility.rb +0 -27
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 31edba322106d5b2f1942c57640f19eff6a265ea970b5bb2cc7f4ef02d1889fd
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: f47474f8b7d745714fb49231755ed2bf41fae62cf8b5ff3817017c8cd8277713
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 3dc6b1b72e834b7b7663b577c830dfa23da87a197752b04988edd2366ac9089685ff01e19a1e3c8244ac9a41f4217c75ec34bc6198ca83cf545bf6079069d71e
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 1dffcf6adbd06fe80117d9a5f9bc3b55fac90bc21abb4fdd4ebeb5b726ade944c799edbbcca3eabbc5a28beff6a302011ecfcce18f041b4ad2228076e164c2d6
         
     | 
    
        data/lib/kennel/api.rb
    CHANGED
    
    | 
         @@ -1,13 +1,13 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            # encapsulates knowledge around how the api works
         
     | 
| 
       3 
     | 
    
         
            -
            # especially 1-off weirdness that should not  
     | 
| 
      
 3 
     | 
    
         
            +
            # especially 1-off weirdness that should not leak into other parts of the code
         
     | 
| 
       4 
4 
     | 
    
         
             
            module Kennel
         
     | 
| 
       5 
5 
     | 
    
         
             
              class Api
         
     | 
| 
       6 
6 
     | 
    
         
             
                CACHE_FILE = "tmp/cache/details"
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
                def initialize(app_key, api_key)
         
     | 
| 
       9 
     | 
    
         
            -
                  @app_key = app_key
         
     | 
| 
       10 
     | 
    
         
            -
                  @api_key = api_key
         
     | 
| 
      
 8 
     | 
    
         
            +
                def initialize(app_key = nil, api_key = nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  @app_key = app_key || ENV.fetch("DATADOG_APP_KEY")
         
     | 
| 
      
 10 
     | 
    
         
            +
                  @api_key = api_key || ENV.fetch("DATADOG_API_KEY")
         
     | 
| 
       11 
11 
     | 
    
         
             
                  url = Utils.path_to_url("", subdomain: "app")
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @client = Faraday.new(url: url) { |c| c.adapter :net_http_persistent }
         
     | 
| 
       13 
13 
     | 
    
         
             
                end
         
     | 
    
        data/lib/kennel/file_cache.rb
    CHANGED
    
    | 
         @@ -15,11 +15,13 @@ module Kennel 
     | 
|
| 
       15 
15 
     | 
    
         
             
                end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                def open
         
     | 
| 
       18 
     | 
    
         
            -
                  load_data
         
     | 
| 
       19 
     | 
    
         
            -
                   
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
                   
     | 
| 
      
 18 
     | 
    
         
            +
                  @data = load_data || {}
         
     | 
| 
      
 19 
     | 
    
         
            +
                  begin
         
     | 
| 
      
 20 
     | 
    
         
            +
                    expire_old_data
         
     | 
| 
      
 21 
     | 
    
         
            +
                    yield self
         
     | 
| 
      
 22 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 23 
     | 
    
         
            +
                    persist
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
       23 
25 
     | 
    
         
             
                end
         
     | 
| 
       24 
26 
     | 
    
         | 
| 
       25 
27 
     | 
    
         
             
                def fetch(key, key_version)
         
     | 
| 
         @@ -35,12 +37,9 @@ module Kennel 
     | 
|
| 
       35 
37 
     | 
    
         
             
                private
         
     | 
| 
       36 
38 
     | 
    
         | 
| 
       37 
39 
     | 
    
         
             
                def load_data
         
     | 
| 
       38 
     | 
    
         
            -
                  @ 
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                    rescue StandardError
         
     | 
| 
       42 
     | 
    
         
            -
                      {}
         
     | 
| 
       43 
     | 
    
         
            -
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  Marshal.load(File.read(@file)) # rubocop:disable Security/MarshalLoad
         
     | 
| 
      
 41 
     | 
    
         
            +
                rescue Errno::ENOENT, TypeError, ArgumentError
         
     | 
| 
      
 42 
     | 
    
         
            +
                  nil
         
     | 
| 
       44 
43 
     | 
    
         
             
                end
         
     | 
| 
       45 
44 
     | 
    
         | 
| 
       46 
45 
     | 
    
         
             
                def persist
         
     | 
| 
         @@ -49,6 +48,7 @@ module Kennel 
     | 
|
| 
       49 
48 
     | 
    
         | 
| 
       50 
49 
     | 
    
         
             
                  Tempfile.create "kennel-file-cache", dir do |tmp|
         
     | 
| 
       51 
50 
     | 
    
         
             
                    Marshal.dump @data, tmp
         
     | 
| 
      
 51 
     | 
    
         
            +
                    tmp.flush
         
     | 
| 
       52 
52 
     | 
    
         
             
                    File.rename tmp.path, @file
         
     | 
| 
       53 
53 
     | 
    
         
             
                  end
         
     | 
| 
       54 
54 
     | 
    
         
             
                end
         
     | 
    
        data/lib/kennel/importer.rb
    CHANGED
    
    | 
         @@ -38,6 +38,8 @@ module Kennel 
     | 
|
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
                  case resource
         
     | 
| 
       40 
40 
     | 
    
         
             
                  when "monitor"
         
     | 
| 
      
 41 
     | 
    
         
            +
                    raise "Import the synthetic test page and not the monitor" if data[:type] == "synthetics alert"
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
       41 
43 
     | 
    
         
             
                    # flatten monitor options so they are all on the base which is how Monitor builds them
         
     | 
| 
       42 
44 
     | 
    
         
             
                    data.merge!(data.delete(:options))
         
     | 
| 
       43 
45 
     | 
    
         
             
                    data.merge!(data.delete(:thresholds) || {})
         
     | 
| 
         @@ -96,7 +98,7 @@ module Kennel 
     | 
|
| 
       96 
98 
     | 
    
         
             
                def link_composite_monitors(data)
         
     | 
| 
       97 
99 
     | 
    
         
             
                  if data[:type] == "composite"
         
     | 
| 
       98 
100 
     | 
    
         
             
                    data[:query].gsub!(/\d+/) do |id|
         
     | 
| 
       99 
     | 
    
         
            -
                      object =  
     | 
| 
      
 101 
     | 
    
         
            +
                      object = @api.show("monitor", id)
         
     | 
| 
       100 
102 
     | 
    
         
             
                      tracking_id = Kennel::Models::Monitor.parse_tracking_id(object)
         
     | 
| 
       101 
103 
     | 
    
         
             
                      tracking_id ? "%{#{tracking_id}}" : id
         
     | 
| 
       102 
104 
     | 
    
         
             
                    rescue StandardError # monitor not found
         
     | 
| 
         @@ -3,7 +3,6 @@ module Kennel 
     | 
|
| 
       3 
3 
     | 
    
         
             
              module Models
         
     | 
| 
       4 
4 
     | 
    
         
             
                class Dashboard < Record
         
     | 
| 
       5 
5 
     | 
    
         
             
                  include TemplateVariables
         
     | 
| 
       6 
     | 
    
         
            -
                  include OptionalValidations
         
     | 
| 
       7 
6 
     | 
    
         | 
| 
       8 
7 
     | 
    
         
             
                  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
         
     | 
| 
       9 
8 
     | 
    
         
             
                    :author_handle, :author_name, :modified_at, :deleted_at, :url, :is_read_only, :notify_list, :restricted_roles
         
     | 
| 
         @@ -87,8 +86,7 @@ module Kennel 
     | 
|
| 
       87 
86 
     | 
    
         
             
                    tags: -> do # not inherited by default to make onboarding to using dashboard tags simple
         
     | 
| 
       88 
87 
     | 
    
         
             
                      team = project.team
         
     | 
| 
       89 
88 
     | 
    
         
             
                      team.tag_dashboards ? team.tags : []
         
     | 
| 
       90 
     | 
    
         
            -
                    end 
     | 
| 
       91 
     | 
    
         
            -
                    id: -> { nil }
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end
         
     | 
| 
       92 
90 
     | 
    
         
             
                  )
         
     | 
| 
       93 
91 
     | 
    
         | 
| 
       94 
92 
     | 
    
         
             
                  class << self
         
     | 
| 
         @@ -152,29 +150,24 @@ module Kennel 
     | 
|
| 
       152 
150 
     | 
    
         
             
                    end
         
     | 
| 
       153 
151 
     | 
    
         
             
                  end
         
     | 
| 
       154 
152 
     | 
    
         | 
| 
       155 
     | 
    
         
            -
                  def  
     | 
| 
       156 
     | 
    
         
            -
                    return @json if @json
         
     | 
| 
      
 153 
     | 
    
         
            +
                  def build_json
         
     | 
| 
       157 
154 
     | 
    
         
             
                    all_widgets = render_definitions(definitions) + widgets
         
     | 
| 
       158 
155 
     | 
    
         
             
                    expand_q all_widgets
         
     | 
| 
       159 
156 
     | 
    
         
             
                    tags = tags()
         
     | 
| 
       160 
157 
     | 
    
         
             
                    tags_as_string = (tags.empty? ? "" : " (#{tags.join(" ")})")
         
     | 
| 
       161 
158 
     | 
    
         | 
| 
       162 
     | 
    
         
            -
                     
     | 
| 
      
 159 
     | 
    
         
            +
                    json = super.merge(
         
     | 
| 
       163 
160 
     | 
    
         
             
                      layout_type: layout_type,
         
     | 
| 
       164 
161 
     | 
    
         
             
                      title: "#{title}#{tags_as_string}#{LOCK}",
         
     | 
| 
       165 
162 
     | 
    
         
             
                      description: description,
         
     | 
| 
       166 
163 
     | 
    
         
             
                      template_variables: render_template_variables,
         
     | 
| 
       167 
164 
     | 
    
         
             
                      template_variable_presets: template_variable_presets,
         
     | 
| 
       168 
165 
     | 
    
         
             
                      widgets: all_widgets
         
     | 
| 
       169 
     | 
    
         
            -
                     
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
                    @json[:reflow_type] = reflow_type if reflow_type # setting nil breaks create with "ordered"
         
     | 
| 
      
 166 
     | 
    
         
            +
                    )
         
     | 
| 
       172 
167 
     | 
    
         | 
| 
       173 
     | 
    
         
            -
                     
     | 
| 
      
 168 
     | 
    
         
            +
                    json[:reflow_type] = reflow_type if reflow_type # setting nil breaks create with "ordered"
         
     | 
| 
       174 
169 
     | 
    
         | 
| 
       175 
     | 
    
         
            -
                     
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
                    @json
         
     | 
| 
      
 170 
     | 
    
         
            +
                    json
         
     | 
| 
       178 
171 
     | 
    
         
             
                  end
         
     | 
| 
       179 
172 
     | 
    
         | 
| 
       180 
173 
     | 
    
         
             
                  def self.url(id)
         
     | 
| 
         @@ -210,9 +203,8 @@ module Kennel 
     | 
|
| 
       210 
203 
     | 
    
         
             
                  end
         
     | 
| 
       211 
204 
     | 
    
         | 
| 
       212 
205 
     | 
    
         
             
                  def validate_update!(_actuals, diffs)
         
     | 
| 
       213 
     | 
    
         
            -
                     
     | 
| 
       214 
     | 
    
         
            -
             
     | 
| 
       215 
     | 
    
         
            -
                    end
         
     | 
| 
      
 206 
     | 
    
         
            +
                    _, path, from, to = diffs.find { |diff| diff[1] == "layout_type" }
         
     | 
| 
      
 207 
     | 
    
         
            +
                    invalid_update!(path, from, to) if path
         
     | 
| 
       216 
208 
     | 
    
         
             
                  end
         
     | 
| 
       217 
209 
     | 
    
         | 
| 
       218 
210 
     | 
    
         
             
                  private
         
     | 
| 
         @@ -2,8 +2,6 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            module Kennel
         
     | 
| 
       3 
3 
     | 
    
         
             
              module Models
         
     | 
| 
       4 
4 
     | 
    
         
             
                class Monitor < Record
         
     | 
| 
       5 
     | 
    
         
            -
                  include OptionalValidations
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
5 
     | 
    
         
             
                  RENOTIFY_INTERVALS = [0, 10, 20, 30, 40, 50, 60, 90, 120, 180, 240, 300, 360, 720, 1440].freeze # minutes
         
     | 
| 
       8 
6 
     | 
    
         
             
                  OPTIONAL_SERVICE_CHECK_THRESHOLDS = [:ok, :warning].freeze
         
     | 
| 
       9 
7 
     | 
    
         
             
                  READONLY_ATTRIBUTES = superclass::READONLY_ATTRIBUTES + [
         
     | 
| 
         @@ -41,8 +39,7 @@ module Kennel 
     | 
|
| 
       41 
39 
     | 
    
         
             
                    renotify_interval: -> { project.team.renotify_interval },
         
     | 
| 
       42 
40 
     | 
    
         
             
                    warning: -> { nil },
         
     | 
| 
       43 
41 
     | 
    
         
             
                    ok: -> { nil },
         
     | 
| 
       44 
     | 
    
         
            -
                     
     | 
| 
       45 
     | 
    
         
            -
                    notify_no_data: -> { true }, # datadog sets this to false by default, but true is the safer
         
     | 
| 
      
 42 
     | 
    
         
            +
                    notify_no_data: -> { true }, # datadog UI sets this to false by default, but true is safer
         
     | 
| 
       46 
43 
     | 
    
         
             
                    no_data_timeframe: -> { 60 },
         
     | 
| 
       47 
44 
     | 
    
         
             
                    notify_audit: -> { MONITOR_OPTION_DEFAULTS.fetch(:notify_audit) },
         
     | 
| 
       48 
45 
     | 
    
         
             
                    new_host_delay: -> { MONITOR_OPTION_DEFAULTS.fetch(:new_host_delay) },
         
     | 
| 
         @@ -56,9 +53,8 @@ module Kennel 
     | 
|
| 
       56 
53 
     | 
    
         
             
                    priority: -> { MONITOR_DEFAULTS.fetch(:priority) }
         
     | 
| 
       57 
54 
     | 
    
         
             
                  )
         
     | 
| 
       58 
55 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
                  def  
     | 
| 
       60 
     | 
    
         
            -
                     
     | 
| 
       61 
     | 
    
         
            -
                    data = {
         
     | 
| 
      
 56 
     | 
    
         
            +
                  def build_json
         
     | 
| 
      
 57 
     | 
    
         
            +
                    data = super.merge(
         
     | 
| 
       62 
58 
     | 
    
         
             
                      name: "#{name}#{LOCK}",
         
     | 
| 
       63 
59 
     | 
    
         
             
                      type: type,
         
     | 
| 
       64 
60 
     | 
    
         
             
                      query: query.strip,
         
     | 
| 
         @@ -79,9 +75,7 @@ module Kennel 
     | 
|
| 
       79 
75 
     | 
    
         
             
                        locked: false, # setting this to true prevents any edit and breaks updates when using replace workflow
         
     | 
| 
       80 
76 
     | 
    
         
             
                        renotify_interval: renotify_interval || 0
         
     | 
| 
       81 
77 
     | 
    
         
             
                      }
         
     | 
| 
       82 
     | 
    
         
            -
                     
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                    data[:id] = id if id
         
     | 
| 
      
 78 
     | 
    
         
            +
                    )
         
     | 
| 
       85 
79 
     | 
    
         | 
| 
       86 
80 
     | 
    
         
             
                    options = data[:options]
         
     | 
| 
       87 
81 
     | 
    
         
             
                    if data.fetch(:type) != "composite"
         
     | 
| 
         @@ -105,6 +99,12 @@ module Kennel 
     | 
|
| 
       105 
99 
     | 
    
         
             
                      end
         
     | 
| 
       106 
100 
     | 
    
         
             
                    end
         
     | 
| 
       107 
101 
     | 
    
         | 
| 
      
 102 
     | 
    
         
            +
                    # setting this via the api breaks the UI with
         
     | 
| 
      
 103 
     | 
    
         
            +
                    # "The no_data_timeframe option is not allowed for log alert monitors"
         
     | 
| 
      
 104 
     | 
    
         
            +
                    if data.fetch(:type) == "log alert"
         
     | 
| 
      
 105 
     | 
    
         
            +
                      options.delete :no_data_timeframe
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
       108 
108 
     | 
    
         
             
                    if windows = threshold_windows
         
     | 
| 
       109 
109 
     | 
    
         
             
                      options[:threshold_windows] = windows
         
     | 
| 
       110 
110 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -120,9 +120,7 @@ module Kennel 
     | 
|
| 
       120 
120 
     | 
    
         
             
                      options[:renotify_statuses] = statuses
         
     | 
| 
       121 
121 
     | 
    
         
             
                    end
         
     | 
| 
       122 
122 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
                     
     | 
| 
       124 
     | 
    
         
            -
             
     | 
| 
       125 
     | 
    
         
            -
                    @as_json = data
         
     | 
| 
      
 123 
     | 
    
         
            +
                    data
         
     | 
| 
       126 
124 
     | 
    
         
             
                  end
         
     | 
| 
       127 
125 
     | 
    
         | 
| 
       128 
126 
     | 
    
         
             
                  def resolve_linked_tracking_ids!(id_map, **args)
         
     | 
| 
         @@ -140,7 +138,7 @@ module Kennel 
     | 
|
| 
       140 
138 
     | 
    
         
             
                    # ensure type does not change, but not if it's metric->query which is supported and used by importer.rb
         
     | 
| 
       141 
139 
     | 
    
         
             
                    _, path, from, to = diffs.detect { |_, path, _, _| path == "type" }
         
     | 
| 
       142 
140 
     | 
    
         
             
                    if path && !(from == "metric alert" && to == "query alert")
         
     | 
| 
       143 
     | 
    
         
            -
                       
     | 
| 
      
 141 
     | 
    
         
            +
                      invalid_update!(path, from, to)
         
     | 
| 
       144 
142 
     | 
    
         
             
                    end
         
     | 
| 
       145 
143 
     | 
    
         
             
                  end
         
     | 
| 
       146 
144 
     | 
    
         | 
| 
         @@ -20,7 +20,7 @@ module Kennel 
     | 
|
| 
       20 
20 
     | 
    
         
             
                  def validated_parts
         
     | 
| 
       21 
21 
     | 
    
         
             
                    all = parts
         
     | 
| 
       22 
22 
     | 
    
         
             
                    unless all.is_a?(Array) && all.all? { |part| part.is_a?(Record) }
         
     | 
| 
       23 
     | 
    
         
            -
                       
     | 
| 
      
 23 
     | 
    
         
            +
                      raise "Project #{kennel_id} #parts must return an array of Records"
         
     | 
| 
       24 
24 
     | 
    
         
             
                    end
         
     | 
| 
       25 
25 
     | 
    
         | 
| 
       26 
26 
     | 
    
         
             
                    validate_parts(all)
         
     | 
| 
         @@ -29,11 +29,6 @@ module Kennel 
     | 
|
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                  private
         
     | 
| 
       31 
31 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
                  # let users know which project/resource failed when something happens during diffing where the backtrace is hidden
         
     | 
| 
       33 
     | 
    
         
            -
                  def invalid!(message)
         
     | 
| 
       34 
     | 
    
         
            -
                    raise ValidationError, "#{kennel_id} #{message}"
         
     | 
| 
       35 
     | 
    
         
            -
                  end
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
32 
     | 
    
         
             
                  # hook for users to add custom validations via `prepend`
         
     | 
| 
       38 
33 
     | 
    
         
             
                  def validate_parts(parts)
         
     | 
| 
       39 
34 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/kennel/models/record.rb
    CHANGED
    
    | 
         @@ -2,6 +2,16 @@ 
     | 
|
| 
       2 
2 
     | 
    
         
             
            module Kennel
         
     | 
| 
       3 
3 
     | 
    
         
             
              module Models
         
     | 
| 
       4 
4 
     | 
    
         
             
                class Record < Base
         
     | 
| 
      
 5 
     | 
    
         
            +
                  class PrepareError < StandardError
         
     | 
| 
      
 6 
     | 
    
         
            +
                    def initialize(tracking_id)
         
     | 
| 
      
 7 
     | 
    
         
            +
                      super("Error while preparing #{tracking_id}")
         
     | 
| 
      
 8 
     | 
    
         
            +
                    end
         
     | 
| 
      
 9 
     | 
    
         
            +
                  end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  UnvalidatedRecordError = Class.new(StandardError)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  include OptionalValidations
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       5 
15 
     | 
    
         
             
                  # Apart from if you just don't like the default for some reason,
         
     | 
| 
       6 
16 
     | 
    
         
             
                  # overriding MARKER_TEXT allows for namespacing within the same
         
     | 
| 
       7 
17 
     | 
    
         
             
                  # Datadog account. If you run one Kennel setup with marker text
         
     | 
| 
         @@ -30,6 +40,8 @@ module Kennel 
     | 
|
| 
       30 
40 
     | 
    
         | 
| 
       31 
41 
     | 
    
         
             
                  settings :id, :kennel_id
         
     | 
| 
       32 
42 
     | 
    
         | 
| 
      
 43 
     | 
    
         
            +
                  defaults(id: nil)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
       33 
45 
     | 
    
         
             
                  class << self
         
     | 
| 
       34 
46 
     | 
    
         
             
                    def parse_any_url(url)
         
     | 
| 
       35 
47 
     | 
    
         
             
                      subclasses.detect do |s|
         
     | 
| 
         @@ -72,7 +84,7 @@ module Kennel 
     | 
|
| 
       72 
84 
     | 
    
         
             
                    end
         
     | 
| 
       73 
85 
     | 
    
         
             
                  end
         
     | 
| 
       74 
86 
     | 
    
         | 
| 
       75 
     | 
    
         
            -
                  attr_reader :project
         
     | 
| 
      
 87 
     | 
    
         
            +
                  attr_reader :project, :unfiltered_validation_errors, :filtered_validation_errors
         
     | 
| 
       76 
88 
     | 
    
         | 
| 
       77 
89 
     | 
    
         
             
                  def initialize(project, *args)
         
     | 
| 
       78 
90 
     | 
    
         
             
                    raise ArgumentError, "First argument must be a project, not #{project.class}" unless project.is_a?(Project)
         
     | 
| 
         @@ -96,7 +108,7 @@ module Kennel 
     | 
|
| 
       96 
108 
     | 
    
         
             
                    @tracking_id ||= begin
         
     | 
| 
       97 
109 
     | 
    
         
             
                      id = "#{project.kennel_id}:#{kennel_id}"
         
     | 
| 
       98 
110 
     | 
    
         
             
                      unless id.match?(ALLOWED_KENNEL_ID_REGEX) # <-> parse_tracking_id
         
     | 
| 
       99 
     | 
    
         
            -
                        raise  
     | 
| 
      
 111 
     | 
    
         
            +
                        raise "Bad kennel/tracking id: #{id.inspect} must match #{ALLOWED_KENNEL_ID_REGEX}"
         
     | 
| 
       100 
112 
     | 
    
         
             
                      end
         
     | 
| 
       101 
113 
     | 
    
         
             
                      id
         
     | 
| 
       102 
114 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -108,7 +120,7 @@ module Kennel 
     | 
|
| 
       108 
120 
     | 
    
         
             
                  def add_tracking_id
         
     | 
| 
       109 
121 
     | 
    
         
             
                    json = as_json
         
     | 
| 
       110 
122 
     | 
    
         
             
                    if self.class.parse_tracking_id(json)
         
     | 
| 
       111 
     | 
    
         
            -
                       
     | 
| 
      
 123 
     | 
    
         
            +
                      raise "#{safe_tracking_id} Remove \"-- #{MARKER_TEXT}\" line from #{self.class::TRACKING_FIELD} to copy a resource"
         
     | 
| 
       112 
124 
     | 
    
         
             
                    end
         
     | 
| 
       113 
125 
     | 
    
         
             
                    json[self.class::TRACKING_FIELD] =
         
     | 
| 
       114 
126 
     | 
    
         
             
                      "#{json[self.class::TRACKING_FIELD]}\n" \
         
     | 
| 
         @@ -119,9 +131,54 @@ module Kennel 
     | 
|
| 
       119 
131 
     | 
    
         
             
                    self.class.remove_tracking_id(as_json)
         
     | 
| 
       120 
132 
     | 
    
         
             
                  end
         
     | 
| 
       121 
133 
     | 
    
         | 
| 
      
 134 
     | 
    
         
            +
                  def build_json
         
     | 
| 
      
 135 
     | 
    
         
            +
                    {
         
     | 
| 
      
 136 
     | 
    
         
            +
                      id: id
         
     | 
| 
      
 137 
     | 
    
         
            +
                    }.compact
         
     | 
| 
      
 138 
     | 
    
         
            +
                  end
         
     | 
| 
      
 139 
     | 
    
         
            +
             
     | 
| 
      
 140 
     | 
    
         
            +
                  def build
         
     | 
| 
      
 141 
     | 
    
         
            +
                    @unfiltered_validation_errors = []
         
     | 
| 
      
 142 
     | 
    
         
            +
                    json = nil
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 145 
     | 
    
         
            +
                      json = build_json
         
     | 
| 
      
 146 
     | 
    
         
            +
                      (id = json.delete(:id)) && json[:id] = id
         
     | 
| 
      
 147 
     | 
    
         
            +
                      validate_json(json)
         
     | 
| 
      
 148 
     | 
    
         
            +
                    rescue StandardError
         
     | 
| 
      
 149 
     | 
    
         
            +
                      if unfiltered_validation_errors.empty?
         
     | 
| 
      
 150 
     | 
    
         
            +
                        @unfiltered_validation_errors = nil
         
     | 
| 
      
 151 
     | 
    
         
            +
                        raise PrepareError, safe_tracking_id
         
     | 
| 
      
 152 
     | 
    
         
            +
                      end
         
     | 
| 
      
 153 
     | 
    
         
            +
                    end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                    @filtered_validation_errors = filter_validation_errors
         
     | 
| 
      
 156 
     | 
    
         
            +
                    @as_json = json # Only valid if filtered_validation_errors.empty?
         
     | 
| 
      
 157 
     | 
    
         
            +
                  end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
                  def as_json
         
     | 
| 
      
 160 
     | 
    
         
            +
                    # A courtesy to those tests that still expect as_json to perform validation and raise on error
         
     | 
| 
      
 161 
     | 
    
         
            +
                    build if @unfiltered_validation_errors.nil?
         
     | 
| 
      
 162 
     | 
    
         
            +
                    raise UnvalidatedRecordError, "#{safe_tracking_id} as_json called on invalid part" unless filtered_validation_errors.empty?
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
                    @as_json
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
             
     | 
| 
      
 167 
     | 
    
         
            +
                  # Can raise DisallowedUpdateError
         
     | 
| 
       122 
168 
     | 
    
         
             
                  def validate_update!(*)
         
     | 
| 
       123 
169 
     | 
    
         
             
                  end
         
     | 
| 
       124 
170 
     | 
    
         | 
| 
      
 171 
     | 
    
         
            +
                  def invalid_update!(field, old_value, new_value)
         
     | 
| 
      
 172 
     | 
    
         
            +
                    raise DisallowedUpdateError, "#{safe_tracking_id} Datadog does not allow update of #{field} (#{old_value.inspect} -> #{new_value.inspect})"
         
     | 
| 
      
 173 
     | 
    
         
            +
                  end
         
     | 
| 
      
 174 
     | 
    
         
            +
             
     | 
| 
      
 175 
     | 
    
         
            +
                  # For use during error handling
         
     | 
| 
      
 176 
     | 
    
         
            +
                  def safe_tracking_id
         
     | 
| 
      
 177 
     | 
    
         
            +
                    tracking_id
         
     | 
| 
      
 178 
     | 
    
         
            +
                  rescue StandardError
         
     | 
| 
      
 179 
     | 
    
         
            +
                    "<unknown; #tracking_id crashed>"
         
     | 
| 
      
 180 
     | 
    
         
            +
                  end
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
       125 
182 
     | 
    
         
             
                  private
         
     | 
| 
       126 
183 
     | 
    
         | 
| 
       127 
184 
     | 
    
         
             
                  def resolve(value, type, id_map, force:)
         
     | 
| 
         @@ -133,26 +190,29 @@ module Kennel 
     | 
|
| 
       133 
190 
     | 
    
         
             
                    id.is_a?(String) && id.include?(":")
         
     | 
| 
       134 
191 
     | 
    
         
             
                  end
         
     | 
| 
       135 
192 
     | 
    
         | 
| 
       136 
     | 
    
         
            -
                  def resolve_link( 
     | 
| 
       137 
     | 
    
         
            -
                    if id_map.new?( 
     | 
| 
      
 193 
     | 
    
         
            +
                  def resolve_link(sought_tracking_id, sought_type, id_map, force:)
         
     | 
| 
      
 194 
     | 
    
         
            +
                    if id_map.new?(sought_type.to_s, sought_tracking_id)
         
     | 
| 
       138 
195 
     | 
    
         
             
                      if force
         
     | 
| 
       139 
     | 
    
         
            -
                         
     | 
| 
       140 
     | 
    
         
            -
                           
     | 
| 
       141 
     | 
    
         
            -
                           
     | 
| 
       142 
     | 
    
         
            -
                         
     | 
| 
      
 196 
     | 
    
         
            +
                        raise UnresolvableIdError, <<~MESSAGE
         
     | 
| 
      
 197 
     | 
    
         
            +
                          #{tracking_id} #{sought_type} #{sought_tracking_id} was referenced but is also created by the current run.
         
     | 
| 
      
 198 
     | 
    
         
            +
                          It could not be created because of a circular dependency. Try creating only some of the resources.
         
     | 
| 
      
 199 
     | 
    
         
            +
                        MESSAGE
         
     | 
| 
       143 
200 
     | 
    
         
             
                      else
         
     | 
| 
       144 
201 
     | 
    
         
             
                        nil # will be re-resolved after the linked object was created
         
     | 
| 
       145 
202 
     | 
    
         
             
                      end
         
     | 
| 
       146 
     | 
    
         
            -
                    elsif id = id_map.get( 
     | 
| 
      
 203 
     | 
    
         
            +
                    elsif id = id_map.get(sought_type.to_s, sought_tracking_id)
         
     | 
| 
       147 
204 
     | 
    
         
             
                      id
         
     | 
| 
       148 
205 
     | 
    
         
             
                    else
         
     | 
| 
       149 
     | 
    
         
            -
                       
     | 
| 
      
 206 
     | 
    
         
            +
                      raise UnresolvableIdError, <<~MESSAGE
         
     | 
| 
      
 207 
     | 
    
         
            +
                        #{tracking_id} Unable to find #{sought_type} #{sought_tracking_id}
         
     | 
| 
      
 208 
     | 
    
         
            +
                        This is either because it doesn't exist, and isn't being created by the current run;
         
     | 
| 
      
 209 
     | 
    
         
            +
                        or it does exist, but is being deleted.
         
     | 
| 
      
 210 
     | 
    
         
            +
                      MESSAGE
         
     | 
| 
       150 
211 
     | 
    
         
             
                    end
         
     | 
| 
       151 
212 
     | 
    
         
             
                  end
         
     | 
| 
       152 
213 
     | 
    
         | 
| 
       153 
     | 
    
         
            -
                  # let users know which project/resource failed when something happens during diffing where the backtrace is hidden
         
     | 
| 
       154 
214 
     | 
    
         
             
                  def invalid!(message)
         
     | 
| 
       155 
     | 
    
         
            -
                     
     | 
| 
      
 215 
     | 
    
         
            +
                    unfiltered_validation_errors << ValidationMessage.new(message)
         
     | 
| 
       156 
216 
     | 
    
         
             
                  end
         
     | 
| 
       157 
217 
     | 
    
         | 
| 
       158 
218 
     | 
    
         
             
                  def raise_with_location(error, message)
         
     | 
    
        data/lib/kennel/models/slo.rb
    CHANGED
    
    | 
         @@ -15,7 +15,6 @@ module Kennel 
     | 
|
| 
       15 
15 
     | 
    
         
             
                  settings :type, :description, :thresholds, :query, :tags, :monitor_ids, :monitor_tags, :name, :groups
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                  defaults(
         
     | 
| 
       18 
     | 
    
         
            -
                    id: -> { nil },
         
     | 
| 
       19 
18 
     | 
    
         
             
                    tags: -> { @project.tags },
         
     | 
| 
       20 
19 
     | 
    
         
             
                    query: -> { DEFAULTS.fetch(:query) },
         
     | 
| 
       21 
20 
     | 
    
         
             
                    description: -> { DEFAULTS.fetch(:description) },
         
     | 
| 
         @@ -24,35 +23,25 @@ module Kennel 
     | 
|
| 
       24 
23 
     | 
    
         
             
                    groups: -> { DEFAULTS.fetch(:groups) }
         
     | 
| 
       25 
24 
     | 
    
         
             
                  )
         
     | 
| 
       26 
25 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
                  def  
     | 
| 
       28 
     | 
    
         
            -
                    super
         
     | 
| 
       29 
     | 
    
         
            -
                    if thresholds.any? { |t| t[:warning] && t[:warning].to_f <= t[:critical].to_f }
         
     | 
| 
       30 
     | 
    
         
            -
                      raise ValidationError, "Threshold warning must be greater-than critical value"
         
     | 
| 
       31 
     | 
    
         
            -
                    end
         
     | 
| 
       32 
     | 
    
         
            -
                  end
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
                  def as_json
         
     | 
| 
       35 
     | 
    
         
            -
                    return @as_json if @as_json
         
     | 
| 
       36 
     | 
    
         
            -
                    data = {
         
     | 
| 
      
 26 
     | 
    
         
            +
                  def build_json
         
     | 
| 
      
 27 
     | 
    
         
            +
                    data = super.merge(
         
     | 
| 
       37 
28 
     | 
    
         
             
                      name: "#{name}#{LOCK}",
         
     | 
| 
       38 
29 
     | 
    
         
             
                      description: description,
         
     | 
| 
       39 
30 
     | 
    
         
             
                      thresholds: thresholds,
         
     | 
| 
       40 
31 
     | 
    
         
             
                      monitor_ids: monitor_ids,
         
     | 
| 
       41 
32 
     | 
    
         
             
                      tags: tags.uniq,
         
     | 
| 
       42 
33 
     | 
    
         
             
                      type: type
         
     | 
| 
       43 
     | 
    
         
            -
                     
     | 
| 
      
 34 
     | 
    
         
            +
                    )
         
     | 
| 
       44 
35 
     | 
    
         | 
| 
       45 
36 
     | 
    
         
             
                    if v = query
         
     | 
| 
       46 
37 
     | 
    
         
             
                      data[:query] = v
         
     | 
| 
       47 
38 
     | 
    
         
             
                    end
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                      data[:id] = v
         
     | 
| 
       50 
     | 
    
         
            -
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
       51 
40 
     | 
    
         
             
                    if v = groups
         
     | 
| 
       52 
41 
     | 
    
         
             
                      data[:groups] = v
         
     | 
| 
       53 
42 
     | 
    
         
             
                    end
         
     | 
| 
       54 
43 
     | 
    
         | 
| 
       55 
     | 
    
         
            -
                     
     | 
| 
      
 44 
     | 
    
         
            +
                    data
         
     | 
| 
       56 
45 
     | 
    
         
             
                  end
         
     | 
| 
       57 
46 
     | 
    
         | 
| 
       58 
47 
     | 
    
         
             
                  def self.api_resource
         
     | 
| 
         @@ -89,6 +78,16 @@ module Kennel 
     | 
|
| 
       89 
78 
     | 
    
         | 
| 
       90 
79 
     | 
    
         
             
                    ignore_default(expected, actual, DEFAULTS)
         
     | 
| 
       91 
80 
     | 
    
         
             
                  end
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
                  private
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                  def validate_json(data)
         
     | 
| 
      
 85 
     | 
    
         
            +
                    super
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                    if data[:thresholds].any? { |t| t[:warning] && t[:warning].to_f <= t[:critical].to_f }
         
     | 
| 
      
 88 
     | 
    
         
            +
                      invalid! "Threshold warning must be greater-than critical value"
         
     | 
| 
      
 89 
     | 
    
         
            +
                    end
         
     | 
| 
      
 90 
     | 
    
         
            +
                  end
         
     | 
| 
       92 
91 
     | 
    
         
             
                end
         
     | 
| 
       93 
92 
     | 
    
         
             
              end
         
     | 
| 
       94 
93 
     | 
    
         
             
            end
         
     | 
| 
         @@ -11,15 +11,14 @@ module Kennel 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  settings :tags, :config, :message, :subtype, :type, :name, :locations, :options
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
                  defaults(
         
     | 
| 
       14 
     | 
    
         
            -
                    id: -> { nil },
         
     | 
| 
       15 
14 
     | 
    
         
             
                    tags: -> { @project.tags },
         
     | 
| 
       16 
15 
     | 
    
         
             
                    message: -> { "\n\n#{project.mention}" }
         
     | 
| 
       17 
16 
     | 
    
         
             
                  )
         
     | 
| 
       18 
17 
     | 
    
         | 
| 
       19 
     | 
    
         
            -
                  def  
     | 
| 
       20 
     | 
    
         
            -
                    return @as_json if @as_json
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def build_json
         
     | 
| 
       21 
19 
     | 
    
         
             
                    locations = locations()
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    super.merge(
         
     | 
| 
       23 
22 
     | 
    
         
             
                      message: message,
         
     | 
| 
       24 
23 
     | 
    
         
             
                      tags: tags,
         
     | 
| 
       25 
24 
     | 
    
         
             
                      config: config,
         
     | 
| 
         @@ -28,13 +27,7 @@ module Kennel 
     | 
|
| 
       28 
27 
     | 
    
         
             
                      options: options,
         
     | 
| 
       29 
28 
     | 
    
         
             
                      name: "#{name}#{LOCK}",
         
     | 
| 
       30 
29 
     | 
    
         
             
                      locations: locations == :all ? LOCATIONS : locations
         
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                    if v = id
         
     | 
| 
       34 
     | 
    
         
            -
                      data[:id] = v
         
     | 
| 
       35 
     | 
    
         
            -
                    end
         
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
                    @as_json = data
         
     | 
| 
      
 30 
     | 
    
         
            +
                    )
         
     | 
| 
       38 
31 
     | 
    
         
             
                  end
         
     | 
| 
       39 
32 
     | 
    
         | 
| 
       40 
33 
     | 
    
         
             
                  def self.api_resource
         
     | 
| 
         @@ -1,15 +1,35 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         
             
            module Kennel
         
     | 
| 
       3 
3 
     | 
    
         
             
              module OptionalValidations
         
     | 
| 
      
 4 
     | 
    
         
            +
                ValidationMessage = Struct.new(:text)
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
       4 
6 
     | 
    
         
             
                def self.included(base)
         
     | 
| 
       5 
7 
     | 
    
         
             
                  base.settings :validate
         
     | 
| 
       6 
8 
     | 
    
         
             
                  base.defaults(validate: -> { true })
         
     | 
| 
       7 
9 
     | 
    
         
             
                end
         
     | 
| 
       8 
10 
     | 
    
         | 
| 
      
 11 
     | 
    
         
            +
                def self.valid?(parts)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  parts_with_errors = parts.reject do |part|
         
     | 
| 
      
 13 
     | 
    
         
            +
                    part.filtered_validation_errors.empty?
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  return true if parts_with_errors.empty?
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  Kennel.err.puts
         
     | 
| 
      
 19 
     | 
    
         
            +
                  parts_with_errors.sort_by(&:safe_tracking_id).each do |part|
         
     | 
| 
      
 20 
     | 
    
         
            +
                    part.filtered_validation_errors.each do |err|
         
     | 
| 
      
 21 
     | 
    
         
            +
                      Kennel.err.puts "#{part.safe_tracking_id} #{err.text}"
         
     | 
| 
      
 22 
     | 
    
         
            +
                    end
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  Kennel.err.puts
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                  false
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
       9 
29 
     | 
    
         
             
                private
         
     | 
| 
       10 
30 
     | 
    
         | 
| 
       11 
31 
     | 
    
         
             
                def validate_json(data)
         
     | 
| 
       12 
     | 
    
         
            -
                  bad = Kennel::Utils.all_keys(data).grep_v(Symbol)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  bad = Kennel::Utils.all_keys(data).grep_v(Symbol).sort.uniq
         
     | 
| 
       13 
33 
     | 
    
         
             
                  return if bad.empty?
         
     | 
| 
       14 
34 
     | 
    
         
             
                  invalid!(
         
     | 
| 
       15 
35 
     | 
    
         
             
                    "Only use Symbols as hash keys to avoid permanent diffs when updating.\n" \
         
     | 
| 
         @@ -17,5 +37,42 @@ module Kennel 
     | 
|
| 
       17 
37 
     | 
    
         
             
                    "#{bad.map(&:inspect).join("\n")}"
         
     | 
| 
       18 
38 
     | 
    
         
             
                  )
         
     | 
| 
       19 
39 
     | 
    
         
             
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def filter_validation_errors
         
     | 
| 
      
 42 
     | 
    
         
            +
                  if validate
         
     | 
| 
      
 43 
     | 
    
         
            +
                    unfiltered_validation_errors
         
     | 
| 
      
 44 
     | 
    
         
            +
                  elsif unfiltered_validation_errors.empty?
         
     | 
| 
      
 45 
     | 
    
         
            +
                    msg = "`validate` is set to false, but there are no validation errors to suppress. Remove `validate: false`"
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                    mode = ENV.fetch("UNNECESSARY_VALIDATE_FALSE") do
         
     | 
| 
      
 48 
     | 
    
         
            +
                      if ENV.key?("PROJECT") || ENV.key?("TRACKING_ID")
         
     | 
| 
      
 49 
     | 
    
         
            +
                        "fail"
         
     | 
| 
      
 50 
     | 
    
         
            +
                      else
         
     | 
| 
      
 51 
     | 
    
         
            +
                        nil
         
     | 
| 
      
 52 
     | 
    
         
            +
                      end
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                    if mode == "fail"
         
     | 
| 
      
 56 
     | 
    
         
            +
                      [ValidationMessage.new(msg)]
         
     | 
| 
      
 57 
     | 
    
         
            +
                    else
         
     | 
| 
      
 58 
     | 
    
         
            +
                      Kennel.out.puts "#{safe_tracking_id} #{msg}" if mode == "show"
         
     | 
| 
      
 59 
     | 
    
         
            +
                      []
         
     | 
| 
      
 60 
     | 
    
         
            +
                    end
         
     | 
| 
      
 61 
     | 
    
         
            +
                  else
         
     | 
| 
      
 62 
     | 
    
         
            +
                    mode = ENV.fetch("SUPPRESSED_ERRORS", "ignore")
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                    if mode == "fail"
         
     | 
| 
      
 65 
     | 
    
         
            +
                      unfiltered_validation_errors
         
     | 
| 
      
 66 
     | 
    
         
            +
                    else
         
     | 
| 
      
 67 
     | 
    
         
            +
                      if mode == "show"
         
     | 
| 
      
 68 
     | 
    
         
            +
                        unfiltered_validation_errors.each do |err|
         
     | 
| 
      
 69 
     | 
    
         
            +
                          Kennel.out.puts "#{safe_tracking_id} `validate: false` suppressing error: #{err.text.gsub("\n", " ")}"
         
     | 
| 
      
 70 
     | 
    
         
            +
                        end
         
     | 
| 
      
 71 
     | 
    
         
            +
                      end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                      []
         
     | 
| 
      
 74 
     | 
    
         
            +
                    end
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
       20 
77 
     | 
    
         
             
              end
         
     | 
| 
       21 
78 
     | 
    
         
             
            end
         
     | 
    
        data/lib/kennel/syncer.rb
    CHANGED
    
    | 
         @@ -6,12 +6,11 @@ module Kennel 
     | 
|
| 
       6 
6 
     | 
    
         
             
              class Syncer
         
     | 
| 
       7 
7 
     | 
    
         
             
                DELETE_ORDER = ["dashboard", "slo", "monitor", "synthetics/tests"].freeze # dashboards references monitors + slos, slos reference monitors
         
     | 
| 
       8 
8 
     | 
    
         
             
                LINE_UP = "\e[1A\033[K" # go up and clear
         
     | 
| 
      
 9 
     | 
    
         
            +
                Plan = Struct.new(:changes, keyword_init: true)
         
     | 
| 
       9 
10 
     | 
    
         | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
       11 
     | 
    
         
            -
                Update = Struct.new(:update_log, keyword_init: true)
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
                def initialize(api, expected, project_filter: nil, tracking_id_filter: nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                def initialize(api, expected, kennel:, project_filter: nil, tracking_id_filter: nil)
         
     | 
| 
       14 
12 
     | 
    
         
             
                  @api = api
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @kennel = kennel
         
     | 
| 
       15 
14 
     | 
    
         
             
                  @project_filter = project_filter
         
     | 
| 
       16 
15 
     | 
    
         
             
                  @tracking_id_filter = tracking_id_filter
         
     | 
| 
       17 
16 
     | 
    
         
             
                  @expected = Set.new expected # need set to speed up deletion
         
     | 
| 
         @@ -32,11 +31,10 @@ module Kennel 
     | 
|
| 
       32 
31 
     | 
    
         
             
                  end
         
     | 
| 
       33 
32 
     | 
    
         | 
| 
       34 
33 
     | 
    
         
             
                  Plan.new(
         
     | 
| 
       35 
     | 
    
         
            -
                     
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
       37 
     | 
    
         
            -
             
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
                    delete: @delete
         
     | 
| 
      
 34 
     | 
    
         
            +
                    changes:
         
     | 
| 
      
 35 
     | 
    
         
            +
                      @create.map { |_id, e| [:create, e.class.api_resource, e.tracking_id, nil] } +
         
     | 
| 
      
 36 
     | 
    
         
            +
                      @update.map { |_id, e| [:create, e.class.api_resource, e.tracking_id, nil] } +
         
     | 
| 
      
 37 
     | 
    
         
            +
                      @delete.map { |_id, _e, a| [:delete, a.fetch(:klass).api_resource, a.fetch(:tracking_id), a.fetch(:id)] }
         
     | 
| 
       40 
38 
     | 
    
         
             
                  )
         
     | 
| 
       41 
39 
     | 
    
         
             
                end
         
     | 
| 
       42 
40 
     | 
    
         | 
| 
         @@ -47,7 +45,7 @@ module Kennel 
     | 
|
| 
       47 
45 
     | 
    
         
             
                end
         
     | 
| 
       48 
46 
     | 
    
         | 
| 
       49 
47 
     | 
    
         
             
                def update
         
     | 
| 
       50 
     | 
    
         
            -
                   
     | 
| 
      
 48 
     | 
    
         
            +
                  changes = []
         
     | 
| 
       51 
49 
     | 
    
         | 
| 
       52 
50 
     | 
    
         
             
                  each_resolved @create do |_, e|
         
     | 
| 
       53 
51 
     | 
    
         
             
                    message = "#{e.class.api_resource} #{e.tracking_id}"
         
     | 
| 
         @@ -55,7 +53,7 @@ module Kennel 
     | 
|
| 
       55 
53 
     | 
    
         
             
                    reply = @api.create e.class.api_resource, e.as_json
         
     | 
| 
       56 
54 
     | 
    
         
             
                    cache_metadata reply, e.class
         
     | 
| 
       57 
55 
     | 
    
         
             
                    id = reply.fetch(:id)
         
     | 
| 
       58 
     | 
    
         
            -
                     
     | 
| 
      
 56 
     | 
    
         
            +
                    changes << [:create, e.class.api_resource, e.tracking_id, id]
         
     | 
| 
       59 
57 
     | 
    
         
             
                    populate_id_map [], [reply] # allow resolving ids we could previously no resolve
         
     | 
| 
       60 
58 
     | 
    
         
             
                    Kennel.out.puts "#{LINE_UP}Created #{message} #{e.class.url(id)}"
         
     | 
| 
       61 
59 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -64,7 +62,7 @@ module Kennel 
     | 
|
| 
       64 
62 
     | 
    
         
             
                    message = "#{e.class.api_resource} #{e.tracking_id} #{e.class.url(id)}"
         
     | 
| 
       65 
63 
     | 
    
         
             
                    Kennel.out.puts "Updating #{message}"
         
     | 
| 
       66 
64 
     | 
    
         
             
                    @api.update e.class.api_resource, id, e.as_json
         
     | 
| 
       67 
     | 
    
         
            -
                     
     | 
| 
      
 65 
     | 
    
         
            +
                    changes << [:update, e.class.api_resource, e.tracking_id, id]
         
     | 
| 
       68 
66 
     | 
    
         
             
                    Kennel.out.puts "#{LINE_UP}Updated #{message}"
         
     | 
| 
       69 
67 
     | 
    
         
             
                  end
         
     | 
| 
       70 
68 
     | 
    
         | 
| 
         @@ -73,15 +71,17 @@ module Kennel 
     | 
|
| 
       73 
71 
     | 
    
         
             
                    message = "#{klass.api_resource} #{a.fetch(:tracking_id)} #{id}"
         
     | 
| 
       74 
72 
     | 
    
         
             
                    Kennel.out.puts "Deleting #{message}"
         
     | 
| 
       75 
73 
     | 
    
         
             
                    @api.delete klass.api_resource, id
         
     | 
| 
       76 
     | 
    
         
            -
                     
     | 
| 
      
 74 
     | 
    
         
            +
                    changes << [:delete, klass.api_resource, a.fetch(:tracking_id), id]
         
     | 
| 
       77 
75 
     | 
    
         
             
                    Kennel.out.puts "#{LINE_UP}Deleted #{message}"
         
     | 
| 
       78 
76 
     | 
    
         
             
                  end
         
     | 
| 
       79 
77 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
                   
     | 
| 
      
 78 
     | 
    
         
            +
                  Plan.new(changes: changes)
         
     | 
| 
       81 
79 
     | 
    
         
             
                end
         
     | 
| 
       82 
80 
     | 
    
         | 
| 
       83 
81 
     | 
    
         
             
                private
         
     | 
| 
       84 
82 
     | 
    
         | 
| 
      
 83 
     | 
    
         
            +
                attr_reader :kennel
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
       85 
85 
     | 
    
         
             
                # loop over items until everything is resolved or crash when we get stuck
         
     | 
| 
       86 
86 
     | 
    
         
             
                # this solves cases like composite monitors depending on each other or monitor->monitor slo->slo monitor chains
         
     | 
| 
       87 
87 
     | 
    
         
             
                def each_resolved(list)
         
     | 
| 
         @@ -104,11 +104,11 @@ module Kennel 
     | 
|
| 
       104 
104 
     | 
    
         
             
                def resolved?(e)
         
     | 
| 
       105 
105 
     | 
    
         
             
                  assert_resolved e
         
     | 
| 
       106 
106 
     | 
    
         
             
                  true
         
     | 
| 
       107 
     | 
    
         
            -
                rescue  
     | 
| 
      
 107 
     | 
    
         
            +
                rescue UnresolvableIdError
         
     | 
| 
       108 
108 
     | 
    
         
             
                  false
         
     | 
| 
       109 
109 
     | 
    
         
             
                end
         
     | 
| 
       110 
110 
     | 
    
         | 
| 
       111 
     | 
    
         
            -
                # raises  
     | 
| 
      
 111 
     | 
    
         
            +
                # raises UnresolvableIdError when not resolved
         
     | 
| 
       112 
112 
     | 
    
         
             
                def assert_resolved(e)
         
     | 
| 
       113 
113 
     | 
    
         
             
                  resolve_linked_tracking_ids! [e], force: true
         
     | 
| 
       114 
114 
     | 
    
         
             
                end
         
     | 
| 
         @@ -121,7 +121,6 @@ module Kennel 
     | 
|
| 
       121 
121 
     | 
    
         
             
                  @warnings = []
         
     | 
| 
       122 
122 
     | 
    
         
             
                  @update = []
         
     | 
| 
       123 
123 
     | 
    
         
             
                  @delete = []
         
     | 
| 
       124 
     | 
    
         
            -
                  @no_change = []
         
     | 
| 
       125 
124 
     | 
    
         
             
                  @id_map = IdMap.new
         
     | 
| 
       126 
125 
     | 
    
         | 
| 
       127 
126 
     | 
    
         
             
                  actual = Progress.progress("Downloading definitions") { download_definitions }
         
     | 
| 
         @@ -154,8 +153,6 @@ module Kennel 
     | 
|
| 
       154 
153 
     | 
    
         
             
                        diff = e.diff(a) # slow ...
         
     | 
| 
       155 
154 
     | 
    
         
             
                        if diff.any?
         
     | 
| 
       156 
155 
     | 
    
         
             
                          @update << [id, e, a, diff]
         
     | 
| 
       157 
     | 
    
         
            -
                        else
         
     | 
| 
       158 
     | 
    
         
            -
                          @no_change << [id, e, a]
         
     | 
| 
       159 
156 
     | 
    
         
             
                        end
         
     | 
| 
       160 
157 
     | 
    
         
             
                      elsif a.fetch(:tracking_id) # was previously managed
         
     | 
| 
       161 
158 
     | 
    
         
             
                        @delete << [id, nil, a]
         
     | 
| 
         @@ -186,7 +183,7 @@ module Kennel 
     | 
|
| 
       186 
183 
     | 
    
         
             
                  @expected.each do |e|
         
     | 
| 
       187 
184 
     | 
    
         
             
                    next unless id = e.id
         
     | 
| 
       188 
185 
     | 
    
         
             
                    resource = e.class.api_resource
         
     | 
| 
       189 
     | 
    
         
            -
                    if  
     | 
| 
      
 186 
     | 
    
         
            +
                    if kennel.strict_imports
         
     | 
| 
       190 
187 
     | 
    
         
             
                      raise "Unable to find existing #{resource} with id #{id}\nIf the #{resource} was deleted, remove the `id: -> { #{id} }` line."
         
     | 
| 
       191 
188 
     | 
    
         
             
                    else
         
     | 
| 
       192 
189 
     | 
    
         
             
                      @warnings << "#{resource} #{e.tracking_id} specifies id #{id}, but no such #{resource} exists. 'id' will be ignored. Remove the `id: -> { #{id} }` line."
         
     | 
| 
         @@ -234,16 +231,19 @@ module Kennel 
     | 
|
| 
       234 
231 
     | 
    
         
             
                      new = Utils.pretty_inspect(new)
         
     | 
| 
       235 
232 
     | 
    
         
             
                    end
         
     | 
| 
       236 
233 
     | 
    
         | 
| 
       237 
     | 
    
         
            -
                     
     | 
| 
       238 
     | 
    
         
            -
                       
     | 
| 
       239 
     | 
    
         
            -
             
     | 
| 
       240 
     | 
    
         
            -
             
     | 
| 
       241 
     | 
    
         
            -
                       
     | 
| 
       242 
     | 
    
         
            -
             
     | 
| 
       243 
     | 
    
         
            -
             
     | 
| 
       244 
     | 
    
         
            -
             
     | 
| 
       245 
     | 
    
         
            -
                       
     | 
| 
       246 
     | 
    
         
            -
             
     | 
| 
      
 234 
     | 
    
         
            +
                    message =
         
     | 
| 
      
 235 
     | 
    
         
            +
                      if use_diff
         
     | 
| 
      
 236 
     | 
    
         
            +
                        "  #{type}#{field}\n" +
         
     | 
| 
      
 237 
     | 
    
         
            +
                          diff(old, new).map { |l| "    #{l}" }.join("\n")
         
     | 
| 
      
 238 
     | 
    
         
            +
                      elsif (old + new).size > 100
         
     | 
| 
      
 239 
     | 
    
         
            +
                        "  #{type}#{field}\n" \
         
     | 
| 
      
 240 
     | 
    
         
            +
                          "    #{old} ->\n" \
         
     | 
| 
      
 241 
     | 
    
         
            +
                          "    #{new}"
         
     | 
| 
      
 242 
     | 
    
         
            +
                      else
         
     | 
| 
      
 243 
     | 
    
         
            +
                        "  #{type}#{field} #{old} -> #{new}"
         
     | 
| 
      
 244 
     | 
    
         
            +
                      end
         
     | 
| 
      
 245 
     | 
    
         
            +
             
     | 
| 
      
 246 
     | 
    
         
            +
                    Kennel.out.puts truncate_diff(message)
         
     | 
| 
       247 
247 
     | 
    
         
             
                  end
         
     | 
| 
       248 
248 
     | 
    
         
             
                end
         
     | 
| 
       249 
249 
     | 
    
         | 
| 
         @@ -267,6 +267,17 @@ module Kennel 
     | 
|
| 
       267 
267 
     | 
    
         
             
                  end
         
     | 
| 
       268 
268 
     | 
    
         
             
                end
         
     | 
| 
       269 
269 
     | 
    
         | 
| 
      
 270 
     | 
    
         
            +
                def truncate_diff(message)
         
     | 
| 
      
 271 
     | 
    
         
            +
                  # min '2' because: -1 makes no sense, 0 does not work with * 2 math, 1 says '1 lines'
         
     | 
| 
      
 272 
     | 
    
         
            +
                  @max_diff_lines ||= [Integer(ENV.fetch("MAX_DIFF_LINES", "50")), 2].max
         
     | 
| 
      
 273 
     | 
    
         
            +
                  warning = Utils.color(
         
     | 
| 
      
 274 
     | 
    
         
            +
                    :magenta,
         
     | 
| 
      
 275 
     | 
    
         
            +
                    "  (Diff for this item truncated after #{@max_diff_lines} lines. " \
         
     | 
| 
      
 276 
     | 
    
         
            +
                      "Rerun with MAX_DIFF_LINES=#{@max_diff_lines * 2} to see more)"
         
     | 
| 
      
 277 
     | 
    
         
            +
                  )
         
     | 
| 
      
 278 
     | 
    
         
            +
                  Utils.truncate_lines(message, to: @max_diff_lines, warning: warning)
         
     | 
| 
      
 279 
     | 
    
         
            +
                end
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
       270 
281 
     | 
    
         
             
                # We've already validated the desired objects ('generated') in isolation.
         
     | 
| 
       271 
282 
     | 
    
         
             
                # Now that we have made the plan, we can perform some more validation.
         
     | 
| 
       272 
283 
     | 
    
         
             
                def validate_plan
         
     | 
    
        data/lib/kennel/tasks.rb
    CHANGED
    
    | 
         @@ -8,6 +8,10 @@ require "json" 
     | 
|
| 
       8 
8 
     | 
    
         
             
            module Kennel
         
     | 
| 
       9 
9 
     | 
    
         
             
              module Tasks
         
     | 
| 
       10 
10 
     | 
    
         
             
                class << self
         
     | 
| 
      
 11 
     | 
    
         
            +
                  def kennel
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @kennel ||= Kennel::Engine.new
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
       11 
15 
     | 
    
         
             
                  def abort(message = nil)
         
     | 
| 
       12 
16 
     | 
    
         
             
                    Kennel.err.puts message if message
         
     | 
| 
       13 
17 
     | 
    
         
             
                    raise SystemExit.new(1), message
         
     | 
| 
         @@ -35,10 +39,10 @@ module Kennel 
     | 
|
| 
       35 
39 
     | 
    
         
             
                    load_environment
         
     | 
| 
       36 
40 
     | 
    
         | 
| 
       37 
41 
     | 
    
         
             
                    if on_default_branch? && git_push?
         
     | 
| 
       38 
     | 
    
         
            -
                      Kennel.strict_imports = false
         
     | 
| 
       39 
     | 
    
         
            -
                      Kennel.update
         
     | 
| 
      
 42 
     | 
    
         
            +
                      Kennel::Tasks.kennel.strict_imports = false
         
     | 
| 
      
 43 
     | 
    
         
            +
                      Kennel::Tasks.kennel.update
         
     | 
| 
       40 
44 
     | 
    
         
             
                    else
         
     | 
| 
       41 
     | 
    
         
            -
                      Kennel.plan # show plan in CI logs
         
     | 
| 
      
 45 
     | 
    
         
            +
                      Kennel::Tasks.kennel.plan # show plan in CI logs
         
     | 
| 
       42 
46 
     | 
    
         
             
                    end
         
     | 
| 
       43 
47 
     | 
    
         
             
                  end
         
     | 
| 
       44 
48 
     | 
    
         | 
| 
         @@ -66,7 +70,7 @@ namespace :kennel do 
     | 
|
| 
       66 
70 
     | 
    
         
             
              # https://help.datadoghq.com/hc/en-us/requests/254114 for automatic validation
         
     | 
| 
       67 
71 
     | 
    
         
             
              desc "Verify that all used monitor  mentions are valid"
         
     | 
| 
       68 
72 
     | 
    
         
             
              task validate_mentions: :environment do
         
     | 
| 
       69 
     | 
    
         
            -
                known = Kennel. 
     | 
| 
      
 73 
     | 
    
         
            +
                known = Kennel::Api.new
         
     | 
| 
       70 
74 
     | 
    
         
             
                  .send(:request, :get, "/monitor/notifications")
         
     | 
| 
       71 
75 
     | 
    
         
             
                  .fetch(:handles)
         
     | 
| 
       72 
76 
     | 
    
         
             
                  .values
         
     | 
| 
         @@ -93,18 +97,18 @@ namespace :kennel do 
     | 
|
| 
       93 
97 
     | 
    
         | 
| 
       94 
98 
     | 
    
         
             
              desc "generate local definitions"
         
     | 
| 
       95 
99 
     | 
    
         
             
              task generate: :environment do
         
     | 
| 
       96 
     | 
    
         
            -
                Kennel.generate
         
     | 
| 
      
 100 
     | 
    
         
            +
                Kennel::Tasks.kennel.generate
         
     | 
| 
       97 
101 
     | 
    
         
             
              end
         
     | 
| 
       98 
102 
     | 
    
         | 
| 
       99 
103 
     | 
    
         
             
              # also generate parts so users see and commit updated generated automatically
         
     | 
| 
       100 
104 
     | 
    
         
             
              desc "show planned datadog changes (scope with PROJECT=name)"
         
     | 
| 
       101 
105 
     | 
    
         
             
              task plan: :generate do
         
     | 
| 
       102 
     | 
    
         
            -
                Kennel.plan
         
     | 
| 
      
 106 
     | 
    
         
            +
                Kennel::Tasks.kennel.plan
         
     | 
| 
       103 
107 
     | 
    
         
             
              end
         
     | 
| 
       104 
108 
     | 
    
         | 
| 
       105 
109 
     | 
    
         
             
              desc "update datadog (scope with PROJECT=name)"
         
     | 
| 
       106 
110 
     | 
    
         
             
              task update_datadog: :environment do
         
     | 
| 
       107 
     | 
    
         
            -
                Kennel.update
         
     | 
| 
      
 111 
     | 
    
         
            +
                Kennel::Tasks.kennel.update
         
     | 
| 
       108 
112 
     | 
    
         
             
              end
         
     | 
| 
       109 
113 
     | 
    
         | 
| 
       110 
114 
     | 
    
         
             
              desc "update on push to the default branch, otherwise show plan"
         
     | 
| 
         @@ -115,13 +119,13 @@ namespace :kennel do 
     | 
|
| 
       115 
119 
     | 
    
         
             
              desc "show unmuted alerts filtered by TAG, for example TAG=team:foo"
         
     | 
| 
       116 
120 
     | 
    
         
             
              task alerts: :environment do
         
     | 
| 
       117 
121 
     | 
    
         
             
                tag = ENV["TAG"] || Kennel::Tasks.abort("Call with TAG=foo:bar")
         
     | 
| 
       118 
     | 
    
         
            -
                Kennel::UnmutedAlerts.print(Kennel. 
     | 
| 
      
 122 
     | 
    
         
            +
                Kennel::UnmutedAlerts.print(Kennel::Api.new, tag)
         
     | 
| 
       119 
123 
     | 
    
         
             
              end
         
     | 
| 
       120 
124 
     | 
    
         | 
| 
       121 
125 
     | 
    
         
             
              desc "show monitors with no data by TAG, for example TAG=team:foo [THRESHOLD_DAYS=7] [FORMAT=json]"
         
     | 
| 
       122 
126 
     | 
    
         
             
              task nodata: :environment do
         
     | 
| 
       123 
127 
     | 
    
         
             
                tag = ENV["TAG"] || Kennel::Tasks.abort("Call with TAG=foo:bar")
         
     | 
| 
       124 
     | 
    
         
            -
                monitors = Kennel. 
     | 
| 
      
 128 
     | 
    
         
            +
                monitors = Kennel::Api.new.list("monitor", monitor_tags: tag, group_states: "no data")
         
     | 
| 
       125 
129 
     | 
    
         
             
                monitors.select! { |m| m[:overall_state] == "No Data" }
         
     | 
| 
       126 
130 
     | 
    
         
             
                monitors.reject! { |m| m[:tags].include? "nodata:ignore" }
         
     | 
| 
       127 
131 
     | 
    
         
             
                if monitors.any?
         
     | 
| 
         @@ -179,7 +183,7 @@ namespace :kennel do 
     | 
|
| 
       179 
183 
     | 
    
         
             
                  Kennel::Tasks.abort("Call with URL= or call with RESOURCE=#{possible_resources.join(" or ")} and ID=")
         
     | 
| 
       180 
184 
     | 
    
         
             
                end
         
     | 
| 
       181 
185 
     | 
    
         | 
| 
       182 
     | 
    
         
            -
                Kennel.out.puts Kennel::Importer.new(Kennel. 
     | 
| 
      
 186 
     | 
    
         
            +
                Kennel.out.puts Kennel::Importer.new(Kennel::Api.new).import(resource, id)
         
     | 
| 
       183 
187 
     | 
    
         
             
              end
         
     | 
| 
       184 
188 
     | 
    
         | 
| 
       185 
189 
     | 
    
         
             
              desc "Dump ALL of datadog config as raw json ... useful for grep/search [TYPE=slo|monitor|dashboard]"
         
     | 
| 
         @@ -190,7 +194,7 @@ namespace :kennel do 
     | 
|
| 
       190 
194 
     | 
    
         
             
                  else
         
     | 
| 
       191 
195 
     | 
    
         
             
                    Kennel::Models::Record.api_resource_map.keys
         
     | 
| 
       192 
196 
     | 
    
         
             
                  end
         
     | 
| 
       193 
     | 
    
         
            -
                api = Kennel. 
     | 
| 
      
 197 
     | 
    
         
            +
                api = Kennel::Api.new
         
     | 
| 
       194 
198 
     | 
    
         
             
                list = nil
         
     | 
| 
       195 
199 
     | 
    
         
             
                first = true
         
     | 
| 
       196 
200 
     | 
    
         | 
| 
         @@ -240,7 +244,7 @@ namespace :kennel do 
     | 
|
| 
       240 
244 
     | 
    
         
             
                klass =
         
     | 
| 
       241 
245 
     | 
    
         
             
                  Kennel::Models::Record.subclasses.detect { |s| s.api_resource == resource } ||
         
     | 
| 
       242 
246 
     | 
    
         
             
                  raise("resource #{resource} not know")
         
     | 
| 
       243 
     | 
    
         
            -
                object = Kennel. 
     | 
| 
      
 247 
     | 
    
         
            +
                object = Kennel::Api.new.show(resource, id)
         
     | 
| 
       244 
248 
     | 
    
         
             
                Kennel.out.puts klass.parse_tracking_id(object)
         
     | 
| 
       245 
249 
     | 
    
         
             
              end
         
     | 
| 
       246 
250 
     | 
    
         | 
    
        data/lib/kennel/utils.rb
    CHANGED
    
    | 
         @@ -57,6 +57,12 @@ module Kennel 
     | 
|
| 
       57 
57 
     | 
    
         
             
                    "\e[#{COLORS.fetch(color)}m#{text}\e[0m"
         
     | 
| 
       58 
58 
     | 
    
         
             
                  end
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
      
 60 
     | 
    
         
            +
                  def truncate_lines(text, to:, warning:)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    lines = text.split(/\n/, to + 1)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    lines[-1] = warning if lines.size > to
         
     | 
| 
      
 63 
     | 
    
         
            +
                    lines.join("\n")
         
     | 
| 
      
 64 
     | 
    
         
            +
                  end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
       60 
66 
     | 
    
         
             
                  def capture_stdout
         
     | 
| 
       61 
67 
     | 
    
         
             
                    old = Kennel.out
         
     | 
| 
       62 
68 
     | 
    
         
             
                    Kennel.out = StringIO.new
         
     | 
    
        data/lib/kennel/version.rb
    CHANGED
    
    
    
        data/lib/kennel.rb
    CHANGED
    
    | 
         @@ -5,7 +5,6 @@ require "zeitwerk" 
     | 
|
| 
       5 
5 
     | 
    
         
             
            require "English"
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            require "kennel/version"
         
     | 
| 
       8 
     | 
    
         
            -
            require "kennel/compatibility"
         
     | 
| 
       9 
8 
     | 
    
         
             
            require "kennel/utils"
         
     | 
| 
       10 
9 
     | 
    
         
             
            require "kennel/progress"
         
     | 
| 
       11 
10 
     | 
    
         
             
            require "kennel/filter"
         
     | 
| 
         @@ -40,21 +39,24 @@ module Teams 
     | 
|
| 
       40 
39 
     | 
    
         
             
            end
         
     | 
| 
       41 
40 
     | 
    
         | 
| 
       42 
41 
     | 
    
         
             
            module Kennel
         
     | 
| 
       43 
     | 
    
         
            -
               
     | 
| 
      
 42 
     | 
    
         
            +
              UnresolvableIdError = Class.new(StandardError)
         
     | 
| 
      
 43 
     | 
    
         
            +
              DisallowedUpdateError = Class.new(StandardError)
         
     | 
| 
      
 44 
     | 
    
         
            +
              GenerationAbortedError = Class.new(StandardError)
         
     | 
| 
      
 45 
     | 
    
         
            +
              UpdateResult = Struct.new(:plan, :update, keyword_init: true)
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 48 
     | 
    
         
            +
                attr_accessor :out, :err
         
     | 
| 
       44 
49 
     | 
    
         
             
              end
         
     | 
| 
       45 
50 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
               
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
              UpdateResult = Struct.new(:plan, :update, keyword_init: true)
         
     | 
| 
      
 51 
     | 
    
         
            +
              self.out = $stdout
         
     | 
| 
      
 52 
     | 
    
         
            +
              self.err = $stderr
         
     | 
| 
       49 
53 
     | 
    
         | 
| 
       50 
54 
     | 
    
         
             
              class Engine
         
     | 
| 
       51 
55 
     | 
    
         
             
                def initialize
         
     | 
| 
       52 
     | 
    
         
            -
                  @out = $stdout
         
     | 
| 
       53 
     | 
    
         
            -
                  @err = $stderr
         
     | 
| 
       54 
56 
     | 
    
         
             
                  @strict_imports = true
         
     | 
| 
       55 
57 
     | 
    
         
             
                end
         
     | 
| 
       56 
58 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
                attr_accessor : 
     | 
| 
      
 59 
     | 
    
         
            +
                attr_accessor :strict_imports
         
     | 
| 
       58 
60 
     | 
    
         | 
| 
       59 
61 
     | 
    
         
             
                def generate
         
     | 
| 
       60 
62 
     | 
    
         
             
                  parts = generated
         
     | 
| 
         @@ -67,12 +69,8 @@ module Kennel 
     | 
|
| 
       67 
69 
     | 
    
         
             
                end
         
     | 
| 
       68 
70 
     | 
    
         | 
| 
       69 
71 
     | 
    
         
             
                def update
         
     | 
| 
       70 
     | 
    
         
            -
                   
     | 
| 
       71 
     | 
    
         
            -
                   
     | 
| 
       72 
     | 
    
         
            -
                  UpdateResult.new(
         
     | 
| 
       73 
     | 
    
         
            -
                    plan: the_plan,
         
     | 
| 
       74 
     | 
    
         
            -
                    update: the_update
         
     | 
| 
       75 
     | 
    
         
            -
                  )
         
     | 
| 
      
 72 
     | 
    
         
            +
                  syncer.plan
         
     | 
| 
      
 73 
     | 
    
         
            +
                  syncer.update if syncer.confirm
         
     | 
| 
       76 
74 
     | 
    
         
             
                end
         
     | 
| 
       77 
75 
     | 
    
         | 
| 
       78 
76 
     | 
    
         
             
                private
         
     | 
| 
         @@ -82,11 +80,11 @@ module Kennel 
     | 
|
| 
       82 
80 
     | 
    
         
             
                end
         
     | 
| 
       83 
81 
     | 
    
         | 
| 
       84 
82 
     | 
    
         
             
                def syncer
         
     | 
| 
       85 
     | 
    
         
            -
                  @syncer ||= Syncer.new(api, generated, project_filter: filter.project_filter, tracking_id_filter: filter.tracking_id_filter)
         
     | 
| 
      
 83 
     | 
    
         
            +
                  @syncer ||= Syncer.new(api, generated, kennel: self, project_filter: filter.project_filter, tracking_id_filter: filter.tracking_id_filter)
         
     | 
| 
       86 
84 
     | 
    
         
             
                end
         
     | 
| 
       87 
85 
     | 
    
         | 
| 
       88 
86 
     | 
    
         
             
                def api
         
     | 
| 
       89 
     | 
    
         
            -
                  @api ||= Api.new 
     | 
| 
      
 87 
     | 
    
         
            +
                  @api ||= Api.new
         
     | 
| 
       90 
88 
     | 
    
         
             
                end
         
     | 
| 
       91 
89 
     | 
    
         | 
| 
       92 
90 
     | 
    
         
             
                def projects_provider
         
     | 
| 
         @@ -99,26 +97,30 @@ module Kennel 
     | 
|
| 
       99 
97 
     | 
    
         | 
| 
       100 
98 
     | 
    
         
             
                def generated
         
     | 
| 
       101 
99 
     | 
    
         
             
                  @generated ||= begin
         
     | 
| 
       102 
     | 
    
         
            -
                    Progress.progress " 
     | 
| 
      
 100 
     | 
    
         
            +
                    parts = Progress.progress "Finding parts" do
         
     | 
| 
       103 
101 
     | 
    
         
             
                      projects = projects_provider.projects
         
     | 
| 
       104 
102 
     | 
    
         
             
                      projects = filter.filter_projects projects
         
     | 
| 
       105 
103 
     | 
    
         | 
| 
       106 
104 
     | 
    
         
             
                      parts = Utils.parallel(projects, &:validated_parts).flatten(1)
         
     | 
| 
       107 
     | 
    
         
            -
                       
     | 
| 
      
 105 
     | 
    
         
            +
                      filter.filter_parts parts
         
     | 
| 
      
 106 
     | 
    
         
            +
                    end
         
     | 
| 
       108 
107 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
       112 
     | 
    
         
            -
             
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
             
     | 
| 
       115 
     | 
    
         
            -
             
     | 
| 
      
 108 
     | 
    
         
            +
                    parts.group_by(&:tracking_id).each do |tracking_id, same|
         
     | 
| 
      
 109 
     | 
    
         
            +
                      next if same.size == 1
         
     | 
| 
      
 110 
     | 
    
         
            +
                      raise <<~ERROR
         
     | 
| 
      
 111 
     | 
    
         
            +
                        #{tracking_id} is defined #{same.size} times
         
     | 
| 
      
 112 
     | 
    
         
            +
                        use a different `kennel_id` when defining multiple projects/monitors/dashboards to avoid this conflict
         
     | 
| 
      
 113 
     | 
    
         
            +
                      ERROR
         
     | 
| 
      
 114 
     | 
    
         
            +
                    end
         
     | 
| 
       116 
115 
     | 
    
         | 
| 
      
 116 
     | 
    
         
            +
                    Progress.progress "Building json" do
         
     | 
| 
       117 
117 
     | 
    
         
             
                      # trigger json caching here so it counts into generating
         
     | 
| 
       118 
     | 
    
         
            -
                      Utils.parallel(parts, &: 
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                      parts
         
     | 
| 
      
 118 
     | 
    
         
            +
                      Utils.parallel(parts, &:build)
         
     | 
| 
       121 
119 
     | 
    
         
             
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    OptionalValidations.valid?(parts) or raise GenerationAbortedError
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
                    parts
         
     | 
| 
       122 
124 
     | 
    
         
             
                  end
         
     | 
| 
       123 
125 
     | 
    
         
             
                end
         
     | 
| 
       124 
126 
     | 
    
         
             
              end
         
     | 
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: kennel
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version: 1. 
     | 
| 
      
 4 
     | 
    
         
            +
              version: 1.124.0
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Michael Grosser
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: bin
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2022- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2022-11-14 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: diff-lcs
         
     | 
| 
         @@ -89,7 +89,6 @@ files: 
     | 
|
| 
       89 
89 
     | 
    
         
             
            - Readme.md
         
     | 
| 
       90 
90 
     | 
    
         
             
            - lib/kennel.rb
         
     | 
| 
       91 
91 
     | 
    
         
             
            - lib/kennel/api.rb
         
     | 
| 
       92 
     | 
    
         
            -
            - lib/kennel/compatibility.rb
         
     | 
| 
       93 
92 
     | 
    
         
             
            - lib/kennel/file_cache.rb
         
     | 
| 
       94 
93 
     | 
    
         
             
            - lib/kennel/filter.rb
         
     | 
| 
       95 
94 
     | 
    
         
             
            - lib/kennel/github_reporter.rb
         
     | 
    
        data/lib/kennel/compatibility.rb
    DELETED
    
    | 
         @@ -1,27 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # frozen_string_literal: true
         
     | 
| 
       2 
     | 
    
         
            -
             
     | 
| 
       3 
     | 
    
         
            -
            module Kennel
         
     | 
| 
       4 
     | 
    
         
            -
              module Compatibility
         
     | 
| 
       5 
     | 
    
         
            -
                def self.included(into)
         
     | 
| 
       6 
     | 
    
         
            -
                  class << into
         
     | 
| 
       7 
     | 
    
         
            -
                    %I[out out= err err= strict_imports strict_imports= generate plan update].each do |sym|
         
     | 
| 
       8 
     | 
    
         
            -
                      define_method(sym) { |*args| instance.public_send(sym, *args) }
         
     | 
| 
       9 
     | 
    
         
            -
                    end
         
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
                    def build_default
         
     | 
| 
       12 
     | 
    
         
            -
                      Kennel::Engine.new
         
     | 
| 
       13 
     | 
    
         
            -
                    end
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
                    def instance
         
     | 
| 
       16 
     | 
    
         
            -
                      @instance ||= build_default
         
     | 
| 
       17 
     | 
    
         
            -
                    end
         
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
                    private
         
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
                    def api
         
     | 
| 
       22 
     | 
    
         
            -
                      instance.send(:api)
         
     | 
| 
       23 
     | 
    
         
            -
                    end
         
     | 
| 
       24 
     | 
    
         
            -
                  end
         
     | 
| 
       25 
     | 
    
         
            -
                end
         
     | 
| 
       26 
     | 
    
         
            -
              end
         
     | 
| 
       27 
     | 
    
         
            -
            end
         
     |