cloud-mu 3.1.2 → 3.2.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 (201) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +15 -3
  3. data/ansible/roles/mu-windows/README.md +33 -0
  4. data/ansible/roles/mu-windows/defaults/main.yml +2 -0
  5. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  6. data/ansible/roles/mu-windows/files/config.xml +76 -0
  7. data/ansible/roles/mu-windows/handlers/main.yml +2 -0
  8. data/ansible/roles/mu-windows/meta/main.yml +53 -0
  9. data/ansible/roles/mu-windows/tasks/main.yml +36 -0
  10. data/ansible/roles/mu-windows/tests/inventory +2 -0
  11. data/ansible/roles/mu-windows/tests/test.yml +5 -0
  12. data/ansible/roles/mu-windows/vars/main.yml +2 -0
  13. data/bin/mu-adopt +10 -13
  14. data/bin/mu-azure-tests +57 -0
  15. data/bin/mu-cleanup +2 -4
  16. data/bin/mu-configure +52 -0
  17. data/bin/mu-deploy +3 -3
  18. data/bin/mu-findstray-tests +25 -0
  19. data/bin/mu-gen-docs +2 -4
  20. data/bin/mu-load-config.rb +2 -3
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +135 -37
  23. data/cloud-mu.gemspec +22 -20
  24. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  25. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  26. data/cookbooks/mu-tools/libraries/helper.rb +3 -2
  27. data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
  28. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  29. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  30. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  31. data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
  32. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  33. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  34. data/cookbooks/mu-tools/resources/disk.rb +1 -1
  35. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  36. data/extras/clean-stock-amis +25 -19
  37. data/extras/generate-stock-images +1 -0
  38. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  39. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  40. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  41. data/extras/image-generators/Google/centos6.yaml +1 -0
  42. data/extras/image-generators/Google/centos7.yaml +1 -1
  43. data/modules/mommacat.ru +6 -16
  44. data/modules/mu.rb +165 -111
  45. data/modules/mu/adoption.rb +401 -68
  46. data/modules/mu/cleanup.rb +199 -306
  47. data/modules/mu/cloud.rb +100 -1632
  48. data/modules/mu/cloud/database.rb +49 -0
  49. data/modules/mu/cloud/dnszone.rb +46 -0
  50. data/modules/mu/cloud/machine_images.rb +212 -0
  51. data/modules/mu/cloud/providers.rb +81 -0
  52. data/modules/mu/cloud/resource_base.rb +920 -0
  53. data/modules/mu/cloud/server.rb +40 -0
  54. data/modules/mu/cloud/server_pool.rb +1 -0
  55. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  56. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  57. data/modules/mu/cloud/wrappers.rb +165 -0
  58. data/modules/mu/config.rb +171 -1767
  59. data/modules/mu/config/alarm.rb +2 -6
  60. data/modules/mu/config/bucket.rb +4 -4
  61. data/modules/mu/config/cache_cluster.rb +1 -1
  62. data/modules/mu/config/collection.rb +4 -4
  63. data/modules/mu/config/container_cluster.rb +9 -4
  64. data/modules/mu/config/database.rb +83 -104
  65. data/modules/mu/config/database.yml +1 -2
  66. data/modules/mu/config/dnszone.rb +6 -6
  67. data/modules/mu/config/doc_helpers.rb +516 -0
  68. data/modules/mu/config/endpoint.rb +4 -4
  69. data/modules/mu/config/firewall_rule.rb +103 -4
  70. data/modules/mu/config/folder.rb +4 -4
  71. data/modules/mu/config/function.rb +3 -3
  72. data/modules/mu/config/group.rb +4 -4
  73. data/modules/mu/config/habitat.rb +4 -4
  74. data/modules/mu/config/loadbalancer.rb +60 -14
  75. data/modules/mu/config/log.rb +4 -4
  76. data/modules/mu/config/msg_queue.rb +4 -4
  77. data/modules/mu/config/nosqldb.rb +4 -4
  78. data/modules/mu/config/notifier.rb +3 -3
  79. data/modules/mu/config/ref.rb +365 -0
  80. data/modules/mu/config/role.rb +4 -4
  81. data/modules/mu/config/schema_helpers.rb +509 -0
  82. data/modules/mu/config/search_domain.rb +4 -4
  83. data/modules/mu/config/server.rb +97 -70
  84. data/modules/mu/config/server.yml +1 -0
  85. data/modules/mu/config/server_pool.rb +5 -9
  86. data/modules/mu/config/storage_pool.rb +1 -1
  87. data/modules/mu/config/tail.rb +200 -0
  88. data/modules/mu/config/user.rb +4 -4
  89. data/modules/mu/config/vpc.rb +70 -27
  90. data/modules/mu/config/vpc.yml +0 -1
  91. data/modules/mu/defaults/AWS.yaml +83 -60
  92. data/modules/mu/defaults/Azure.yaml +1 -0
  93. data/modules/mu/defaults/Google.yaml +3 -2
  94. data/modules/mu/deploy.rb +30 -26
  95. data/modules/mu/groomer.rb +17 -2
  96. data/modules/mu/groomers/ansible.rb +188 -41
  97. data/modules/mu/groomers/chef.rb +116 -55
  98. data/modules/mu/logger.rb +127 -148
  99. data/modules/mu/master.rb +389 -2
  100. data/modules/mu/master/chef.rb +3 -4
  101. data/modules/mu/master/ldap.rb +3 -3
  102. data/modules/mu/master/ssl.rb +12 -3
  103. data/modules/mu/mommacat.rb +217 -2612
  104. data/modules/mu/mommacat/daemon.rb +397 -0
  105. data/modules/mu/mommacat/naming.rb +473 -0
  106. data/modules/mu/mommacat/search.rb +495 -0
  107. data/modules/mu/mommacat/storage.rb +722 -0
  108. data/modules/mu/{clouds → providers}/README.md +1 -1
  109. data/modules/mu/{clouds → providers}/aws.rb +271 -112
  110. data/modules/mu/{clouds → providers}/aws/alarm.rb +5 -3
  111. data/modules/mu/{clouds → providers}/aws/bucket.rb +26 -22
  112. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +33 -67
  113. data/modules/mu/{clouds → providers}/aws/collection.rb +24 -23
  114. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +681 -721
  115. data/modules/mu/providers/aws/database.rb +1744 -0
  116. data/modules/mu/{clouds → providers}/aws/dnszone.rb +64 -63
  117. data/modules/mu/{clouds → providers}/aws/endpoint.rb +22 -27
  118. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +214 -244
  119. data/modules/mu/{clouds → providers}/aws/folder.rb +7 -7
  120. data/modules/mu/{clouds → providers}/aws/function.rb +17 -22
  121. data/modules/mu/{clouds → providers}/aws/group.rb +23 -23
  122. data/modules/mu/{clouds → providers}/aws/habitat.rb +17 -14
  123. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +57 -48
  124. data/modules/mu/{clouds → providers}/aws/log.rb +15 -12
  125. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +17 -16
  126. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +18 -11
  127. data/modules/mu/{clouds → providers}/aws/notifier.rb +11 -6
  128. data/modules/mu/{clouds → providers}/aws/role.rb +112 -86
  129. data/modules/mu/{clouds → providers}/aws/search_domain.rb +39 -33
  130. data/modules/mu/{clouds → providers}/aws/server.rb +835 -1133
  131. data/modules/mu/{clouds → providers}/aws/server_pool.rb +56 -60
  132. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +24 -42
  133. data/modules/mu/{clouds → providers}/aws/user.rb +21 -22
  134. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  135. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +0 -0
  136. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  137. data/modules/mu/{clouds → providers}/aws/vpc.rb +523 -929
  138. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  139. data/modules/mu/{clouds → providers}/azure.rb +29 -9
  140. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
  141. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
  142. data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
  143. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
  144. data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
  145. data/modules/mu/{clouds → providers}/azure/server.rb +95 -48
  146. data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
  147. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  148. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  149. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  150. data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
  151. data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
  152. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  153. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  156. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  158. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  159. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  160. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  161. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  162. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
  163. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  164. data/modules/mu/{clouds → providers}/google.rb +67 -30
  165. data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
  166. data/modules/mu/{clouds → providers}/google/container_cluster.rb +84 -77
  167. data/modules/mu/{clouds → providers}/google/database.rb +10 -20
  168. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
  169. data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
  170. data/modules/mu/{clouds → providers}/google/function.rb +139 -167
  171. data/modules/mu/{clouds → providers}/google/group.rb +29 -34
  172. data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
  173. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +18 -20
  174. data/modules/mu/{clouds → providers}/google/role.rb +92 -58
  175. data/modules/mu/{clouds → providers}/google/server.rb +242 -155
  176. data/modules/mu/{clouds → providers}/google/server_pool.rb +25 -44
  177. data/modules/mu/{clouds → providers}/google/user.rb +95 -31
  178. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  179. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  180. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  181. data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
  182. data/modules/tests/bucket.yml +4 -0
  183. data/modules/tests/centos6.yaml +11 -0
  184. data/modules/tests/centos7.yaml +11 -0
  185. data/modules/tests/centos8.yaml +12 -0
  186. data/modules/tests/ecs.yaml +23 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/rds.yaml +108 -0
  189. data/modules/tests/regrooms/aws-iam.yaml +201 -0
  190. data/modules/tests/regrooms/bucket.yml +19 -0
  191. data/modules/tests/regrooms/rds.yaml +123 -0
  192. data/modules/tests/server-with-scrub-muisms.yaml +1 -0
  193. data/modules/tests/super_simple_bok.yml +1 -3
  194. data/modules/tests/win2k12.yaml +17 -5
  195. data/modules/tests/win2k16.yaml +25 -0
  196. data/modules/tests/win2k19.yaml +25 -0
  197. data/requirements.txt +1 -0
  198. data/spec/mu/clouds/azure_spec.rb +2 -2
  199. metadata +232 -154
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1985
@@ -64,10 +64,9 @@ module MU
64
64
  size = @config['basis']['launch_config']['size']
