cfn-guardian 0.12.1 → 0.13.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a1ed7183d14b7d1bc05fdfcffc1ff4272c04ff6db43fc47e4fdcf2124b1d898
4
- data.tar.gz: 9b3bcc2a4b2d578983f549f93a0ca482b9842a30eac3ef84fe6e4781575a5440
3
+ metadata.gz: eb1ea2bebda2731175f9c469a64e461c7bceeb232edf05b2c4c7a93c5229a36a
4
+ data.tar.gz: a2a994f4e1fbc4a2c4f616c079c00ec4972bcf2ab7b52afc648fab15a51ad69e
5
5
  SHA512:
6
- metadata.gz: b058796599f33efd09aff544129071b087a5cde59d87b06d3b19f5036947fc4b1679a32ba371a1a7707b05923a978c31af25e72f486b65c0f9e96d0bedff5122
7
- data.tar.gz: 00ec6b9c0c79dd9afc146e135a0eb9512008a1dae030f8e38beed322f5ca3f6ed2ba5eaeb2a82af5b2b1335970d3f3244ff4773c65db3056d81a1b951e692e07
6
+ metadata.gz: f7fc64feea4f5e3e3742de29ea27395d77c51379adbc96b2329d659d81c5a306431396cccd5c8a8834307043e9d40fab1373da866340c516db2ec0acfa0987c1
7
+ data.tar.gz: e381d7b7d87ae20dc4af84f90243d8744c013e934e769764dc9fce9a8448fc8a8d6b5bad6ff8bda914aa2fc0a97063944ab342f982e83558e2c63052952eb412
@@ -21,8 +21,9 @@ jobs:
21
21
 
22
22
  - name: rspec
23
23
  run: |
24
- gem install rspec
25
- rspec
24
+ gem install bundler -v 2.4.22
25
+ bundle install
26
+ bundle exec rspec
26
27
 
27
28
  - name: build gem
28
29
  run: |
data/Dockerfile CHANGED
@@ -18,4 +18,4 @@ RUN addgroup -g 1000 guardian && \
18
18
 
19
19
  USER guardian
20
20
 
21
- RUN cfndsl -u 11.5.0
21
+ RUN cfndsl -u
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- cfn-guardian (0.10.4)
4
+ cfn-guardian (0.13.0)
5
5
  aws-sdk-cloudformation (~> 1.76, < 2)
6
6
  aws-sdk-cloudwatch (~> 1.72, < 2)
7
7
  aws-sdk-codecommit (~> 1.53, < 2)
@@ -10,71 +10,97 @@ PATH
10
10
  aws-sdk-rds (~> 1.174, < 2)
11
11
  aws-sdk-s3 (~> 1.119, < 2)
12
12
  cfndsl (~> 1.0, < 2)
13
- rexml
13
+ rexml (= 3.3.0)
14
14
  term-ansicolor (~> 1, < 2)
15
15
  terminal-table (~> 1, < 2)
16
16
  thor (~> 0.20)
17
+ tins (~> 1.42.0)
17
18
 
18
19
  GEM
19
20
  remote: https://rubygems.org/
20
21
  specs:
