cloud-mu 1.9.0.pre.beta → 2.0.0.pre.alpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/Berksfile +16 -54
  3. data/Berksfile.lock +14 -62
  4. data/bin/mu-aws-setup +131 -108
  5. data/bin/mu-configure +311 -74
  6. data/bin/mu-gcp-setup +84 -62
  7. data/bin/mu-load-config.rb +46 -2
  8. data/bin/mu-self-update +11 -9
  9. data/bin/mu-upload-chef-artifacts +4 -4
  10. data/{mu.gemspec → cloud-mu.gemspec} +2 -2
  11. data/cookbooks/awscli/Berksfile +8 -0
  12. data/cookbooks/mu-activedirectory/Berksfile +11 -0
  13. data/cookbooks/mu-firewall/Berksfile +9 -0
  14. data/cookbooks/mu-firewall/metadata.rb +1 -1
  15. data/cookbooks/mu-glusterfs/Berksfile +10 -0
  16. data/cookbooks/mu-jenkins/Berksfile +14 -0
  17. data/cookbooks/mu-master/Berksfile +23 -0
  18. data/cookbooks/mu-master/attributes/default.rb +1 -1
  19. data/cookbooks/mu-master/metadata.rb +2 -2
  20. data/cookbooks/mu-master/recipes/default.rb +1 -1
  21. data/cookbooks/mu-master/recipes/init.rb +7 -3
  22. data/cookbooks/mu-master/recipes/ssl-certs.rb +1 -0
  23. data/cookbooks/mu-mongo/Berksfile +10 -0
  24. data/cookbooks/mu-openvpn/Berksfile +11 -0
  25. data/cookbooks/mu-php54/Berksfile +13 -0
  26. data/cookbooks/mu-splunk/Berksfile +10 -0
  27. data/cookbooks/mu-tools/Berksfile +21 -0
  28. data/cookbooks/mu-tools/files/default/Mu_CA.pem +15 -15
  29. data/cookbooks/mu-utility/Berksfile +9 -0
  30. data/cookbooks/mu-utility/metadata.rb +2 -1
  31. data/cookbooks/nagios/Berksfile +7 -4
  32. data/cookbooks/s3fs/Berksfile +9 -0
  33. data/environments/dev.json +6 -6
  34. data/environments/prod.json +6 -6
  35. data/modules/mu.rb +20 -42
  36. data/modules/mu/cleanup.rb +102 -100
  37. data/modules/mu/cloud.rb +90 -28
  38. data/modules/mu/clouds/aws.rb +449 -218
  39. data/modules/mu/clouds/aws/alarm.rb +29 -17
  40. data/modules/mu/clouds/aws/cache_cluster.rb +78 -64
  41. data/modules/mu/clouds/aws/collection.rb +25 -18
  42. data/modules/mu/clouds/aws/container_cluster.rb +73 -66
  43. data/modules/mu/clouds/aws/database.rb +124 -116
  44. data/modules/mu/clouds/aws/dnszone.rb +27 -20
  45. data/modules/mu/clouds/aws/firewall_rule.rb +30 -22
  46. data/modules/mu/clouds/aws/folder.rb +18 -3
  47. data/modules/mu/clouds/aws/function.rb +77 -23
  48. data/modules/mu/clouds/aws/group.rb +19 -12
  49. data/modules/mu/clouds/aws/habitat.rb +153 -0
  50. data/modules/mu/clouds/aws/loadbalancer.rb +59 -52
  51. data/modules/mu/clouds/aws/log.rb +30 -23
  52. data/modules/mu/clouds/aws/msg_queue.rb +29 -20
  53. data/modules/mu/clouds/aws/notifier.rb +222 -0
  54. data/modules/mu/clouds/aws/role.rb +178 -90
  55. data/modules/mu/clouds/aws/search_domain.rb +40 -24
  56. data/modules/mu/clouds/aws/server.rb +169 -137
  57. data/modules/mu/clouds/aws/server_pool.rb +60 -83
  58. data/modules/mu/clouds/aws/storage_pool.rb +59 -31
  59. data/modules/mu/clouds/aws/user.rb +36 -27
  60. data/modules/mu/clouds/aws/userdata/linux.erb +101 -93
  61. data/modules/mu/clouds/aws/vpc.rb +250 -189
  62. data/modules/mu/clouds/azure.rb +132 -0
  63. data/modules/mu/clouds/cloudformation.rb +65 -1
  64. data/modules/mu/clouds/cloudformation/alarm.rb +8 -0
  65. data/modules/mu/clouds/cloudformation/cache_cluster.rb +7 -0
  66. data/modules/mu/clouds/cloudformation/collection.rb +7 -0
  67. data/modules/mu/clouds/cloudformation/database.rb +7 -0
  68. data/modules/mu/clouds/cloudformation/dnszone.rb +7 -0
  69. data/modules/mu/clouds/cloudformation/firewall_rule.rb +9 -2
  70. data/modules/mu/clouds/cloudformation/loadbalancer.rb +7 -0
  71. data/modules/mu/clouds/cloudformation/log.rb +7 -0
  72. data/modules/mu/clouds/cloudformation/server.rb +7 -0
  73. data/modules/mu/clouds/cloudformation/server_pool.rb +7 -0
  74. data/modules/mu/clouds/cloudformation/vpc.rb +7 -0
  75. data/modules/mu/clouds/google.rb +214 -110
  76. data/modules/mu/clouds/google/container_cluster.rb +42 -24
  77. data/modules/mu/clouds/google/database.rb +15 -6
  78. data/modules/mu/clouds/google/firewall_rule.rb +17 -25
  79. data/modules/mu/clouds/google/group.rb +13 -5
  80. data/modules/mu/clouds/google/habitat.rb +105 -0
  81. data/modules/mu/clouds/google/loadbalancer.rb +28 -20
  82. data/modules/mu/clouds/google/server.rb +93 -354
  83. data/modules/mu/clouds/google/server_pool.rb +18 -10
  84. data/modules/mu/clouds/google/user.rb +22 -14
  85. data/modules/mu/clouds/google/vpc.rb +97 -69
  86. data/modules/mu/config.rb +133 -38
  87. data/modules/mu/config/alarm.rb +25 -0
  88. data/modules/mu/config/cache_cluster.rb +5 -3
  89. data/modules/mu/config/cache_cluster.yml +23 -0
  90. data/modules/mu/config/database.rb +25 -16
  91. data/modules/mu/config/database.yml +3 -3
  92. data/modules/mu/config/function.rb +1 -2
  93. data/modules/mu/config/{project.rb → habitat.rb} +10 -10
  94. data/modules/mu/config/notifier.rb +85 -0
  95. data/modules/mu/config/notifier.yml +9 -0
  96. data/modules/mu/config/role.rb +1 -1
  97. data/modules/mu/config/search_domain.yml +2 -2
  98. data/modules/mu/config/server.rb +13 -1
  99. data/modules/mu/config/server.yml +3 -3
  100. data/modules/mu/config/server_pool.rb +3 -1
  101. data/modules/mu/config/storage_pool.rb +3 -1
  102. data/modules/mu/config/storage_pool.yml +19 -0
  103. data/modules/mu/config/vpc.rb +70 -8
  104. data/modules/mu/groomers/chef.rb +2 -3
  105. data/modules/mu/kittens.rb +500 -122
  106. data/modules/mu/master.rb +5 -5
  107. data/modules/mu/mommacat.rb +151 -91
  108. data/modules/tests/super_complex_bok.yml +12 -0
  109. data/modules/tests/super_simple_bok.yml +12 -0
  110. data/spec/mu/clouds/azure_spec.rb +82 -0
  111. data/spec/spec_helper.rb +105 -0
  112. metadata +26 -5
  113. data/modules/mu/clouds/aws/notification.rb +0 -139
  114. data/modules/mu/config/notification.rb +0 -44