65
65
  @config['image_id'] = @config['basis']['launch_config']['image_id']
66
66
  end
67
- az = @config['availability_zone']
68
- if az.nil?
69
- az = MU::Cloud::Google.listAZs(@config['region']).sample
70
- end
67
+ # XXX this should create a non-regional instance group
68
+ # az = @config['availability_zone']
69
+ # az ||= MU::Cloud::Google.listAZs(@config['region']).sample
71
70
 
72
71
  metadata = { # :items?
73
72
  "startup-script" => @userdata
@@ -90,8 +89,8 @@ module MU
90
89
  machine_type: size,
91
90
  service_accounts: [@service_acct],
92
91
  labels: labels,
93
- disks: MU::Cloud::Google::Server.diskConfig(@config, false, false, credentials: @config['credentials']),
94
- network_interfaces: MU::Cloud::Google::Server.interfaceConfig(@config, @vpc),
92
+ disks: MU::Cloud.resourceClass("Google", "Server").diskConfig(@config, false, false, credentials: @config['credentials']),
93
+ network_interfaces: MU::Cloud.resourceClass("Google", "Server").interfaceConfig(@config, @vpc),
95
94
  metadata: metadata,
96
95
  tags: MU::Cloud::Google.compute(:Tags).new(items: [MU::Cloud::Google.nameStr(@mu_name)])
97
96
  )