21
- aws-eventstream (1.2.0)
22
- aws-partitions (1.737.0)
23
- aws-sdk-cloudformation (1.76.0)
24
- aws-sdk-core (~> 3, >= 3.165.0)
25
- aws-sigv4 (~> 1.1)
26
- aws-sdk-cloudwatch (1.72.0)
27
- aws-sdk-core (~> 3, >= 3.165.0)
28
- aws-sigv4 (~> 1.1)
29
- aws-sdk-codecommit (1.53.0)
30
- aws-sdk-core (~> 3, >= 3.165.0)
31
- aws-sigv4 (~> 1.1)
32
- aws-sdk-codepipeline (1.55.0)
33
- aws-sdk-core (~> 3, >= 3.165.0)
34
- aws-sigv4 (~> 1.1)
35
- aws-sdk-core (3.171.0)
36
- aws-eventstream (~> 1, >= 1.0.2)
37
- aws-partitions (~> 1, >= 1.651.0)
22
+ aws-eventstream (1.4.0)
23
+ aws-partitions (1.1239.0)
24
+ aws-sdk-cloudformation (1.150.0)
25
+ aws-sdk-core (~> 3, >= 3.244.0)
26
+ aws-sigv4 (~> 1.5)
27
+ aws-sdk-cloudwatch (1.134.0)
28
+ aws-sdk-core (~> 3, >= 3.244.0)
29
+ aws-sigv4 (~> 1.5)
30
+ aws-sdk-codecommit (1.97.0)
31
+ aws-sdk-core (~> 3, >= 3.244.0)
32
+ aws-sigv4 (~> 1.5)
33
+ aws-sdk-codepipeline (1.113.0)
34
+ aws-sdk-core (~> 3, >= 3.244.0)
38
35
  aws-sigv4 (~> 1.5)
36
+ aws-sdk-core (3.244.0)
37
+ aws-eventstream (~> 1, >= 1.3.0)
38
+ aws-partitions (~> 1, >= 1.992.0)
39
+ aws-sigv4 (~> 1.9)
40
+ base64
41
+ bigdecimal
39
42
  jmespath (~> 1, >= 1.6.1)
40
- aws-sdk-ec2 (1.371.0)
41
- aws-sdk-core (~> 3, >= 3.165.0)
42
- aws-sigv4 (~> 1.1)
43
- aws-sdk-kms (1.63.0)
44
- aws-sdk-core (~> 3, >= 3.165.0)
45
- aws-sigv4 (~> 1.1)
46
- aws-sdk-rds (1.174.0)
47
- aws-sdk-core (~> 3, >= 3.165.0)
48
- aws-sigv4 (~> 1.1)
49
- aws-sdk-s3 (1.119.2)
50
- aws-sdk-core (~> 3, >= 3.165.0)
43
+ logger
44
+ aws-sdk-ec2 (1.611.0)
45
+ aws-sdk-core (~> 3, >= 3.244.0)
46
+ aws-sigv4 (~> 1.5)
47
+ aws-sdk-kms (1.123.0)
48
+ aws-sdk-core (~> 3, >= 3.244.0)
49
+ aws-sigv4 (~> 1.5)
50
+ aws-sdk-rds (1.311.0)
51
+ aws-sdk-core (~> 3, >= 3.244.0)
52
+ aws-sigv4 (~> 1.5)
53
+ aws-sdk-s3 (1.219.0)
54
+ aws-sdk-core (~> 3, >= 3.244.0)
51
55
  aws-sdk-kms (~> 1)
52
- aws-sigv4 (~> 1.4)
53
- aws-sigv4 (1.5.2)
56
+ aws-sigv4 (~> 1.5)
57
+ aws-sigv4 (1.12.1)
54
58
  aws-eventstream (~> 1, >= 1.0.2)
55
- cfndsl (1.6.0)
59
+ base64 (0.3.0)
60
+ bigdecimal (4.1.1)
61
+ cfndsl (1.7.3)
56
62
  hana (~> 1.3)
63
+ diff-lcs (1.6.2)
57
64
  hana (1.3.7)
58
65
  jmespath (1.6.2)
66
+ logger (1.7.0)
59
67
  rake (13.0.6)
60
- rexml (3.2.5)
68
+ rexml (3.3.0)
69
+ strscan
70
+ rspec (3.13.2)
71
+ rspec-core (~> 3.13.0)
72
+ rspec-expectations (~> 3.13.0)
73
+ rspec-mocks (~> 3.13.0)
74
+ rspec-core (3.13.6)
75
+ rspec-support (~> 3.13.0)
76
+ rspec-expectations (3.13.5)
77
+ diff-lcs (>= 1.2.0, < 2.0)
78
+ rspec-support (~> 3.13.0)
79
+ rspec-mocks (3.13.8)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.13.0)
82
+ rspec-support (3.13.7)
83
+ strscan (3.1.8)
61
84
  sync (0.5.0)
