cloud-mu 3.2.0 → 3.5.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.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  4. data/bin/mu-adopt +12 -1
  5. data/bin/mu-aws-setup +41 -7
  6. data/bin/mu-azure-setup +34 -0
  7. data/bin/mu-configure +214 -119
  8. data/bin/mu-gcp-setup +37 -2
  9. data/bin/mu-load-config.rb +2 -1
  10. data/bin/mu-node-manage +3 -0
  11. data/bin/mu-refresh-ssl +67 -0
  12. data/bin/mu-run-tests +28 -6
  13. data/bin/mu-self-update +30 -10
  14. data/bin/mu-upload-chef-artifacts +30 -26
  15. data/cloud-mu.gemspec +10 -8
  16. data/cookbooks/mu-master/attributes/default.rb +5 -1
  17. data/cookbooks/mu-master/metadata.rb +2 -2
  18. data/cookbooks/mu-master/recipes/default.rb +81 -26
  19. data/cookbooks/mu-master/recipes/init.rb +197 -62
  20. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  21. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  22. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  23. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  24. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  25. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  26. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  27. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  28. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  30. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  31. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  32. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  33. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  34. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  35. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  36. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  37. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  38. data/extras/Gemfile.lock.bootstrap +394 -0
  39. data/extras/bucketstubs/error.html +0 -0
  40. data/extras/bucketstubs/index.html +0 -0
  41. data/extras/clean-stock-amis +11 -3
  42. data/extras/generate-stock-images +6 -3
  43. data/extras/git_rpm/build.sh +20 -0
  44. data/extras/git_rpm/mugit.spec +53 -0
  45. data/extras/image-generators/AWS/centos7.yaml +19 -16
  46. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  47. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  48. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  49. data/extras/openssl_rpm/build.sh +19 -0
  50. data/extras/openssl_rpm/mussl.spec +46 -0
  51. data/extras/python_rpm/muthon.spec +14 -4
  52. data/extras/ruby_rpm/muby.spec +9 -5
  53. data/extras/sqlite_rpm/build.sh +19 -0
  54. data/extras/sqlite_rpm/muqlite.spec +47 -0
  55. data/install/installer +7 -5
  56. data/modules/mommacat.ru +2 -2
  57. data/modules/mu.rb +14 -7
  58. data/modules/mu/adoption.rb +5 -5
  59. data/modules/mu/cleanup.rb +47 -25
  60. data/modules/mu/cloud.rb +29 -1
  61. data/modules/mu/cloud/dnszone.rb +0 -2
  62. data/modules/mu/cloud/machine_images.rb +1 -1
  63. data/modules/mu/cloud/providers.rb +6 -1
  64. data/modules/mu/cloud/resource_base.rb +16 -7
  65. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  66. data/modules/mu/cloud/wrappers.rb +20 -7
  67. data/modules/mu/config.rb +28 -12
  68. data/modules/mu/config/bucket.rb +31 -2
  69. data/modules/mu/config/cache_cluster.rb +1 -1
  70. data/modules/mu/config/cdn.rb +100 -0
  71. data/modules/mu/config/container_cluster.rb +1 -1
  72. data/modules/mu/config/database.rb +3 -3
  73. data/modules/mu/config/dnszone.rb +4 -3
  74. data/modules/mu/config/endpoint.rb +1 -0
  75. data/modules/mu/config/firewall_rule.rb +1 -1
  76. data/modules/mu/config/function.rb +16 -7
  77. data/modules/mu/config/job.rb +89 -0
  78. data/modules/mu/config/notifier.rb +7 -18
  79. data/modules/mu/config/ref.rb +55 -9
  80. data/modules/mu/config/schema_helpers.rb +12 -3
  81. data/modules/mu/config/server.rb +11 -5
  82. data/modules/mu/config/server_pool.rb +2 -2
  83. data/modules/mu/config/vpc.rb +11 -10
  84. data/modules/mu/defaults/AWS.yaml +106 -106
  85. data/modules/mu/deploy.rb +40 -14
  86. data/modules/mu/groomers/chef.rb +2 -2
  87. data/modules/mu/master.rb +70 -3
  88. data/modules/mu/mommacat.rb +28 -9
  89. data/modules/mu/mommacat/daemon.rb +13 -7
  90. data/modules/mu/mommacat/naming.rb +2 -2
  91. data/modules/mu/mommacat/search.rb +16 -5
  92. data/modules/mu/mommacat/storage.rb +67 -32
  93. data/modules/mu/providers/aws.rb +298 -85
  94. data/modules/mu/providers/aws/alarm.rb +5 -5
  95. data/modules/mu/providers/aws/bucket.rb +284 -50
  96. data/modules/mu/providers/aws/cache_cluster.rb +26 -26
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/providers/aws/collection.rb +16 -16
  99. data/modules/mu/providers/aws/container_cluster.rb +84 -64
  100. data/modules/mu/providers/aws/database.rb +59 -55
  101. data/modules/mu/providers/aws/dnszone.rb +29 -12
  102. data/modules/mu/providers/aws/endpoint.rb +535 -50
  103. data/modules/mu/providers/aws/firewall_rule.rb +32 -26
  104. data/modules/mu/providers/aws/folder.rb +1 -1
  105. data/modules/mu/providers/aws/function.rb +300 -134
  106. data/modules/mu/providers/aws/group.rb +16 -14
  107. data/modules/mu/providers/aws/habitat.rb +4 -4
  108. data/modules/mu/providers/aws/job.rb +469 -0
  109. data/modules/mu/providers/aws/loadbalancer.rb +67 -45
  110. data/modules/mu/providers/aws/log.rb +17 -17
  111. data/modules/mu/providers/aws/msg_queue.rb +22 -13
  112. data/modules/mu/providers/aws/nosqldb.rb +99 -8
  113. data/modules/mu/providers/aws/notifier.rb +137 -65
  114. data/modules/mu/providers/aws/role.rb +119 -83
  115. data/modules/mu/providers/aws/search_domain.rb +166 -30
  116. data/modules/mu/providers/aws/server.rb +209 -118
  117. data/modules/mu/providers/aws/server_pool.rb +95 -130
  118. data/modules/mu/providers/aws/storage_pool.rb +19 -11
  119. data/modules/mu/providers/aws/user.rb +5 -5
  120. data/modules/mu/providers/aws/userdata/linux.erb +5 -4
  121. data/modules/mu/providers/aws/vpc.rb +109 -54
  122. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  123. data/modules/mu/providers/azure.rb +78 -12
  124. data/modules/mu/providers/azure/server.rb +20 -4
  125. data/modules/mu/providers/cloudformation/server.rb +1 -1
  126. data/modules/mu/providers/google.rb +21 -5
  127. data/modules/mu/providers/google/bucket.rb +1 -1
  128. data/modules/mu/providers/google/container_cluster.rb +1 -1
  129. data/modules/mu/providers/google/database.rb +1 -1
  130. data/modules/mu/providers/google/firewall_rule.rb +1 -1
  131. data/modules/mu/providers/google/folder.rb +7 -3
  132. data/modules/mu/providers/google/function.rb +66 -31
  133. data/modules/mu/providers/google/group.rb +1 -1
  134. data/modules/mu/providers/google/habitat.rb +1 -1
  135. data/modules/mu/providers/google/loadbalancer.rb +1 -1
  136. data/modules/mu/providers/google/role.rb +6 -3
  137. data/modules/mu/providers/google/server.rb +1 -1
  138. data/modules/mu/providers/google/server_pool.rb +1 -1
  139. data/modules/mu/providers/google/user.rb +1 -1
  140. data/modules/mu/providers/google/vpc.rb +28 -3
  141. data/modules/tests/aws-jobs-functions.yaml +46 -0
  142. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  143. data/modules/tests/centos6.yaml +4 -0
  144. data/modules/tests/centos7.yaml +4 -0
  145. data/modules/tests/ecs.yaml +2 -2
  146. data/modules/tests/eks.yaml +1 -1
  147. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  148. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  149. data/modules/tests/k8s.yaml +1 -1
  150. data/modules/tests/microservice_app.yaml +288 -0
  151. data/modules/tests/rds.yaml +5 -5
  152. data/modules/tests/regrooms/rds.yaml +5 -5
  153. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  154. data/modules/tests/super_complex_bok.yml +2 -2
  155. data/modules/tests/super_simple_bok.yml +2 -2
  156. metadata +42 -17
