cloud-mu 3.1.4 → 3.3.1

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 (203) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  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 +16 -12
  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 -1
  21. data/bin/mu-node-manage +15 -16
  22. data/bin/mu-run-tests +37 -12
  23. data/cloud-mu.gemspec +5 -3
  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 +1 -1
  27. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  28. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  29. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  30. data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
  31. data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
  32. data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
  33. data/extras/clean-stock-amis +25 -19
  34. data/extras/generate-stock-images +1 -0
  35. data/extras/image-generators/AWS/win2k12.yaml +18 -13
  36. data/extras/image-generators/AWS/win2k16.yaml +18 -13
  37. data/extras/image-generators/AWS/win2k19.yaml +21 -0
  38. data/modules/mommacat.ru +1 -1
  39. data/modules/mu.rb +158 -107
  40. data/modules/mu/adoption.rb +386 -59
  41. data/modules/mu/cleanup.rb +214 -303
  42. data/modules/mu/cloud.rb +128 -1632
  43. data/modules/mu/cloud/database.rb +49 -0
  44. data/modules/mu/cloud/dnszone.rb +44 -0
  45. data/modules/mu/cloud/machine_images.rb +212 -0
  46. data/modules/mu/cloud/providers.rb +81 -0
  47. data/modules/mu/cloud/resource_base.rb +926 -0
  48. data/modules/mu/cloud/server.rb +40 -0
  49. data/modules/mu/cloud/server_pool.rb +1 -0
  50. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  51. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  52. data/modules/mu/cloud/wrappers.rb +169 -0
  53. data/modules/mu/config.rb +135 -82
  54. data/modules/mu/config/alarm.rb +2 -6
  55. data/modules/mu/config/bucket.rb +32 -3
  56. data/modules/mu/config/cache_cluster.rb +2 -2
  57. data/modules/mu/config/cdn.rb +100 -0
  58. data/modules/mu/config/collection.rb +1 -1
  59. data/modules/mu/config/container_cluster.rb +7 -2
  60. data/modules/mu/config/database.rb +84 -105
  61. data/modules/mu/config/database.yml +1 -2
  62. data/modules/mu/config/dnszone.rb +5 -4
  63. data/modules/mu/config/doc_helpers.rb +5 -6
  64. data/modules/mu/config/endpoint.rb +2 -1
  65. data/modules/mu/config/firewall_rule.rb +3 -19
  66. data/modules/mu/config/folder.rb +1 -1
  67. data/modules/mu/config/function.rb +17 -8
  68. data/modules/mu/config/group.rb +1 -1
  69. data/modules/mu/config/habitat.rb +1 -1
  70. data/modules/mu/config/job.rb +89 -0
  71. data/modules/mu/config/loadbalancer.rb +57 -11
  72. data/modules/mu/config/log.rb +1 -1
  73. data/modules/mu/config/msg_queue.rb +1 -1
  74. data/modules/mu/config/nosqldb.rb +1 -1
  75. data/modules/mu/config/notifier.rb +8 -19
  76. data/modules/mu/config/ref.rb +92 -14
  77. data/modules/mu/config/role.rb +1 -1
  78. data/modules/mu/config/schema_helpers.rb +38 -37
  79. data/modules/mu/config/search_domain.rb +1 -1
  80. data/modules/mu/config/server.rb +12 -13
  81. data/modules/mu/config/server.yml +1 -0
  82. data/modules/mu/config/server_pool.rb +3 -7
  83. data/modules/mu/config/storage_pool.rb +1 -1
  84. data/modules/mu/config/tail.rb +11 -0
  85. data/modules/mu/config/user.rb +1 -1
  86. data/modules/mu/config/vpc.rb +27 -23
  87. data/modules/mu/config/vpc.yml +0 -1
  88. data/modules/mu/defaults/AWS.yaml +91 -68
  89. data/modules/mu/defaults/Azure.yaml +1 -0
  90. data/modules/mu/defaults/Google.yaml +1 -0
  91. data/modules/mu/deploy.rb +33 -19
  92. data/modules/mu/groomer.rb +16 -1
  93. data/modules/mu/groomers/ansible.rb +123 -21
  94. data/modules/mu/groomers/chef.rb +64 -11
  95. data/modules/mu/logger.rb +120 -144
  96. data/modules/mu/master.rb +97 -4
  97. data/modules/mu/master/ssl.rb +0 -1
  98. data/modules/mu/mommacat.rb +154 -867
  99. data/modules/mu/mommacat/daemon.rb +23 -14
  100. data/modules/mu/mommacat/naming.rb +110 -3
  101. data/modules/mu/mommacat/search.rb +495 -0
  102. data/modules/mu/mommacat/storage.rb +225 -192
  103. data/modules/mu/{clouds → providers}/README.md +1 -1
  104. data/modules/mu/{clouds → providers}/aws.rb +281 -64
  105. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  106. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  107. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  108. data/modules/mu/providers/aws/cdn.rb +782 -0
  109. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  110. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +708 -749
  111. data/modules/mu/providers/aws/database.rb +1744 -0
  112. data/modules/mu/{clouds → providers}/aws/dnszone.rb +75 -57
  113. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  114. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +212 -242
  115. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  116. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  117. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  118. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  119. data/modules/mu/providers/aws/job.rb +466 -0
  120. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +50 -41
  121. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  122. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  123. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  124. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  125. data/modules/mu/{clouds → providers}/aws/role.rb +94 -57
  126. data/modules/mu/{clouds → providers}/aws/search_domain.rb +173 -42
  127. data/modules/mu/{clouds → providers}/aws/server.rb +782 -1107
  128. data/modules/mu/{clouds → providers}/aws/server_pool.rb +36 -46
  129. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  130. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  131. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  132. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  133. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
  134. data/modules/mu/{clouds → providers}/aws/vpc.rb +429 -849
  135. data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
  136. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  137. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  138. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  139. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  140. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  141. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  142. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  143. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  144. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  145. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  146. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  147. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  148. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  149. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  150. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  151. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  152. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  153. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  154. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  155. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  156. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  157. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  158. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  159. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  160. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  161. data/modules/mu/{clouds → providers}/google.rb +29 -6
  162. data/modules/mu/{clouds → providers}/google/bucket.rb +5 -5
  163. data/modules/mu/{clouds → providers}/google/container_cluster.rb +59 -37
  164. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  165. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  166. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  167. data/modules/mu/{clouds → providers}/google/function.rb +14 -8
  168. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  169. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  170. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  171. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  172. data/modules/mu/{clouds → providers}/google/server.rb +142 -55
  173. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  174. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  175. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  176. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  177. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  178. data/modules/mu/{clouds → providers}/google/vpc.rb +46 -15
  179. data/modules/tests/aws-jobs-functions.yaml +46 -0
  180. data/modules/tests/centos6.yaml +15 -0
  181. data/modules/tests/centos7.yaml +15 -0
  182. data/modules/tests/centos8.yaml +12 -0
  183. data/modules/tests/ecs.yaml +23 -0
  184. data/modules/tests/eks.yaml +1 -1
  185. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  186. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  187. data/modules/tests/includes-and-params.yaml +2 -1
  188. data/modules/tests/microservice_app.yaml +288 -0
  189. data/modules/tests/rds.yaml +108 -0
  190. data/modules/tests/regrooms/rds.yaml +123 -0
  191. data/modules/tests/server-with-scrub-muisms.yaml +2 -1
  192. data/modules/tests/super_complex_bok.yml +2 -2
  193. data/modules/tests/super_simple_bok.yml +3 -5
  194. data/modules/tests/win2k12.yaml +25 -0
  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 +169 -93
  200. data/extras/image-generators/AWS/windows.yaml +0 -18
  201. data/modules/mu/clouds/aws/database.rb +0 -1974
  202. data/modules/mu/clouds/aws/endpoint.rb +0 -596
  203. data/modules/tests/needwork/win2k12.yaml +0 -13
