cloud-mu 3.1.3 → 3.1.4

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 (114) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +10 -2
  3. data/bin/mu-adopt +5 -1
  4. data/bin/mu-load-config.rb +2 -3
  5. data/bin/mu-run-tests +112 -27
  6. data/cloud-mu.gemspec +20 -20
  7. data/cookbooks/mu-tools/libraries/helper.rb +2 -1
  8. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  9. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  10. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  11. data/extras/image-generators/Google/centos6.yaml +1 -0
  12. data/extras/image-generators/Google/centos7.yaml +1 -1
  13. data/modules/mommacat.ru +5 -15
  14. data/modules/mu.rb +10 -14
  15. data/modules/mu/adoption.rb +20 -14
  16. data/modules/mu/cleanup.rb +13 -9
  17. data/modules/mu/cloud.rb +26 -26
  18. data/modules/mu/clouds/aws.rb +100 -59
  19. data/modules/mu/clouds/aws/alarm.rb +4 -2
  20. data/modules/mu/clouds/aws/bucket.rb +25 -21
  21. data/modules/mu/clouds/aws/cache_cluster.rb +25 -23
  22. data/modules/mu/clouds/aws/collection.rb +21 -20
  23. data/modules/mu/clouds/aws/container_cluster.rb +47 -26
  24. data/modules/mu/clouds/aws/database.rb +57 -68
  25. data/modules/mu/clouds/aws/dnszone.rb +14 -14
  26. data/modules/mu/clouds/aws/endpoint.rb +20 -16
  27. data/modules/mu/clouds/aws/firewall_rule.rb +19 -16
  28. data/modules/mu/clouds/aws/folder.rb +7 -7
  29. data/modules/mu/clouds/aws/function.rb +15 -12
  30. data/modules/mu/clouds/aws/group.rb +14 -10
  31. data/modules/mu/clouds/aws/habitat.rb +16 -13
  32. data/modules/mu/clouds/aws/loadbalancer.rb +16 -15
  33. data/modules/mu/clouds/aws/log.rb +13 -10
  34. data/modules/mu/clouds/aws/msg_queue.rb +15 -8
  35. data/modules/mu/clouds/aws/nosqldb.rb +18 -11
  36. data/modules/mu/clouds/aws/notifier.rb +11 -6
  37. data/modules/mu/clouds/aws/role.rb +87 -70
  38. data/modules/mu/clouds/aws/search_domain.rb +30 -19
  39. data/modules/mu/clouds/aws/server.rb +102 -72
  40. data/modules/mu/clouds/aws/server_pool.rb +47 -28
  41. data/modules/mu/clouds/aws/storage_pool.rb +5 -6
  42. data/modules/mu/clouds/aws/user.rb +13 -10
  43. data/modules/mu/clouds/aws/vpc.rb +135 -121
  44. data/modules/mu/clouds/azure.rb +16 -9
  45. data/modules/mu/clouds/azure/container_cluster.rb +2 -3
  46. data/modules/mu/clouds/azure/firewall_rule.rb +10 -10
  47. data/modules/mu/clouds/azure/habitat.rb +8 -6
  48. data/modules/mu/clouds/azure/loadbalancer.rb +5 -5
  49. data/modules/mu/clouds/azure/role.rb +8 -10
  50. data/modules/mu/clouds/azure/server.rb +65 -25
  51. data/modules/mu/clouds/azure/user.rb +5 -7
  52. data/modules/mu/clouds/azure/vpc.rb +12 -15
  53. data/modules/mu/clouds/cloudformation.rb +8 -7
  54. data/modules/mu/clouds/cloudformation/vpc.rb +2 -4
  55. data/modules/mu/clouds/google.rb +39 -24
  56. data/modules/mu/clouds/google/bucket.rb +9 -11
  57. data/modules/mu/clouds/google/container_cluster.rb +27 -42
  58. data/modules/mu/clouds/google/database.rb +6 -9
  59. data/modules/mu/clouds/google/firewall_rule.rb +11 -10
  60. data/modules/mu/clouds/google/folder.rb +16 -9
  61. data/modules/mu/clouds/google/function.rb +127 -161
  62. data/modules/mu/clouds/google/group.rb +21 -18
  63. data/modules/mu/clouds/google/habitat.rb +18 -15
  64. data/modules/mu/clouds/google/loadbalancer.rb +14 -16
  65. data/modules/mu/clouds/google/role.rb +48 -31
  66. data/modules/mu/clouds/google/server.rb +105 -105
  67. data/modules/mu/clouds/google/server_pool.rb +12 -31
  68. data/modules/mu/clouds/google/user.rb +67 -13
  69. data/modules/mu/clouds/google/vpc.rb +58 -65
  70. data/modules/mu/config.rb +89 -1738
  71. data/modules/mu/config/bucket.rb +3 -3
  72. data/modules/mu/config/collection.rb +3 -3
  73. data/modules/mu/config/container_cluster.rb +2 -2
  74. data/modules/mu/config/dnszone.rb +5 -5
  75. data/modules/mu/config/doc_helpers.rb +517 -0
  76. data/modules/mu/config/endpoint.rb +3 -3
  77. data/modules/mu/config/firewall_rule.rb +118 -3
  78. data/modules/mu/config/folder.rb +3 -3
  79. data/modules/mu/config/function.rb +2 -2
  80. data/modules/mu/config/group.rb +3 -3
  81. data/modules/mu/config/habitat.rb +3 -3
  82. data/modules/mu/config/loadbalancer.rb +3 -3
  83. data/modules/mu/config/log.rb +3 -3
  84. data/modules/mu/config/msg_queue.rb +3 -3
  85. data/modules/mu/config/nosqldb.rb +3 -3
  86. data/modules/mu/config/notifier.rb +2 -2
  87. data/modules/mu/config/ref.rb +333 -0
  88. data/modules/mu/config/role.rb +3 -3
  89. data/modules/mu/config/schema_helpers.rb +508 -0
  90. data/modules/mu/config/search_domain.rb +3 -3
  91. data/modules/mu/config/server.rb +86 -58
  92. data/modules/mu/config/server_pool.rb +2 -2
  93. data/modules/mu/config/tail.rb +189 -0
  94. data/modules/mu/config/user.rb +3 -3
  95. data/modules/mu/config/vpc.rb +44 -4
  96. data/modules/mu/defaults/Google.yaml +2 -2
  97. data/modules/mu/deploy.rb +13 -10
  98. data/modules/mu/groomer.rb +1 -1
  99. data/modules/mu/groomers/ansible.rb +69 -24
  100. data/modules/mu/groomers/chef.rb +52 -44
  101. data/modules/mu/logger.rb +17 -14
  102. data/modules/mu/master.rb +317 -2
  103. data/modules/mu/master/chef.rb +3 -4
  104. data/modules/mu/master/ldap.rb +3 -3
  105. data/modules/mu/master/ssl.rb +12 -2
  106. data/modules/mu/mommacat.rb +85 -1766
  107. data/modules/mu/mommacat/daemon.rb +394 -0
  108. data/modules/mu/mommacat/naming.rb +366 -0
  109. data/modules/mu/mommacat/storage.rb +689 -0
  110. data/modules/tests/bucket.yml +4 -0
  111. data/modules/tests/{win2k12.yaml → needwork/win2k12.yaml} +0 -0
  112. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  113. data/modules/tests/regrooms/bucket.yml +19 -0
  114. metadata +112 -102