@@ -38,7 +38,7 @@ module MU
38
38
  @config["log_group_name"] = @mu_name
39
39
  @config["log_stream_name"] =
40
40
  if @config["enable_cloudtrail_logging"]
41
- "#{MU.account_number}_CloudTrail_#{@config["region"]}"
41
+ "#{MU::Cloud::AWS.credToAcct(@config['credentials'])}_CloudTrail_#{@config["region"]}"
42
42
  else
43
43
  @mu_name
44
44
  end
@@ -56,7 +56,7 @@ module MU
56
56
  end
57
57
 
58
58
  MU.log "Creating log group #{@mu_name}"
59
- MU::Cloud::AWS.cloudwatchlogs(@config["region"]).create_log_group(
59
+ MU::Cloud::AWS.cloudwatchlogs(region: @config["region"], credentials: @config["credentials"]).create_log_group(
60
60
  log_group_name: @config["log_group_name"],
61
61
  tags: tags
62
62
  )
@@ -76,19 +76,19 @@ module MU
76
76
  end
77
77
  end while resp.nil?
78
78
 
79
- MU::Cloud::AWS.cloudwatchlogs(@config["region"]).create_log_stream(
79
+ MU::Cloud::AWS.cloudwatchlogs(region: @config["region"], credentials: @config["credentials"]).create_log_stream(
80
80
  log_group_name: @config["log_group_name"],
81
81
  log_stream_name: @config["log_stream_name"]
82
82
  )
83
83
 
84
- MU::Cloud::AWS.cloudwatchlogs(@config["region"]).put_retention_policy(
84
+ MU::Cloud::AWS.cloudwatchlogs(region: @config["region"], credentials: @config["credentials"]).put_retention_policy(
85
85
  log_group_name: @config["log_group_name"],
86
86
  retention_in_days: @config["retention_period"]
87
87
  )
88
88
 
89
89
  if @config["filters"] && !@config["filters"].empty?
90
90
  @config["filters"].each{ |filter|
91
- MU::Cloud::AWS.cloudwatchlogs(@config["region"]).put_metric_filter(
91
+ MU::Cloud::AWS.cloudwatchlogs(region: @config["region"], credentials: @config["credentials"]).put_metric_filter(
92
92
  log_group_name: @config["log_group_name"],
93
93
  filter_name: filter["name"],
94
94
  filter_pattern: filter["search_pattern"],
@@ -102,8 +102,8 @@ module MU
102
102
  end
103
103
 
104
104
  if @config["enable_cloudtrail_logging"]
105
- trail_resp = MU::Cloud::AWS.cloudtrail(@config["region"]).describe_trails.trail_list.first
106
- raise MuError, "Can't find a cloudtrail in #{MU.account_number}/#{@config["region"]}. Please create cloudtrail before enabling logging on it" unless trail_resp
105
+ trail_resp = MU::Cloud::AWS.cloudtrail(region: @config["region"], credentials: @config["credentials"]).describe_trails.trail_list.first
106
+ raise MuError, "Can't find a cloudtrail in #{MU::Cloud::AWS.credToAcct(@config['credentials'])}/#{@config["region"]}. Please create cloudtrail before enabling logging on it" unless trail_resp
107
107
 
108
108
  iam_policy = '{
109
109
  "Version": "2012-10-17",
@@ -116,7 +116,7 @@ module MU
116
116
  "logs:PutLogEventsBatch",
117
117
  "logs:PutLogEvents"
118
118
  ],
119
- "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+':logs:'+@config["region"]+':'+MU.account_number+':log-group:'+@config["log_group_name"]+':log-stream:'+@config["log_stream_name"]+'*"
119
+ "Resource": "arn:'+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+':logs:'+@config["region"]+':'+MU::Cloud::AWS.credToAcct(@config['credentials'])+':log-group:'+@config["log_group_name"]+':log-stream:'+@config["log_stream_name"]+'*"
120
120
  }
121
121
  ]
122
122
  }'
@@ -141,12 +141,12 @@ module MU
141
141
 
142
142
  iam_role_name = "#{@mu_name}-CloudTrail"
143
143
  MU.log "Creating IAM role #{iam_role_name}"
144
- iam_resp = MU::Cloud::AWS.iam(@config["region"]).create_role(
144
+ iam_resp = MU::Cloud::AWS.iam.create_role(
145
145
  role_name: iam_role_name,
146
146
  assume_role_policy_document: iam_assume_role_policy
147
147
  )
148
148
 
149
- MU::Cloud::AWS.iam(@config["region"]).put_role_policy(
149
+ MU::Cloud::AWS.iam.put_role_policy(
150
150
  role_name: iam_role_name,
151
151
  policy_name: "CloudTrail_CloudWatchLogs",
152
152
  policy_document: iam_policy
@@ -156,7 +156,7 @@ module MU
156
156
 
157
157
  retries = 0
158
158
  begin
159
- MU::Cloud::AWS.cloudtrail(@config["region"]).update_trail(
159
+ MU::Cloud::AWS.cloudtrail(region: @config["region"], credentials: @config["credentials"]).update_trail(
160
160
  name: trail_resp.name,
161
161
  cloud_watch_logs_log_group_arn: log_group_resp.arn,
162
162
  cloud_watch_logs_role_arn: iam_resp.role.arn
@@ -183,7 +183,7 @@ module MU
183
183
  prettyname = service.sub(/\..*/, "").capitalize
184
184
  doc = '{ "Version": "2012-10-17", "Statement": [ { "Sid": "'+prettyname+'LogsToCloudWatchLogs", "Effect": "Allow", "Principal": { "Service": [ "'+service+'" ] }, "Action": [ "logs:PutLogEvents", "logs:PutLogEventsBatch", "logs:CreateLogStream" ], "Resource": "'+log_arn+'" } ] }'
185
185
 
186
- MU::Cloud::AWS.cloudwatchlogs(region).put_resource_policy(
186
+ MU::Cloud::AWS.cloudwatchlogs(region: region).put_resource_policy(
187
187
  policy_name: "Allow"+prettyname,
188
188
  policy_document: doc
189
189
  )
@@ -209,15 +209,22 @@ module MU
209
209
  }
210
210
  end
211
211
 
212
+ # Does this resource type exist as a global (cloud-wide) artifact, or
213
+ # is it localized to a region/zone?
214
+ # @return [Boolean]
215
+ def self.isGlobal?
216
+ false
217
+ end
218
+
212
219
  # Remove all logs associated with the currently loaded deployment.
213
220
  # @param noop [Boolean]: If true, will only print what would be done
214
221
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
215
222
  # @param region [String]: The cloud provider region
216
223
  # @return [void]
217
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, flags: {})
224
+ def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
218
225
  log_groups =
219
226
  begin
220
- MU::Cloud::AWS.cloudwatchlogs(region).describe_log_groups.log_groups
227
+ MU::Cloud::AWS.cloudwatchlogs(credentials: credentials, region: region).describe_log_groups.log_groups
221
228
  # TO DO: Why is it returning UnknownOperationException instead of valid error?
222
229
  rescue Aws::CloudWatchLogs::Errors::UnknownOperationException => e
223
230
  MU.log e.inspect
@@ -227,10 +234,10 @@ module MU
227
234
  if !log_groups.empty?
228
235
  log_groups.each{ |lg|
229
236
  if lg.log_group_name.match(MU.deploy_id)
230
- log_streams = MU::Cloud::AWS.cloudwatchlogs(region).describe_log_streams(log_group_name: lg.log_group_name).log_streams
237
+ log_streams = MU::Cloud::AWS.cloudwatchlogs(credentials: credentials, region: region).describe_log_streams(log_group_name: lg.log_group_name).log_streams
231
238
  if !log_streams.empty?
232
239
  log_streams.each{ |ls|
233
- MU::Cloud::AWS.cloudwatchlogs(region).delete_log_stream(
240
+ MU::Cloud::AWS.cloudwatchlogs(credentials: credentials, region: region).delete_log_stream(
234
241
  log_group_name: lg.log_group_name,
235
242
  log_stream_name: ls.log_stream_name
236
243
  ) unless noop
@@ -239,7 +246,7 @@ module MU
239
246
  }
240
247
  end
241
248
 
242
- MU::Cloud::AWS.cloudwatchlogs(region).delete_log_group(
249
+ MU::Cloud::AWS.cloudwatchlogs(credentials: credentials, region: region).delete_log_group(
243
250
  log_group_name: lg.log_group_name
244
251
  ) unless noop
245
252
  MU.log "Deleted log group #{lg.log_group_name}"
@@ -248,7 +255,7 @@ module MU
248
255
  end
249
256
 
250
257
  unless noop
251
- MU::Cloud::AWS.iam.list_roles.roles.each{ |role|
258
+ MU::Cloud::AWS.iam(credentials: credentials).list_roles.roles.each{ |role|
252
259
  match_string = "#{MU.deploy_id}.*CloudTrail"
253
260
  # Maybe we should have a more generic way to delete IAM profiles and policies. The call itself should be moved from MU::Cloud::AWS::Server.
254
261
  # MU::Cloud::AWS::Server.removeIAMProfile(role.role_name) if role.role_name.match(match_string)
@@ -261,13 +268,13 @@ module MU
261
268
  # @param region [String]: The cloud provider region.
262
269
  # @param flags [Hash]: Optional flags
263
270
  # @return [OpenStruct]: The cloud provider's complete descriptions of matching log group.
264
- def self.find(cloud_id: nil, region: MU.curRegion, flags: {})
271
+ def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {})
265
272
  found = nil
266
273
  if !cloud_id.nil? and !cloud_id.match(/^arn:/i)
267
274
  found ||= {}
268
- found[cloud_id] = MU::Cloud::AWS::Log.getLogGroupByName(cloud_id, region: region)
275
+ found[cloud_id] = MU::Cloud::AWS::Log.getLogGroupByName(cloud_id, region: region, credentials: nil)
269
276
  else
270
- resp = MU::Cloud::AWS.cloudwatchlogs(region).describe_log_groups.log_groups.each { |group|
277
+ resp = MU::Cloud::AWS.cloudwatchlogs(region: region, credentials: credentials).describe_log_groups.log_groups.each { |group|
271
278
  if group.arn == cloud_id or group.arn.sub(/:\*$/, "") == cloud_id
272
279
  found ||= {}
273
280
  found[group.log_group_name] = group
@@ -354,8 +361,8 @@ module MU
354
361
  # @param name [String]: The cloud provider's identifier for this log group.
355
362
  # @param region [String]: The cloud provider region
356
363
  # @return [OpenStruct]
357
- def self.getLogGroupByName(name, region: MU.curRegion)
358
- MU::Cloud::AWS.cloudwatchlogs(region).describe_log_groups(log_group_name_prefix: name).log_groups.first
364
+ def self.getLogGroupByName(name, region: MU.curRegion, credentials: nil)
365
+ MU::Cloud::AWS.cloudwatchlogs(region: region, credentials: credentials).describe_log_groups(log_group_name_prefix: name).log_groups.first
359
366
  end
360
367
  end
361
368
  end
@@ -48,7 +48,7 @@ module MU
48
48
  namestr += ".fifo" if attrs['FifoQueue']
49
49
 
50
50
  MU.log "Creating SQS queue #{namestr}", details: attrs
51
- resp = MU::Cloud::AWS.sqs(@config['region']).create_queue(
51
+ resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).create_queue(
52
52
  queue_name: namestr,
53
53
  attributes: attrs
54
54
  )
@@ -75,7 +75,7 @@ module MU
75
75
  }
76
76
  if changed
77
77
  MU.log "Updating SQS queue #{@mu_name}", MU::NOTICE, details: new_attrs
78
- resp = MU::Cloud::AWS.sqs(@config['region']).set_queue_attributes(
78
+ resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).set_queue_attributes(
79
79
  queue_url: @cloud_id,
80
80
  attributes: new_attrs
81
81
  )
@@ -86,7 +86,7 @@ module MU
86
86
  # Canonical Amazon Resource Number for this resource
87
87
  # @return [String]
88
88
  def arn
89
- "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":sqs:"+@config['region']+":"+MU.account_number+":"+@cloud_id
89
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":sqs:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":"+@cloud_id
90
90
  end
91
91
 
92
92
  # Retrieve the AWS descriptor for this SQS queue. AWS doesn't exactly
@@ -94,7 +94,7 @@ module MU
94
94
  # @return [Hash]: AWS doesn't return anything but the SQS URL, so supplement with attributes
95
95
  def cloud_desc
96
96
  if !@cloud_id
97
- resp = MU::Cloud::AWS.sqs(@config['region']).list_queues(
97
+ resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).list_queues(
98
98
  queue_name_prefix: @mu_name
99
99
  )
100
100
  return nil if !resp or !resp.queue_urls
@@ -109,7 +109,8 @@ module MU
109
109
  return nil if !@cloud_id
110
110
  MU::Cloud::AWS::MsgQueue.find(
111
111
  cloud_id: @cloud_id.dup,
112
- region: @config['region']
112
+ region: @config['region'],
113
+ credentials: @config['credentials']
113
114
  )
114
115
  end
115
116
 
@@ -119,18 +120,26 @@ module MU
119
120
  cloud_desc
120
121
  deploy_struct = MU::Cloud::AWS::MsgQueue.find(
121
122
  cloud_id: @cloud_id,
122
- region: @config['region']
123
+ region: @config['region'],
124
+ credentials: @config['credentials']
123
125
  )
124
126
  return deploy_struct
125
127
  end
126
128
 
129
+ # Does this resource type exist as a global (cloud-wide) artifact, or
130
+ # is it localized to a region/zone?
131
+ # @return [Boolean]
132
+ def self.isGlobal?
133
+ false
134
+ end
135
+
127
136
  # Remove all msg_queues associated with the currently loaded deployment.
128
137
  # @param noop [Boolean]: If true, will only print what would be done
129
138
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
130
139
  # @param region [String]: The cloud provider region
131
140
  # @return [void]
132
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, flags: {})
133
- resp = MU::Cloud::AWS.sqs(region).list_queues(
141
+ def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
142
+ resp = MU::Cloud::AWS.sqs(credentials: credentials, region: region).list_queues(
134
143
  queue_name_prefix: MU.deploy_id
135
144
  )
136
145
  if resp and resp.queue_urls
@@ -139,7 +148,7 @@ module MU
139
148
  threads << Thread.new {
140
149
  MU.log "Deleting SQS queue #{url}"
141
150
  if !noop
142
- MU::Cloud::AWS.sqs(region).delete_queue(
151
+ MU::Cloud::AWS.sqs(credentials: credentials, region: region).delete_queue(
143
152
  queue_url: url
144
153
  )
145
154
  sleep 60 # per API docs, this is how long it takes to really delete
@@ -157,14 +166,14 @@ module MU
157
166
  # @param region [String]: The cloud provider region.
158
167
  # @param flags [Hash]: Optional flags
159
168
  # @return [Hash]: AWS doesn't return anything but the SQS URL, so supplement with attributes
160
- def self.find(cloud_id: nil, region: MU.curRegion, flags: {})
169
+ def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {})
161
170
  flags['account'] ||= MU.account_number
162
171
  return nil if !cloud_id
163
172
 
164
173
  # If it's a URL, make sure it's good
165
174
  begin
166
175
  if cloud_id.match(/^https?:/i)
167
- resp = MU::Cloud::AWS.sqs(region).get_queue_attributes(
176
+ resp = MU::Cloud::AWS.sqs(region: region, credentials: credentials).get_queue_attributes(
168
177
  queue_url: cloud_id,
169
178
  attribute_names: ["All"]
170
179
  )
@@ -175,7 +184,7 @@ module MU
175
184
  end
176
185
  else
177
186
  # If it's a plain queue name, resolve it to a URL
178
- resp = MU::Cloud::AWS.sqs(region).get_queue_url(
187
+ resp = MU::Cloud::AWS.sqs(region: region, credentials: credentials).get_queue_url(
179
188
  queue_name: cloud_id,
180
189
  queue_owner_aws_account_id: flags['account']
181
190
  )
@@ -186,7 +195,7 @@ module MU
186
195
 
187
196
  # Go fetch its attributes
188
197
  if cloud_id
189
- resp = MU::Cloud::AWS.sqs(region).get_queue_attributes(
198
+ resp = MU::Cloud::AWS.sqs(region: region, credentials: credentials).get_queue_attributes(
190
199
  queue_url: cloud_id,
191
200
  attribute_names: ["All"]
192
201
  )
@@ -372,7 +381,7 @@ MU.log "RETURNING FROM FIND ON #{cloud_id}", MU::WARN, details: caller
372
381
  ok = false
373
382
  end
374
383
  begin
375
- MU::Cloud::AWS.kms(queue['region']).describe_key(key_id: queue['kms']['key_id'])
384
+ MU::Cloud::AWS.kms(region: queue['region']).describe_key(key_id: queue['kms']['key_id'])
376
385
  rescue Aws::KMS::Errors::NotFoundException => e
377
386
  MU.log "KMS key '#{queue['kms']['key_id']}' specified in Queue '#{queue['name']}' was not found.", MU::ERR, details: "Key IDs are of the form bf64a093-2c3d-46fa-0d4f-8232fa7ed53. Keys can be created at https://console.aws.amazon.com/iam/home#/encryptionKeys/#{queue['region']}"
378
387
  ok = false
@@ -402,18 +411,18 @@ MU.log "RETURNING FROM FIND ON #{cloud_id}", MU::WARN, details: caller
402
411
  }
403
412
 
404
413
  if @config['failqueue']
405
- sibling = @deploy.findLitterMate(type: "msg_queue", name: config['failqueue']['name'])
406
- id = config['failqueue']['name']
414
+ sibling = @deploy.findLitterMate(type: "msg_queue", name: @config['failqueue']['name'])
415
+ id = @config['failqueue']['name']
407
416
  if sibling # resolve sibling queues to something useful
408
417
  id = sibling.cloud_id
409
418
  end
410
- desc = MU::Cloud::AWS::MsgQueue.find(cloud_id: id)
419
+ desc = MU::Cloud::AWS::MsgQueue.find(cloud_id: id, credentials: @config['credentials'])
411
420
  if !desc
412
- raise MuError, "Failed to get cloud descriptor for SQS queue #{config['failqueue']['name']}"
421
+ raise MuError, "Failed to get cloud descriptor for SQS queue #{@config['failqueue']['name']}"
413
422
  end
414
423
  rdr_pol = {
415
424
  "deadLetterTargetArn" => desc["QueueArn"],
416
- "maxReceiveCount" => config['failqueue']['retries_before_fail']
425
+ "maxReceiveCount" => @config['failqueue']['retries_before_fail']
417
426
  }
418
427
  attrs["RedrivePolicy"] = JSON.generate(rdr_pol)
419
428
  end
@@ -465,7 +474,7 @@ MU.log "RETURNING FROM FIND ON #{cloud_id}", MU::WARN, details: caller
465
474
  end
466
475
 
467
476
  begin
468
- MU::Cloud::AWS.sqs(@config['region']).tag_queue(
477
+ MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).tag_queue(
469
478
  queue_url: url,
470
479
  tags: tags
471
480
  )
@@ -0,0 +1,222 @@
1
+ # Copyright:: Copyright (c) 2014 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
+ # Support for AWS SNS
19
+ class Notifier < MU::Cloud::Notifier
20
+ @deploy = nil
21
+ @config = nil
22
+
23
+ attr_reader :mu_name
24
+ attr_reader :config
25
+ attr_reader :cloud_id
26
+
27
+ # @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
28
+ # @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::logs}
29
+ def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
30
+ @deploy = mommacat
31
+ @config = MU::Config.manxify(kitten_cfg)
32
+ @cloud_id ||= cloud_id
33
+ @mu_name ||= @deploy.getResourceName(@config["name"])
34
+ end
35
+
36
+ # Called automatically by {MU::Deploy#createResources}
37
+ def create
38
+ MU::Cloud::AWS.sns(region: @config['region'], credentials: @config['credentials']).create_topic(name: @mu_name)
39
+ @cloud_id = @mu_name
40
+ MU.log "Created SNS topic #{@mu_name}"
41
+ end
42
+
43
+ # Called automatically by {MU::Deploy#createResources}
44
+ def groom
45
+ if @config['subscriptions']
46
+ @config['subscriptions'].each { |sub|
47
+ MU::Cloud::AWS::Notifier.subscribe(
48
+ arn: arn,
49
+ endpoint: sub['endpoint'],
50
+ region: @config['region'],
51
+ credentials: @config['credentials'],
52
+ protocol: sub['type']
53
+ )
54
+ }
55
+ end
56
+ end
57
+
58
+ # Does this resource type exist as a global (cloud-wide) artifact, or
59
+ # is it localized to a region/zone?
60
+ # @return [Boolean]
61
+ def self.isGlobal?
62
+ false
63
+ end
64
+
65
+ # Remove all notifiers associated with the currently loaded deployment.
66
+ # @param noop [Boolean]: If true, will only print what would be done
67
+ # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
68
+ # @param region [String]: The cloud provider region
69
+ # @return [void]
70
+ def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
71
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).list_topics.topics.each { |topic|
72
+ if topic.topic_arn.match(MU.deploy_id)
73
+ # We don't have a way to tag our SNS topics, so we will delete any topic that has the MU-ID in its ARN.
74
+ # This may fail to find notifier groups in some cases (eg. cache_cluster) so we might want to delete from each API as well.
75
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).delete_topic(topic_arn: topic.topic_arn)
76
+ MU.log "Deleted SNS topic: #{topic.topic_arn}"
77
+ end
78
+ }
79
+ end
80
+
81
+ # Canonical Amazon Resource Number for this resource
82
+ # @return [String]
83
+ def arn
84
+ "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":sns:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":"+@cloud_id
85
+ end
86
+
87
+ # Return the metadata for this user cofiguration
88
+ # @return [Hash]
89
+ def notify
90
+ desc = MU::Cloud::AWS.sns(region: @config["region"], credentials: @config["credentials"]).get_topic_attributes(topic_arn: arn).attributes
91
+ MU.structToHash(desc)
92
+ end
93
+
94
+ # Locate an existing notifier.
95
+ # @param cloud_id [String]: The cloud provider's identifier for this resource.
96
+ # @param region [String]: The cloud provider region.
97
+ # @param flags [Hash]: Optional flags
98
+ # @return [OpenStruct]: The cloud provider's complete descriptions of matching notifier.
99
+ def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {})
100
+ found = {}
101
+ if cloud_id
102
+ arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws")+":sns:"+region+":"+MU::Cloud::AWS.credToAcct(credentials)+":"+cloud_id
103
+ desc = MU::Cloud::AWS.sns(region: region, credentials: credentials).get_topic_attributes(topic_arn: arn).attributes
104
+ found[cloud_id] = desc if desc
105
+ end
106
+ found
107
+ end
108
+
109
+ # Cloud-specific configuration properties.
110
+ # @param config [MU::Config]: The calling MU::Config object
111
+ # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
112
+ def self.schema(config)
113
+ toplevel_required = []
114
+ schema = {
115
+ "subscriptions" => {
116
+ "type" => "array",
117
+ "items" => {
118
+ "type" => "object",
119
+ "required" => ["endpoint"],
120
+ "properties" => {
121
+ "type" => {
122
+ "type" => "string",
123
+ "description" => "",
124
+ "enum" => ["http", "https", "email", "email-json", "sms", "sqs", "application", "lambda"]
125
+ }
126
+ }
127
+ }
128
+ }
129
+
130
+ }
131
+ [toplevel_required, schema]
132
+ end
133
+
134
+ # Cloud-specific pre-processing of {MU::Config::BasketofKittens::notifier}, bare and unvalidated.
135
+
136
+ # @param notifier [Hash]: The resource to process and validate
137
+ # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
138
+ # @return [Boolean]: True if validation succeeded, False otherwise
139
+ def self.validateConfig(notifier, configurator)
140
+ ok = true
141
+
142
+ if notifier['subscriptions']
143
+ notifier['subscriptions'].each { |sub|
144
+ if !sub["type"]
145
+ if sub["endpoint"].match(/^http:/i)
146
+ sub["type"] = "http"
147
+ elsif sub["endpoint"].match(/^https:/i)
148
+ sub["type"] = "https"
149
+ elsif sub["endpoint"].match(/^sqs:/i)
150
+ sub["type"] = "sqs"
151
+ elsif sub["endpoint"].match(/^\+?[\d\-]+$/)
152
+ sub["type"] = "sms"
153
+ elsif sub["endpoint"].match(/\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i)
154
+ sub["type"] = "email"
155
+ else
156
+ MU.log "Notifier #{notifier['name']} subscription #{sub['endpoint']} did not specify a type, and I'm unable to guess one", MU::ERR
157
+ ok = false
158
+ end
159
+ end
160
+ }
161
+ end
162
+
163
+ ok
164
+ end
165
+
166
+
167
+ # Subscribe to a notifier group. This can either be an email address, SQS queue, application endpoint, etc...
168
+ # Will create the subscription only if it doesn't already exist.
169
+ # @param arn [String]: The cloud provider's identifier of the notifier group.
170
+ # @param protocol [String]: The type of the subscription (eg. email,https, etc..).
171
+ # @param endpoint [String]: The endpoint of the subscription. This will depend on the 'protocol' (as an example if protocol is email, endpoint will be the email address) ..
172
+ # @param region [String]: The cloud provider region.
173
+ def self.subscribe(arn: nil, protocol: nil, endpoint: nil, region: MU.curRegion, credentials: nil)
174
+ retries = 0
175
+ begin
176
+ resp = MU::Cloud::AWS.sns(region: region, credentials: credentials).list_subscriptions_by_topic(topic_arn: arn).subscriptions
177
+ rescue Aws::SNS::Errors::NotFound
178
+ if retries < 5
179
+ MU.log "Couldn't find topic #{arn}, retrying several times in case of a lagging resource"
180
+ retries += 1
181
+ sleep 30
182
+ retry
183
+ else
184
+ raise MuError, "Couldn't find topic #{arn}, giving up"
185
+ end
186
+ end
187
+
188
+ already_subscribed = false
189
+ if resp && !resp.empty?
190
+ resp.each { |subscription|
191
+ already_subscribed = true if subscription.protocol == protocol && subscription.endpoint == endpoint
192
+ }
193
+ end
194
+
195
+ unless already_subscribed
196
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).subscribe(topic_arn: arn, protocol: protocol, endpoint: endpoint)
197
+ MU.log "Subscribed #{endpoint} to SNS topic #{arn}"
198
+ end
199
+ end
200
+
201
+ # Test if a notifier group exists
202
+ # Create a new notifier group. Will check if the group exists before creating it.
203
+ # @param topic_name [String]: The cloud provider's name for the notifier group.
204
+ # @param region [String]: The cloud provider region.
205
+ # @param account_number [String]: The cloud provider account number.
206
+ # @return [string]: The cloud provider's identifier.
207
+ def self.topicExist(topic_name, region: MU.curRegion, account_number: MU.account_number, credentials: nil)
208
+ arn = "arn:#{MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws"}:sns:#{region}:#{account_number}:#{topic_name}"
209
+ match = nil
210
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).list_topics.topics.each { |topic|
211
+ if topic.topic_arn == arn
212
+ match = topic.topic_arn
213
+ break
214
+ end
215
+ }
216
+ return match
217
+ end
218
+
219
+ end
220
+ end
221
+ end
222
+ end