62
- term-ansicolor (1.7.1)
63
- tins (~> 1.0)
85
+ term-ansicolor (1.11.3)
86
+ tins (~> 1)
64
87
  terminal-table (1.8.0)
65
88
  unicode-display_width (~> 1.1, >= 1.1.1)
66
89
  thor (0.20.3)
67
- tins (1.32.1)
90
+ tins (1.42.0)
91
+ bigdecimal
68
92
  sync
69
93
  unicode-display_width (1.8.0)
70
94
 
71
95
  PLATFORMS
96
+ aarch64-linux
72
97
  x86_64-darwin-21
73
98
 
74
99
  DEPENDENCIES
75
100
  bundler (~> 2.0)
76
101
  cfn-guardian!
77
102
  rake (~> 13.0)
103
+ rspec (~> 3.0)
78
104
 
79
105
  BUNDLED WITH
80
106
  2.3.19
data/cfn-guardian.gemspec CHANGED
@@ -43,4 +43,5 @@ Gem::Specification.new do |spec|
43
43
 
44
44
  spec.add_development_dependency "bundler", "~> 2.0"
45
45
  spec.add_development_dependency "rake", "~> 13.0"
46
+ spec.add_development_dependency "rspec", "~> 3.0"
46
47
  end
@@ -42,7 +42,7 @@ Resources:
42
42
 
43
43
  For health endpoints that require authenticated requests, Guardian can send HMAC-signed headers so your application can verify the request came from Guardian and resist replay attacks.
44
44
 