@@ -45,7 +45,6 @@ module MU
45
45
  resp = MU::Cloud::AWS.orgs(credentials: @config['credentials']).describe_create_account_status(
46
46
  create_account_request_id: createid
47
47
  )
48
- createstatus = resp.create_account_status.state
49
48
  if !["SUCCEEDED", "IN_PROGRESS"].include?(resp.create_account_status.state)
50
49
  raise MuError, "Failed to create account #{@mu_name}: #{resp.create_account_status.failure_reason}"
51
50
  end
@@ -59,9 +58,12 @@ module MU
59
58
  MU.log "Creation of account #{@mu_name} (#{resp.create_account_status.account_id}) complete"
60
59
  end
61
60
 
61
+ @cloud_desc_cache = nil
62
62
  # Return the cloud descriptor for the Habitat
63
- def cloud_desc
64
- MU::Cloud::AWS::Habitat.find(cloud_id: @cloud_id).values.first
63
+ def cloud_desc(use_cache: true)
64
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
65
+ @cloud_desc_cache = MU::Cloud::AWS::Habitat.find(cloud_id: @cloud_id).values.first
66
+ @cloud_desc_cache
65
67
  end
66
68
 
67
69
  # Canonical Amazon Resource Number for this resource
@@ -87,10 +89,11 @@ module MU
87
89
  # Remove all AWS accounts associated with the currently loaded deployment. Try to, anyway.
88
90
  # @param noop [Boolean]: If true, will only print what would be done
89
91
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
90
- # @param region [String]: The cloud provider region
91
92
  # @return [void]
92
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
93
+ def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
93
94
  return if !orgMasterCreds?(credentials)
95
+ MU.log "AWS::Habitat.cleanup: need to support flags['known']", MU::DEBUG, details: flags
96
+ MU.log "Placeholder: AWS Habitat artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
94
97
 
95
98
  resp = MU::Cloud::AWS.orgs(credentials: credentials).list_accounts
96
99
 
@@ -108,14 +111,14 @@ module MU
108
111
 
109
112
  # Locate an existing account
110
113
  # @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching account
111
- def self.find(**args)
114
+ def self.find(**_args)
112
115
  {}
113
116
  end
114
117
 
115
118
  # Cloud-specific configuration properties.
116
- # @param config [MU::Config]: The calling MU::Config object
119
+ # @param _config [MU::Config]: The calling MU::Config object
117
120
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
118
- def self.schema(config)
121
+ def self.schema(_config)
119
122
  toplevel_required = []