@@ -61,7 +61,7 @@ module MU
61
61
  if @config['parent']['name'] and !@config['parent']['id']
62
62
  @config['parent']['deploy_id'] = @deploy.deploy_id
63
63
  end
64
- parent = MU::Cloud::Google::Folder.resolveParent(@config['parent'], credentials: @config['credentials'])
64
+ parent = MU::Cloud.resourceClass("Google", "Folder").resolveParent(@config['parent'], credentials: @config['credentials'])
65
65
  if !parent
66
66
  MU.log "Unable to resolve parent resource of Google Project #{@config['name']}", MU::ERR, details: @config['parent']
67
67
  raise "Unable to resolve parent resource of Google Project #{@config['name']}"
@@ -222,7 +222,7 @@ module MU
222
222
  # @param noop [Boolean]: If true, will only print what would be done
223
223
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
224
224
  # @return [void]
225
- def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
225
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, credentials: nil, flags: {})
226
226
  resp = MU::Cloud::Google.resource_manager(credentials: credentials).list_projects
227
227
 
228
228
  if resp and resp.projects
@@ -285,7 +285,7 @@ module MU
285
285
  next if p.lifecycle_state == "DELETE_REQUESTED"
286
286
  found[p.project_id] = p
287
287
  }
288
- @@list_projects_cache = found
288
+ @@list_projects_cache = found.clone
289
289
  end
290
290
 
291
291
  found
@@ -376,11 +376,7 @@ module MU
376
376
  end
377
377
 
378
378
  if habitat['parent'] and habitat['parent']['name'] and !habitat['parent']['deploy_id'] and configurator.haveLitterMate?(habitat['parent']['name'], "folders")
379
- habitat["dependencies"] ||= []
380
- habitat["dependencies"] << {
381
- "type" => "folder",
382
- "name" => habitat['parent']['name']
383
- }
379
+ MU::Config.addDependency(habitat, habitat['parent']['name'], "folder")
384
380
  end
385
381
 
386
382
  ok
@@ -146,9 +146,9 @@ module MU
146
146
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
147
147
  # @param region [String]: The cloud provider region
148
148
  # @return [void]