45
- When enabled, the HTTP check Lambda (see [aws-lambda-http-check](https://github.com/base2/aws-lambda-http-check)) adds these headers to each request:
45
+ When enabled, the HTTP check Lambda (see [aws-lambda-http-check](https://github.com/base2services/aws-lambda-http-check)) adds these headers to each request:
46
46
 
47
47
  | Header (default prefix `X-Health`) | Description |
48
48
  |-----------------------------------|-------------|
data/docs/overview.md CHANGED
@@ -22,4 +22,5 @@
22
22
  8. [Composite Alarms](composite_alarms.md)
23
23
  9. [Alarms for Custom Metrics](custom_metrics.md)
24
24
  10. [Dimension Variables](variables.md)
25
- 11. [Alarm Tags](alarm_tags.md)
25
+ 11. [Search Expression Alarms](search_expressions.md)
26
+ 12. [Alarm Tags](alarm_tags.md)
@@ -0,0 +1,120 @@
1
+ # Search Expression Alarms
2
+
3
+ Search expression alarms use CloudWatch [SEARCH()](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/search-expression-syntax.html) to dynamically match metrics instead of targeting a fixed set of dimensions. This is useful when the physical resource ID changes between deployments, such as Auto Scaling Groups that use replacement update policies in CloudFormation.
4
+
5
+ ## The Problem
6
+
7
+ When a CloudFormation stack replaces an ASG on deployment, the physical ASG name changes (e.g. `my-app-AsgGroup-abc123` becomes `my-app-AsgGroup-xyz789`). Standard alarms use fixed dimensions that reference the exact ASG name, so they break after every deployment until Guardian is recompiled and redeployed with the new name.
8
+
9
+ ## How It Works
10
+
11
+ Instead of emitting a CloudWatch alarm with fixed `Dimensions`, `MetricName`, `Namespace`, and `Statistic` properties, a search expression alarm emits the CloudFormation `Metrics` property (a list of `MetricDataQuery` objects) with:
12
+
13
+ 1. A **SEARCH()** expression that dynamically matches metrics by partial or exact name
14
+ 2. An **aggregation function** (e.g. `MAX`, `AVG`, `SUM`) that reduces the matched metrics to a single time series for threshold evaluation
15
+
16
+ ## Configuration
17
+
18
+ Add `SearchExpression` and optionally `SearchAggregation` to an alarm template. When `SearchExpression` is set, the `Dimensions`, `MetricName`, `Namespace`, `Statistic`, and `Period` properties are not used since CloudWatch treats these as mutually exclusive with the alarm `Metrics` property.
19
+
20
+ ### Properties
21
+
22
+ | Property | Required | Default | Description |
23
+ | --- | --- | --- | --- |
24
+ | `SearchExpression` | Yes | - | A CloudWatch SEARCH() expression string. Supports `${Resource::...}` [variables](variables.md). |
25
+ | `SearchAggregation` | No | `MAX` | Aggregation function applied to the search results. Valid values: `MAX`, `MIN`, `AVG`, `SUM`. |
26
+
27
+ ### Overriding Default Alarms
28
+
29
+ You can convert existing default alarms to use search expressions by overriding them in the template:
30
+
31
+ ```yaml
32
+ Resources:
33
+ AutoScalingGroup:
34
+ - Id: my-app-AsgGroup
35
+
36
+ Templates:
37
+ AutoScalingGroup:
38
+ CPUUtilizationHighBase:
39
+ SearchExpression: "SEARCH('{AWS/EC2,AutoScalingGroupName} MetricName=\"CPUUtilization\" \"${Resource::Id}\"', 'Minimum', 60)"
40
+ SearchAggregation: MAX
41
+ StatusCheckFailed:
42
+ SearchExpression: "SEARCH('{AWS/EC2,AutoScalingGroupName} MetricName=\"StatusCheckFailed\" \"${Resource::Id}\"', 'Maximum', 60)"
43
+ SearchAggregation: MAX
44
+ ```
45
+
46
+ In this example the `Id` is the stable prefix of the ASG name. The double quotes around `\"${Resource::Id}\"` inside the SEARCH expression perform an exact substring match, so `my-app-AsgGroup-abc123` and `my-app-AsgGroup-xyz789` both match but unrelated ASGs do not.
47
+
48
+ ### Creating New Alarms
49
+
50
+ You can also create new search expression alarms that don't override any defaults:
51
+
52
+ ```yaml
53
+ Templates:
54
+ AutoScalingGroup:
55
+ NetworkOutHigh:
56
+ SearchExpression: "SEARCH('{AWS/EC2,AutoScalingGroupName} MetricName=\"NetworkOut\" \"${Resource::Id}\"', 'Average', 300)"
57
+ SearchAggregation: SUM
58
+ Threshold: 1000000000
59
+ ComparisonOperator: GreaterThanThreshold
60
+ EvaluationPeriods: 3
61
+ AlarmAction: Warning
62
+ ```
63
+
64
+ ### Using With Other Resource Groups
65
+
66
+ Search expressions work with any resource group, not just AutoScalingGroup:
67
+
68
+ ```yaml
69
+ Resources:
70
+ ECSService:
71
+ - Id: my-service
72
+ Cluster: my-cluster
73
+
74
+ Templates:
75
+ ECSService:
76
+ CPUUtilizationHigh:
77
+ SearchExpression: "SEARCH('{AWS/ECS,ServiceName,ClusterName} MetricName=\"CPUUtilization\" \"${Resource::Id}\"', 'Average', 60)"
78
+ SearchAggregation: MAX
79
+ Threshold: 90
80
+ EvaluationPeriods: 5
81
+ ```
82
+
83
+ ## Variables
84
+
85
+ `${Resource::...}` variables are interpolated inside search expressions the same way as in [dimension variables](variables.md). You can reference any key from the resource definition:
86
+
87
+ ```yaml
88
+ Resources:
89
+ AutoScalingGroup:
90
+ - Id: my-app-AsgGroup
91
+ Environment: production
92
+
93
+ Templates:
94
+ AutoScalingGroup:
95
+ CPUUtilizationHighBase:
96
+ SearchExpression: "SEARCH('{AWS/EC2,AutoScalingGroupName} MetricName=\"CPUUtilization\" \"${Resource::Id}\"', 'Minimum', 60)"
97
+ SearchAggregation: MAX
98
+ ```
99
+
100
+ ## CloudWatch SEARCH() Syntax Quick Reference
101
+
102
+ The general format is:
103
+
104
+ ```
105
+ SEARCH('{Namespace,DimensionName} SearchTerm', 'Statistic', Period)
106
+ ```
107
+
108
+ - **Partial match**: `my-app` matches any metric with a token `my` or `app` in any dimension value
109
+ - **Exact match**: `"my-app-AsgGroup"` matches only the exact substring `my-app-AsgGroup`
110
+ - **Boolean operators**: `AND`, `OR`, `NOT` can be used to combine terms
111
+ - **Property designators**: `MetricName="CPUUtilization"` restricts matching to the metric name
112
+
113
+ See the [CloudWatch search expression syntax documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/search-expression-syntax.html) for full details.
114
+
115
+ ## Limitations
116
+
117
+ - **2-week lookback**: SEARCH() only finds metrics that have reported data within the last 2 weeks
118
+ - **100 metric limit**: A single SEARCH expression can match up to 100 time series
119
+ - **1024 character limit**: The search expression query string cannot exceed 1024 characters
120
+ - **Aggregation required**: Since SEARCH can return multiple time series, the aggregation function reduces them to a single series for threshold comparison
data/docs/variables.md CHANGED
@@ -1,6 +1,6 @@
1
1
  ## Dimension Variables
2
2
 
3
- variables can be used to reference resource group values such as the resource Id within the dimensions section of an alarm template.
3
+ Variables can be used to reference resource group values such as the resource Id within the dimensions section of an alarm template. They are also supported inside [search expressions](search_expressions.md).
4
4
 
5
5
  For example here we are creating an alarm for a disk usage metric for a group of EC2 instances.
6
6
 
@@ -190,9 +190,24 @@ module CfnGuardian
190
190
  @resources.each do |resource|
191
191
  case resource.type
192
192
  when 'Alarm'
193
- %w(metric_name namespace).each do |property|
194
- if resource.send(property).nil?
195
- @errors << "CfnGuardian::AlarmPropertyError - alarm #{resource.name} for resource #{resource.resource_id} has nil value for property #{property.to_camelcase}. This could be due to incorrect spelling of a default alarm name or missing property #{property.to_camelcase} on a new alarm."
193
+ if resource.search_expression
194
+ if !resource.search_expression.is_a?(String) || resource.search_expression.strip.empty?
195
+ @errors << "CfnGuardian::AlarmPropertyError - alarm #{resource.name} for resource #{resource.resource_id} has an invalid SearchExpression. Must be a non-empty string."
196
+ end
197
+ if resource.search_aggregation
198
+ valid_aggregations = %w(MAX MIN AVG SUM)
199
+ normalized = resource.search_aggregation.to_s.upcase
200
+ if valid_aggregations.include?(normalized)
201
+ resource.search_aggregation = normalized
202
+ else
203
+ @errors << "CfnGuardian::AlarmPropertyError - alarm #{resource.name} for resource #{resource.resource_id} has invalid SearchAggregation '#{resource.search_aggregation}'. Must be one of: #{valid_aggregations.join(', ')}."
204
+ end
205
+ end
206
+ else
207
+ %w(metric_name namespace).each do |property|
208
+ if resource.send(property).nil?
209
+ @errors << "CfnGuardian::AlarmPropertyError - alarm #{resource.name} for resource #{resource.resource_id} has nil value for property #{property.to_camelcase}. This could be due to incorrect spelling of a default alarm name or missing property #{property.to_camelcase} on a new alarm."
210
+ end
196
211
  end
197
212
  end
198
213
  when 'Check'
@@ -31,7 +31,9 @@ module CfnGuardian
31
31
  :unit,
32
32
  :maintenance_groups,
33
33
  :additional_notifiers,
34
- :tags
34
+ :tags,
35
+ :search_expression,
36
+ :search_aggregation
35
37
 
36
38
  def initialize(resource)
37
39
  @type = 'Alarm'
@@ -60,6 +62,8 @@ module CfnGuardian
60
62
  @maintenance_groups = []
61
63
  @additional_notifiers = []
62
64
  @tags = {}
65
+ @search_expression = nil
66
+ @search_aggregation = nil
63
67
  end
64
68
 
65
69
  def metric_name=(metric_name)
@@ -110,15 +110,29 @@ module CfnGuardian::Resource
110
110
  @alarms.each {|a| a.group = @override_group}
111
111
  end
112
112
 
113
- # String interpolation for alarm dimensions
114
113
  @alarms.each do |alarm|
115
- next if alarm.dimensions.nil?
116
- alarm.dimensions.each do |k,v|
117
- if v.is_a?(String) && v.match?(/^\${Resource::.*[A-Za-z]}$/)
118
- resource_key = v.tr('${}', '').split('Resource::').last
114
+ # String interpolation for alarm dimensions
115
+ unless alarm.dimensions.nil?
116
+ alarm.dimensions.each do |k,v|
117
+ if v.is_a?(String) && v.match?(/^\${Resource::.*[A-Za-z]}$/)
118
+ resource_key = v.tr('${}', '').split('Resource::').last
119
+ if @resource.has_key?(resource_key)
120
+ logger.debug "overriding alarm #{alarm.name} dimension key '#{k}' with value '#{@resource[resource_key]}'"
121
+ alarm.dimensions[k] = @resource[resource_key]
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ # String interpolation for search expressions
128
+ if alarm.search_expression.is_a?(String)
129
+ alarm.search_expression = alarm.search_expression.gsub(/\${Resource::([A-Za-z0-9_]+)}/) do
130
+ resource_key = Regexp.last_match(1)
119
131
  if @resource.has_key?(resource_key)
120
- logger.debug "overriding alarm #{alarm.name} dimension key '#{k}' with value '#{@resource[resource_key]}'"
121
- alarm.dimensions[k] = @resource[resource_key]
132
+ logger.debug "interpolating search_expression variable '#{resource_key}' with value '#{@resource[resource_key]}' for alarm #{alarm.name}"
133
+ @resource[resource_key]
134
+ else
135
+ "${Resource::#{resource_key}}"
122
136
  end
123
137
  end
124
138
  end
@@ -33,6 +33,7 @@ module CfnGuardian
33
33
  def add_alarm(alarm)
34
34
  actions = alarm.alarm_action.kind_of?(Array) ? alarm.alarm_action.map{|action| Ref(action)} : [Ref(alarm.alarm_action)]
35
35
  actions.concat alarm.maintenance_groups.map {|mg| Ref(mg)} if alarm.maintenance_groups.any?
36
+ use_search = alarm.search_expression.is_a?(String) && !alarm.search_expression.strip.empty?
36
37
 
37
38
  @template.declare do
38
39
  CloudWatch_Alarm("#{alarm.resource_hash}#{alarm.group}#{alarm.name.gsub(/[^0-9a-zA-Z]/i, '')}#{alarm.type}"[0..255]) do
@@ -40,20 +41,38 @@ module CfnGuardian
40
41
  AlarmDescription "Guardian alarm #{alarm.name} for the resource #{alarm.resource_id} in alarm group #{alarm.group}"
41
42
  AlarmName CfnGuardian::CloudWatch.get_alarm_name(alarm)
42
43
  ComparisonOperator alarm.comparison_operator
43
- Dimensions alarm.dimensions.map {|k,v| {Name: k, Value: v}} unless alarm.dimensions.nil?
44
44
  EvaluationPeriods alarm.evaluation_periods
45
- Statistic alarm.statistic if alarm.extended_statistic.nil?
46
- Period alarm.period
47
45
  Threshold alarm.threshold
48
- MetricName alarm.metric_name
49
- Namespace alarm.namespace
50
46
  AlarmActions actions
51
47
  OKActions actions unless alarm.ok_action_disabled
52
48
  TreatMissingData alarm.treat_missing_data unless alarm.treat_missing_data.nil?
53
49
  DatapointsToAlarm alarm.datapoints_to_alarm unless alarm.datapoints_to_alarm.nil?
54
- ExtendedStatistic alarm.extended_statistic unless alarm.extended_statistic.nil?
55
- EvaluateLowSampleCountPercentile alarm.evaluate_low_sample_count_percentile unless alarm.evaluate_low_sample_count_percentile.nil?
56
- Unit alarm.unit unless alarm.unit.nil?
50
+
51
+ if use_search
52
+ aggregation = alarm.search_aggregation || 'MAX'
53
+ Metrics [
54
+ {
55
+ Id: 'search_expression',
56
+ Expression: alarm.search_expression,
57
+ Period: alarm.period,
58
+ ReturnData: false
59
+ },
60
+ {
61
+ Id: 'aggregate',
62
+ Expression: "#{aggregation}(search_expression)",
63
+ ReturnData: true
64
+ }
65
+ ]
66
+ else
67
+ Dimensions alarm.dimensions.map {|k,v| {Name: k, Value: v}} unless alarm.dimensions.nil?
68
+ Statistic alarm.statistic if alarm.extended_statistic.nil?
69
+ Period alarm.period
70
+ MetricName alarm.metric_name
71
+ Namespace alarm.namespace
72
+ ExtendedStatistic alarm.extended_statistic unless alarm.extended_statistic.nil?
73
+ EvaluateLowSampleCountPercentile alarm.evaluate_low_sample_count_percentile unless alarm.evaluate_low_sample_count_percentile.nil?
74
+ Unit alarm.unit unless alarm.unit.nil?
75
+ end
57
76
  end
58
77
  end
59
78
  end
@@ -1,4 +1,4 @@
1
1
  module CfnGuardian
2
- VERSION = "0.12.1"
2
+ VERSION = "0.13.1"
3
3
  CHANGE_SET_VERSION = VERSION.gsub('.', '-').freeze
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cfn-guardian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.13.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Guslington
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-06 00:00:00.000000000 Z
11
+ date: 2026-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -280,6 +280,20 @@ dependencies:
280
280
  - - "~>"
281
281
  - !ruby/object:Gem::Version
282
282
  version: '13.0'
283
+ - !ruby/object:Gem::Dependency
284
+ name: rspec
285
+ requirement: !ruby/object:Gem::Requirement
286
+ requirements:
287
+ - - "~>"
288
+ - !ruby/object:Gem::Version
289
+ version: '3.0'
290
+ type: :development
291
+ prerelease: false
292
+ version_requirements: !ruby/object:Gem::Requirement
293
+ requirements:
294
+ - - "~>"
295
+ - !ruby/object:Gem::Version
296
+ version: '3.0'
283
297
  description: Manages AWS cloudwatch alarms with default templates using cloudformation
284
298
  email:
285
299
  - itsupport@base2services.com
@@ -323,6 +337,7 @@ files:
323
337
  - docs/notifiers.md
324
338
  - docs/overview.md
325
339
  - docs/resources.md
340
+ - docs/search_expressions.md
326
341
  - docs/variables.md
327
342
  - exe/cfn-guardian
328
343
  - lib/cfnguardian.rb