@@ -32,7 +32,7 @@ module MU
32
32
  # Called automatically by {MU::Deploy#createResources}
33
33
  def create
34
34
  begin
35
- MU::Cloud::AWS.iam(credentials: @config['credentials']).get_group(
35
+ MU::Cloud::AWS.iam(credentials: @credentials).get_group(
36
36
  group_name: @mu_name,
37
37
  path: @config['path']
38
38
  )
@@ -42,7 +42,7 @@ module MU
42
42
  rescue Aws::IAM::Errors::NoSuchEntity
43
43
  @config['path'] ||= "/"+@deploy.deploy_id+"/"
44
44
  MU.log "Creating IAM group #{@config['path']}#{@mu_name}"
45
- MU::Cloud::AWS.iam(credentials: @config['credentials']).create_group(
45
+ MU::Cloud::AWS.iam(credentials: @credentials).create_group(
46
46
  group_name: @mu_name,
47
47
  path: @config['path']
48
48
  )
@@ -64,7 +64,7 @@ module MU
64
64
  if found.size == 1
65
65
  userdesc = found.values.first
66
66
  MU.log "Adding IAM user #{userdesc.path}#{userdesc.user_name} to group #{@mu_name}", MU::NOTICE
67
- MU::Cloud::AWS.iam(credentials: @config['credentials']).add_user_to_group(
67
+ MU::Cloud::AWS.iam(credentials: @credentials).add_user_to_group(
68
68
  user_name: userid,
69
69
  group_name: @mu_name
70
70
  )
@@ -77,7 +77,7 @@ module MU
77
77
  extras = cloud_desc.users.map { |u| u.user_name } - @config['members']
78
78
  extras.each { |user_name|
79
79
  MU.log "Purging user #{user_name} from IAM group #{@cloud_id}", MU::NOTICE
80
- MU::Cloud::AWS.iam(credentials: @config['credentials']).remove_user_from_group(
80
+ MU::Cloud::AWS.iam(credentials: @credentials).remove_user_from_group(
81
81
  user_name: user_name,
82
82
  group_name: @cloud_id
83
83
  )
@@ -155,7 +155,8 @@ module MU
155
155
  # return [Struct]
156
156
  def cloud_desc(use_cache: true)
157
157
  return @cloud_desc_cache if @cloud_desc_cache and use_cache
158
- @cloud_desc_cache = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_group(
158
+ return nil if !@mu_name
159
+ @cloud_desc_cache = MU::Cloud::AWS.iam(credentials: @credentials).get_group(
159
160
  group_name: @mu_name
160
161
  )
161
162
  @cloud_desc_cache
@@ -186,12 +187,12 @@ module MU
186
187
  # @param noop [Boolean]: If true, will only print what would be done
187
188
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
188
189
  # @return [void]
189
- def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
190
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, credentials: nil, flags: {})
190
191
  MU.log "AWS::Group.cleanup: need to support flags['known']", MU::DEBUG, details: flags
191
192
  MU.log "Placeholder: AWS Group artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
192
193
 
193
194
  resp = MU::Cloud::AWS.iam(credentials: credentials).list_groups(
194
- path_prefix: "/"+MU.deploy_id+"/"
195
+ path_prefix: "/"+deploy_id+"/"
195
196
  )
196
197
  if resp and resp.groups
197
198
  resp.groups.each { |g|
@@ -266,7 +267,7 @@ module MU
266
267
  def toKitten(**_args)
267
268
  bok = {
268
269
  "cloud" => "AWS",
269
- "credentials" => @config['credentials'],
270
+ "credentials" => @credentials,
270
271
  "cloud_id" => @cloud_id
271
272
  }
272
273
 
@@ -274,14 +275,15 @@ module MU
274
275
  MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
275
276
  return nil
276
277
  end
277
-
278
- bok["name"] = cloud_desc.group.group_name
279
278
 
280
- if cloud_desc.group.path != "/"
281
- bok["path"] = cloud_desc.group.path
279
+ group_desc = cloud_desc(use_cache: false).respond_to?(:group) ? cloud_desc.group : cloud_desc
280
+ bok["name"] = group_desc.group_name
281
+
282
+ if group_desc.path != "/"
283
+ bok["path"] = group_desc.path
282
284
  end
283
285
 
284
- if cloud_desc.users and cloud_desc.users.size > 0
286
+ if cloud_desc.respond_to?(:users) and cloud_desc.users and cloud_desc.users.size > 0
285
287
  bok["members"] = cloud_desc.users.map { |u| u.user_name }
286
288
  end
287
289
 
@@ -290,7 +292,7 @@ module MU
290
292
  if resp and resp.policy_names and resp.policy_names.size > 0
291
293
  resp.policy_names.each { |pol_name|
292
294
  pol = MU::Cloud::AWS.iam(credentials: @credentials).get_group_policy(group_name: @cloud_id, policy_name: pol_name)
293
- doc = JSON.parse(URI.decode(pol.policy_document))
295
+ doc = JSON.parse(CGI.unescape(pol.policy_document))
294
296
  bok["inline_policies"] = MU::Cloud.resourceClass("AWS", "Role").doc2MuPolicies(pol.policy_name, doc, bok["inline_policies"])
295
297
  }
296
298
  end
@@ -34,7 +34,7 @@ module MU
34
34
  end
35
35
 
36
36
  MU.log "Creating AWS account #{@mu_name} with contact email #{@config['email']}"
37
- resp = MU::Cloud::AWS.orgs(credentials: @config['credentials']).create_account(
37
+ resp = MU::Cloud::AWS.orgs(credentials: @credentials).create_account(
38
38
  account_name: @mu_name,
39
39
  email: @config['email']
40
40
  )
@@ -42,7 +42,7 @@ module MU
42
42
  createid = resp.create_account_status.id
43
43
 
44
44
  begin
45
- resp = MU::Cloud::AWS.orgs(credentials: @config['credentials']).describe_create_account_status(
45
+ resp = MU::Cloud::AWS.orgs(credentials: @credentials).describe_create_account_status(
46
46
  create_account_request_id: createid
47
47
  )
48
48
  if !["SUCCEEDED", "IN_PROGRESS"].include?(resp.create_account_status.state)
@@ -90,7 +90,7 @@ module MU
90
90
  # @param noop [Boolean]: If true, will only print what would be done
91
91
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
92
92
  # @return [void]
93
- def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
93
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, credentials: nil, flags: {})
94
94
  return if !orgMasterCreds?(credentials)
95
95
  MU.log "AWS::Habitat.cleanup: need to support flags['known']", MU::DEBUG, details: flags
96
96
  MU.log "Placeholder: AWS Habitat artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
@@ -99,7 +99,7 @@ module MU
99
99
 
100
100
  if resp and resp.accounts
101
101
  resp.accounts.each { |acct|
102
- if acct.name.match(/^#{Regexp.quote(MU.deploy_id)}/) or acct.name.match(/BUNS/)
102
+ if acct.name.match(/^#{Regexp.quote(deploy_id)}/) or acct.name.match(/BUNS/)
103
103
  if !noop
104
104
  pp acct
105
105
  end
@@ -0,0 +1,469 @@
1
+ # Copyright:: Copyright (c) 2020 eGlobalTech, Inc., all rights reserved
2
+ #
3
+ # Licensed under the BSD-3 license (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License in the root of the project or at
6
+ #
7
+ # http://egt-labs.com/mu/LICENSE.html
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module MU
16
+ class Cloud
17
+ class AWS
18
+ # A scheduled task facility as configured in {MU::Config::BasketofKittens::jobs}
19
+ class Job < MU::Cloud::Job
20
+
21
+ # Initialize this cloud resource object. Calling +super+ will invoke the initializer defined under {MU::Cloud}, which should set the attribtues listed in {MU::Cloud::PUBLIC_ATTRS} as well as applicable dependency shortcuts, like +@vpc+, for us.
22
+ # @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
23
+ def initialize(**args)
24
+ super
25
+ @mu_name ||= @deploy.getResourceName(@config["name"])
26
+ end
27
+
28
+ # Called automatically by {MU::Deploy#createResources}
29
+ def create
30
+ @cloud_id = @mu_name
31
+
32
+ params = get_properties
33
+
34
+ MU.log "Creating CloudWatch Event #{@mu_name}", MU::NOTICE, details: params
35
+
36
+ MU::Cloud::AWS.cloudwatchevents(region: @region, credentials: @credentials).put_rule(params)
37
+ end
38
+
39
+ # Called automatically by {MU::Deploy#createResources}
40
+ def groom
41
+ new_props = get_properties
42
+ current = MU.structToHash(cloud_desc(use_cache: false))
43
+ params = {}
44
+ new_props.each_pair { |k, v|
45
+ next if k == :tags # doesn't seem to do anything
46
+ if v != current[k]
47
+ params[k] = v
48
+ end
49
+ }
50
+
51
+ if params.size > 0
52
+ MU.log "Updating CloudWatch Event #{@cloud_id}", MU::NOTICE, details: params
53
+ MU::Cloud::AWS.cloudwatchevents(region: @region, credentials: @credentials).put_rule(new_props)
54
+ end
55
+
56
+ if @config['targets']
57
+ target_params = []
58
+ @config['targets'].each { |t|
59
+ MU.retrier([MuNonFatal], max:5, wait: 9) {
60
+ target_ref = MU::Config::Ref.get(t)
61
+ target_obj = target_ref.kitten(@deploy, cloud: "AWS")
62
+ this_target = if target_ref.is_mu_type? and target_obj and
63
+ !target_obj.arn.nil?
64
+ if target_ref.type == "functions"
65
+ target_obj.addTrigger(arn, "events", @mu_name)
66
+ end
67
+ {
68
+ id: target_obj.cloud_id,
69
+ arn: target_obj.arn
70
+ }
71
+ elsif target_ref.id and target_ref.id.match(/^arn:/)
72
+ {
73
+ id: target_ref.id || target_ref.name,
74
+ arn: target_ref.id
75
+ }
76
+ else
77
+ raise MuNonFatal.new "Failed to retrieve ARN from CLoudWatch Event target descriptor", details: target_ref.to_h
78
+ end
79
+ if t['role']
80
+ role_obj = MU::Config::Ref.get(t['role']).kitten(@deploy, cloud: "AWS")
81
+ raise MuError.new "Failed to fetch object from role reference", details: t['role'].to_h if !role_obj
82
+ params[:role_arn] = role_obj.arn
83
+ end
84
+ [:input, :input_path, :input_transformer, :kinesis_parameters, :run_command_parameters, :batch_parameters, :sqs_parameters, :ecs_parameters].each { |attr|
85
+ if t[attr.to_s]
86
+ this_target[attr] = MU.structToHash(t[attr.to_s])
87
+ end
88
+ }
89
+ target_params << this_target
90
+ }
91
+ }
92
+ MU::Cloud::AWS.cloudwatchevents(region: @region, credentials: @credentials).put_targets(
93
+ rule: @cloud_id,
94
+ event_bus_name: cloud_desc.event_bus_name,
95
+ targets: target_params
96
+ )
97
+ end
98
+
99
+ end
100
+
101
+ # Canonical Amazon Resource Number for this resource
102
+ # @return [String]
103
+ def arn
104
+ cloud_desc ? cloud_desc.arn : nil
105
+ end
106
+
107
+ # Return the metadata for this job
108
+ # @return [Hash]
109
+ def notify
110
+ MU.structToHash(cloud_desc, stringify_keys: true)
111
+ end
112
+
113
+ # Does this resource type exist as a global (cloud-wide) artifact, or
114
+ # is it localized to a region/zone?
115
+ # @return [Boolean]
116
+ def self.isGlobal?
117
+ false
118
+ end
119
+
120
+ # Denote whether this resource implementation is experiment, ready for
121
+ # testing, or ready for production use.
122
+ def self.quality
123
+ MU::Cloud::BETA
124
+ end
125
+
126
+ # Remove all jobs associated with the currently loaded deployment.
127
+ # @param noop [Boolean]: If true, will only print what would be done
128
+ # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
129
+ # @param region [String]: The cloud provider region
130
+ # @return [void]
131
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
132
+ found = find(region: region, credentials: credentials)
133
+
134
+ found.each_pair { |id, desc|
135
+ if (desc.description and desc.description == deploy_id) or
136
+ (flags and flags['known'] and flags['known'].include?(id))
137
+ MU.log "Deleting CloudWatch Event #{id}"
138
+ if !noop
139
+ resp = MU::Cloud::AWS.cloudwatchevents(region: region, credentials: credentials).list_targets_by_rule(
140
+ rule: id,
141
+ event_bus_name: desc.event_bus_name,
142
+ )
143
+ if resp and resp.targets and !resp.targets.empty?
144
+ MU::Cloud::AWS.cloudwatchevents(region: region, credentials: credentials).remove_targets(
145
+ rule: id,
146
+ event_bus_name: desc.event_bus_name,
147
+ ids: resp.targets.map { |t| t.id }
148
+ )
149
+ end
150
+
151
+ MU::Cloud::AWS.cloudwatchevents(region: region, credentials: credentials).delete_rule(
152
+ name: id,
153
+ event_bus_name: desc.event_bus_name
154
+ )
155
+ end
156
+ end
157
+ }
158
+ end
159
+
160
+ # Locate an existing event.
161
+ # @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching CloudWatch Event
162
+ def self.find(**args)
163
+ found = {}
164
+
165
+ MU::Cloud::AWS.cloudwatchevents(region: args[:region], credentials: args[:credentials]).list_rules.rules.each { |r|
166
+ next if args[:cloud_id] and ![r.name, r.arn].include?(args[:cloud_id])
167
+ found[r.name] = r
168
+ }
169
+
170
+ found
171
+ end
172
+
173
+ # Reverse-map our cloud description into a runnable config hash.
174
+ # We assume that any values we have in +@config+ are placeholders, and
175
+ # calculate our own accordingly based on what's live in the cloud.
176
+ def toKitten(**_args)
177
+ bok = {
178
+ "cloud" => "AWS",
179
+ "credentials" => @credentials,
180
+ "cloud_id" => @cloud_id,
181
+ "region" => @region
182
+ }
183
+
184
+ if !cloud_desc
185
+ MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
186
+ return nil
187
+ end
188
+ bok['name'] = cloud_desc.name
189
+ if cloud_desc.description and !cloud_desc.description.empty?
190
+ bok['description'] = cloud_desc.description
191
+ end
192
+
193
+ bok['disabled'] = true if cloud_desc.state == "DISABLED"
194
+
195
+ # schedule_expression="cron(15 6 * * ? *)"
196
+ if cloud_desc.schedule_expression
197
+ if cloud_desc.schedule_expression.match(/cron\((\S+) (\S+) (\S+) (\S+) (\S+) (\S+)\)/)
198
+ bok['schedule'] = {
199
+ "minute" => Regexp.last_match[1],
200
+ "hour" => Regexp.last_match[2],
201
+ "day_of_month" => Regexp.last_match[3],
202
+ "month" => Regexp.last_match[4],
203
+ "day_of_week" => Regexp.last_match[5],
204
+ "year" => Regexp.last_match[6]
205
+ }
206
+ else
207
+ MU.log "HALP", MU::ERR, details: cloud_desc.schedule_expression
208
+ end
209
+ end
210
+
211
+ if cloud_desc.role_arn
212
+ shortname = cloud_desc.role_arn.sub(/.*?role\/([^\/]+)$/, '\1')
213
+ bok['role'] = MU::Config::Ref.get(
214
+ id: shortname,
215
+ cloud: "AWS",
216
+ type: "roles"
217
+ )
218
+ end
219
+
220
+ targets = MU::Cloud::AWS.cloudwatchevents(region: @region, credentials: @credentials).list_targets_by_rule(
221
+ rule: @cloud_id,
222
+ event_bus_name: cloud_desc.event_bus_name
223
+ ).targets
224
+ targets.each { |t|
225
+ bok['targets'] ||= []
226
+ _arn, _plat, service, region, account, resource = t.arn.split(/:/, 6)
227
+ target_type = if service == "lambda"
228
+ resource.sub!(/^function:/, '')
229
+ "functions"
230
+ elsif service == "sns"
231
+ "notifiers"
232
+ elsif service == "sqs"
233
+ "msg_queues"
234
+ else
235
+ service
236
+ end
237
+ ref_params = {
238
+ id: resource,
239
+ region: region,
240
+ type: target_type,
241
+ cloud: "AWS",
242
+ credentials: @credentials,
243
+ habitat: MU::Config::Ref.get(
244
+ id: account,
245
+ cloud: "AWS",
246
+ credentials: @credentials
247
+ )
248
+ }
249
+ [:input, :input_path, :input_transformer, :kinesis_parameters, :run_command_parameters, :batch_parameters, :sqs_parameters].each { |attr|
250
+ if t.respond_to?(attr) and !t.send(attr).nil?
251
+ ref_params[attr] = MU.structToHash(t.send(attr), stringify_keys: true)
252
+ end
253
+ }
254
+
255
+ bok['targets'] << MU::Config::Ref.get(ref_params)
256
+ }
257
+
258
+ # XXX cloud_desc.event_pattern - what do we want to do with this?
259
+
260
+ bok
261
+ end
262
+
263
+
264
+ # Cloud-specific configuration properties.
265
+ # @param _config [MU::Config]: The calling MU::Config object
266
+ # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
267
+ def self.schema(_config)
268
+ toplevel_required = []
269
+
270
+ target_schema = MU::Config::Ref.schema(any_type: true, desc: "A resource which will be invoked by this event. Can be a reference to a sibling Mu resource, typically a +Function+ or +MsgQueue+, or to an unadorned external cloud resource.")
271
+ target_params = {
272
+ "role" => MU::Config::Ref.schema(type: "roles", desc: "A sibling {MU::Config::BasketofKittens::roles} entry or the id of an existing IAM role to assign to use when interacting with this target.", omit_fields: ["region", "tag"]),
273
+ "input" => {
274
+ "type" => "string"
275
+ },
276
+ "input_path" => {
277
+ "type" => "string"
278
+ },
279
+ "run_command_parameters" => {
280
+ "type" => "object",
281
+ "description" => "Parameters used when you are using the rule to invoke Amazon EC2 Run Command",
282
+ "required" => ["run_command_targets"],
283
+ "properties" => {
284
+ "run_command_targets" => {
285
+ "type" => "array",
286
+ "items" => {
287
+ "type" => "object",
288
+ "description" => "Currently, AWS supports including only one +run_command_targets+ block, which specifies either an array of InstanceIds or a tag.",
289
+ "required" => ["key", "values"],
290
+ "properties" => {
291
+ "key" => {
292
+ "type" => "string",
293
+ "description" => "Can be either +tag: tag-key+ or +InstanceIds+"
294
+ },
295
+ "values" => {
296
+ "type" => "array",
297
+ "items" => {
298
+ "description" => "If +key+ is +tag: tag-key+, +values+ is a list of tag values; if +key+ is +InstanceIds+, +values+ is a list of Amazon EC2 instance IDs.",
299
+ "type" => "string"
300
+ }
301
+ }
302
+ }
303
+ }
304
+ }
305
+ }
306
+ },
307
+ "input_transformer" => {
308
+ "type" => "object",
309
+ "description" => "Settings to enable you to provide custom input to a target based on certain event data. You can extract one or more key-value pairs from the event and then use that data to send customized input to the target.",
310
+ "required" => ["input_template"],
311
+ "properties" => {
312
+ "input_template" => {
313
+ "type" => "string",
314
+ "description" => "Input template where you specify placeholders that will be filled with the values of the keys from +input_paths_map+ to customize the data sent to the target."
315
+ },
316
+ "input_paths_map" => {
317
+ "type" => "object",
318
+ "description" => "Hash representing JSON paths to be extracted from the event"
319
+ }
320
+ }
321
+ },
322
+ "batch_parameters" => {
323
+ "type" => "object",
324
+ "description" => "If the event target is an AWS Batch job, this contains the job definition, job name, and other parameters. See: https://docs.aws.amazon.com/batch/latest/userguide/jobs.html",
325
+ "required" => ["job_definition", "job_name"],
326
+ "properties" => {
327
+ "job_definition" => {
328
+ "description" => "The ARN or name of the job definition to use if the event target is an AWS Batch job.",
329
+ "type" => "string"
330
+ },
331
+ "job_name" => {
332
+ "description" => "The name to use for this execution of the job, if the target is an AWS Batch job.",
333
+ "type" => "string"
334
+ },
335
+ "array_properties" => {
336
+ "type" => "object",
337
+ "description" => "The array properties for the submitted job, such as the size of the array.",
338
+ "properties" => {
339
+ "size" => {
340
+ "description" => "Size of the submitted array",
341
+ "type" => "integer"
342
+ }
343
+ }
344
+ },
345
+ "retry_strategy" => {
346
+ "type" => "object",
347
+ "description" => "The retry strategy to use for failed jobs, if the target is an AWS Batch job.",
348
+ "properties" => {
349
+ "attempts" => {
350
+ "description" => "Number of retry attempts, valid values from 1-10",
351
+ "type" => "integer"
352
+ }
353
+ }
354
+ }
355
+ }
356
+ },
357
+ "sqs_parameters" => {
358
+ "type" => "object",
359
+ "description" => "Contains the message group ID to use when the target is an SQS FIFO queue.",
360
+ "required" => ["message_group_id"],
361
+ "properties" => {
362
+ "message_group_id" => {
363
+ "type" => "string"
364
+ }
365
+ }
366
+ },
367
+ "kinesis_parameters" => {
368
+ "type" => "object",
369
+ "description" => "The custom parameter you can use to control the shard assignment, when the target is a Kinesis data stream.",
370
+ "required" => ["partition_key_path"],
371
+ "properties" => {
372
+ "partition_key_path" => {
373
+ "type" => "string"
374
+ }
375
+ }
376
+ },
377
+ "http_parameters" => {
378
+ "type" => "object",
379
+ "description" => "Contains the HTTP parameters to use when the target is a API Gateway REST endpoint.",
380
+ "properties" => {
381
+ "path_parameter_values" => {
382
+ "type" => "array",
383
+ "items" => {
384
+ "description" => "The path parameter values to be used to populate API Gateway REST API path wildcards (\"*\").",
385
+ "type" => "string"
386
+ }
387
+ },
388
+ "header_parameters" => {
389
+ "description" => "Key => value pairs to pass as headers",
390
+ "type" => "object"
391
+ },
392
+ "query_string_parameters" => {
393
+ "description" => "Key => value pairs to pass as query strings",
394
+ "type" => "object"
395
+ }
396
+ }
397
+ }
398
+ }
399
+ target_schema["properties"].merge!(target_params)
400
+
401
+ schema = {
402
+ "disabled" => {
403
+ "type" => "boolean",
404
+ "description" => "Leave this job in place but disabled",
405
+ "default" => false
406
+ },
407
+ "role" => MU::Config::Ref.schema(type: "roles", desc: "A sibling {MU::Config::BasketofKittens::roles} entry or the id of an existing IAM role to assign to this CloudWatch Event.", omit_fields: ["region", "tag"]),
408
+ "targets" => {
409
+ "type" => "array",
410
+ "items" => target_schema
411
+ }
412
+ }
413
+ [toplevel_required, schema]
414
+ end
415
+
416
+ # Cloud-specific pre-processing of {MU::Config::BasketofKittens::jobs}, bare and unvalidated.
417
+ # @param job [Hash]: The resource to process and validate
418
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
419
+ # @return [Boolean]: True if validation succeeded, False otherwise
420
+ def self.validateConfig(job, _configurator)
421
+ ok = true
422
+
423
+ job['targets'].each { |t|
424
+ target_ref = MU::Config::Ref.get(t)
425
+ if target_ref.is_mu_type? and target_ref.name
426
+ MU::Config.addDependency(job, target_ref.name, target_ref.type)
427
+ end
428
+ }
429
+
430
+ ok
431
+ end
432
+
433
+ private
434
+
435
+ def get_properties
436
+ params = {
437
+ name: @cloud_id,
438
+ state: @config['disabled'] ? "DISABLED" : "ENABLED",
439
+ event_bus_name: "default" # XXX expose, or create a deploy-specific one?
440
+ }
441
+
442
+ params[:description] = if @config['description'] and @config['scrub_mu_isms']
443
+ @config['description']
444
+ else
445
+ @deploy.deploy_id
446
+ end
447
+
448
+ if @tags
449
+ params[:tags] = @tags.each_key.map { |k| { :key => k, :value => @tags[k] } }
450
+ end
451
+
452
+ if @config['role']
453
+ role_obj = MU::Config::Ref.get(@config['role']).kitten(@deploy, cloud: "AWS")
454
+ raise MuError.new "Failed to fetch object from role reference", details: @config['role'].to_h if !role_obj
455
+ params[:role_arn] = role_obj.arn
456
+ end
457
+
458
+ if @config['schedule']
459
+ params[:schedule_expression] = "cron(" + ["minute", "hour", "day_of_month", "month", "day_of_week", "year"].map { |i| @config['schedule'][i] }.join(" ") +")"
460
+ end
461
+
462
+
463
+ params
464
+ end
465
+
466
+ end
467
+ end
468
+ end
469
+ end