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
@@ -86,7 +86,6 @@ module MU
86
86
 
87
87
  client = ::MsRest::ServiceClient.new(cred_obj)
88
88
  cloud_desc.client_secret_url.match(/^(http.*?\.azure\.net)(\/.*)/)
89
- base = Regexp.last_match[1]
90
89
  path = Regexp.last_match[2]
91
90
  #MU.log "Calling into #{base} #{path}"
92
91
  promise = client.make_request_async(
@@ -97,7 +96,7 @@ module MU
97
96
 
98
97
  # XXX this is async, need to stop and wait somehow
99
98
  promise.then do | result|
100
- resp = result.response
99
+ result.response
101
100
  # MU.log "RESPONSE", MU::WARN, details: resp
102
101
  end
103
102
  end
@@ -106,7 +105,6 @@ module MU
106
105
 
107
106
  # Called automatically by {MU::Deploy#createResources}
108
107
  def groom
109
- rgroup_name = @deploy.deploy_id+"-"+@config['region'].upcase
110
108
  if @config['roles']
111
109
  @config['roles'].each { |role|
112
110
  MU::Cloud::Azure::Role.assignTo(cloud_desc.principal_id, role_name: role, credentials: @config['credentials'])
@@ -191,9 +189,9 @@ module MU
191
189
  end
192
190
 
193
191
  # Cloud-specific configuration properties.
194
- # @param config [MU::Config]: The calling MU::Config object
192
+ # @param _config [MU::Config]: The calling MU::Config object
195
193
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
196
- def self.schema(config)
194
+ def self.schema(_config)
197
195
  toplevel_required = []
198
196
  schema = {
199
197
  "region" => MU::Config.region_primitive,
@@ -221,9 +219,9 @@ module MU
221
219
 
222
220
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::users}, bare and unvalidated.
223
221
  # @param user [Hash]: The resource to process and validate
224
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
222
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
225
223
  # @return [Boolean]: True if validation succeeded, False otherwise
226
- def self.validateConfig(user, configurator)
224
+ def self.validateConfig(user, _configurator)
227
225
  ok = true
228
226
  user['region'] ||= MU::Cloud::Azure.myRegion(user['credentials'])
229
227
 
@@ -53,7 +53,6 @@ module MU
53
53
  def groom
54
54
 
55
55
  if @config['peers']
56
- count = 0
57
56
  @config['peers'].each { |peer|
58
57
  if peer['vpc']['name']
59
58
  peer_obj = @deploy.findLitterMate(name: peer['vpc']['name'], type: "vpcs", habitat: peer['vpc']['project'])
@@ -113,17 +112,16 @@ module MU
113
112
  # Describe this VPC
114
113
  # @return [Hash]
115
114
  def notify
116
- base = {}
117
115
  base = MU.structToHash(cloud_desc)
118
116
  base["cloud_id"] = @cloud_id.name
119
117
  base.merge!(@config.to_h)
120
118
  base
121
119
  end
122
- #
120
+
123
121
  # Describe this VPC from the cloud platform's perspective
124
122
  # @return [Hash]
125
- def cloud_desc
126
- if @cloud_desc_cache
123
+ def cloud_desc(use_cache: true)
124
+ if @cloud_desc_cache and use_cache
127
125
  return @cloud_desc_cache
128
126
  end
129
127
  @cloud_desc_cache = MU::Cloud::Azure::VPC.find(cloud_id: @cloud_id, resource_group: @resource_group).values.first
@@ -192,10 +190,9 @@ module MU
192
190
  # @param use_cache [Boolean]: If available, use saved deployment metadata to describe subnets, instead of querying the cloud API
193
191
  # @return [Array<Hash>]: A list of cloud provider identifiers of subnets associated with this VPC.
194
192
  def loadSubnets(use_cache: false)
195
- desc = cloud_desc
196
193
  @subnets = []
197
194
 
198
- MU::Cloud::Azure.network(credentials: @credentials).subnets.list(@resource_group, cloud_desc.name).each { |subnet|
195
+ MU::Cloud::Azure.network(credentials: @credentials).subnets.list(@resource_group, cloud_desc(use_cache: use_cache).name).each { |subnet|
199
196
  subnet_cfg = {
200
197
  "cloud_id" => subnet.name,
201
198
  "mu_name" => subnet.name,
@@ -334,7 +331,7 @@ module MU
334
331
  # We assume that any values we have in +@config+ are placeholders, and
335
332
  # calculate our own accordingly based on what's live in the cloud.
336
333
  # XXX add flag to return the diff between @config and live cloud
337
- def toKitten(rootparent: nil, billing: nil)
334
+ def toKitten(**_args)
338
335
  return nil if cloud_desc.name == "default" # parent project builds these
339
336
  bok = {
340
337
  "cloud" => "Azure",
@@ -346,9 +343,9 @@ module MU
346
343
  end
347
344
 
348
345
  # Cloud-specific configuration properties.
349
- # @param config [MU::Config]: The calling MU::Config object
346
+ # @param _config [MU::Config]: The calling MU::Config object
350
347
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
351
- def self.schema(config = nil)
348
+ def self.schema(_config = nil)
352
349
  toplevel_required = []
353
350
  schema = {
354
351
  "peers" => {
@@ -522,7 +519,6 @@ MU.structToHash(ext_vpc).diff(MU.structToHash(vpc_obj))
522
519
  # this is slow, so maybe thread it
523
520
  rtb_map = {}
524
521
  routethreads = []
525
- create_nat_gateway = false
526
522
  @config['route_tables'].each { |rtb_cfg|
527
523
  routethreads << Thread.new(rtb_cfg) { |rtb|
528
524
  rtb_name = @mu_name+"-"+rtb['name'].upcase
@@ -573,6 +569,9 @@ MU.structToHash(ext_vpc).diff(MU.structToHash(vpc_obj))
573
569
  routename = rtb_name+"-"+route['destination_network'].gsub(/[^a-z0-9]/i, "_")
574
570
  route_obj.next_hop_type = if route['gateway'] == "#NAT" and @config['bastion']
575
571
  routename = rtb_name+"-NAT"
572
+ if @config['bastion'].is_a?(Hash) and !@config['bastion']['id'] and !@config['bastion']['deploy_id']
573
+ @config['bastion']['deploy_id'] = @deploy.deploy_id
574
+ end
576
575
  bastion_ref = MU::Config::Ref.get(@config['bastion'])
577
576
  if bastion_ref.kitten and bastion_ref.kitten.cloud_desc
578
577
  iface_id = Id.new(bastion_ref.kitten.cloud_desc.network_profile.network_interfaces.first.id)
@@ -718,8 +717,6 @@ MU.structToHash(ext_subnet).diff(MU.structToHash(subnet_obj))
718
717
  loadSubnets
719
718
  end
720
719
 
721
- protected
722
-
723
720
  # Subnets are almost a first-class resource. So let's kinda sorta treat
724
721
  # them like one. This should only be invoked on objects that already
725
722
  # exists in the cloud layer.
@@ -772,8 +769,8 @@ MU.structToHash(ext_subnet).diff(MU.structToHash(subnet_obj))
772
769
  end
773
770
 
774
771
  # Describe this VPC Subnet from the cloud platform's perspective
775
- def cloud_desc
776
- return @cloud_desc_cache if !@cloud_desc_cache.nil?
772
+ def cloud_desc(use_cache: true)
773
+ return @cloud_desc_cache if @cloud_desc_cache and use_cache
777
774
  @cloud_desc_cache = MU::Cloud::Azure.network(credentials: @parent.credentials).subnets.get(@parent.resource_group, @parent.cloud_desc.name, @cloud_id.to_s)
778
775
  @cloud_desc_cache
779
776
  end
@@ -73,8 +73,8 @@ module MU
73
73
  # Stub method- there's no such thing as being "hosted" in a CloudFormation
74
74
  # environment. Calls {MU::Cloud::AWS.listAZs} to return sensible
75
75
  # values, if we happen to have AWS credentials configured.
76
- def self.listAZs(region: MU.curRegion, account: nil, credentials: nil)
77
- MU::Cloud::AWS.listAZs(region: region, account: account, credentials: credentials)
76
+ def self.listAZs(region: MU.curRegion, credentials: nil)
77
+ MU::Cloud::AWS.listAZs(region: region, credentials: credentials)
78
78
  end
79
79
 
80
80
  # Stub method- there's no such thing as being "hosted" in a CloudFormation
@@ -466,7 +466,7 @@ module MU
466
466
  desc["DependsOn"] = []
467
467
  if !cloudobj.nil? and cloudobj.respond_to?(:dependencies) and type != "subnet"
468
468
  cloudobj.dependencies(use_cache: true).first.each_pair { |resource_classname, resources|
469
- resources.each_pair { |sibling_name, sibling_obj|
469
+ resources.each_pair { |_sibling_name, sibling_obj|
470
470
  next if sibling_obj == cloudobj
471
471
  # desc["DependsOn"] << (resource_classname+sibling_obj.cloudobj.mu_name).gsub!(/[^a-z0-9]/i, "")
472
472
  desc["DependsOn"] << sibling_obj.cloudobj.cfm_name
@@ -498,7 +498,6 @@ module MU
498
498
  # @param name [String]: The name of key we're creating/appending
499
499
  # @param value [MU::Config::Tail|String]: The value to set. If it's a {MU::Config::Tail} object, we'll treat it as a reference to a parameter.
500
500
  def self.setCloudFormationProp(resource, name, value)
501
- realvalue = value
502
501
  is_list_element = false
503
502
 
504
503
  # Recursively resolve MU::Config::Tail references
@@ -508,9 +507,11 @@ module MU
508
507
  tree[key] = self.resolveTails(val)
509
508
  }
510
509
  elsif tree.is_a?(Array)
510
+ newtree = []
511
511
  tree.each { |elt|
512
- elt = self.resolveTails(elt)
512
+ newtree << self.resolveTails(elt)
513
513
  }
514
+ tree = newtree
514
515
  elsif tree.class.to_s == "MU::Config::Tail"
515
516
  if tree.is_list_element
516
517
  return { "Fn::Select" => [tree.index, { "Ref" => "#{tree.getPrettyName}" }] }
@@ -584,7 +585,7 @@ module MU
584
585
  cfm_template["Conditions"][cond['name']] = JSON.parse(cond['cloudcode'])
585
586
  }
586
587
  end
587
- tails.each_pair { |param, data|
588
+ tails.each_pair { |_param, data|
588
589
  tail = data
589
590
  next if tail.is_a?(MU::Config::Tail) and (tail.pseudo or !tail.runtimecode.nil?)
590
591
  default = ""
@@ -638,7 +639,7 @@ module MU
638
639
  }
639
640
  end
640
641
  }
641
- MU::Cloud.resource_types.each { |cloudclass, data|
642
+ MU::Cloud.resource_types.values.each { |data|
642
643
  if !config[data[:cfg_plural]].nil? and
643
644
  config[data[:cfg_plural]].size > 0
644
645
  config[data[:cfg_plural]].each { |resource|
@@ -240,8 +240,6 @@ module MU
240
240
  {}
241
241
  end
242
242
 
243
- protected
244
-
245
243
  # Subnets are almost a first-class resource. So let's kinda sorta treat
246
244
  # them like one. This should only be invoked on objects that already
247
245
  # exists in the cloud layer.
@@ -288,13 +286,13 @@ module MU
288
286
 
289
287
  # Placeholder. This is a NOOP for CloudFormation, which doesn't build
290
288
  # resources directly.
291
- def self.find(*args)
289
+ def self.find(**args)
292
290
  MU.log "find() not implemented for CloudFormation layer", MU::DEBUG
293
291
  nil
294
292
  end
295
293
  # Placeholder. This is a NOOP for CloudFormation, which doesn't build
296
294
  # resources directly.
297
- def self.cleanup(*args)
295
+ def self.cleanup(**args)
298
296
  MU.log "cleanup() not implemented for CloudFormation layer", MU::DEBUG
299
297
  nil
300
298
  end
@@ -52,6 +52,17 @@ module MU
52
52
  [:url]
53
53
  end
54
54
 
55
+ # Most of our resource implementation +find+ methods have to mangle their
56
+ # args to make sure they've extracted a project or location argument from
57
+ # other available information. This does it for them.
58
+ # @return [Hash]
59
+ def self.findLocationArgs(**args)
60
+ args[:project] ||= args[:habitat]
61
+ args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
62
+ args[:location] ||= args[:region] || args[:availability_zone] || "-"
63
+ args
64
+ end
65
+
55
66
  # A hook that is always called just before any of the instance method of
56
67
  # our resource implementations gets invoked, so that we can ensure that
57
68
  # repetitive setup tasks (like resolving +:resource_group+ for Azure
@@ -141,10 +152,9 @@ module MU
141
152
  def self.habitat(cloudobj, nolookup: false, deploy: nil)
142
153
  @@habmap ||= {}
143
154
  # XXX whaddabout config['habitat'] HNNNGH
144
-
145
155
  return nil if !cloudobj.cloudclass.canLiveIn.include?(:Habitat)
146
156
 
147
- # XXX users are assholes because they're valid two different ways ugh ugh
157
+ # XXX these are assholes because they're valid two different ways ugh ugh
148
158
  return nil if [MU::Cloud::Google::Group, MU::Cloud::Google::Folder].include?(cloudobj.cloudclass)
149
159
  if cloudobj.config and cloudobj.config['project']
150
160
  if nolookup
@@ -154,7 +164,6 @@ module MU
154
164
  return @@habmap[cloudobj.config['project']]
155
165
  end
156
166
  deploy ||= cloudobj.deploy if cloudobj.respond_to?(:deploy)
157
-
158
167
  projectobj = projectLookup(cloudobj.config['project'], deploy, raise_on_fail: false)
159
168
 
160
169
  if projectobj
@@ -363,7 +372,7 @@ module MU
363
372
  cfg = credConfig(credentials)
364
373
  return if !cfg or !cfg['project']
365
374
  flags["project"] ||= cfg['project']
366
- name = deploy_id+"-secret"
375
+
367
376
  resp = MU::Cloud::Google.storage(credentials: credentials).list_objects(
368
377
  adminBucketName(credentials),
369
378
  prefix: deploy_id
@@ -420,7 +429,7 @@ module MU
420
429
  MU.log e.message, MU::WARN, details: e.inspect
421
430
  if e.inspect.match(/body: "Not Found"/)
422
431
  raise MuError, "Google admin bucket #{adminBucketName(credentials)} or key #{name} does not appear to exist or is not visible with #{credentials ? credentials : "default"} credentials"
423
- elsif e.message.match(/notFound: |Unknown user:/)
432
+ elsif e.message.match(/notFound: |Unknown user:|conflict: /)
424
433
  if retries < 5
425
434
  sleep 5
426
435
  retries += 1
@@ -429,7 +438,7 @@ MU.log e.message, MU::WARN, details: e.inspect
429
438
  raise e
430
439
  end
431
440
  elsif e.inspect.match(/The metadata for object "null" was edited during the operation/)
432
- MU.log e.message+" - Google admin bucket #{adminBucketName(credentials)}/#{name} with #{credentials ? credentials : "default"} credentials", MU::WARN, details: aclobj
441
+ MU.log e.message+" - Google admin bucket #{adminBucketName(credentials)}/#{name} with #{credentials ? credentials : "default"} credentials", MU::DEBUG, details: aclobj
433
442
  sleep 10
434
443
  retry
435
444
  else
@@ -540,7 +549,7 @@ MU.log e.message, MU::WARN, details: e.inspect
540
549
  listRegions(credentials: credentials)
541
550
  listInstanceTypes(credentials: credentials)
542
551
  listProjects(credentials)
543
- rescue ::Google::Apis::ClientError => e
552
+ rescue ::Google::Apis::ClientError
544
553
  MU.log "Found machine credentials #{@@svc_account_name}, but these don't appear to have sufficient permissions or scopes", MU::WARN, details: scopes
545
554
  @@authorizers.delete(credentials)
546
555
  return nil
@@ -717,7 +726,6 @@ MU.log e.message, MU::WARN, details: e.inspect
717
726
  raise e
718
727
  end
719
728
 
720
- regions = []
721
729
  result.items.each { |region|
722
730
  @@regions[region.name] = []
723
731
  region.zones.each { |az|
@@ -846,7 +854,7 @@ MU.log e.message, MU::WARN, details: e.inspect
846
854
  if subclass.nil?
847
855
  begin
848
856
  @@admin_directory_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "AdminDirectoryV1::DirectoryService", scopes: use_scopes, masquerade: MU::Cloud::Google.credConfig(credentials)['masquerade_as'], credentials: credentials, auth_error_quiet: true)
849
- rescue Signet::AuthorizationError => e
857
+ rescue Signet::AuthorizationError
850
858
  MU.log "Falling back to read-only access to DirectoryService API for credential set '#{credentials}'", MU::WARN
851
859
  @@admin_directory_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "AdminDirectoryV1::DirectoryService", scopes: readscopes, masquerade: MU::Cloud::Google.credConfig(credentials)['masquerade_as'], credentials: credentials)
852
860
  @@readonly[credentials] ||= {}
@@ -1052,8 +1060,6 @@ MU.log e.message, MU::WARN, details: e.inspect
1052
1060
  @@customer_ids_cache[credentials]
1053
1061
  end
1054
1062
 
1055
- private
1056
-
1057
1063
  # Wrapper class for Google APIs, so that we can catch some common
1058
1064
  # transient endpoint errors without having to spray rescues all over the
1059
1065
  # codebase.
@@ -1109,12 +1115,13 @@ MU.log e.message, MU::WARN, details: e.inspect
1109
1115
  # @param filter [String]: The Compute API filter string to use to isolate appropriate resources
1110
1116
  def delete(type, project, region = nil, noop = false, filter = "description eq #{MU.deploy_id}", credentials: nil)
1111
1117
  list_sym = "list_#{type.sub(/y$/, "ie")}s".to_sym
1118
+ credentials ||= @credentials
1112
1119
  resp = nil
1113
1120
  begin
1114
1121
  if region
1115
- resp = MU::Cloud::Google.compute(credentials: @credentials).send(list_sym, project, region, filter: filter, mu_gcp_enable_apis: false)
1122
+ resp = MU::Cloud::Google.compute(credentials: credentials).send(list_sym, project, region, filter: filter, mu_gcp_enable_apis: false)
1116
1123
  else
1117
- resp = MU::Cloud::Google.compute(credentials: @credentials).send(list_sym, project, filter: filter, mu_gcp_enable_apis: false)
1124
+ resp = MU::Cloud::Google.compute(credentials: credentials).send(list_sym, project, filter: filter, mu_gcp_enable_apis: false)
1118
1125
  end
1119
1126
 
1120
1127
  rescue ::Google::Apis::ClientError => e
@@ -1137,9 +1144,9 @@ MU.log e.message, MU::WARN, details: e.inspect
1137
1144
  resp = nil
1138
1145
  failed = false
1139
1146
  if region
1140
- resp = MU::Cloud::Google.compute(credentials: @credentials).send(delete_sym, project, region, obj.name)
1147
+ resp = MU::Cloud::Google.compute(credentials: credentials).send(delete_sym, project, region, obj.name)
1141
1148
  else
1142
- resp = MU::Cloud::Google.compute(credentials: @credentials).send(delete_sym, project, obj.name)
1149
+ resp = MU::Cloud::Google.compute(credentials: credentials).send(delete_sym, project, obj.name)
1143
1150
  end
1144
1151
 
1145
1152
  if resp.error and resp.error.errors and resp.error.errors.size > 0
@@ -1195,11 +1202,19 @@ MU.log e.message, MU::WARN, details: e.inspect
1195
1202
  retval = nil
1196
1203
  retries = 0
1197
1204
  wait_backoff = 5
1198
- if next_page_token
1199
- if arguments.size == 1 and arguments.first.is_a?(Hash)
1200
- arguments[0][:page_token] = next_page_token
1201
- else
1202
- arguments << { :page_token => next_page_token }
1205
+ if next_page_token
1206
+ if method_sym != :list_entry_log_entries
1207
+ if arguments.size == 1 and arguments.first.is_a?(Hash)
1208
+ arguments[0][:page_token] = next_page_token
1209
+ else
1210
+ arguments << { :page_token => next_page_token }
1211
+ end
1212
+ elsif arguments.first.class == ::Google::Apis::LoggingV2::ListLogEntriesRequest
1213
+ arguments[0] = ::Google::Apis::LoggingV2::ListLogEntriesRequest.new(
1214
+ resource_names: arguments.first.resource_names,
1215
+ filter: arguments.first.filter,
1216
+ page_token: next_page_token
1217
+ )
1203
1218
  end
1204
1219
  end
1205
1220
  begin
@@ -1318,7 +1333,6 @@ MU.log e.message, MU::WARN, details: e.inspect
1318
1333
  if retval.class.name.match(/.*?::Operation$/)
1319
1334
 
1320
1335
  retries = 0
1321
- orig_target = retval.name
1322
1336
 
1323
1337
  # Check whether the various types of +Operation+ responses say
1324
1338
  # they're done, without knowing which specific API they're from
@@ -1390,7 +1404,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1390
1404
  # take advantage.
1391
1405
  # XXX might want to do something similar for delete ops? just the
1392
1406
  # but where we wait for the operation to definitely be done
1393
- had_been_found = false
1407
+ # had_been_found = false
1394
1408
  if method_sym.to_s.match(/^(insert|create|patch)_/)
1395
1409
  get_method = method_sym.to_s.gsub(/^(insert|patch|create_disk|create)_/, "get_").to_sym
1396
1410
  cloud_id = if retval.respond_to?(:target_link)
@@ -1417,7 +1431,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1417
1431
  #if method_sym == :insert_instance
1418
1432
  #MU.log "actual_resource", MU::WARN, details: actual_resource
1419
1433
  #end
1420
- had_been_found = true
1434
+ # had_been_found = true
1421
1435
  if actual_resource.respond_to?(:status) and
1422
1436
  ["PROVISIONING", "STAGING", "PENDING", "CREATING", "RESTORING"].include?(actual_resource.status)
1423
1437
  retries = 0
@@ -1440,6 +1454,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1440
1454
  if overall_retval
1441
1455
  if method_sym.to_s.match(/^list_(.*)/)
1442
1456
  require 'google/apis/iam_v1'
1457
+ require 'google/apis/logging_v2'
1443
1458
  what = Regexp.last_match[1].to_sym
1444
1459
  whatassign = (Regexp.last_match[1]+"=").to_sym
1445
1460
  if overall_retval.class == ::Google::Apis::IamV1::ListServiceAccountsResponse
@@ -1451,7 +1466,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1451
1466
  newarray = retval.public_send(what) + overall_retval.public_send(what)
1452
1467
  overall_retval.public_send(whatassign, newarray)
1453
1468
  end
1454
- else
1469
+ elsif !retval.respond_to?(:next_page_token) or retval.next_page_token.nil? or retval.next_page_token.empty?
1455
1470
  MU.log "Not sure how to append #{method_sym.to_s} results to #{overall_retval.class.name} (apparently #{what.to_s} and #{whatassign.to_s} aren't it), returning first page only", MU::WARN, details: retval
1456
1471
  return retval
1457
1472
  end
@@ -34,7 +34,7 @@ module MU
34
34
 
35
35
  # Called automatically by {MU::Deploy#createResources}
36
36
  def groom
37
- @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
37
+ @project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloud_id
38
38
 
39
39
  current = cloud_desc
40
40
  changed = false
@@ -143,15 +143,14 @@ module MU
143
143
  # Remove all buckets associated with the currently loaded deployment.
144
144
  # @param noop [Boolean]: If true, will only print what would be done
145
145
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
146
- # @param region [String]: The cloud provider region
147
146
  # @return [void]
148
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
147
+ def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {})
149
148
  flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
150
149
 
151
150
  resp = MU::Cloud::Google.storage(credentials: credentials).list_buckets(flags['project'])
152
151
  if resp and resp.items
153
152
  resp.items.each { |bucket|
154
- if bucket.labels and bucket.labels["mu-id"] == MU.deploy_id.downcase
153
+ if bucket.labels and bucket.labels["mu-id"] == MU.deploy_id.downcase and (ignoremaster or bucket.labels['mu-master-ip'] == MU.mu_public_ip.gsub(/\./, "_"))
155
154
  MU.log "Deleting Cloud Storage bucket #{bucket.name}"
156
155
  if !noop
157
156
  MU::Cloud::Google.storage(credentials: credentials).delete_bucket(bucket.name)
@@ -172,8 +171,7 @@ module MU
172
171
  # Locate an existing bucket.
173
172
  # @return [OpenStruct]: The cloud provider's complete descriptions of matching bucket.
174
173
  def self.find(**args)
175
- args[:project] ||= args[:habitat]
176
- args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
174
+ args = MU::Cloud::Google.findLocationArgs(args)
177
175
 
178
176
  found = {}
179
177
  if args[:cloud_id]
@@ -198,7 +196,7 @@ module MU
198
196
  # Reverse-map our cloud description into a runnable config hash.
199
197
  # We assume that any values we have in +@config+ are placeholders, and
200
198
  # calculate our own accordingly based on what's live in the cloud.
201
- def toKitten(rootparent: nil, billing: nil, habitats: nil)
199
+ def toKitten(**_args)
202
200
  bok = {
203
201
  "cloud" => "Google",
204
202
  "credentials" => @config['credentials'],
@@ -300,9 +298,9 @@ module MU
300
298
  end
301
299
 
302
300
  # Cloud-specific configuration properties.
303
- # @param config [MU::Config]: The calling MU::Config object
301
+ # @param _config [MU::Config]: The calling MU::Config object
304
302
  # @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
305
- def self.schema(config)
303
+ def self.schema(_config)
306
304
  toplevel_required = []
307
305
  schema = {
308
306
  "storage_class" => {
@@ -322,9 +320,9 @@ module MU
322
320
  # Cloud-specific pre-processing of {MU::Config::BasketofKittens::bucket}, bare and unvalidated.
323
321
 
324
322
  # @param bucket [Hash]: The resource to process and validate
325
- # @param configurator [MU::Config]: The overall deployment configurator of which this resource is a member
323
+ # @param _configurator [MU::Config]: The overall deployment configurator of which this resource is a member
326
324
  # @return [Boolean]: True if validation succeeded, False otherwise
327
- def self.validateConfig(bucket, configurator)
325
+ def self.validateConfig(bucket, _configurator)
328
326
  ok = true
329
327
  bucket['project'] ||= MU::Cloud::Google.defaultProject(bucket['credentials'])
330
328