120
123
  schema = {
121
124
  "email" => {
@@ -126,9 +129,10 @@ module MU
126
129
  [toplevel_required, schema]
127
130
  end
128
131
 
129
- # @param account_number [String]
132
+ # @param _account_number [String]
133
+ # @param _credentials [String]
130
134
  # @return [Boolean]
131
- def self.isLive?(account_number, credentials = nil)
135
+ def self.isLive?(_account_number, _credentials = nil)
132
136
  true
133
137
  end
134
138
 
@@ -138,7 +142,6 @@ module MU
138
142
  # @param credentials [String]
139
143
  # @return [Boolean]
140
144
  def self.orgMasterCreds?(credentials = nil)
141
- user_list = MU::Cloud::AWS.iam(credentials: credentials).list_users.users
142
145
  acct_num = MU::Cloud::AWS.iam(credentials: credentials).list_users.users.first.arn.split(/:/)[4]
143
146
 
144
147
  parentorg = MU::Cloud::AWS::Folder.find(credentials: credentials).values.first
@@ -147,9 +150,9 @@ module MU
147
150
 
148
151
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::habitats}, bare and unvalidated.
149
152
  # @param habitat [Hash]: The resource to process and validate
150
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
153
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
151
154
  # @return [Boolean]: True if validation succeeded, False otherwise
152
- def self.validateConfig(habitat, configurator)
155
+ def self.validateConfig(habitat, _configurator)
153
156
  ok = true
154
157
 
155
158
  if !habitat["email"]
@@ -288,7 +288,7 @@ module MU
288
288
  else
289
289
  @config["listeners"].each { |l|
290
290
  if l['ssl_certificate_id']
291
- resp = MU::Cloud::AWS.elb(region: @config['region'], credentials: @config['credentials']).set_load_balancer_policies_of_listener(
291
+ MU::Cloud::AWS.elb(region: @config['region'], credentials: @config['credentials']).set_load_balancer_policies_of_listener(
292
292
  load_balancer_name: @cloud_id,
293
293
  load_balancer_port: l['lb_port'],
294
294
  policy_names: [
@@ -330,7 +330,7 @@ module MU
330
330
  }
331
331
  )
332
332
  else
333
- @targetgroups.each_pair { |tg_name, tg|
333
+ @targetgroups.values.each { |tg|
334
334
  MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).modify_target_group_attributes(
335
335
  target_group_arn: tg.target_group_arn,
336
336
  attributes: [
@@ -400,7 +400,7 @@ module MU
400
400
  timeout = 0
401
401
  MU.log "Disabling connection draining on #{lb.dns_name}"
402
402
  end
403
- @targetgroups.each_pair { |tg_name, tg|
403
+ @targetgroups.values.each { |tg|
404
404
  MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).modify_target_group_attributes(
405
405
  target_group_arn: tg.target_group_arn,
406
406
  attributes: [
@@ -473,7 +473,7 @@ module MU
473
473
  end
474
474
  end
475
475
  else
476
- @targetgroups.each_pair { |tg_name, tg|
476
+ @targetgroups.values.each { |tg|
477
477
  MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).modify_target_group_attributes(
478
478
  target_group_arn: tg.target_group_arn,
479
479
  attributes: [
@@ -553,15 +553,17 @@ module MU
553
553
  end
554
554
  end
555
555
 
556
+ @cloud_desc_cache = nil
556
557
  # Wrapper for cloud_desc method that deals with elb vs. elb2 resources.
557
- def cloud_desc
558
+ def cloud_desc(use_cache: true)
559
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
558
560
  if @config['classic']
559
- resp = MU::Cloud::AWS.elb(region: @config['region'], credentials: @config['credentials']).describe_load_balancers(
561
+ @cloud_desc_cache = MU::Cloud::AWS.elb(region: @config['region'], credentials: @config['credentials']).describe_load_balancers(
560
562
  load_balancer_names: [@cloud_id]
561
563
  ).load_balancer_descriptions.first
562
- return resp
564
+ return @cloud_desc_cache
563
565
  else
564
- resp = MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).describe_load_balancers(
566
+ @cloud_desc_cache = MU::Cloud::AWS.elb2(region: @config['region'], credentials: @config['credentials']).describe_load_balancers(
565
567
  names: [@cloud_id]
566
568
  ).load_balancers.first
567
569
  if @targetgroups.nil? and !@deploy.nil? and
@@ -573,7 +575,7 @@ module MU
573
575
  }
574
576
  end
575
577
 
576
- return resp
578
+ return @cloud_desc_cache
577
579
  end
578
580
  end
579
581
 
@@ -692,7 +694,6 @@ module MU
692
694
  classic = false
693
695
  end
694
696
  begin
695
- tags = []
696
697
  matched = false
697
698
  if flags and flags['vpc_id']
698
699
  matched = true if lb.vpc_id == flags['vpc_id']
@@ -773,9 +774,9 @@ module MU
773
774
  end
774
775
 
775
776
  # Cloud-specific configuration properties.
776
- # @param config [MU::Config]: The calling MU::Config object
777
+ # @param _config [MU::Config]: The calling MU::Config object
777
778
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
778
- def self.schema(config)
779
+ def self.schema(_config)
779
780
  toplevel_required = []
780
781
  schema = {
781
782
  "targetgroups" => {
@@ -818,9 +819,9 @@ module MU
818
819
 
819
820
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::loadbalancers}, bare and unvalidated.
820
821
  # @param lb [Hash]: The resource to process and validate
821
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
822
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
822
823
  # @return [Boolean]: True if validation succeeded, False otherwise
823
- def self.validateConfig(lb, configurator)
824
+ def self.validateConfig(lb, _configurator)
824
825
  ok = true
825
826
 
826
827
  # XXX what about raw targetgroup ssl declarations?
@@ -830,7 +831,7 @@ module MU
830
831
  if lb['cloud'] != "CloudFormation" # XXX or maybe do this anyway?
831
832
  begin
832
833
  listener["ssl_certificate_id"] = MU::Cloud::AWS.findSSLCertificate(name: listener["ssl_certificate_name"].to_s, id: listener["ssl_certificate_id"].to_s, region: lb['region'])
833
- rescue MuError => e
834
+ rescue MuError
834
835
  ok = false
835
836
  next
836
837
  end
@@ -203,6 +203,9 @@ module MU
203
203
  # @param region [String]: The cloud provider region
204
204
  # @return [void]
205
205
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
206
+ MU.log "AWS::Log.cleanup: need to support flags['known']", MU::DEBUG, details: flags
207
+ MU.log "Placeholder: AWS Log artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
208
+
206
209
  log_groups = self.find(credentials: credentials, region: region).values
207
210
  if !log_groups.empty?
208
211
  log_groups.each{ |lg|
@@ -227,13 +230,13 @@ module MU
227
230
  }
228
231
  end
229
232
 
230
- unless noop
231
- MU::Cloud::AWS.iam(credentials: credentials).list_roles.roles.each{ |role|
232
- match_string = "#{MU.deploy_id}.*CloudTrail"
233
+ # unless noop
234
+ # MU::Cloud::AWS.iam(credentials: credentials).list_roles.roles.each{ |role|
235
+ # match_string = "#{MU.deploy_id}.*CloudTrail"
233
236
  # 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.
234
237
  # MU::Cloud::AWS::Server.removeIAMProfile(role.role_name) if role.role_name.match(match_string)
235
- }
236
- end
238
+ # }
239
+ # end
237
240
  end
238
241
 
239
242
  # Locate an existing log group.
@@ -264,7 +267,7 @@ module MU
264
267
  # Reverse-map our cloud description into a runnable config hash.
265
268
  # We assume that any values we have in +@config+ are placeholders, and
266
269
  # calculate our own accordingly based on what's live in the cloud.
267
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
270
+ def toKitten(**_args)
268
271
  bok = {
269
272
  "cloud" => "AWS",
270
273
  "credentials" => @config['credentials'],
@@ -304,9 +307,9 @@ module MU
304
307
 
305
308
 
306
309
  # Cloud-specific configuration properties.
307
- # @param config [MU::Config]: The calling MU::Config object
310
+ # @param _config [MU::Config]: The calling MU::Config object
308
311
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
309
- def self.schema(config)
312
+ def self.schema(_config)
310
313
  toplevel_required = []
311
314
  schema = {
312
315
  "retention_period" => {
@@ -357,9 +360,9 @@ module MU
357
360
 
358
361
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::logs}, bare and unvalidated.
359
362
  # @param log [Hash]: The resource to process and validate
360
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
363
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
361
364
  # @return [Boolean]: True if validation succeeded, False otherwise
362
- def self.validateConfig(log, configurator)
365
+ def self.validateConfig(log, _configurator)
363
366
  ok = true
364
367
 
365
368
  if log["filters"] && !log["filters"].empty?
@@ -53,14 +53,14 @@ module MU
53
53
  new_attrs = genQueueAttrs
54
54
 
55
55
  changed = false
56
- new_attrs.each_pair { |k, v|
56
+ new_attrs.each_pair { |k, _v|
57
57
  if !cur_attrs.has_key?(k) or cur_attrs[k] != new_attrs[k]
58
58
  changed = true
59
59
  end
60
60
  }
61
61
  if changed
62
62
  MU.log "Updating SQS queue #{@mu_name}", MU::NOTICE, details: new_attrs
63
- resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).set_queue_attributes(
63
+ MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).set_queue_attributes(
64
64
  queue_url: @cloud_id,
65
65
  attributes: new_attrs
66
66
  )
@@ -74,10 +74,13 @@ module MU
74
74
  "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":sqs:"+@config['region']+":"+MU::Cloud::AWS.credToAcct(@config['credentials'])+":"+@cloud_id
75
75
  end
76
76
 
77
+ @cloud_desc_cache = nil
77
78
  # Retrieve the AWS descriptor for this SQS queue. AWS doesn't exactly
78
79
  # provide one; if you want real information for SQS ask notify()
79
80
  # @return [Hash]: AWS doesn't return anything but the SQS URL, so supplement with attributes
80
- def cloud_desc
81
+ def cloud_desc(use_cache: true)
82
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
83
+
81
84
  if !@cloud_id
82
85
  resp = MU::Cloud::AWS.sqs(region: @config['region'], credentials: @config['credentials']).list_queues(
83
86
  queue_name_prefix: @mu_name
@@ -92,11 +95,12 @@ module MU
92
95
  end
93
96
 
94
97
  return nil if !@cloud_id
95
- MU::Cloud::AWS::MsgQueue.find(
98
+ @cloud_desc_cache = MU::Cloud::AWS::MsgQueue.find(
96
99
  cloud_id: @cloud_id.dup,
97
100
  region: @config['region'],
98
101
  credentials: @config['credentials']
99
102
  )
103
+ @cloud_desc_cache
100
104
  end
101
105
 
102
106
  # Return the metadata for this MsgQueue rule
@@ -130,6 +134,9 @@ module MU
130
134
  # @param region [String]: The cloud provider region
131
135
  # @return [void]
132
136
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
137
+ MU.log "AWS::MsgQueue.cleanup: need to support flags['known']", MU::DEBUG, details: flags
138
+ MU.log "Placeholder: AWS MsgQueue artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
139
+
133
140
  resp = MU::Cloud::AWS.sqs(credentials: credentials, region: region).list_queues(
134
141
  queue_name_prefix: MU.deploy_id
135
142
  )
@@ -182,7 +189,7 @@ module MU
182
189
  args[:cloud_id] = resp.queue_url if resp and resp.queue_url
183
190
  end
184
191
  end
185
- rescue ::Aws::SQS::Errors::NonExistentQueue => e
192
+ rescue ::Aws::SQS::Errors::NonExistentQueue
186
193
  end
187
194
 
188
195
  # Go fetch its attributes
@@ -211,9 +218,9 @@ module MU
211
218
  end
212
219
 
213
220
  # Cloud-specific configuration properties.
214
- # @param config [MU::Config]: The calling MU::Config object
221
+ # @param _config [MU::Config]: The calling MU::Config object
215
222
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
216
- def self.schema(config)
223
+ def self.schema(_config)
217
224
  toplevel_required = []
218
225
  schema = {
219
226
  "max_msg_size" => {
@@ -382,7 +389,7 @@ module MU
382
389
  end
383
390
  begin
384
391
  MU::Cloud::AWS.kms(region: queue['region']).describe_key(key_id: queue['kms']['key_id'])
385
- rescue Aws::KMS::Errors::NotFoundException => e
392
+ rescue Aws::KMS::Errors::NotFoundException
386
393
  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']}"
387
394
  ok = false
388
395
  end
@@ -164,6 +164,8 @@ module MU
164
164
  # @param region [String]: The cloud provider region
165
165
  # @return [void]
166
166
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
167
+ MU.log "AWS::NoSQLDb.cleanup: need to support flags['known']", MU::DEBUG, details: flags
168
+
167
169
  resp = MU::Cloud::AWS.dynamo(credentials: credentials, region: region).list_tables
168
170
  if resp and resp.table_names
169
171
  resp.table_names.each { |table|
@@ -178,16 +180,23 @@ module MU
178
180
  begin
179
181
  tags = MU::Cloud::AWS.dynamo(credentials: credentials, region: region).list_tags_of_resource(resource_arn: desc.table_arn)
180
182
  if tags and tags.tags
183
+ deploy_match = false
184
+ master_match = false
181
185
  tags.tags.each { |tag|
182
186
  if tag.key == "MU-ID" and tag.value == MU.deploy_id
183
- MU.log "Deleting DynamoDB table #{desc.table_name}"
184
- if !noop
185
- MU::Cloud::AWS.dynamo(credentials: credentials, region: region).delete_table(table_name: desc.table_name)
186
- end
187
+ deploy_match = true
188
+ elsif tag.key == "MU-MASTER-IP" and tag.value == MU.mu_public_ip
189
+ master_match = true
187
190
  end
188
191
  }
192
+ if deploy_match and (master_match or ignoremaster)
193
+ MU.log "Deleting DynamoDB table #{desc.table_name}"
194
+ if !noop
195
+ MU::Cloud::AWS.dynamo(credentials: credentials, region: region).delete_table(table_name: desc.table_name)
196
+ end
197
+ end
189
198
  end
190
- rescue Aws::DynamoDB::Errors::ResourceNotFoundException => e
199
+ rescue Aws::DynamoDB::Errors::ResourceNotFoundException
191
200
  end
192
201
 
193
202
  }
@@ -236,9 +245,9 @@ module MU
236
245
  end
237
246
 
238
247
  # Cloud-specific configuration properties.
239
- # @param config [MU::Config]: The calling MU::Config object
248
+ # @param _config [MU::Config]: The calling MU::Config object
240
249
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
241
- def self.schema(config)
250
+ def self.schema(_config)
242
251
  toplevel_required = ["attributes"]
243
252
 
244
253
 
@@ -360,9 +369,9 @@ module MU
360
369
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::nosqldbs}, bare and unvalidated.
361
370
 
362
371
  # @param db [Hash]: The resource to process and validate
363
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
372
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
364
373
  # @return [Boolean]: True if validation succeeded, False otherwise
365
- def self.validateConfig(db, configurator)
374
+ def self.validateConfig(db, _configurator)
366
375
  ok = true
367
376
 
368
377
  partition = nil
@@ -399,8 +408,6 @@ module MU
399
408
  ok
400
409
  end
401
410
 
402
- private
403
-
404
411
  end
405
412
  end
406
413
  end
@@ -66,12 +66,17 @@ module MU
66
66
  # @param region [String]: The cloud provider region
67
67
  # @return [void]
68
68
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
69
+ MU.log "AWS::Notifier.cleanup: need to support flags['known']", MU::DEBUG, details: flags
70
+ MU.log "Placeholder: AWS Notifier artifacts do not support tags, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: ignoremaster
71
+
69
72
  MU::Cloud::AWS.sns(region: region, credentials: credentials).list_topics.topics.each { |topic|
70
73
  if topic.topic_arn.match(MU.deploy_id)
71
74
  # 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.
72
75
  # This may fail to find notifier groups in some cases (eg. cache_cluster) so we might want to delete from each API as well.
73
- MU::Cloud::AWS.sns(region: region, credentials: credentials).delete_topic(topic_arn: topic.topic_arn)
74
- MU.log "Deleted SNS topic: #{topic.topic_arn}"
76
+ MU.log "Deleting SNS topic: #{topic.topic_arn}"
77
+ if !noop
78
+ MU::Cloud::AWS.sns(region: region, credentials: credentials).delete_topic(topic_arn: topic.topic_arn)
79
+ end
75
80
  end
76
81
  }
77
82
  end
@@ -116,9 +121,9 @@ module MU
116
121
  end
117
122
 
118
123
  # Cloud-specific configuration properties.
119
- # @param config [MU::Config]: The calling MU::Config object
124
+ # @param _config [MU::Config]: The calling MU::Config object
120
125
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
121
- def self.schema(config)
126
+ def self.schema(_config)
122
127
  toplevel_required = []
123
128
  schema = {
124
129
  "subscriptions" => {
@@ -143,9 +148,9 @@ module MU
143
148
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::notifier}, bare and unvalidated.
144
149
 
145
150
  # @param notifier [Hash]: The resource to process and validate
146
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
151
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
147
152
  # @return [Boolean]: True if validation succeeded, False otherwise
148
- def self.validateConfig(notifier, configurator)
153
+ def self.validateConfig(notifier, _configurator)
149
154
  ok = true
150
155
 
151
156
  if notifier['subscriptions']
@@ -43,7 +43,7 @@ module MU
43
43
 
44
44
  policy_name = @mu_name+"-"+policy.keys.first.upcase
45
45
  MU.log "Creating IAM policy #{policy_name}"
46
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
46
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_policy(
47
47
  policy_name: policy_name,
48
48
  path: "/"+@deploy.deploy_id+"/",
49
49
  policy_document: JSON.generate(policy.values.first),
@@ -56,8 +56,8 @@ module MU
56
56
  MU.log "Creating IAM role #{@mu_name}"
57
57
  @cloud_id = @mu_name
58
58
  path = @config['strip_path'] ? nil : "/"+@deploy.deploy_id+"/"
59
- resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).create_role(
60
- path: nil,
59
+ MU::Cloud::AWS.iam(credentials: @config['credentials']).create_role(
60
+ path: path,
61
61
  role_name: @mu_name,
62
62
  description: "Generated by Mu",
63
63
  assume_role_policy_document: gen_assume_role_policy_doc,
@@ -89,7 +89,6 @@ module MU
89
89
  end
90
90
 
91
91
  if @config['raw_policies'] or @config['attachable_policies']
92
- attached_policies = []
93
92
  configured_policies = []
94
93
 
95
94
  if @config['raw_policies']
@@ -128,7 +127,7 @@ module MU
128
127
 
129
128
  # XXX not sure we're binding these sanely, validate that
130
129
  if @config['raw_policies']
131
- pol_arns = MU::Cloud::AWS::Role.manageRawPolicies(
130
+ MU::Cloud::AWS::Role.manageRawPolicies(
132
131
  @config['raw_policies'],
133
132
  basename: @deploy.getResourceName(@config['name']),
134
133
  credentials: @credentials
@@ -183,7 +182,7 @@ module MU
183
182
  desc
184
183
  end
185
184
 
186
- rescue Aws::IAM::Errors::NoSuchEntity => e
185
+ rescue Aws::IAM::Errors::NoSuchEntity
187
186
  MU.log "Creating IAM policy #{policy_name}", details: policy.values.first
188
187
  MU::Cloud::AWS.iam(credentials: credentials).create_policy(
189
188
  policy_name: policy_name,
@@ -208,59 +207,62 @@ module MU
208
207
  end
209
208
  end
210
209
 
210
+ @cloud_desc_cache = nil
211
211
  # Return a hash containing a +role+ element and a +policies+ element,
212
212
  # populated with one or both depending on what this resource has
213
213
  # defined.
214
- def cloud_desc
215
- desc = {}
214
+ def cloud_desc(use_cache: true)
215
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
216
+
217
+ @cloud_desc_cache = {}
216
218
  if @config['bare_policies']
217
219
  if @cloud_id
218
220
  pol_desc = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
219
221
  if pol_desc
220
- desc['policies'] = [pol_desc]
221
- return desc
222
+ @cloud_desc_cache['policies'] = [pol_desc]
223
+ return @cloud_desc_cache
222
224
  end
223
225
  end
224
226
 
225
227
  if @deploy and @deploy.deploy_id
226
- desc["policies"] = MU::Cloud::AWS.iam(credentials: @credentials).list_policies(
228
+ @cloud_desc_cache["policies"] = MU::Cloud::AWS.iam(credentials: @credentials).list_policies(
227
229
  path_prefix: "/"+@deploy.deploy_id+"/"
228
230
  ).policies
229
- desc["policies"].reject! { |p|
231
+ @cloud_desc_cache["policies"].reject! { |p|
230
232
  !p.policy_name.match(/^#{Regexp.quote(@mu_name)}-/)
231
233
  }
232
234
  # this is quasi-wrong because we can be mulitple cloud is, but
233
235
  # we can't really set this type to has_multiples because that's
234
236
  # just how managed policies work not anything else, goddammit
235
237
  # AWS why can't you just bundle everything in roles
236
- if desc["policies"] and desc["policies"].size > 0
237
- @cloud_id ||= desc["policies"].first.arn
238
+ if @cloud_desc_cache["policies"] and @cloud_desc_cache["policies"].size > 0
239
+ @cloud_id ||= @cloud_desc_cache["policies"].first.arn
238
240
  end
239
241
  end
240
242
  else
241
243
  if @cloud_id.match(/^arn:aws(:?-us-gov)?:[^:]*:[^:]*:\d*:policy\//)
242
244
  pol_desc = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
243
245
  if pol_desc
244
- desc['policies'] = [pol_desc]
245
- return desc
246
+ @cloud_desc_cache['policies'] = [pol_desc]
247
+ return @cloud_desc_cache
246
248
  end
247
249
  end
248
250
  begin
249
- desc['role'] = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
250
- desc['role'] ||= MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @mu_name).values.first
251
+ @cloud_desc_cache['role'] = MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @cloud_id).values.first
252
+ @cloud_desc_cache['role'] ||= MU::Cloud::AWS::Role.find(credentials: @credentials, cloud_id: @mu_name).values.first
251
253
  MU::Cloud::AWS.iam(credentials: @credentials).list_attached_role_policies(
252
254
  role_name: @mu_name
253
255
  ).attached_policies.each { |p|
254
- desc["policies"] ||= []
255
- desc["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_policy(
256
+ @cloud_desc_cache["policies"] ||= []
257
+ @cloud_desc_cache["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_policy(
256
258
  policy_arn: p.policy_arn
257
259
  ).policy
258
260
  }
259
261
 
260
262
  inline = MU::Cloud::AWS.iam(credentials: @credentials).list_role_policies(role_name: @mu_name).policy_names
261
263
  inline.each { |pol_name|
262
- desc["policies"] ||= []
263
- desc["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_role_policy(
264
+ @cloud_desc_cache["policies"] ||= []
265
+ @cloud_desc_cache["policies"] << MU::Cloud::AWS.iam(credentials: @credentials).get_role_policy(
264
266
  role_name: @mu_name,
265
267
  policy_name: pol_name
266
268
  )
@@ -270,9 +272,9 @@ rescue ::Aws::IAM::Errors::ValidationError => e
270
272
  MU.log @cloud_id+" "+@mu_name, MU::WARN, details: e.inspect
271
273
  end
272
274
  end
273
- desc['cloud_id'] ||= @cloud_id
275
+ @cloud_desc_cache['cloud_id'] ||= @cloud_id
274
276
 
275
- desc
277
+ @cloud_desc_cache
276
278
  end
277
279
 
278
280
  # Return the metadata for this user cofiguration
@@ -284,8 +286,7 @@ end
284
286
  # Insert a new target entity into an existing policy.
285
287
  # @param policy [String]: The name of the policy to which we're appending, which must already exist as part of this role resource
286
288
  # @param targets [Array<String>]: The target resource. If +target_type+ isn't specified, this should be a fully-resolved ARN.
287
- # @param mu_type [String]: A valid Mu resource type
288
- def injectPolicyTargets(policy, targets, mu_type = nil)
289
+ def injectPolicyTargets(policy, targets)
289
290
  if !policy.match(/^#{@deploy.deploy_id}/)
290
291
  policy = @mu_name+"-"+policy.upcase
291
292
  end
@@ -409,9 +410,8 @@ end
409
410
  # Remove all roles associated with the currently loaded deployment.
410
411
  # @param noop [Boolean]: If true, will only print what would be done
411
412
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
412
- # @param region [String]: The cloud provider region
413
413
  # @return [void]
414
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
414
+ def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
415
415
 
416
416
  resp = MU::Cloud::AWS.iam(credentials: credentials).list_policies(
417
417
  path_prefix: "/"+MU.deploy_id+"/"
@@ -438,12 +438,18 @@ end
438
438
  # check tags. Hardly seems efficient.
439
439
  desc = MU::Cloud::AWS.iam(credentials: credentials).get_role(role_name: r.role_name)
440
440
  if desc.role and desc.role.tags and desc.role.tags
441
+ master_match = false
442
+ deploy_match = false
441
443
  desc.role.tags.each { |t|
442
444
  if t.key == "MU-ID" and t.value == MU.deploy_id
443
- deleteme << r
444
- break
445
+ deploy_match = true
446
+ elsif t.key == "MU-MASTER-IP" and t.value == MU.mu_public_ip
447
+ master_match = true
445
448
  end
446
449
  }
450
+ if deploy_match and (master_match or ignoremaster)
451
+ deleteme << r
452
+ end
447
453
  end
448
454
  }
449
455
 
@@ -481,7 +487,7 @@ end
481
487
  MU::Cloud::AWS.iam(credentials: credentials).delete_instance_profile(instance_profile_name: r.role_name)
482
488
  rescue Aws::IAM::Errors::ValidationError => e
483
489
  MU.log "Cleaning up IAM role #{r.role_name}: #{e.inspect}", MU::WARN
484
- rescue Aws::IAM::Errors::NoSuchEntity => e
490
+ rescue Aws::IAM::Errors::NoSuchEntity
485
491
  end
486
492
 
487
493
  MU::Cloud::AWS.iam(credentials: credentials).delete_role(
@@ -552,7 +558,7 @@ end
552
558
  # Reverse-map our cloud description into a runnable config hash.
553
559
  # We assume that any values we have in +@config+ are placeholders, and
554
560
  # calculate our own accordingly based on what's live in the cloud.
555
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
561
+ def toKitten(**_args)
556
562
  bok = {
557
563
  "cloud" => "AWS",
558
564
  "credentials" => @config['credentials'],
@@ -721,7 +727,7 @@ end
721
727
  "targets" => s["Resource"].map { |r|
722
728
  if r.match(/^arn:aws(-us-gov)?:([^:]+):.*?:([^:]*)$/)
723
729
  # XXX which cases even count for blind references to sibling resources?
724
- type = if Regexp.last_match[1] == "s3"
730
+ if Regexp.last_match[1] == "s3"
725
731
  "bucket"
726
732
  elsif Regexp.last_match[1]
727
733
  MU.log "Service #{Regexp.last_match[1]} to type...", MU::WARN, details: r
@@ -741,7 +747,7 @@ end
741
747
 
742
748
  # Attach this role or group of loose policies to the specified entity.
743
749
  # @param entitytype [String]: The type of entity (user, group or role for policies; instance_profile for roles)
744
- def bindTo(entitytype, entityname, policies = [])
750
+ def bindTo(entitytype, entityname)
745
751
  if entitytype == "instance_profile"
746
752
  begin
747
753
  resp = MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
@@ -754,7 +760,7 @@ end
754
760
  role_name: @mu_name
755
761
  )
756
762
  end
757
- rescue Exception => e
763
+ rescue StandardError => e
758
764
  MU.log "Error binding role #{@mu_name} to instance profile #{entityname}: #{e.message}", MU::ERR
759
765
  raise e
760
766
  end
@@ -783,7 +789,6 @@ end
783
789
  rescue Aws::IAM::Errors::NoSuchEntity => e
784
790
  if subpaths.size > 0
785
791
  p_arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(@config["region"]) ? "aws-us-gov" : "aws")+":iam::aws:policy/#{subpaths.shift}/"+policy
786
- retried = true
787
792
  retry
788
793
  end
789
794
  raise e
@@ -847,7 +852,7 @@ end
847
852
  MU::Cloud::AWS.iam(credentials: @config['credentials']).create_instance_profile(
848
853
  instance_profile_name: @mu_name
849
854
  )
850
- rescue Aws::IAM::Errors::EntityAlreadyExists => e
855
+ rescue Aws::IAM::Errors::EntityAlreadyExists
851
856
  MU::Cloud::AWS.iam(credentials: @config['credentials']).get_instance_profile(
852
857
  instance_profile_name: @mu_name
853
858
  )
@@ -905,9 +910,9 @@ end
905
910
  end
906
911
 
907
912
  # Cloud-specific configuration properties.
908
- # @param config [MU::Config]: The calling MU::Config object
913
+ # @param _config [MU::Config]: The calling MU::Config object
909
914
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
910
- def self.schema(config)
915
+ def self.schema(_config)
911
916
  toplevel_required = []
912
917
  aws_resource_types = MU::Cloud.resource_types.keys.reject { |t|
913
918
  begin
@@ -1012,10 +1017,9 @@ end
1012
1017
  subpaths = ["service-role", "aws-service-role", "job-function"]
1013
1018
  begin
1014
1019
  MU::Cloud::AWS.iam(credentials: credentials).get_policy(policy_arn: arn)
1015
- rescue Aws::IAM::Errors::NoSuchEntity => e
1020
+ rescue Aws::IAM::Errors::NoSuchEntity
1016
1021
  if subpaths.size > 0
1017
1022
  arn = "arn:"+(MU::Cloud::AWS.isGovCloud?(region) ? "aws-us-gov" : "aws")+":iam::aws:policy/#{subpaths.shift}/"+ref["id"]
1018
- retried = true
1019
1023
  retry
1020
1024
  end
1021
1025
  MU.log "No such canned AWS IAM policy '#{arn}'", MU::ERR
@@ -1029,9 +1033,9 @@ end
1029
1033
 
1030
1034
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::roles}, bare and unvalidated.
1031
1035
  # @param role [Hash]: The resource to process and validate
1032
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
1036
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
1033
1037
  # @return [Boolean]: True if validation succeeded, False otherwise
1034
- def self.validateConfig(role, configurator)
1038
+ def self.validateConfig(role, _configurator)
1035
1039
  ok = true
1036
1040
 
1037
1041
  # munge things declared with the deprecated import keyword into
@@ -1094,9 +1098,7 @@ end
1094
1098
  # @param policies [Array<Hash>]: One or more policy chunks
1095
1099
  # @param deploy_obj [MU::MommaCat]: Deployment object to use when looking up sibling Mu resources
1096
1100
  # @return [Array<Hash>]
1097
- def self.genPolicyDocument(policies, deploy_obj: nil)
1098
- iam_policies = []
1099
-
1101
+ def self.genPolicyDocument(policies, deploy_obj: nil, bucket_style: false)
1100
1102
  if policies
1101
1103
  name = nil
1102
1104
  doc = {
@@ -1134,12 +1136,21 @@ end
1134
1136
  )
1135
1137
  if sibling
1136
1138
  id = sibling.cloudobj.arn
1137
- statement["Principal"] << id
1139
+ if bucket_style
1140
+ statement["Principal"] << { "AWS" => id }
1141
+ else
1142
+ statement["Principal"] << id
1143
+ end
1138
1144
  else
1139
1145
  raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["identifier"]} when generating IAM policy"
1140
1146
  end
1141
1147
  else
1142
- statement["Principal"] << grantee["identifier"]
1148
+ bucket_prefix = grantee["identifier"].match(/^[^\.]+\.amazonaws\.com$/) ? "Service" : "AWS"
1149
+ if bucket_style
1150
+ statement["Principal"] << { bucket_prefix => grantee["identifier"] }
1151
+ else
1152
+ statement["Principal"] << grantee["identifier"]
1153
+ end
1143
1154
  end
1144
1155
  }
1145
1156
  if policy["grant_to"].size == 1
@@ -1162,6 +1173,8 @@ end
1162
1173
  stream_id = id.sub(/:([^:]+)$/, ":log-stream:*")
1163
1174
  # "arn:aws:logs:us-east-2:accountID:log-group:log_group_name:log-stream:CloudTrail_log_stream_name_prefix*"
1164
1175
  statement["Resource"] << stream_id
1176
+ elsif id.match(/:s3:/)
1177
+ statement["Resource"] << id+"/*"
1165
1178
  end
1166
1179
  else
1167
1180
  raise MuError, "Couldn't find a #{target["entity_type"]} named #{target["identifier"]} when generating IAM policy"
@@ -1180,6 +1193,32 @@ end
1180
1193
  []
1181
1194
  end
1182
1195
 
1196
+ # Update a policy, handling deletion of old versions as needed
1197
+ # @param arn [String]:
1198
+ # @param doc [Hash]:
1199
+ # @param credentials [String]:
1200
+ def self.update_policy(arn, doc, credentials: nil)
1201
+ # XXX this is just blindly replacing identical versions, when it should check
1202
+ # and guard
1203
+ begin
1204
+ MU::Cloud::AWS.iam(credentials: credentials).create_policy_version(
1205
+ policy_arn: arn,
1206
+ set_as_default: true,
1207
+ policy_document: JSON.generate(doc)
1208
+ )
1209
+ rescue Aws::IAM::Errors::LimitExceeded
1210
+ delete_version = MU::Cloud::AWS.iam(credentials: credentials).list_policy_versions(
1211
+ policy_arn: arn,
1212
+ ).versions.last.version_id
1213
+ MU.log "Purging oldest version (#{delete_version}) of IAM policy #{arn}", MU::NOTICE
1214
+ MU::Cloud::AWS.iam(credentials: credentials).delete_policy_version(
1215
+ policy_arn: arn,
1216
+ version_id: delete_version
1217
+ )
1218
+ retry
1219
+ end
1220
+ end
1221
+
1183
1222
  private
1184
1223
 
1185
1224
  # Convert entries from the cloud-neutral @config['policies'] list into
@@ -1261,28 +1300,6 @@ end
1261
1300
  MU::Cloud::AWS::Role.update_policy(arn, doc, credentials: @credentials)
1262
1301
  end
1263
1302
 
1264
- # Update a policy, handling deletion of old versions as needed
1265
- def self.update_policy(arn, doc, credentials: nil)
1266
- # XXX this is just blindly replacing identical versions, when it should check
1267
- # and guard
1268
- begin
1269
- MU::Cloud::AWS.iam(credentials: credentials).create_policy_version(
1270
- policy_arn: arn,
1271
- set_as_default: true,
1272
- policy_document: JSON.generate(doc)
1273
- )
1274
- rescue Aws::IAM::Errors::LimitExceeded => e
1275
- delete_version = MU::Cloud::AWS.iam(credentials: credentials).list_policy_versions(
1276
- policy_arn: arn,
1277
- ).versions.last.version_id
1278
- MU.log "Purging oldest version (#{delete_version}) of IAM policy #{arn}", MU::NOTICE
1279
- MU::Cloud::AWS.iam(credentials: credentials).delete_policy_version(
1280
- policy_arn: arn,
1281
- version_id: delete_version
1282
- )
1283
- retry
1284
- end
1285
- end
1286
1303
 
1287
1304
  end
1288
1305
  end