149
- def self.cleanup(noop: false, ignoremaster: false, region: nil, credentials: nil, flags: {})
150
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
151
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
149
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: nil, credentials: nil, flags: {})
150
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
151
+ return if !MU::Cloud.resourceClass("Google", "Habitat").isLive?(flags["habitat"], credentials)
152
152
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
153
153
  if !ignoremaster and MU.mu_public_ip
154
154
  filter += %Q{ AND (labels.mu-master-ip = "#{MU.mu_public_ip.gsub(/\./, "_")}")}
@@ -159,7 +159,7 @@ module MU
159
159
  ["forwarding_rule", "region_backend_service"].each { |type|
160
160
  MU::Cloud::Google.compute(credentials: credentials).delete(
161
161
  type,
162
- flags["project"],
162
+ flags["habitat"],
163
163
  region,
164
164
  noop
165
165
  )
@@ -170,7 +170,7 @@ module MU
170
170
  ["global_forwarding_rule", "target_http_proxy", "target_https_proxy", "url_map", "backend_service", "health_check", "http_health_check", "https_health_check"].each { |type|
171
171
  MU::Cloud::Google.compute(credentials: credentials).delete(
172
172
  type,
173
- flags["project"],
173
+ flags["habitat"],
174
174
  nil,
175
175
  noop
176
176
  )
@@ -271,13 +271,13 @@ module MU
271
271
  my_org = MU::Cloud::Google.getOrg(credentials)
272
272
  if my_org
273
273
  scopes["organizations"] = [my_org.name]
274
- folders = MU::Cloud::Google::Folder.find(credentials: credentials)
274
+ folders = MU::Cloud.resourceClass("Google", "Folder").find(credentials: credentials)
275
275
  if folders and folders.size > 0
276
276
  scopes["folders"] = folders.keys
277
277
  end
278
278
  end
279
279
 
280
- projects = MU::Cloud::Google::Habitat.find(credentials: credentials)
280
+ projects = MU::Cloud.resourceClass("Google", "Habitat").find(credentials: credentials)
281
281
  if projects and projects.size > 0
282
282
  scopes["projects"] = projects.keys
283
283
  end
@@ -407,12 +407,12 @@ module MU
407
407
  # email field (which is the "real" id most of the time)
408
408
  real_id = nil
409
409
  if entity_type == "group"
410
- found = MU::Cloud::Google::Group.find(cloud_id: entity_id, credentials: credentials)
410
+ found = MU::Cloud.resourceClass("Google", "Group").find(cloud_id: entity_id, credentials: credentials)
411
411
  if found[entity_id]
412
412
  real_id = found[entity_id].id
413
413
  end
414
414
  elsif entity_type == "user"
415
- found = MU::Cloud::Google::User.find(cloud_id: entity_id, credentials: credentials)
415
+ found = MU::Cloud.resourceClass("Google", "User").find(cloud_id: entity_id, credentials: credentials)
416
416
  if found[entity_id]
417
417
  real_id = found[entity_id].id
418
418
  end
@@ -465,7 +465,7 @@ module MU
465
465
  # @param noop [Boolean]: If true, will only print what would be done
466
466
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
467
467
  # @return [void]
468
- def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
468
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, credentials: nil, flags: {})
469
469
  customer = MU::Cloud::Google.customerID(credentials)
470
470
  my_org = MU::Cloud::Google.getOrg(credentials)
471
471
 
@@ -563,7 +563,7 @@ module MU
563
563
  if args[:project]
564
564
  canned = Hash[MU::Cloud::Google.iam(credentials: args[:credentials]).list_roles.roles.map { |r| [r.name, r] }]
565
565
  begin
566
- MU::Cloud::Google::Habitat.bindings(args[:project], credentials: args[:credentials]).each { |binding|
566
+ MU::Cloud.resourceClass("Google", "Habitat").bindings(args[:project], credentials: args[:credentials]).each { |binding|
567
567
  found[binding.role] = canned[binding.role]
568
568
  }
569
569
  rescue ::Google::Apis::ClientError => e
@@ -591,10 +591,15 @@ module MU
591
591
  bindings['by_scope']['projects'][args[:project]]
592
592
  bindings['by_scope']['projects'][args[:project]].keys.each { |r|
593
593
  if r.match(/^roles\//)
594
- role = MU::Cloud::Google.iam(credentials: args[:credentials]).get_role(r)
595
- found[role.name] = role
594
+ begin
595
+ role = MU::Cloud::Google.iam(credentials: args[:credentials]).get_role(r)
596
+ found[role.name] = role
597
+ rescue ::Google::Apis::ClientError => e
598
+ raise e if !e.message.match(/(?:forbidden|notFound): /)
599
+ MU.log "Failed MU::Cloud::Google.iam(credentials: #{args[:credentials]}).get_role(#{r})", MU::WARN, details: e.message
600
+ end
596
601
  elsif !found[r]
597
- MU.log "NEED TO GET #{r}", MU::WARN
602
+ # MU.log "NEED TO GET #{r}", MU::WARN
598
603
  end
599
604
  }
600
605
  end
@@ -688,7 +693,7 @@ module MU
688
693
  ids, _names, _privs = MU::Cloud::Google::Role.privilege_service_to_name(@config['credentials'])
689
694
  cloud_desc.role_privileges.each { |priv|
690
695
  if !ids[priv.service_id]
691
- MU.log "Role privilege defined for a service id with no name I can find, writing with raw id", MU::WARN, details: priv
696
+ MU.log "Role privilege defined for a service id with no name I can find, writing with raw id", MU::DEBUG, details: priv
692
697
  bok["import"] << priv.service_id+"/"+priv.privilege_name
693
698
  else
694
699
  bok["import"] << ids[priv.service_id]+"/"+priv.privilege_name
@@ -731,26 +736,45 @@ module MU
731
736
  bindings[scopetype].each_pair { |scope_id, entity_types|
732
737
  # If we've been given a habitat filter, skip over bindings
733
738
  # that don't match it.
734
- if scopetype == "projects" and args[:habitats] and
735
- !args[:habitats].empty? and
736
- !args[:habitats].include?(scope_id)
737
- next
739
+ if scopetype == "projects"
740
+ if (args[:habitats] and !args[:habitats].empty? and
741
+ !args[:habitats].include?(scope_id)) or
742
+ !MU::Cloud::Google.listHabitats(@credentials).include?(scope_id)
743
+ next
744
+ end
738
745
  end
739
746
 
740
747
  entity_types.each_pair { |entity_type, entities|
741
748
  mu_entitytype = (entity_type == "serviceAccount" ? "user" : entity_type)+"s"
742
749
  entities.each { |entity|
750
+ next if entity.nil?
751
+ foreign = if entity_type == "serviceAccount" and entity.match(/@(.*?)\.iam\.gserviceaccount\.com/)
752
+ !MU::Cloud::Google.listHabitats(@credentials).include?(Regexp.last_match[1])
753
+ end
754
+
743
755
  entity_ref = if entity_type == "organizations"
744
756
  { "id" => ((org == my_org.name and @config['credentials']) ? @config['credentials'] : org) }
745
757
  elsif entity_type == "domain"
746
758
  { "id" => entity }
747
759
  else
760
+ shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(mu_entitytype)
761
+ if args[:types].include?(shortclass) and
762
+ !(entity_type == "serviceAccount" and
763
+ MU::Cloud::Google::User.cannedServiceAcctName?(entity))
764
+ MU.log "Role #{@cloud_id}: Skipping #{shortclass} binding for #{entity}; we are adopting that type and will set bindings from that resource", MU::DEBUG
765
+ next
766
+ end
767
+
748
768
  MU::Config::Ref.get(
749
769
  id: entity,
750
770
  cloud: "Google",
751
771
  type: mu_entitytype
752
772
  )
753
773
  end
774
+ if entity_ref.nil?
775
+ MU.log "I somehow ended up with a nil entity reference for #{entity_type} #{entity}", MU::ERR, details: [ bok, bindings ]
776
+ next
777
+ end
754
778
  refmap ||= {}
755
779
  refmap[entity_ref] ||= {}
756
780
  refmap[entity_ref][scopetype] ||= []
@@ -769,6 +793,7 @@ module MU
769
793
  }
770
794
  }
771
795
  }
796
+
772
797
  bok["bindings"] ||= []
773
798
  refmap.each_pair { |entity, scopes|
774
799
  newbinding = { "entity" => entity }
@@ -891,7 +916,7 @@ module MU
891
916
  end
892
917
 
893
918
  role = MU::Cloud::Google.admin_directory(credentials: credentials).get_role(MU::Cloud::Google.customerID(credentials), binding.role_id)
894
- MU.log "Failed to find entity #{binding.assigned_to} referenced in GSuite/Cloud Identity binding to role #{role.role_name}", MU::WARN, details: role
919
+ MU.log "Failed to find entity #{binding.assigned_to} referenced in GSuite/Cloud Identity binding to role #{role.role_name}", MU::DEBUG, details: role
895
920
  }
896
921
 
897
922
  resp = MU::Cloud::Google.resource_manager(credentials: credentials).get_organization_iam_policy(my_org.name)
@@ -899,15 +924,17 @@ module MU
899
924
  insertBinding("organizations", my_org.name, binding)
900
925
  }
901
926
 
902
- MU::Cloud::Google::Folder.find(credentials: credentials).keys.each { |folder|
903
- MU::Cloud::Google::Folder.bindings(folder, credentials: credentials).each { |binding|
927
+ MU::Cloud.resourceClass("Google", "Folder").find(credentials: credentials).keys.each { |folder|
928
+ folder_bindings = MU::Cloud.resourceClass("Google", "Folder").bindings(folder, credentials: credentials)
929
+ next if !folder_bindings
930
+ folder_bindings.each { |binding|
904
931
  insertBinding("folders", folder, binding)
905
932
  }
906
933
  }
907
934
  end
908
- MU::Cloud::Google::Habitat.find(credentials: credentials).keys.each { |project|
935
+ MU::Cloud::Google.listHabitats(credentials).each { |project|
909
936
  begin
910
- MU::Cloud::Google::Habitat.bindings(project, credentials: credentials).each { |binding|
937
+ MU::Cloud.resourceClass("Google", "Habitat").bindings(project, credentials: credentials).each { |binding|
911
938
  insertBinding("projects", project, binding)
912
939
  }
913
940
  rescue ::Google::Apis::ClientError => e
@@ -1090,7 +1117,7 @@ If this value is not specified, and the role name matches the name of an existin
1090
1117
  MU.log "None of the directory service privileges available to credentials #{role['credentials']} map to the ones declared for role #{role['name']}", MU::ERR, details: role['import'].sort
1091
1118
  ok = false
1092
1119
  elsif missing.size > 0
1093
- MU.log "Some directory service privileges declared for role #{role['name']} aren't available to credentials #{role['credentials']}, will skip", MU::WARN, details: missing
1120
+ MU.log "Some directory service privileges declared for role #{role['name']} aren't available to credentials #{role['credentials']}, will skip", MU::DEBUG, details: missing
1094
1121
  end
1095
1122
  end
1096
1123
  end
@@ -1105,11 +1132,7 @@ If this value is not specified, and the role name matches the name of an existin
1105
1132
  if role['role_source'] == "project"
1106
1133
  role['project'] ||= MU::Cloud::Google.defaultProject(role['credentials'])
1107
1134
  if configurator.haveLitterMate?(role['project'], "habitats")
1108
- role['dependencies'] ||= []
1109
- role['dependencies'] << {
1110
- "type" => "habitat",
1111
- "name" => role['project']
1112
- }
1135
+ MU::Config.addDependency(role, role['project'], "habitat")
1113
1136
  end
1114
1137
  end
1115
1138
 
@@ -1117,14 +1140,10 @@ If this value is not specified, and the role name matches the name of an existin
1117
1140
  role['bindings'].each { |binding|
1118
1141
  if binding['entity'] and binding['entity']['name'] and
1119
1142
  configurator.haveLitterMate?(binding['entity']['name'], binding['entity']['type'])
1120
- role['dependencies'] ||= []
1121
- role['dependencies'] << {
1122
- "type" => binding['entity']['type'].sub(/s$/, ''),
1123
- "name" => binding['entity']['name']
1124
- }
1125
-
1143
+ MU::Config.addDependency(role, binding['entity']['name'], binding['entity']['type'])
1126
1144
  end
1127
1145
  }
1146
+ role['bindings'].uniq!
1128
1147
  end
1129
1148
 
1130
1149
  ok
@@ -164,19 +164,26 @@ module MU
164
164
  def self.diskConfig(config, create = true, disk_as_url = true, credentials: nil)
165
165
  disks = []
166
166
  if config['image_id'].nil? and config['basis'].nil?
167
- pp config.keys
168
167
  raise MuError, "Can't generate disk configuration for server #{config['name']} without an image ID or basis specified"
169
168
  end
170
169
 
171
170
  img = fetchImage(config['image_id'] || config['basis']['launch_config']['image_id'], credentials: credentials)
172
171
 
173
- # XXX slurp settings from /dev/sda or w/e by convention?
172
+ # if img.source_disk and img.source_disk.match(/projects\/([^\/]+)\/zones\/([^\/]+)\/disks\/(.*)/)
173
+ # _junk, proj, az, name = Regexp.last_match
174
+ # disk_desc = MU::Cloud::Google.compute(credentials: credentials).get_disk(proj, az, name)
175
+ # pp disk_desc
176
+ # raise "nah"
177
+ # end
178
+
174
179
  disktype = "projects/#{config['project']}/zones/#{config['availability_zone']}/diskTypes/pd-standard"
175
- disktype = "pd-standard" if !disk_as_url
176
- # disk_type: projects/project/zones/#{config['availability_zone']}/diskTypes/pd-standard Other values include pd-ssd and local-ssd
180
+
181
+
182
+ disktype.gsub!(/.*?\/([^\/])$/, '\1') if !disk_as_url
183
+
177
184
  imageobj = MU::Cloud::Google.compute(:AttachedDiskInitializeParams).new(
178
185
  source_image: img.self_link,
179
- disk_size_gb: 10, # this is binary? 2gb, that says
186
+ disk_size_gb: img.disk_size_gb,
180
187
  disk_type: disktype,
181
188
  )
182
189
  disks << MU::Cloud::Google.compute(:AttachedDisk).new(
@@ -329,7 +336,7 @@ next if !create
329
336
  end
330
337
  metadata["startup-script"] = @userdata if @userdata and !@userdata.empty?
331
338
 
332
- deploykey = @config['ssh_user']+":"+@deploy.ssh_public_key
339
+ deploykey = @config["ssh_user"]+":"+@deploy.ssh_public_key
333
340
  if metadata["ssh-keys"]
334
341
  metadata["ssh-keys"] += "\n"+deploykey
335
342
  else
@@ -485,7 +492,7 @@ next if !create
485
492
  return nil if @config.nil? or @deploy.nil?
486
493
 
487
494
  nat_ssh_key = nat_ssh_user = nat_ssh_host = nil
488
- if !@config["vpc"].nil? and !MU::Cloud::Google::VPC.haveRouteToInstance?(cloud_desc, credentials: @config['credentials'])
495
+ if !@config["vpc"].nil? and !MU::Cloud.resourceClass("Google", "VPC").haveRouteToInstance?(cloud_desc, credentials: @config['credentials'])
489
496
 
490
497
  if !@nat.nil?
491
498
  if @nat.cloud_desc.nil?
@@ -502,7 +509,8 @@ next if !create
502
509
 
503
510
  if @config['ssh_user'].nil?
504
511
  if windows?
505
- @config['ssh_user'] = "Administrator"
512
+ @config['ssh_user'] = @config['windows_admin_user']
513
+ @config['ssh_user'] ||= "Administrator"
506
514
  else
507
515
  @config['ssh_user'] = "root"
508
516
  end
@@ -615,7 +623,7 @@ next if !create
615
623
  end
616
624
 
617
625
  _nat_ssh_key, _nat_ssh_user, nat_ssh_host, _canonical_ip, _ssh_user, _ssh_key_name = getSSHConfig
618
- if !nat_ssh_host and !MU::Cloud::Google::VPC.haveRouteToInstance?(cloud_desc, credentials: @config['credentials'])
626
+ if !nat_ssh_host and !MU::Cloud.resourceClass("Google", "VPC").haveRouteToInstance?(cloud_desc, credentials: @config['credentials'])
619
627
  # XXX check if canonical_ip is in the private ranges
620
628
  # raise MuError, "#{node} has no NAT host configured, and I have no other route to it"
621
629
  end
@@ -800,29 +808,6 @@ next if !create
800
808
 
801
809
  # punchAdminNAT
802
810
 
803
- # MU::Cloud::AWS::Server.tagVolumes(@cloud_id)
804
-
805
- # If we have a loadbalancer configured, attach us to it
806
- # if !@config['loadbalancers'].nil?
807
- # if @loadbalancers.nil?
808
- # raise MuError, "#{@mu_name} is configured to use LoadBalancers, but none have been loaded by dependencies()"
809
- # end
810
- # @loadbalancers.each { |lb|
811
- # lb.registerNode(@cloud_id)
812
- # }
813
- # end
814
-
815
- # Let us into any databases we depend on.
816
- # This is probelmtic with autscaling - old ips are not removed, and access to the database can easily be given at the BoK level
817
- # if @dependencies.has_key?("database")
818
- # @dependencies['database'].values.each { |db|
819
- # db.allowHost(@deploydata["private_ip_address"]+"/32")
820
- # if @deploydata["public_ip_address"]
821
- # db.allowHost(@deploydata["public_ip_address"]+"/32")
822
- # end
823
- # }
824
- # end
825
-
826
811
  @groomer.saveDeployData
827
812
 
828
813
  begin
@@ -1007,7 +992,7 @@ next if !create
1007
992
  # Our deploydata gets corrupted often with server pools, this will cause us to use the wrong IP to identify a node
1008
993
  # which will cause us to create certificates, DNS records and other artifacts with incorrect information which will cause our deploy to fail.
1009
994
  # The cloud_id is always correct so lets use 'cloud_desc' to get the correct IPs
1010
- if MU::Cloud::Google::VPC.haveRouteToInstance?(cloud_desc, credentials: @config['credentials']) or public_ips.size == 0
995
+ if MU::Cloud.resourceClass("Google", "VPC").haveRouteToInstance?(cloud_desc, credentials: @config['credentials']) or public_ips.size == 0
1011
996
  @config['canonical_ip'] = private_ips.first
1012
997
  return private_ips.first
1013
998
  else
@@ -1016,10 +1001,109 @@ next if !create
1016
1001
  end
1017
1002
  end
1018
1003
 
1004
+ # Return all of the IP addresses, public and private, from all of our
1005
+ # network interfaces.
1006
+ # @return [Array<String>]
1007
+ def listIPs
1008
+ ips = []
1009
+ cloud_desc.network_interfaces.each { |iface|
1010
+ ips << iface.network_ip
1011
+ if iface.access_configs
1012
+ iface.access_configs.each { |acfg|
1013
+ ips << acfg.nat_ip if acfg.nat_ip
1014
+ }
1015
+ end
1016
+ }
1017
+ ips
1018
+ end
1019
+
1019
1020
  # return [String]: A password string.
1020
- def getWindowsAdminPassword
1021
+ def getWindowsAdminPassword(use_cache: true)
1022
+ @config['windows_auth_vault'] ||= {
1023
+ "vault" => @mu_name,
1024
+ "item" => "windows_credentials",
1025
+ "password_field" => "password"
1026
+ }
1027
+
1028
+ if use_cache
1029
+ begin
1030
+ win_admin_password = @groomer.getSecret(
1031
+ vault: @config['windows_auth_vault']['vault'],
1032
+ item: @config['windows_auth_vault']['item'],
1033
+ field: @config["windows_auth_vault"]["password_field"]
1034
+ )
1035
+ return win_admin_password if win_admin_password
1036
+ rescue MU::Groomer::MuNoSuchSecret, MU::Groomer::RunError
1037
+ end
1038
+ end
1039
+
1040
+ require 'openssl/oaep'
1041
+ timeout = 300
1042
+
1043
+ serial_out = nil
1044
+ key = OpenSSL::PKey::RSA.generate 2048
1045
+
1046
+ missing_response = Proc.new {
1047
+ !serial_out or !serial_out.contents or serial_out.contents.empty? or JSON.parse(serial_out.contents)["userName"] != @config['windows_admin_username']
1048
+ }
1049
+
1050
+ did_metadata = false
1051
+ MU.retrier(loop_if: missing_response, wait: 10, max: timeout/10) {
1052
+ serial_out = MU::Cloud::Google.compute(credentials: @credentials).get_instance_serial_port_output(@project_id, @config['availability_zone'], @cloud_id, port: 4)
1053
+
1054
+ if missing_response.call and
1055
+ !cloud_desc(use_cache: false).metadata.items.map { |i| i.key }.include?("windows-keys")
1056
+ keybytes = Base64.decode64(key.public_key.export.gsub(/-----(?:BEGIN|END) PUBLIC KEY-----/, ''))
1057
+ modulus = keybytes.byteslice(33,256)
1058
+ exponent = keybytes.byteslice(291,3)
1059
+ keydata = {
1060
+ "userName" => @config['windows_admin_username'],
1061
+ "modulus" => Base64.strict_encode64(modulus),
1062
+ "exponent" => Base64.strict_encode64(exponent),
1063
+ "email" => MU.muCfg['mu_admin_email'],
1064
+ "expireOn" => (Time.now.utc+timeout).strftime('%Y-%m-%dT%H:%M:%SZ')
1065
+ }
1066
+
1067
+ new_items = cloud_desc.metadata.items.map { |item|
1068
+ MU::Cloud::Google.compute(:Metadata)::Item.new(
1069
+ key: item.key,
1070
+ value: item.value
1071
+ )
1072
+ }
1073
+ new_items.reject! { |item| item.key == "windows-keys" }
1074
+ new_items << MU::Cloud::Google.compute(:Metadata)::Item.new(
1075
+ key: "windows-keys",
1076
+ value: JSON.generate(keydata)
1077
+ )
1078
+ new_metadata = MU::Cloud::Google.compute(:Metadata).new(
1079
+ fingerprint: cloud_desc(use_cache: false).metadata.fingerprint,
1080
+ items: new_items
1081
+ )
1082
+
1083
+ MU::Cloud::Google.compute(credentials: @credentials).set_instance_metadata(@project_id, @config['availability_zone'], @cloud_id, new_metadata)
1084
+ end
1085
+ }
1086
+
1087
+ return nil if missing_response.call
1088
+
1089
+ pwdata = JSON.parse(serial_out.contents)
1090
+ if pwdata['encryptedPassword'] and pwdata['userName'] == @config['windows_admin_username']
1091
+ decrypted_pw = key.private_decrypt_oaep(Base64.strict_decode64(pwdata['encryptedPassword']))
1092
+ creds = {
1093
+ "username" => @config['windows_admin_username'],
1094
+ "password" => decrypted_pw,
1095
+ "sshd_username" => "sshd_service",
1096
+ "sshd_password" => decrypted_pw
1097
+ }
1098
+ @groomer.saveSecret(vault: @mu_name, item: "windows_credentials", data: creds, permissions: "name:#{@mu_name}")
1099
+
1100
+ return decrypted_pw
1101
+ end
1102
+
1103
+ nil
1021
1104
  end
1022
1105
 
1106
+
1023
1107
  # Add a volume to this instance
1024
1108
  # @param dev [String]: Device name to use when attaching to instance
1025
1109
  # @param size [String]: Size (in gb) of the new volume
@@ -1206,9 +1290,9 @@ next if !create
1206
1290
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
1207
1291
  # @param region [String]: The cloud provider region
1208
1292
  # @return [void]
1209
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1210
- flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
1211
- return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
1293
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
1294
+ flags["habitat"] ||= MU::Cloud::Google.defaultProject(credentials)
1295
+ return if !MU::Cloud.resourceClass("Google", "Habitat").isLive?(flags["habitat"], credentials)
1212
1296
 
1213
1297
  # XXX make damn sure MU.deploy_id is set
1214
1298
  filter = %Q{(labels.mu-id = "#{MU.deploy_id.downcase}")}
@@ -1219,13 +1303,12 @@ next if !create
1219
1303
  MU::Cloud::Google.listAZs(region).each { |az|
1220
1304
  disks = []
1221
1305
  resp = MU::Cloud::Google.compute(credentials: credentials).list_instances(
1222
- flags["project"],
1306
+ flags["habitat"],
1223
1307
  az,
1224
1308
  filter: filter
1225
1309
  )
1226
1310
  if !resp.items.nil? and resp.items.size > 0
1227
1311
  resp.items.each { |instance|
1228
- saname = instance.tags.items.first.gsub(/[^a-z]/, "") # XXX this nonsense again
1229
1312
  MU.log "Terminating instance #{instance.name}"
1230
1313
  if !instance.disks.nil? and instance.disks.size > 0
1231
1314
  instance.disks.each { |disk|
@@ -1233,17 +1316,21 @@ next if !create
1233
1316
  }
1234
1317
  end
1235
1318
  MU::Cloud::Google.compute(credentials: credentials).delete_instance(
1236
- flags["project"],
1319
+ flags["habitat"],
1237
1320
  az,
1238
1321
  instance.name
1239
1322
  ) if !noop
1240
- MU.log "Removing service account #{saname}"
1241
- begin
1242
- MU::Cloud::Google.iam(credentials: credentials).delete_project_service_account(
1243
- "projects/#{flags["project"]}/serviceAccounts/#{saname}@#{flags["project"]}.iam.gserviceaccount.com"
1244
- ) if !noop
1245
- rescue ::Google::Apis::ClientError => e
1246
- raise e if !e.message.match(/^notFound: /)
1323
+ if instance.service_accounts
1324
+ instance.service_accounts.each { |sa|
1325
+ MU.log "Removing service account #{sa.email}"
1326
+ begin
1327
+ MU::Cloud::Google.iam(credentials: credentials).delete_project_service_account(
1328
+ "projects/#{flags["habitat"]}/serviceAccounts/#{sa.email}"
1329
+ ) if !noop
1330
+ rescue ::Google::Apis::ClientError => e
1331
+ raise e if !e.message.match(/^notFound: /)
1332
+ end
1333
+ }
1247
1334
  end
1248
1335
  # XXX wait-loop on pending?
1249
1336
  # pp deletia
@@ -1256,7 +1343,7 @@ next if !create
1256
1343
  # XXX honor snapshotting
1257
1344
  MU::Cloud::Google.compute(credentials: credentials).delete(
1258
1345
  "disk",
1259
- flags["project"],
1346
+ flags["habitat"],
1260
1347
  az,
1261
1348
  noop
1262
1349
  ) if !noop
@@ -1269,10 +1356,10 @@ next if !create
1269
1356
  def self.schema(config)
1270
1357
  toplevel_required = []
1271
1358
  schema = {
1272
- "roles" => MU::Cloud::Google::User.schema(config)[1]["roles"],
1359
+ "roles" => MU::Cloud.resourceClass("Google", "User").schema(config)[1]["roles"],
1273
1360
  "windows_admin_username" => {
1274
1361
  "type" => "string",
1275
- "default" => "Administrator"
1362
+ "default" => "muadmin"
1276
1363
  },
1277
1364
  "create_image" => {
1278
1365
  "properties" => {
@@ -1380,8 +1467,7 @@ next if !create
1380
1467
  foundmatch = false
1381
1468
  MU::Cloud.availableClouds.each { |cloud|
1382
1469
  next if cloud == "Google"
1383
- cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
1384
- foreign_types = (cloudbase.listInstanceTypes).values.first
1470
+ foreign_types = (MU::Cloud.cloudClass(cloud).listInstanceTypes).values.first
1385
1471
  if foreign_types.size == 1
1386
1472
  foreign_types = foreign_types.values.first
1387
1473
  end
@@ -1438,6 +1524,7 @@ next if !create
1438
1524
  end
1439
1525
 
1440
1526
  if server['service_account']
1527
+ server['service_account'] = server['service_account'].to_h
1441
1528
  server['service_account']['cloud'] = "Google"
1442
1529
  server['service_account']['habitat'] ||= server['project']
1443
1530
  found = MU::Config::Ref.get(server['service_account'])
@@ -1446,12 +1533,12 @@ next if !create
1446
1533
  ok = false
1447
1534
  end
1448
1535
  else
1449
- server = MU::Cloud::Google::User.genericServiceAccount(server, configurator)
1536
+ server = MU::Cloud.resourceClass("Google", "User").genericServiceAccount(server, configurator)
1450
1537
  end
1451
1538
 
1452
1539
  subnets = nil
1453
1540
  if !server['vpc']
1454
- vpcs = MU::Cloud::Google::VPC.find(credentials: server['credentials'])
1541
+ vpcs = MU::Cloud.resourceClass("Google", "VPC").find(credentials: server['credentials'])
1455
1542
  if vpcs["default"]
1456
1543
  server["vpc"] ||= {}
1457
1544
  server["vpc"]["vpc_id"] = vpcs["default"].self_link
@@ -1466,7 +1553,7 @@ next if !create
1466
1553
  if !server['vpc']['subnet_id'] and server['vpc']['subnet_name'].nil?
1467
1554
  if !subnets
1468
1555
  if server["vpc"]["vpc_id"]
1469
- vpcs = MU::Cloud::Google::VPC.find(cloud_id: server["vpc"]["vpc_id"])
1556
+ vpcs = MU::Cloud.resourceClass("Google", "VPC").find(cloud_id: server["vpc"]["vpc_id"])
1470
1557
  subnets = vpcs["default"].subnetworks.sample
1471
1558
  end
1472
1559
  end