cfn-guardian 0.8.6 → 0.9.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/cfn-guardian.gemspec +2 -0
- data/exe/cfn-guardian +1 -1
- data/lib/cfnguardian/compile.rb +3 -9
- data/lib/cfnguardian/deploy.rb +21 -21
- data/lib/cfnguardian/error.rb +4 -2
- data/lib/cfnguardian/resources/base.rb +13 -13
- data/lib/cfnguardian/resources/nrpe.rb +2 -0
- data/lib/cfnguardian/validate.rb +5 -1
- data/lib/cfnguardian/version.rb +1 -1
- data/lib/cfnguardian.rb +146 -33
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ada08812236c0f6a899a13847fda66a1e2b5c218f8f422cf210bed1ac04be44
|
4
|
+
data.tar.gz: c21ed6acd2eab4f673f9ac06ae06eb3a39059e1ba0bd82eddbb06ab57e908020
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2a39f90cea2a6ccf82e5a82d66b4586fe9d5bd227e470bd8322fda671b9bcadf5d82f814c33f602460db3f81b15e2b492e3812b8770568e6f4742c4089f477e8
|
7
|
+
data.tar.gz: 26ce905c4f0b8f6e52d13968f74528c125d9d1e43938f9f31ccc40fd8f65d91f76802c804b6bbd4f61550966eee5cdc0b5f918bdad22b2091c9750799ea7eefd
|
data/cfn-guardian.gemspec
CHANGED
@@ -36,6 +36,8 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_dependency 'aws-sdk-codecommit', '~> 1.28', '<2'
|
37
37
|
spec.add_dependency 'aws-sdk-codepipeline', '~> 1.28', '<2'
|
38
38
|
|
39
|
+
spec.add_runtime_dependency('rexml', '>= 0')
|
40
|
+
|
39
41
|
spec.add_development_dependency "bundler", "~> 2.0"
|
40
42
|
spec.add_development_dependency "rake", "~> 13.0"
|
41
43
|
end
|
data/exe/cfn-guardian
CHANGED
data/lib/cfnguardian/compile.rb
CHANGED
@@ -93,7 +93,7 @@ module CfnGuardian
|
|
93
93
|
if @templates.has_key?(group) && @templates[group].has_key?('Inherit')
|
94
94
|
begin
|
95
95
|
resource_class = Kernel.const_get("CfnGuardian::Resource::#{@templates[group]['Inherit']}").new(resource, group)
|
96
|
-
logger.debug "
|
96
|
+
logger.debug "Inherited resource group #{@templates[group]['Inherit']} for group #{group}"
|
97
97
|
rescue NameError => e
|
98
98
|
logger.warn "'#{@templates[group]['Inherit']}' resource group doesn't exist and is unable to be inherited from"
|
99
99
|
next
|
@@ -181,9 +181,7 @@ module CfnGuardian
|
|
181
181
|
raise CfnGuardian::ValidationError, "#{errors.size} errors found\n[*] #{errors.join("\n[*] ")}" if errors.any?
|
182
182
|
end
|
183
183
|
|
184
|
-
def compile_templates(
|
185
|
-
clean_out_directory()
|
186
|
-
|
184
|
+
def compile_templates(template_file)
|
187
185
|
main_stack = CfnGuardian::Stacks::Main.new()
|
188
186
|
main_stack.build_template(@stacks,@checks,@topics,@maintenance_groups,@ssm_parameters)
|
189
187
|
|
@@ -192,11 +190,7 @@ module CfnGuardian
|
|
192
190
|
|
193
191
|
valid = main_stack.template.validate
|
194
192
|
FileUtils.mkdir_p 'out'
|
195
|
-
File.write("out
|
196
|
-
end
|
197
|
-
|
198
|
-
def clean_out_directory
|
199
|
-
Dir["out/*.yaml"].each {|file| File.delete(file)}
|
193
|
+
File.write("out/#{template_file}", JSON.parse(valid.to_json).to_yaml)
|
200
194
|
end
|
201
195
|
|
202
196
|
def load_parameters(options)
|
data/lib/cfnguardian/deploy.rb
CHANGED
@@ -2,40 +2,37 @@ require 'aws-sdk-cloudformation'
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'cfnguardian/version'
|
4
4
|
require 'cfnguardian/log'
|
5
|
+
require 'cfnguardian/error'
|
5
6
|
|
6
7
|
module CfnGuardian
|
7
8
|
class Deploy
|
8
9
|
include Logging
|
9
10
|
|
10
|
-
def initialize(opts,bucket,parameters)
|
11
|
-
@stack_name =
|
11
|
+
def initialize(opts,bucket,parameters,template_file,stack_name)
|
12
|
+
@stack_name = stack_name
|
12
13
|
@bucket = bucket
|
13
|
-
@
|
14
|
-
@template_path = "out
|
15
|
-
@template_url = "https://#{@bucket}.s3.amazonaws.com/#{@
|
14
|
+
@s3_path = "#{stack_name}/#{template_file}"
|
15
|
+
@template_path = "out/#{template_file}"
|
16
|
+
@template_url = "https://#{@bucket}.s3.amazonaws.com/#{@s3_path}"
|
16
17
|
@parameters = parameters
|
17
18
|
@changeset_role_arn = opts.fetch(:role_arn, nil)
|
18
19
|
|
19
|
-
@tags = {}
|
20
|
-
if
|
21
|
-
@tags
|
20
|
+
@tags = opts.fetch(:tags, {})
|
21
|
+
if ENV.has_key?('CODEBUILD_RESOLVED_SOURCE_VERSION')
|
22
|
+
@tags[:'guardian:config:commit'] = ENV['CODEBUILD_RESOLVED_SOURCE_VERSION']
|
22
23
|
end
|
23
|
-
@tags.merge!(opts.fetch(:tags, {}))
|
24
24
|
|
25
25
|
@client = Aws::CloudFormation::Client.new()
|
26
26
|
end
|
27
27
|
|
28
28
|
def upload_templates
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
key: prefix
|
37
|
-
})
|
38
|
-
end
|
29
|
+
body = File.read(@template_path)
|
30
|
+
client = Aws::S3::Client.new()
|
31
|
+
client.put_object({
|
32
|
+
body: body,
|
33
|
+
bucket: @bucket,
|
34
|
+
key: @s3_path
|
35
|
+
})
|
39
36
|
end
|
40
37
|
|
41
38
|
# TODO: check for REVIEW_IN_PROGRESS
|
@@ -99,8 +96,11 @@ module CfnGuardian
|
|
99
96
|
@client.wait_until :change_set_create_complete, change_set_name: change_set_id
|
100
97
|
rescue Aws::Waiters::Errors::FailureStateError => e
|
101
98
|
change_set = get_change_set(change_set_id)
|
102
|
-
|
103
|
-
|
99
|
+
if change_set.status_reason.include?("The submitted information didn't contain changes.") || change_set.status_reason.include?("No updates are to be performed") && @ignore_empty_change_set
|
100
|
+
raise CfnGuardian::EmptyChangeSetError, "No changes to deploy. Stack #{@stack_name} is up to date"
|
101
|
+
else
|
102
|
+
raise CfnGuardian::ChangeSetError, "Failed to create the changeset : #{e.message} Status: #{change_set.status} Reason: #{change_set.status_reason}"
|
103
|
+
end
|
104
104
|
end
|
105
105
|
end
|
106
106
|
|
data/lib/cfnguardian/error.rb
CHANGED
@@ -20,13 +20,13 @@ module CfnGuardian::Resource
|
|
20
20
|
@event_subscriptions = []
|
21
21
|
end
|
22
22
|
|
23
|
-
# Overidden by
|
23
|
+
# Overidden by inherited classes to define default alarms
|
24
24
|
def default_alarms()
|
25
25
|
return @alarms
|
26
26
|
end
|
27
27
|
|
28
28
|
def get_alarms(group,overides={})
|
29
|
-
# deep copying the overrides to
|
29
|
+
# deep copying the overrides to preserve its reference before doing any changes to it
|
30
30
|
overides = Marshal.load(Marshal.dump(overides))
|
31
31
|
|
32
32
|
# generate default alarms
|
@@ -72,12 +72,12 @@ module CfnGuardian::Resource
|
|
72
72
|
alarm = find_alarm(properties['Inherit'])
|
73
73
|
if !alarm.nil?
|
74
74
|
logger.debug("creating new alarm #{name} for alarm group #{self.class.to_s.split('::').last} inheriting properties from alarm #{properties['Inherit']}")
|
75
|
-
|
75
|
+
inherited_alarm = alarm.clone
|
76
76
|
alarm.name = name
|
77
|
-
properties.each {|attr,value| update_object(
|
78
|
-
@alarms.push(
|
77
|
+
properties.each {|attr,value| update_object(inherited_alarm,attr,value)}
|
78
|
+
@alarms.push(inherited_alarm)
|
79
79
|
else
|
80
|
-
logger.warn "alarm '#{properties['Inherit']}' doesn't
|
80
|
+
logger.warn "alarm '#{properties['Inherit']}' doesn't exist and cannot be inherited"
|
81
81
|
end
|
82
82
|
next
|
83
83
|
end
|
@@ -124,7 +124,7 @@ module CfnGuardian::Resource
|
|
124
124
|
return @alarms.select{|a| a.enabled}
|
125
125
|
end
|
126
126
|
|
127
|
-
# Overidden by
|
127
|
+
# Overidden by inherited classes to define default events
|
128
128
|
def default_events()
|
129
129
|
return @events
|
130
130
|
end
|
@@ -134,7 +134,7 @@ module CfnGuardian::Resource
|
|
134
134
|
return @events.select{|e| e.enabled}
|
135
135
|
end
|
136
136
|
|
137
|
-
# Overidden by
|
137
|
+
# Overidden by inherited classes to define default checks
|
138
138
|
def default_checks()
|
139
139
|
return @checks
|
140
140
|
end
|
@@ -144,7 +144,7 @@ module CfnGuardian::Resource
|
|
144
144
|
return @checks
|
145
145
|
end
|
146
146
|
|
147
|
-
# Overidden by
|
147
|
+
# Overidden by inherited classes to define default checks
|
148
148
|
def default_metric_filters()
|
149
149
|
return @metric_filters
|
150
150
|
end
|
@@ -154,20 +154,20 @@ module CfnGuardian::Resource
|
|
154
154
|
return @metric_filters
|
155
155
|
end
|
156
156
|
|
157
|
-
# Overidden by
|
157
|
+
# Overidden by inherited classes to define default checks
|
158
158
|
def default_event_subscriptions()
|
159
159
|
return @event_subscriptions
|
160
160
|
end
|
161
161
|
|
162
162
|
def get_event_subscriptions(group, overides)
|
163
|
-
# generate
|
163
|
+
# generate default event subscriptions
|
164
164
|
default_event_subscriptions()
|
165
165
|
|
166
|
-
#
|
166
|
+
# override the defaults
|
167
167
|
overides.each do |name, properties|
|
168
168
|
event_subscription = find_event_subscriptions(name)
|
169
169
|
|
170
|
-
#
|
170
|
+
# disable the event subscription if the value is false
|
171
171
|
if [false].include?(properties)
|
172
172
|
unless event_subscription.nil?
|
173
173
|
event_subscription.enabled = false
|
@@ -17,12 +17,14 @@ module CfnGuardian::Resource
|
|
17
17
|
alarm.name = "#{command.to_camelcase}Warning"
|
18
18
|
alarm.metric_name = command
|
19
19
|
alarm.threshold = 0
|
20
|
+
alarm.alarm_action = 'Warning'
|
20
21
|
@alarms.push(alarm)
|
21
22
|
|
22
23
|
alarm = CfnGuardian::Models::NrpeAlarm.new(host,@environment)
|
23
24
|
alarm.name = "#{command.to_camelcase}Critical"
|
24
25
|
alarm.metric_name = command
|
25
26
|
alarm.threshold = 1
|
27
|
+
alarm.alarm_action = 'Critical'
|
26
28
|
@alarms.push(alarm)
|
27
29
|
end
|
28
30
|
end
|
data/lib/cfnguardian/validate.rb
CHANGED
@@ -3,6 +3,7 @@ require 'fileutils'
|
|
3
3
|
require 'cfnguardian/version'
|
4
4
|
require 'cfnguardian/log'
|
5
5
|
require 'cfnguardian/s3'
|
6
|
+
require 'cfnguardian/error'
|
6
7
|
|
7
8
|
module CfnGuardian
|
8
9
|
class Validate
|
@@ -25,7 +26,10 @@ module CfnGuardian
|
|
25
26
|
success << validate_local(template)
|
26
27
|
end
|
27
28
|
end
|
28
|
-
|
29
|
+
|
30
|
+
if success.include?(false)
|
31
|
+
raise CfnGuardian::TemplateValidationError, "One or more templates failed to validate"
|
32
|
+
end
|
29
33
|
end
|
30
34
|
|
31
35
|
def validate_local(path)
|
data/lib/cfnguardian/version.rb
CHANGED
data/lib/cfnguardian.rb
CHANGED
@@ -16,6 +16,10 @@ require "cfnguardian/tagger"
|
|
16
16
|
module CfnGuardian
|
17
17
|
class Cli < Thor
|
18
18
|
include Logging
|
19
|
+
|
20
|
+
def self.exit_on_failure?
|
21
|
+
true
|
22
|
+
end
|
19
23
|
|
20
24
|
map %w[--version -v] => :__print_version
|
21
25
|
desc "--version, -v", "print the version"
|
@@ -40,28 +44,26 @@ module CfnGuardian
|
|
40
44
|
method_option :sns_task, type: :string, desc: "sns topic arn for the task alarms"
|
41
45
|
method_option :sns_informational, type: :string, desc: "sns topic arn for the informational alarms"
|
42
46
|
method_option :sns_events, type: :string, desc: "sns topic arn for the informational alarms"
|
43
|
-
|
47
|
+
method_option :template_file, type: :string, default: 'guardian.compiled.yaml', desc: "name of the compiled cloudformation template file"
|
44
48
|
|
45
49
|
def compile
|
46
50
|
set_log_level(options[:debug])
|
47
51
|
|
48
52
|
set_region(options[:region],options[:validate])
|
49
53
|
s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
|
50
|
-
|
54
|
+
|
55
|
+
clean_out_directory()
|
56
|
+
|
51
57
|
compiler = CfnGuardian::Compile.new(options[:config])
|
52
58
|
compiler.get_resources
|
53
|
-
compiler.compile_templates(
|
59
|
+
compiler.compile_templates(options[:template_file])
|
54
60
|
logger.info "Cloudformation templates compiled successfully in out/ directory"
|
55
61
|
if options[:validate]
|
56
62
|
s3.create_bucket_if_not_exists()
|
57
63
|
validator = CfnGuardian::Validate.new(s3.bucket)
|
58
|
-
|
59
|
-
logger.error("One or more templates failed to validate")
|
60
|
-
exit(1)
|
61
|
-
else
|
62
|
-
logger.info "Cloudformation templates were validated successfully"
|
63
|
-
end
|
64
|
+
validator.validate
|
64
65
|
end
|
66
|
+
|
65
67
|
logger.warn "AWS cloudwatch alarms defined in the templates will cost roughly $#{'%.2f' % compiler.cost} per month"
|
66
68
|
|
67
69
|
if options[:template_config]
|
@@ -80,15 +82,16 @@ module CfnGuardian
|
|
80
82
|
method_option :bucket, type: :string, desc: "provide custom bucket name, will create a default bucket if not provided"
|
81
83
|
method_option :path, type: :string, default: "guardian", desc: "S3 path location for nested stacks"
|
82
84
|
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
83
|
-
method_option :stack_name, aliases: :s, type: :string, desc: "set the Cloudformation stack name. Defaults to `guardian`"
|
85
|
+
method_option :stack_name, aliases: :s, type: :string, default: 'guardian', desc: "set the Cloudformation stack name. Defaults to `guardian`"
|
84
86
|
method_option :sns_critical, type: :string, desc: "sns topic arn for the critical alarms"
|
85
87
|
method_option :sns_warning, type: :string, desc: "sns topic arn for the warning alarms"
|
86
88
|
method_option :sns_task, type: :string, desc: "sns topic arn for the task alarms"
|
87
89
|
method_option :sns_informational, type: :string, desc: "sns topic arn for the informational alarms"
|
88
90
|
method_option :sns_events, type: :string, desc: "sns topic arn for the informational alarms"
|
89
91
|
method_option :tags, type: :hash, desc: "additional tags on the cloudformation stack"
|
90
|
-
method_option :tag_yaml, type: :string, desc: "additional tags on the cloudformation stack in a yaml file"
|
91
92
|
method_option :role_arn, type: :string, desc: "IAM role arn that CloudFormation assumes when executing the change set"
|
93
|
+
method_option :template_file, type: :string, default: 'guardian.compiled.yaml', desc: "name of the compiled cloudformation template file"
|
94
|
+
method_option :fail_empty_change_set, type: :boolean, default: true, desc: "fail a cloudformation changeset if it contains no changes"
|
92
95
|
|
93
96
|
def deploy
|
94
97
|
set_log_level(options[:debug])
|
@@ -96,22 +99,19 @@ module CfnGuardian
|
|
96
99
|
set_region(options[:region],true)
|
97
100
|
s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
|
98
101
|
|
102
|
+
clean_out_directory()
|
103
|
+
|
99
104
|
compiler = CfnGuardian::Compile.new(options[:config])
|
100
105
|
compiler.get_resources
|
101
|
-
compiler.compile_templates(
|
106
|
+
compiler.compile_templates(options[:template_file])
|
102
107
|
parameters = compiler.load_parameters(options)
|
103
108
|
logger.info "Cloudformation templates compiled successfully in out/ directory"
|
104
109
|
|
105
110
|
s3.create_bucket_if_not_exists
|
106
111
|
validator = CfnGuardian::Validate.new(s3.bucket)
|
107
|
-
|
108
|
-
logger.error("One or more templates failed to validate")
|
109
|
-
exit(1)
|
110
|
-
else
|
111
|
-
logger.info "Cloudformation templates were validated successfully"
|
112
|
-
end
|
112
|
+
validator.validate
|
113
113
|
|
114
|
-
deployer = CfnGuardian::Deploy.new(options,s3.bucket,parameters)
|
114
|
+
deployer = CfnGuardian::Deploy.new(options,s3.bucket,parameters,options[:template_file],options[:stack_name])
|
115
115
|
deployer.upload_templates
|
116
116
|
change_set, change_set_type = deployer.create_change_set()
|
117
117
|
deployer.wait_for_changeset(change_set.id)
|
@@ -119,26 +119,139 @@ module CfnGuardian
|
|
119
119
|
deployer.wait_for_execute(change_set_type)
|
120
120
|
end
|
121
121
|
|
122
|
+
desc "bulk-deploy", "Generates and deploys multiple monitoring CloudFormation templates from multiple config yamls"
|
123
|
+
long_desc <<-LONG
|
124
|
+
For each alarm configuration yamll file provided guardian will generate a CloudFormation template in output to the out/ directory.
|
125
|
+
The templates are copied to the s3 bucket and the cloudformation stacks are deployed.
|
126
|
+
The names of the Cloudformation stacks are determined by the config yaml name. e.g. alarms.myenv.yaml will deploy the stack myenv-guardian
|
127
|
+
LONG
|
128
|
+
method_option :config, aliases: :c, type: :array, desc: "yaml config files", required: true
|
129
|
+
method_option :bucket, type: :string, desc: "provide custom bucket name, will create a default bucket if not provided"
|
130
|
+
method_option :path, type: :string, default: "guardian", desc: "S3 path location for nested stacks"
|
131
|
+
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
132
|
+
method_option :sns_critical, type: :string, desc: "sns topic arn for the critical alarms"
|
133
|
+
method_option :sns_warning, type: :string, desc: "sns topic arn for the warning alarms"
|
134
|
+
method_option :sns_task, type: :string, desc: "sns topic arn for the task alarms"
|
135
|
+
method_option :sns_informational, type: :string, desc: "sns topic arn for the informational alarms"
|
136
|
+
method_option :sns_events, type: :string, desc: "sns topic arn for the informational alarms"
|
137
|
+
method_option :tags, type: :hash, desc: "additional tags on the cloudformation stack"
|
138
|
+
method_option :role_arn, type: :string, desc: "IAM role arn that CloudFormation assumes when executing the change set"
|
139
|
+
method_option :fail_empty_change_set, type: :boolean, default: true, desc: "fail a cloudformation changeset if it contains no changes"
|
140
|
+
|
141
|
+
def bulk_deploy
|
142
|
+
set_log_level(options[:debug])
|
143
|
+
|
144
|
+
set_region(options[:region],true)
|
145
|
+
s3 = CfnGuardian::S3.new(options[:bucket],options[:path])
|
146
|
+
s3.create_bucket_if_not_exists
|
147
|
+
|
148
|
+
clean_out_directory()
|
149
|
+
|
150
|
+
template_file_suffix = 'compiled.yaml'
|
151
|
+
|
152
|
+
compiled = []
|
153
|
+
|
154
|
+
options[:config].each do |config|
|
155
|
+
config_basename = File.basename(config, ".alarms.yaml")
|
156
|
+
guardian_name = config_basename == "alarms.yaml" ? "" : "-#{config_basename}"
|
157
|
+
template_file = "guardian#{guardian_name}.#{template_file_suffix}"
|
158
|
+
|
159
|
+
compiler = CfnGuardian::Compile.new(config)
|
160
|
+
compiler.get_resources
|
161
|
+
compiler.compile_templates(template_file)
|
162
|
+
logger.info "compiled template to out/#{template_file} from yaml config #{config}"
|
163
|
+
parameters = compiler.load_parameters(options)
|
164
|
+
|
165
|
+
compiled << {template_file: template_file, parameters: parameters}
|
166
|
+
logger.debug("template file #{template_file} generated with parameters: #{parameters}")
|
167
|
+
end
|
168
|
+
|
169
|
+
validator = CfnGuardian::Validate.new(s3.bucket)
|
170
|
+
validator.validate
|
171
|
+
|
172
|
+
changesets = []
|
173
|
+
|
174
|
+
compiled.each do |stack|
|
175
|
+
stack_name = stack[:template_file].gsub(".#{template_file_suffix}", "")
|
176
|
+
deployer = CfnGuardian::Deploy.new(options,s3.bucket,stack[:parameters],stack[:template_file],stack_name)
|
177
|
+
deployer.upload_templates
|
178
|
+
logger.info("creating changeset for stack #{stack_name}")
|
179
|
+
change_set, change_set_type = deployer.create_change_set()
|
180
|
+
changesets << {deployer: deployer, id: change_set.id, type: change_set_type}
|
181
|
+
end
|
182
|
+
|
183
|
+
changesets_executed = []
|
184
|
+
changesets.each do |changeset|
|
185
|
+
begin
|
186
|
+
changeset[:deployer].wait_for_changeset(changeset[:id])
|
187
|
+
rescue CfnGuardian::EmptyChangeSetError => e
|
188
|
+
if options[:fail_empty_change_set]
|
189
|
+
raise e
|
190
|
+
else
|
191
|
+
logger.info e.message
|
192
|
+
next
|
193
|
+
end
|
194
|
+
end
|
195
|
+
logger.info("executing changeset #{changeset[:id]}")
|
196
|
+
changeset[:deployer].execute_change_set(changeset[:id])
|
197
|
+
changesets_executed << changeset
|
198
|
+
end
|
199
|
+
|
200
|
+
changesets_executed.each do |changeset|
|
201
|
+
logger.info("waiting for changeset #{changeset[:id]} to complete")
|
202
|
+
changeset[:deployer].wait_for_execute(changeset[:type])
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
122
206
|
desc "tag-alarms", "apply tags to the cloudwatch alarms deployed"
|
123
207
|
long_desc <<-LONG
|
124
208
|
Because Cloudformation desn't support tagging cloudwatch alarms this command
|
125
209
|
applies tags to each cloudwatch alarm created by guardian.
|
126
210
|
Guardian defines default tags and this can be added to through the alarms.yaml config.
|
127
211
|
LONG
|
128
|
-
method_option :config, aliases: :c, type: :
|
212
|
+
method_option :config, aliases: :c, type: :array, desc: "yaml config files", required: true
|
129
213
|
method_option :region, aliases: :r, type: :string, desc: "set the AWS region"
|
214
|
+
method_option :tags, type: :hash, desc: "additional tags on the cloudformation stack"
|
130
215
|
|
131
216
|
def tag_alarms
|
132
217
|
set_log_level(options[:debug])
|
133
218
|
set_region(options[:region],true)
|
134
219
|
|
135
|
-
|
136
|
-
compiler.get_resources
|
137
|
-
alarms = compiler.alarms
|
220
|
+
tags = options.fetch(:tags, {})
|
138
221
|
|
139
|
-
|
140
|
-
|
141
|
-
|
222
|
+
if ENV.has_key?('CODEBUILD_RESOLVED_SOURCE_VERSION')
|
223
|
+
tags[:'guardian:config:commit'] = ENV['CODEBUILD_RESOLVED_SOURCE_VERSION']
|
224
|
+
end
|
225
|
+
|
226
|
+
options[:config].each do |config|
|
227
|
+
config_basename = File.basename(config, "alarms.yaml")
|
228
|
+
stack_name_suffix = config_basename != "" ? "#{config_basename}-" : ""
|
229
|
+
tags[:'guardian:stack:name'] = "guardian#{stack_name_suffix}"
|
230
|
+
tags[:'guardian:config:yaml'] = config
|
231
|
+
|
232
|
+
logger.info "tagging alarms from config file #{config}"
|
233
|
+
compiler = CfnGuardian::Compile.new(config)
|
234
|
+
compiler.get_resources
|
235
|
+
alarms = compiler.alarms
|
236
|
+
global_tags = compiler.global_tags.merge(tags)
|
237
|
+
|
238
|
+
tagger = CfnGuardian::Tagger.new()
|
239
|
+
|
240
|
+
counter = 0
|
241
|
+
max_retries = 3
|
242
|
+
alarms.each do |alarm|
|
243
|
+
begin
|
244
|
+
tagger.tag_alarm(alarm, global_tags)
|
245
|
+
rescue Aws::CloudWatch::Errors::Throttling => e
|
246
|
+
logger.info "cloud watch throttling alarm tagging requests, retrying ..."
|
247
|
+
if (counter += 1) < max_retries
|
248
|
+
sleep(1)
|
249
|
+
redo
|
250
|
+
else
|
251
|
+
loger.warn "throttled max times (#{max_retries}) tagging alarm #{alarm.name}, skipping ..."
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
142
255
|
end
|
143
256
|
end
|
144
257
|
|
@@ -165,7 +278,6 @@ module CfnGuardian
|
|
165
278
|
:title => "Guardian Alarm Drift".green,
|
166
279
|
:headings => ['Alarm Name', 'Property', 'Expected', 'Actual', 'Type'],
|
167
280
|
:rows => rows.flatten(1))
|
168
|
-
exit(1)
|
169
281
|
end
|
170
282
|
end
|
171
283
|
|
@@ -189,8 +301,7 @@ module CfnGuardian
|
|
189
301
|
elsif options[:defaults]
|
190
302
|
config_file = default_config()
|
191
303
|
else
|
192
|
-
|
193
|
-
exit -1
|
304
|
+
raise Thor::Error, 'one of `--config YAML` or `--defaults` must be supplied'
|
194
305
|
end
|
195
306
|
|
196
307
|
compiler = CfnGuardian::Compile.new(config_file)
|
@@ -198,8 +309,7 @@ module CfnGuardian
|
|
198
309
|
alarms = filter_compiled_alarms(compiler.alarms,options[:filter])
|
199
310
|
|
200
311
|
if alarms.empty?
|
201
|
-
|
202
|
-
exit 1
|
312
|
+
raise Thor::Error, "No matches found"
|
203
313
|
end
|
204
314
|
|
205
315
|
headings = ['Property', 'Config']
|
@@ -429,8 +539,7 @@ module CfnGuardian
|
|
429
539
|
Aws.config.update({region: ENV['AWS_DEFAULT_REGION']})
|
430
540
|
else
|
431
541
|
if required
|
432
|
-
|
433
|
-
exit(1)
|
542
|
+
raise Thor::Error "No AWS region found. Please suppy the region using option `--region` or setting environment variables `AWS_REGION` `AWS_DEFAULT_REGION`"
|
434
543
|
end
|
435
544
|
end
|
436
545
|
end
|
@@ -461,5 +570,9 @@ module CfnGuardian
|
|
461
570
|
return !parameter.nil? ? parameter.parameter_value : nil
|
462
571
|
end
|
463
572
|
|
573
|
+
def clean_out_directory
|
574
|
+
Dir["out/*.yaml"].each {|file| File.delete(file)}
|
575
|
+
end
|
576
|
+
|
464
577
|
end
|
465
578
|
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.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Guslington
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -184,6 +184,20 @@ dependencies:
|
|
184
184
|
- - "<"
|
185
185
|
- !ruby/object:Gem::Version
|
186
186
|
version: '2'
|
187
|
+
- !ruby/object:Gem::Dependency
|
188
|
+
name: rexml
|
189
|
+
requirement: !ruby/object:Gem::Requirement
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
version: '0'
|
194
|
+
type: :runtime
|
195
|
+
prerelease: false
|
196
|
+
version_requirements: !ruby/object:Gem::Requirement
|
197
|
+
requirements:
|
198
|
+
- - ">="
|
199
|
+
- !ruby/object:Gem::Version
|
200
|
+
version: '0'
|
187
201
|
- !ruby/object:Gem::Dependency
|
188
202
|
name: bundler
|
189
203
|
requirement: !ruby/object:Gem::Requirement
|