@@ -170,8 +169,7 @@ module MU
170
169
  # Locate an existing ServerPool or ServerPools and return an array containing matching Google resource descriptors for those that match.
171
170
  # @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching ServerPools
172
171
  def self.find(**args)
173
- args[:project] ||= args[:habitat]
174
- args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
172
+ args = MU::Cloud::Google.findLocationArgs(args)
175
173
 
176
174
  regions = if args[:region]
177
175
  [args[:region]]
@@ -213,7 +211,7 @@ module MU
213
211
  # Reverse-map our cloud description into a runnable config hash.
214
212
  # We assume that any values we have in +@config+ are placeholders, and
215
213
  # calculate our own accordingly based on what's live in the cloud.
216
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
214
+ def toKitten(**_args)
217
215
  bok = {
218
216
  "cloud" => "Google",
219
217
  "credentials" => @credentials,
@@ -326,11 +324,11 @@ end
326
324
  def self.schema(config)
327
325
  toplevel_required = []
328
326
  schema = {
329
- "ssh_user" => MU::Cloud::Google::Server.schema(config)[1]["ssh_user"],
330
- "metadata" => MU::Cloud::Google::Server.schema(config)[1]["metadata"],
331
- "service_account" => MU::Cloud::Google::Server.schema(config)[1]["service_account"],
332
- "scopes" => MU::Cloud::Google::Server.schema(config)[1]["scopes"],
333
- "network_tags" => MU::Cloud::Google::Server.schema(config)[1]["network_tags"],
327
+ "ssh_user" => MU::Cloud.resourceClass("Google", "Server").schema(config)[1]["ssh_user"],
328
+ "metadata" => MU::Cloud.resourceClass("Google", "Server").schema(config)[1]["metadata"],
329
+ "service_account" => MU::Cloud.resourceClass("Google", "Server").schema(config)[1]["service_account"],
330
+ "scopes" => MU::Cloud.resourceClass("Google", "Server").schema(config)[1]["scopes"],
331
+ "network_tags" => MU::Cloud.resourceClass("Google", "Server").schema(config)[1]["network_tags"],
334
332
  "availability_zone" => {
335
333
  "type" => "string",
336
334
  "description" => "Target a specific availability zone for this pool, which will create zonal instance managers and scalers instead of regional ones."
@@ -362,7 +360,7 @@ end
362
360
  # @return [Boolean]: True if validation succeeded, False otherwise
363
361
  def self.validateConfig(pool, configurator)
364
362
  ok = true
365
- start = Time.now
363
+ #start = Time.now
366
364
  pool['project'] ||= MU::Cloud::Google.defaultProject(pool['credentials'])
367
365
  if pool['service_account']
368
366
  pool['service_account']['cloud'] = "Google"
@@ -373,29 +371,7 @@ start = Time.now
373
371
  ok = false
374
372
  end
375
373
  else
376
- user = {
377
- "name" => pool['name'],
378
- "cloud" => "Google",
379
- "project" => pool["project"],
380
- "credentials" => pool["credentials"],
381
- "type" => "service"
382
- }
383
- if user["name"].length < 6
384
- user["name"] += Password.pronounceable(6)
385
- end
386
- configurator.insertKitten(user, "users", true)
387
- pool['dependencies'] ||= []
388
- pool['service_account'] = MU::Config::Ref.get(
389
- type: "users",
390
- cloud: "Google",
391
- name: pool["name"],
392
- project: pool["project"],
393
- credentials: pool["credentials"]
394
- )
395
- pool['dependencies'] << {
396
- "type" => "user",
397
- "name" => pool["name"]
398
- }
374
+ pool = MU::Cloud::Google::User.genericServiceAccount(pool, configurator)
399
375
  end
400
376
 
401
377
  pool['named_ports'] ||= []
@@ -406,7 +382,7 @@ start = Time.now
406
382
  if pool['basis']['launch_config']
407
383
  launch = pool["basis"]["launch_config"]
408
384
 
409
- launch['size'] = MU::Cloud::Google::Server.validateInstanceType(launch["size"], pool["region"])
385
+ launch['size'] = MU::Cloud.resourceClass("Google", "Server").validateInstanceType(launch["size"], pool["region"])
410
386
  ok = false if launch['size'].nil?
411
387
 
412
388
  if launch['image_id'].nil?
@@ -421,7 +397,7 @@ start = Time.now
421
397
 
422
398
  real_image = nil
423
399
  begin
424
- real_image = MU::Cloud::Google::Server.fetchImage(launch['image_id'].to_s, credentials: pool['credentials'])
400
+ real_image = MU::Cloud.resourceClass("Google", "Server").fetchImage(launch['image_id'].to_s, credentials: pool['credentials'])
425
401
  rescue ::Google::Apis::ClientError => e
426
402
  MU.log e.inspect, MU::WARN
427
403
  end
@@ -456,14 +432,19 @@ start = Time.now
456
432
  # @param region [String]: The cloud provider region
457
433
  # @return [void]
458
434
  def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
459
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
460
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
435
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
436
+ return if !MU::Cloud.resourceClass("Google", "Habitat").isLive?(flags["habitat"], credentials)
437
+ filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
438
+ if !ignoremaster and MU.mu_public_ip
439
+ filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
440
+ end
441
+ MU.log "Placeholder: Google ServerPool artifacts do not support labels, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: filter
461
442
 
462
443
  if !flags["global"]
463
444
  ["region_autoscaler", "region_instance_group_manager"].each { |type|
464
445
  MU::Cloud::Google.compute(credentials: credentials).delete(
465
446
  type,
466
- flags["project"],
447
+ flags["habitat"],
467
448
  region,
468
449
  noop
469
450
  )
@@ -471,7 +452,7 @@ start = Time.now
471
452
  else
472
453
  MU::Cloud::Google.compute(credentials: credentials).delete(
473
454
  "instance_template",
474
- flags["project"],
455
+ flags["habitat"],
475
456
  noop
476
457
  )
477
458
  end
@@ -26,10 +26,12 @@ module MU
26
26
  # If we're being reverse-engineered from a cloud descriptor, use that
27
27
  # to determine what sort of account we are.
28
28
  if args[:from_cloud_desc]
29
+ @cloud_desc_cache = args[:from_cloud_desc]
29
30
  MU::Cloud::Google.admin_directory
30
31
  MU::Cloud::Google.iam
31
32
  if args[:from_cloud_desc].class == ::Google::Apis::AdminDirectoryV1::User
32
33
  @config['type'] = "interactive"
34
+ @cloud_id = args[:from_cloud_desc].primary_email
33
35
  elsif args[:from_cloud_desc].class == ::Google::Apis::IamV1::ServiceAccount
34
36
  @config['type'] = "service"
35
37
  @config['name'] = args[:from_cloud_desc].display_name
@@ -48,6 +50,10 @@ module MU
48
50
  @config['name']
49
51
  end
50
52
 
53
+ if @config['type'] == "interactive" and @config['email']
54
+ @cloud_id ||= @config['email']
55
+ end
56
+
51
57
  end
52
58
 
53
59
  # Called automatically by {MU::Deploy#createResources}
@@ -58,7 +64,7 @@ module MU
58
64
  account_id: acct_id,
59
65
  service_account: MU::Cloud::Google.iam(:ServiceAccount).new(
60
66
  display_name: @mu_name,
61
- description: @config['scrub_mu_isms'] ? nil : @deploy.deploy_id
67
+ description: @config['scrub_mu_isms'] ? @config['description'] : @deploy.deploy_id
62
68
  )
63
69
  )
64
70
  if @config['use_if_exists']
@@ -90,7 +96,7 @@ module MU
90
96
  end
91
97
  elsif @config['external']
92
98
  @cloud_id = @config['email']
93
- MU::Cloud::Google::Role.bindFromConfig("user", @cloud_id, @config['roles'], credentials: @config['credentials'])
99
+ MU::Cloud.resourceClass("Google", "Role").bindFromConfig("user", @cloud_id, @config['roles'], credentials: @config['credentials'])
94
100
  else
95
101
  if !@config['email']
96
102
  domains = MU::Cloud::Google.admin_directory(credentials: @credentials).list_domains(@customer)
@@ -122,10 +128,10 @@ module MU
122
128
  # Called automatically by {MU::Deploy#createResources}
123
129
  def groom
124
130
  if @config['external']
125
- MU::Cloud::Google::Role.bindFromConfig("user", @cloud_id, @config['roles'], credentials: @config['credentials'])
131
+ MU::Cloud.resourceClass("Google", "Role").bindFromConfig("user", @cloud_id, @config['roles'], credentials: @config['credentials'])
126
132
  elsif @config['type'] == "interactive"
127
133
  need_update = false
128
- MU::Cloud::Google::Role.bindFromConfig("user", @cloud_id, @config['roles'], credentials: @config['credentials'])
134
+ MU::Cloud.resourceClass("Google", "Role").bindFromConfig("user", @cloud_id, @config['roles'], credentials: @config['credentials'])
129
135
 
130
136
  if @config['force_password_change'] and !cloud_desc.change_password_at_next_login
131
137
  MU.log "Forcing #{@mu_name} to change their password at next login", MU::NOTICE
@@ -170,7 +176,7 @@ module MU
170
176
  end
171
177
 
172
178
  else
173
- MU::Cloud::Google::Role.bindFromConfig("serviceAccount", @cloud_id.gsub(/.*?\/([^\/]+)$/, '\1'), @config['roles'], credentials: @config['credentials'])
179
+ MU::Cloud.resourceClass("Google", "Role").bindFromConfig("serviceAccount", @cloud_id.gsub(/.*?\/([^\/]+)$/, '\1'), @config['roles'], credentials: @config['credentials'])
174
180
  if @config['create_api_key']
175
181
  resp = MU::Cloud::Google.iam(credentials: @config['credentials']).list_project_service_account_keys(
176
182
  cloud_desc.name
@@ -187,21 +193,36 @@ module MU
187
193
  end
188
194
  end
189
195
 
196
+ @cloud_desc_cache = nil
190
197
  # Retrieve the cloud descriptor for this resource.
191
198
  # @return [Google::Apis::Core::Hashable]
192
- def cloud_desc
199
+ def cloud_desc(use_cache: true)
200
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
193
201
  if @config['type'] == "interactive" or !@config['type']
194
202
  @config['type'] ||= "interactive"
195
203
  if !@config['external']
196
- return MU::Cloud::Google.admin_directory(credentials: @config['credentials']).get_user(@cloud_id)
204
+ @cloud_id ||= @config['email']
205
+ @cloud_desc_cache = MU::Cloud::Google.admin_directory(credentials: @config['credentials']).get_user(@cloud_id)
197
206
  else
198
207
  return nil
199
208
  end
200
209
  else
201
210
  @config['type'] ||= "service"
202
- MU::Cloud::Google.iam(credentials: @config['credentials']).get_project_service_account(@cloud_id)
211
+ # this often fails even when it succeeded earlier, so try to be
212
+ # resilient on GCP's behalf
213
+ retries = 0
214
+ begin
215
+ @cloud_desc_cache = MU::Cloud::Google.iam(credentials: @config['credentials']).get_project_service_account(@cloud_id)
216
+ rescue ::Google::Apis::ClientError => e
217
+ if e.message.match(/notFound:/) and retries < 10
218
+ sleep 3
219
+ retries += 1
220
+ retry
221
+ end
222
+ end
203
223
  end
204
224
 
225
+ @cloud_desc_cache
205
226
  end
206
227
 
207
228
  # Return the metadata for this user configuration
@@ -212,7 +233,7 @@ module MU
212
233
  else
213
234
  {}
214
235
  end
215
- description.delete(:etag)
236
+ description.delete(:etag) if description
216
237
  description
217
238
  end
218
239
 
@@ -232,12 +253,17 @@ module MU
232
253
  # Remove all users associated with the currently loaded deployment.
233
254
  # @param noop [Boolean]: If true, will only print what would be done
234
255
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
235
- # @param region [String]: The cloud provider region
236
256
  # @return [void]
237
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
238
- my_domains = MU::Cloud::Google.getDomains(credentials)
257
+ def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
258
+ MU::Cloud::Google.getDomains(credentials)
239
259
  my_org = MU::Cloud::Google.getOrg(credentials)
240
260
 
261
+ filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
262
+ if !ignoremaster and MU.mu_public_ip
263
+ filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
264
+ end
265
+ MU.log "Placeholder: Google User artifacts do not support labels, so ignoremaster cleanup flag has no effect", MU::DEBUG, details: filter
266
+
241
267
  # We don't have a good way of tagging directory users, so we rely
242
268
  # on the known parameter, which is pulled from deployment metadata
243
269
  if flags['known'] and my_org
@@ -256,15 +282,15 @@ module MU
256
282
  next if user_email.nil?
257
283
  next if !user_email.match(/^[^\/]+@[^\/]+$/)
258
284
 
259
- MU::Cloud::Google::Role.removeBindings("user", user_email, credentials: credentials, noop: noop)
285
+ MU::Cloud.resourceClass("Google", "Role").removeBindings("user", user_email, credentials: credentials, noop: noop)
260
286
  }
261
287
 
262
288
  end
263
289
  end
264
290
 
265
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
291
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
266
292
  resp = MU::Cloud::Google.iam(credentials: credentials).list_project_service_accounts(
267
- "projects/"+flags["project"]
293
+ "projects/"+flags["habitat"]
268
294
  )
269
295
 
270
296
  if resp and resp.accounts and MU.deploy_id
@@ -319,7 +345,7 @@ module MU
319
345
  MU::Cloud::Google.iam(credentials: args[:credentials]).list_project_service_accounts(
320
346
  "projects/"+args[:project]
321
347
  )
322
- rescue ::Google::Apis::ClientError => e
348
+ rescue ::Google::Apis::ClientError
323
349
  MU.log "Do not have permissions to retrieve service accounts for project #{args[:project]}", MU::WARN
324
350
  end
325
351
 
@@ -337,8 +363,10 @@ module MU
337
363
  else
338
364
  if cred_cfg['masquerade_as']
339
365
  resp = MU::Cloud::Google.admin_directory(credentials: args[:credentials]).list_users(customer: MU::Cloud::Google.customerID(args[:credentials]), show_deleted: false)
366
+ # XXX this ain't exactly performant, do some caching or something
340
367
  if resp and resp.users
341
368
  resp.users.each { |u|
369
+ next if args[:cloud_id] and !args[:cloud_id] != u.primary_email
342
370
  found[u.primary_email] = u
343
371
  }
344
372
  end
@@ -355,6 +383,7 @@ module MU
355
383
  name.match(/\b\d+\-compute@developer\.gserviceaccount\.com$/) or
356
384
  name.match(/\bproject-\d+@storage-transfer-service\.iam\.gserviceaccount\.com$/) or
357
385
  name.match(/\b\d+@cloudbuild\.gserviceaccount\.com$/) or
386
+ name.match(/\b\d+@cloudservices\.gserviceaccount\.com$/) or
358
387
  name.match(/\bservice-\d+@containerregistry\.iam\.gserviceaccount\.com$/) or
359
388
  name.match(/\bservice-\d+@container-analysis\.iam\.gserviceaccount\.com$/) or
360
389
  name.match(/\bservice-\d+@compute-system\.iam\.gserviceaccount\.com$/) or
@@ -382,7 +411,7 @@ module MU
382
411
  # Reverse-map our cloud description into a runnable config hash.
383
412
  # We assume that any values we have in +@config+ are placeholders, and
384
413
  # calculate our own accordingly based on what's live in the cloud.
385
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
414
+ def toKitten(**_args)
386
415
  if MU::Cloud::Google::User.cannedServiceAcctName?(@cloud_id)
387
416
  return nil
388
417
  end
@@ -397,7 +426,7 @@ module MU
397
426
  return nil
398
427
  end
399
428
 
400
- user_roles = MU::Cloud::Google::Role.getAllBindings(@config['credentials'])["by_entity"]
429
+ user_roles = MU::Cloud.resourceClass("Google", "Role").getAllBindings(@config['credentials'])["by_entity"]
401
430
 
402
431
  if cloud_desc.nil?
403
432
  MU.log "FAILED TO FIND CLOUD DESCRIPTOR FOR #{self}", MU::ERR, details: @config
@@ -410,6 +439,10 @@ module MU
410
439
 
411
440
  if bok['type'] == "service"
412
441
  bok['name'].gsub!(/@.*/, '')
442
+ if cloud_desc.description and !cloud_desc.description.empty? and
443
+ !cloud_desc.description.match(/^[A-Z0-9_-]+-[A-Z0-9_-]+-\d{10}-[A-Z]{2}$/)
444
+ bok['description'] = cloud_desc.description
445
+ end
413
446
  bok['project'] = @project_id
414
447
  keys = MU::Cloud::Google.iam(credentials: @config['credentials']).list_project_service_account_keys(@cloud_id)
415
448
 
@@ -420,13 +453,13 @@ module MU
420
453
  if user_roles["serviceAccount"] and
421
454
  user_roles["serviceAccount"][bok['cloud_id']] and
422
455
  user_roles["serviceAccount"][bok['cloud_id']].size > 0
423
- bok['roles'] = MU::Cloud::Google::Role.entityBindingsToSchema(user_roles["serviceAccount"][bok['cloud_id']])
456
+ bok['roles'] = MU::Cloud.resourceClass("Google", "Role").entityBindingsToSchema(user_roles["serviceAccount"][bok['cloud_id']])
424
457
  end
425
458
  else
426
459
  if user_roles["user"] and
427
460
  user_roles["user"][bok['cloud_id']] and
428
461
  user_roles["user"][bok['cloud_id']].size > 0
429
- bok['roles'] = MU::Cloud::Google::Role.entityBindingsToSchema(user_roles["user"][bok['cloud_id']], credentials: @config['credentials'])
462
+ bok['roles'] = MU::Cloud.resourceClass("Google", "Role").entityBindingsToSchema(user_roles["user"][bok['cloud_id']], credentials: @config['credentials'])
430
463
  end
431
464
  bok['given_name'] = cloud_desc.name.given_name if cloud_desc.name.given_name and !cloud_desc.name.given_name.empty?
432
465
  bok['family_name'] = cloud_desc.name.family_name if cloud_desc.name.family_name and !cloud_desc.name.family_name.empty?
@@ -441,9 +474,9 @@ module MU
441
474
  end
442
475
 
443
476
  # Cloud-specific configuration properties.
444
- # @param config [MU::Config]: The calling MU::Config object
477
+ # @param _config [MU::Config]: The calling MU::Config object
445
478
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
446
- def self.schema(config)
479
+ def self.schema(_config)
447
480
  toplevel_required = []
448
481
  schema = {
449
482
  "name" => {
@@ -482,6 +515,10 @@ If we are binding (rather than creating) a user and no roles are specified, we w
482
515
  "type" => "string",
483
516
  "description" => "Alias for +family_name+"
484
517
  },
518
+ "description" => {
519
+ "type" => "string",
520
+ "description" => "Comment field for service accounts, which we normally use to store the originating deploy's deploy id, since GCP service accounts do not have labels. This field is only honored if +scrub_mu_isms+ is set."
521
+ },
485
522
  "email" => {
486
523
  "type" => "string",
487
524
  "description" => "Canonical email address for a +directory+ user. If not specified, will be set to +name@domain+."
@@ -509,7 +546,7 @@ If we are binding (rather than creating) a user and no roles are specified, we w
509
546
  "roles" => {
510
547
  "type" => "array",
511
548
  "description" => "One or more Google IAM roles to associate with this user.",
512
- "items" => MU::Cloud::Google::Role.ref_schema
549
+ "items" => MU::Cloud.resourceClass("Google", "Role").ref_schema
513
550
  }
514
551
  }
515
552
  [toplevel_required, schema]
@@ -517,9 +554,9 @@ If we are binding (rather than creating) a user and no roles are specified, we w
517
554
 
518
555
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::users}, bare and unvalidated.
519
556
  # @param user [Hash]: The resource to process and validate
520
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
557
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
521
558
  # @return [Boolean]: True if validation succeeded, False otherwise
522
- def self.validateConfig(user, configurator)
559
+ def self.validateConfig(user, _configurator)
523
560
  ok = true
524
561
 
525
562
  my_domains = MU::Cloud::Google.getDomains(user['credentials'])
@@ -595,15 +632,11 @@ If we are binding (rather than creating) a user and no roles are specified, we w
595
632
  ok = false
596
633
  end
597
634
 
598
- user['dependencies'] ||= []
599
635
  if user['roles']
600
636
  user['roles'].each { |r|
601
637
  if r['role'] and r['role']['name'] and
602
638
  (!r['role']['deploy_id'] and !r['role']['id'])
603
- user['dependencies'] << {
604
- "type" => "role",
605
- "name" => r['role']['name']
606
- }
639
+ MU::Config.addDependency(user, r['role']['name'], "role")
607
640
  end
608
641
 
609
642
  if !r["projects"] and !r["organizations"] and !r["folders"]
@@ -621,7 +654,38 @@ If we are binding (rather than creating) a user and no roles are specified, we w
621
654
  ok
622
655
  end
623
656
 
624
- private
657
+ # Create and inject a service account on behalf of the parent resource.
658
+ # Return the modified parent configuration hash with references to the
659
+ # new addition.
660
+ # @param parent [Hash]
661
+ # @param configurator [MU::Config]
662
+ # @return [Hash]
663
+ def self.genericServiceAccount(parent, configurator)
664
+ user = {
665
+ "name" => parent['name'],
666
+ "cloud" => "Google",
667
+ "project" => parent["project"],
668
+ "credentials" => parent["credentials"],
669
+ "type" => "service"
670
+ }
671
+ if user["name"].length < 6
672
+ user["name"] += Password.pronounceable(6)
673
+ end
674
+ if parent['roles']
675
+ user['roles'] = parent['roles'].dup
676
+ end
677
+ configurator.insertKitten(user, "users", true)
678
+ parent['service_account'] = MU::Config::Ref.get(
679
+ type: "users",
680
+ cloud: "Google",
681
+ name: user["name"],
682
+ project: user["project"],
683
+ credentials: user["credentials"]
684
+ )
685
+ MU::Config.addDependency(parent, user['name'], "user")
686
+
687
+ parent
688
+ end
625
689
 
626
690
  end
627
691
  end