cloud-mu 3.2.0 → 3.5.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 (156) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/ansible/roles/mu-nat/tasks/main.yml +3 -0
  4. data/bin/mu-adopt +12 -1
  5. data/bin/mu-aws-setup +41 -7
  6. data/bin/mu-azure-setup +34 -0
  7. data/bin/mu-configure +214 -119
  8. data/bin/mu-gcp-setup +37 -2
  9. data/bin/mu-load-config.rb +2 -1
  10. data/bin/mu-node-manage +3 -0
  11. data/bin/mu-refresh-ssl +67 -0
  12. data/bin/mu-run-tests +28 -6
  13. data/bin/mu-self-update +30 -10
  14. data/bin/mu-upload-chef-artifacts +30 -26
  15. data/cloud-mu.gemspec +10 -8
  16. data/cookbooks/mu-master/attributes/default.rb +5 -1
  17. data/cookbooks/mu-master/metadata.rb +2 -2
  18. data/cookbooks/mu-master/recipes/default.rb +81 -26
  19. data/cookbooks/mu-master/recipes/init.rb +197 -62
  20. data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
  21. data/cookbooks/mu-master/recipes/vault.rb +78 -77
  22. data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
  23. data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
  24. data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
  25. data/cookbooks/mu-tools/attributes/default.rb +12 -0
  26. data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
  27. data/cookbooks/mu-tools/libraries/helper.rb +98 -4
  28. data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
  29. data/cookbooks/mu-tools/recipes/apply_security.rb +31 -9
  30. data/cookbooks/mu-tools/recipes/aws_api.rb +8 -2
  31. data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
  32. data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
  33. data/cookbooks/mu-tools/recipes/google_api.rb +7 -0
  34. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  35. data/cookbooks/mu-tools/resources/disk.rb +113 -42
  36. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  37. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  38. data/extras/Gemfile.lock.bootstrap +394 -0
  39. data/extras/bucketstubs/error.html +0 -0
  40. data/extras/bucketstubs/index.html +0 -0
  41. data/extras/clean-stock-amis +11 -3
  42. data/extras/generate-stock-images +6 -3
  43. data/extras/git_rpm/build.sh +20 -0
  44. data/extras/git_rpm/mugit.spec +53 -0
  45. data/extras/image-generators/AWS/centos7.yaml +19 -16
  46. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  47. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  48. data/extras/image-generators/VMWare/centos8.yaml +15 -0
  49. data/extras/openssl_rpm/build.sh +19 -0
  50. data/extras/openssl_rpm/mussl.spec +46 -0
  51. data/extras/python_rpm/muthon.spec +14 -4
  52. data/extras/ruby_rpm/muby.spec +9 -5
  53. data/extras/sqlite_rpm/build.sh +19 -0
  54. data/extras/sqlite_rpm/muqlite.spec +47 -0
  55. data/install/installer +7 -5
  56. data/modules/mommacat.ru +2 -2
  57. data/modules/mu.rb +14 -7
  58. data/modules/mu/adoption.rb +5 -5
  59. data/modules/mu/cleanup.rb +47 -25
  60. data/modules/mu/cloud.rb +29 -1
  61. data/modules/mu/cloud/dnszone.rb +0 -2
  62. data/modules/mu/cloud/machine_images.rb +1 -1
  63. data/modules/mu/cloud/providers.rb +6 -1
  64. data/modules/mu/cloud/resource_base.rb +16 -7
  65. data/modules/mu/cloud/ssh_sessions.rb +5 -1
  66. data/modules/mu/cloud/wrappers.rb +20 -7
  67. data/modules/mu/config.rb +28 -12
  68. data/modules/mu/config/bucket.rb +31 -2
  69. data/modules/mu/config/cache_cluster.rb +1 -1
  70. data/modules/mu/config/cdn.rb +100 -0
  71. data/modules/mu/config/container_cluster.rb +1 -1
  72. data/modules/mu/config/database.rb +3 -3
  73. data/modules/mu/config/dnszone.rb +4 -3
  74. data/modules/mu/config/endpoint.rb +1 -0
  75. data/modules/mu/config/firewall_rule.rb +1 -1
  76. data/modules/mu/config/function.rb +16 -7
  77. data/modules/mu/config/job.rb +89 -0
  78. data/modules/mu/config/notifier.rb +7 -18
  79. data/modules/mu/config/ref.rb +55 -9
  80. data/modules/mu/config/schema_helpers.rb +12 -3
  81. data/modules/mu/config/server.rb +11 -5
  82. data/modules/mu/config/server_pool.rb +2 -2
  83. data/modules/mu/config/vpc.rb +11 -10
  84. data/modules/mu/defaults/AWS.yaml +106 -106
  85. data/modules/mu/deploy.rb +40 -14
  86. data/modules/mu/groomers/chef.rb +2 -2
  87. data/modules/mu/master.rb +70 -3
  88. data/modules/mu/mommacat.rb +28 -9
  89. data/modules/mu/mommacat/daemon.rb +13 -7
  90. data/modules/mu/mommacat/naming.rb +2 -2
  91. data/modules/mu/mommacat/search.rb +16 -5
  92. data/modules/mu/mommacat/storage.rb +67 -32
  93. data/modules/mu/providers/aws.rb +298 -85
  94. data/modules/mu/providers/aws/alarm.rb +5 -5
  95. data/modules/mu/providers/aws/bucket.rb +284 -50
  96. data/modules/mu/providers/aws/cache_cluster.rb +26 -26
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/providers/aws/collection.rb +16 -16
  99. data/modules/mu/providers/aws/container_cluster.rb +84 -64
  100. data/modules/mu/providers/aws/database.rb +59 -55
  101. data/modules/mu/providers/aws/dnszone.rb +29 -12
  102. data/modules/mu/providers/aws/endpoint.rb +535 -50
  103. data/modules/mu/providers/aws/firewall_rule.rb +32 -26
  104. data/modules/mu/providers/aws/folder.rb +1 -1
  105. data/modules/mu/providers/aws/function.rb +300 -134
  106. data/modules/mu/providers/aws/group.rb +16 -14
  107. data/modules/mu/providers/aws/habitat.rb +4 -4
  108. data/modules/mu/providers/aws/job.rb +469 -0
  109. data/modules/mu/providers/aws/loadbalancer.rb +67 -45
  110. data/modules/mu/providers/aws/log.rb +17 -17
  111. data/modules/mu/providers/aws/msg_queue.rb +22 -13
  112. data/modules/mu/providers/aws/nosqldb.rb +99 -8
  113. data/modules/mu/providers/aws/notifier.rb +137 -65
  114. data/modules/mu/providers/aws/role.rb +119 -83
  115. data/modules/mu/providers/aws/search_domain.rb +166 -30
  116. data/modules/mu/providers/aws/server.rb +209 -118
  117. data/modules/mu/providers/aws/server_pool.rb +95 -130
  118. data/modules/mu/providers/aws/storage_pool.rb +19 -11
  119. data/modules/mu/providers/aws/user.rb +5 -5
  120. data/modules/mu/providers/aws/userdata/linux.erb +5 -4
  121. data/modules/mu/providers/aws/vpc.rb +109 -54
  122. data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
  123. data/modules/mu/providers/azure.rb +78 -12
  124. data/modules/mu/providers/azure/server.rb +20 -4
  125. data/modules/mu/providers/cloudformation/server.rb +1 -1
  126. data/modules/mu/providers/google.rb +21 -5
  127. data/modules/mu/providers/google/bucket.rb +1 -1
  128. data/modules/mu/providers/google/container_cluster.rb +1 -1
  129. data/modules/mu/providers/google/database.rb +1 -1
  130. data/modules/mu/providers/google/firewall_rule.rb +1 -1
  131. data/modules/mu/providers/google/folder.rb +7 -3
  132. data/modules/mu/providers/google/function.rb +66 -31
  133. data/modules/mu/providers/google/group.rb +1 -1
  134. data/modules/mu/providers/google/habitat.rb +1 -1
  135. data/modules/mu/providers/google/loadbalancer.rb +1 -1
  136. data/modules/mu/providers/google/role.rb +6 -3
  137. data/modules/mu/providers/google/server.rb +1 -1
  138. data/modules/mu/providers/google/server_pool.rb +1 -1
  139. data/modules/mu/providers/google/user.rb +1 -1
  140. data/modules/mu/providers/google/vpc.rb +28 -3
  141. data/modules/tests/aws-jobs-functions.yaml +46 -0
  142. data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
  143. data/modules/tests/centos6.yaml +4 -0
  144. data/modules/tests/centos7.yaml +4 -0
  145. data/modules/tests/ecs.yaml +2 -2
  146. data/modules/tests/eks.yaml +1 -1
  147. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  148. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  149. data/modules/tests/k8s.yaml +1 -1
  150. data/modules/tests/microservice_app.yaml +288 -0
  151. data/modules/tests/rds.yaml +5 -5
  152. data/modules/tests/regrooms/rds.yaml +5 -5
  153. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  154. data/modules/tests/super_complex_bok.yml +2 -2
  155. data/modules/tests/super_simple_bok.yml +2 -2
  156. metadata +42 -17
@@ -29,18 +29,21 @@ module MU
29
29
  attr_reader :mu_name
30
30
  attr_reader :name
31
31
  attr_reader :az
32
+ attr_reader :config
32
33
  attr_reader :cloud_desc
33
34
 
34
35
  # @param parent [MU::Cloud::AWS::VPC]: The parent VPC of this subnet.
35
36
  # @param config [Hash<String>]:
36
37
  def initialize(parent, config)
37
- @parent = parent
38
38
  @config = MU::Config.manxify(config)
39
+ MU::Cloud::AWS.resourceInitHook(self, @deploy)
40
+ @parent = parent
39
41
  @cloud_id = config['cloud_id']
42
+ @credentials ||= config['credentials']
40
43
  @mu_name = config['mu_name']
41
44
  @name = config['name']
42
45
  @deploydata = config # This is a dummy for the sake of describe()
43
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_subnets(subnet_ids: [@cloud_id]).subnets.first
46
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_subnets(subnet_ids: [@cloud_id]).subnets.first
44
47
  @az = resp.availability_zone
45
48
  @ip_block = resp.cidr_block
46
49
  @cloud_desc = resp # XXX this really isn't the cloud implementation's business
@@ -50,11 +53,11 @@ module MU
50
53
  # Return the cloud identifier for the default route of this subnet.
51
54
  # @return [String,nil]
52
55
  def defaultRoute
53
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
56
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_route_tables(
54
57
  filters: [{name: "association.subnet-id", values: [@cloud_id]}]
55
58
  )
56
59
  if resp.route_tables.size == 0 # use default route table for the VPC
57
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
60
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_route_tables(
58
61
  filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
59
62
  )
60
63
  end
@@ -75,11 +78,11 @@ module MU
75
78
  # @return [Boolean]
76
79
  def private?
77
80
  return false if @cloud_id.nil?
78
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
81
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_route_tables(
79
82
  filters: [{name: "association.subnet-id", values: [@cloud_id]}]
80
83
  )
81
84
  if resp.route_tables.size == 0 # use default route table for the VPC
82
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_route_tables(
85
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_route_tables(
83
86
  filters: [{name: "vpc-id", values: [@parent.cloud_id]}]
84
87
  )
85
88
  end
@@ -106,14 +109,14 @@ module MU
106
109
 
107
110
  subnetthreads = Array.new
108
111
 
109
- azs = MU::Cloud::AWS.listAZs(region: @config['region'], credentials: @config['credentials'])
112
+ azs = MU::Cloud::AWS.listAZs(region: @region, credentials: @credentials)
110
113
  @config['subnets'].each { |subnet|
111
114
  subnet_name = @config['name']+"-"+subnet['name']
112
115
  az = subnet['availability_zone'] ? subnet['availability_zone'] : azs.op
113
116
  MU.log "Creating Subnet #{subnet_name} (#{subnet['ip_block']}) in #{az}", details: subnet
114
117
 
115
118
  subnetthreads << Thread.new {
116
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_subnet(
119
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).create_subnet(
117
120
  vpc_id: @cloud_id,
118
121
  cidr_block: subnet['ip_block'],
119
122
  availability_zone: az
@@ -123,7 +126,7 @@ module MU
123
126
  tag_me(subnet_id, @mu_name+"-"+subnet['name'])
124
127
 
125
128
  loop_if = Proc.new {
126
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_subnets(subnet_ids: [subnet_id]).subnets.first
129
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_subnets(subnet_ids: [subnet_id]).subnets.first
127
130
  (!resp or resp.state != "available")
128
131
  }
129
132
 
@@ -141,7 +144,7 @@ module MU
141
144
  end
142
145
  MU.log "Associating Route Table '#{subnet['route_table']}' (#{routes[subnet['route_table']]['route_table_id']}) with #{subnet_name}"
143
146
  MU.retrier([Aws::EC2::Errors::InvalidRouteTableIDNotFound], wait: 10, max: 10) {
144
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).associate_route_table(
147
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).associate_route_table(
145
148
  route_table_id: routes[subnet['route_table']]['route_table_id'],
146
149
  subnet_id: subnet_id
147
150
  )
@@ -150,7 +153,7 @@ module MU
150
153
 
151
154
  if subnet.has_key?("map_public_ips")
152
155
  MU.retrier([Aws::EC2::Errors::InvalidSubnetIDNotFound], wait: 10, max: 10) {
153
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).modify_subnet_attribute(
156
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).modify_subnet_attribute(
154
157
  subnet_id: subnet_id,
155
158
  map_public_ip_on_launch: {
156
159
  value: subnet['map_public_ips'],
@@ -167,7 +170,7 @@ module MU
167
170
  loggroup = @deploy.findLitterMate(name: @config['name']+"loggroup", type: "logs")
168
171
  logrole = @deploy.findLitterMate(name: @config['name']+"logrole", type: "roles")
169
172
  MU.log "Enabling traffic logging on Subnet #{subnet_name} in VPC #{@mu_name} to log group #{loggroup.mu_name}"
170
- MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_flow_logs(
173
+ MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).create_flow_logs(
171
174
  resource_ids: [subnet_id],
172
175
  resource_type: "Subnet",
173
176
  traffic_type: subnet["traffic_type_to_log"],
@@ -188,30 +191,31 @@ module MU
188
191
  def allocate_eip_for_nat
189
192
  MU::MommaCat.lock("nat-gateway-eipalloc")
190
193
 
191
- eips = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_addresses(
192
- filters: [
193
- {
194
- name: "domain",
195
- values: ["vpc"]
196
- }
197
- ]
198
- ).addresses
199
-
200
- allocation_id = nil
201
- eips.each { |eip|
202
- next if !eip.association_id.nil? and !eip.association_id.empty?
203
- if (eip.private_ip_address.nil? || eip.private_ip_address.empty?) and MU::MommaCat.lock(eip.allocation_id, true, true)
204
- if !@eip_allocation_ids.include?(eip.allocation_id)
205
- allocation_id = eip.allocation_id
206
- break
207
- end
208
- end
209
- }
210
-
211
- if allocation_id.nil?
212
- allocation_id = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).allocate_address(domain: "vpc").allocation_id
213
- MU::MommaCat.lock(allocation_id, false, true)
214
- end
194
+ # eips = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_addresses(
195
+ # filters: [
196
+ # {
197
+ # name: "domain",
198
+ # values: ["vpc"]
199
+ # }
200
+ # ]
201
+ # ).addresses
202
+
203
+ # allocation_id = nil
204
+ # eips.each { |eip|
205
+ # next if !eip.association_id.nil? and !eip.association_id.empty?
206
+ # if (eip.private_ip_address.nil? || eip.private_ip_address.empty?) and MU::MommaCat.lock(eip.allocation_id, true, true)
207
+ # if !@eip_allocation_ids.include?(eip.allocation_id)
208
+ # allocation_id = eip.allocation_id
209
+ # break
210
+ # end
211
+ # end
212
+ # }
213
+
214
+ # if allocation_id.nil?
215
+ allocation_id = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).allocate_address(domain: "vpc").allocation_id
216
+ tag_me(allocation_id)
217
+ # MU::MommaCat.lock(allocation_id, false, true)
218
+ # end
215
219
 
216
220
  @eip_allocation_ids << allocation_id
217
221
 
@@ -223,15 +227,15 @@ module MU
223
227
  def create_nat_gateway(subnet)
224
228
  allocation_id = allocate_eip_for_nat
225
229
 
226
- nat_gateway_id = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).create_nat_gateway(
230
+ nat_gateway_id = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).create_nat_gateway(
227
231
  subnet_id: subnet['subnet_id'],
228
232
  allocation_id: allocation_id,
229
233
  ).nat_gateway.nat_gateway_id
230
234
 
231
235
  ensure_unlock = Proc.new { MU::MommaCat.unlock(allocation_id, true) }
232
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
236
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
233
237
  loop_if = Proc.new {
234
- resp = MU::Cloud::AWS.ec2(region: @config['region'], credentials: @config['credentials']).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
238
+ resp = MU::Cloud::AWS.ec2(region: @region, credentials: @credentials).describe_nat_gateways(nat_gateway_ids: [nat_gateway_id]).nat_gateways.first
235
239
  resp.class != Aws::EC2::Types::NatGateway or resp.state == "pending"
236
240
  }
237
241
 
@@ -288,9 +288,14 @@ module MU
288
288
  raise MuError, "Nil response from Azure API attempting list_locations(#{subscription})"
289
289
  end
290
290
 
291
- sdk_response.value.each do | region |
292
- @@regions.push(region.name)
293
- end
291
+ sdk_response.value.each { |region|
292
+ begin
293
+ listInstanceTypes(region.name) # use this to filter for broken regions
294
+ @@regions.push(region.name)
295
+ rescue APIError => e
296
+ MU.log "Azure region "+region.name+" does not appear operational, skipping", MU::WARN
297
+ end
298
+ }
294
299
 
295
300
  return us_only ? @@regions.reject { |r| !r.match(/us\d?$/) } : @@regions
296
301
  end
@@ -350,13 +355,42 @@ module MU
350
355
  listRegions.each { |region|
351
356
  next if !deploy.regionsUsed.include?(region)
352
357
  begin
353
- createResourceGroup(deploy.deploy_id+"-"+region.upcase, region, credentials: creds)
358
+ rg_obj = createResourceGroup(deploy.deploy_id+"-"+region.upcase, region, credentials: creds)
359
+ createVault(rg_obj.name, region, deploy, credentials: creds)
354
360
  rescue ::MsRestAzure::AzureOperationError
355
361
  end
356
362
  }
357
363
  }
358
364
  end
359
365
 
366
+ # Arguably this should be a first class resource, but for now we'll do
367
+ # it here since we're going to have a generic deployment vault in every
368
+ # resource group.
369
+ # @param rg [String]: The name of the resource group in which we'll reside
370
+ # @param region [String]: The region in which we'll reside
371
+ # @param deploy [MU::MommaCat]: The deployment which we serve
372
+ # @param credentials [String]:
373
+ def self.createVault(rg, region, deploy, credentials: nil)
374
+ cred_hash = MU::Cloud::Azure.getSDKOptions(credentials)
375
+ vaultname = deploy.getResourceName(region, max_length: 23, disallowed_chars: /[^a-z0-9-]/i, never_gen_unique: true)
376
+ MU::Cloud::Azure.ensureProvider("Microsoft.KeyVault", credentials: credentials)
377
+ sku = MU::Cloud::Azure.keyvault(:Sku).new
378
+ sku.name = "standard" # ...I'm angry about this
379
+
380
+ props = MU::Cloud::Azure.keyvault(:VaultProperties).new
381
+ props.tenant_id = cred_hash[:tenant_id]
382
+ props.enabled_for_deployment = true
383
+ props.sku = sku
384
+ props.access_policies = []
385
+
386
+ params = MU::Cloud::Azure.keyvault(:VaultCreateOrUpdateParameters).new
387
+ params.location = region
388
+ params.properties = props
389
+
390
+ MU.log "Creating KeyVault #{vaultname} in #{region}"
391
+ MU::Cloud::Azure.keyvault(credentials: credentials).vaults.create_or_update(rg, vaultname, params)
392
+ end
393
+
360
394
  @@rg_semaphore = Mutex.new
361
395
 
362
396
  # Purge cloud-specific deploy meta-artifacts (SSH keys, resource groups,
@@ -400,17 +434,30 @@ module MU
400
434
  end
401
435
  }
402
436
  MU.log "Configuring resource group #{name} in #{region}", details: rg_obj
403
- MU::Cloud::Azure.resources(credentials: credentials).resource_groups.create_or_update(
437
+ rg = MU::Cloud::Azure.resources(credentials: credentials).resource_groups.create_or_update(
404
438
  name,
405
439
  rg_obj
406
440
  )
441
+
442
+ rg
407
443
  end
408
444
 
409
445
  # Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
410
- # @param deploy_id [String]: The deploy for which we're writing the secret
446
+ # @param deploy [MU::MommaCat]: The deploy for which we're writing the secret
411
447
  # @param value [String]: The contents of the secret
412
- def self.writeDeploySecret(deploy_id, value, name = nil, credentials: nil)
413
- # XXX this ain't it hoss
448
+ def self.writeDeploySecret(deploy, value, name = nil, credentials: nil)
449
+ deploy_id = deploy.deploy_id
450
+
451
+ listRegions.each { |region|
452
+ next if !deploy.regionsUsed.include?(region)
453
+ rg = deploy_id+"-"+region.upcase
454
+ vaultname = deploy.getResourceName(region, max_length: 23, disallowed_chars: /[^a-z0-9-]/i, never_gen_unique: true)
455
+
456
+ resp = MU::Cloud::Azure.keyvault(credentials: credentials).vaults.get(rg, vaultname)
457
+ next if !resp
458
+ MU.log "vault existence check #{vaultname}", MU::WARN, details: resp
459
+
460
+ }
414
461
  end
415
462
 
416
463
  # Return the name strings of all known sets of credentials for this cloud
@@ -522,7 +569,7 @@ module MU
522
569
 
523
570
  begin
524
571
  Timeout.timeout(2) do
525
- resp = JSON.parse(open("#{base_url}/?#{arg_str}","Metadata"=>"true").read)
572
+ resp = JSON.parse(URI.open("#{base_url}/?#{arg_str}","Metadata"=>"true").read)
526
573
  MU.log "curl -H Metadata:true "+"#{base_url}/?#{arg_str}", loglevel, details: resp
527
574
  if svc != "instance"
528
575
  return resp
@@ -777,6 +824,24 @@ module MU
777
824
  return @@resources_api[credentials]
778
825
  end
779
826
 
827
+ # The Azure KeyVault API
828
+ # @param model [<Azure::Apis::KeyVault::Mgmt::V2018_02_14::Models>]: If specified, will return the class ::Azure::Apis::KeyVault::Mgmt::V2018_02_14::Models::model instead of an API client instance
829
+ # @param model_version [String]: Use an alternative model version supported by the SDK when requesting a +model+
830
+ # @param alt_object [String]: Return an instance of something other than the usual API client object
831
+ # @param credentials [String]: The credential set (subscription, effectively) in which to operate
832
+ # @return [MU::Cloud::Azure::SDKClient]
833
+ def self.keyvault(model = nil, alt_object: nil, credentials: nil, model_version: "V2018_02_14")
834
+ require 'azure_mgmt_key_vault'
835
+
836
+ if model and model.is_a?(Symbol)
837
+ return Object.const_get("Azure").const_get("KeyVault").const_get("Mgmt").const_get(model_version).const_get("Models").const_get(model)
838
+ else
839
+ @@keyvault_api[credentials] ||= MU::Cloud::Azure::SDKClient.new(api: "KeyVault", credentials: credentials, subclass: alt_object)
840
+ end
841
+
842
+ return @@keyvault_api[credentials]
843
+ end
844
+
780
845
  # The Azure Features API
781
846
  # @param model [<Azure::Apis::Features::Mgmt::V2015_12_01::Models>]: If specified, will return the class ::Azure::Apis::Features::Mgmt::V2015_12_01::Models::model instead of an API client instance
782
847
  # @param model_version [String]: Use an alternative model version supported by the SDK when requesting a +model+
@@ -931,6 +996,7 @@ module MU
931
996
  @@resources_api = {}
932
997
  @@containers_api = {}
933
998
  @@features_api = {}
999
+ @@keyvault_api = {}
934
1000
  @@apis_api = {}
935
1001
  @@marketplace_api = {}
936
1002
  @@service_identity_api = {}
@@ -1069,9 +1135,9 @@ module MU
1069
1135
  retry
1070
1136
  end
1071
1137
 
1072
- MU.log "#{@parent.api.class.name}.#{@myname}.#{method_sym.to_s} returned '"+err["code"]+"' - "+err["message"], MU::WARN, details: caller
1073
- MU.log e.backtrace[0], MU::WARN, details: parsed
1074
- raise MU::Cloud::Azure::APIError, err["code"]+": "+err["message"]+" (call was #{@parent.api.class.name}.#{@myname}.#{method_sym.to_s})"
1138
+ # MU.log "#{@parent.api.class.name}.#{@myname}.#{method_sym.to_s} returned '"+err["code"]+"' - "+err["message"], MU::WARN, details: caller
1139
+ # MU.log e.backtrace[0], MU::WARN, details: parsed
1140
+ raise MU::Cloud::Azure::APIError.new err["code"]+": "+err["message"]+" (call was #{@parent.api.class.name}.#{@myname}.#{method_sym.to_s})", details: parsed, silent: true
1075
1141
  end
1076
1142
  end
1077
1143
  rescue JSON::ParserError
@@ -150,7 +150,7 @@ module MU
150
150
 
151
151
  if !@nat.nil? and @nat.mu_name != @mu_name
152
152
  if @nat.cloud_desc.nil?
153
- MU.log "NAT was missing cloud descriptor when called in #{@mu_name}'s getSSHConfig", MU::ERR
153
+ MU.log "NAT #{@nat} was missing cloud descriptor when called in #{@mu_name}'s getSSHConfig", MU::ERR
154
154
  return nil
155
155
  end
156
156
  _foo, _bar, _baz, nat_ssh_host, nat_ssh_user, nat_ssh_key = @nat.getSSHConfig
@@ -220,6 +220,7 @@ module MU
220
220
  # @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching instances
221
221
  def self.find(**args)
222
222
  found = {}
223
+ MU.log "Azure::Server.find called", MU::NOTICE, details: args
223
224
  # told one, we may have to search all the ones we can see.
224
225
  resource_groups = if args[:resource_group]
225
226
  [args[:resource_group]]
@@ -417,6 +418,7 @@ module MU
417
418
 
418
419
  # return [String]: A password string.
419
420
  def getWindowsAdminPassword
421
+ @deploy.fetchSecret(@mu_name, "windows_admin_password")
420
422
  end
421
423
 
422
424
  # Add a volume to this instance
@@ -452,7 +454,7 @@ module MU
452
454
  # @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
453
455
  # @param region [String]: The cloud provider region
454
456
  # @return [void]
455
- def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
457
+ def self.cleanup(noop: false, deploy_id: MU.deploy_id, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
456
458
  end
457
459
 
458
460
  # Cloud-specific configuration properties.
@@ -612,7 +614,7 @@ module MU
612
614
  ok = false
613
615
  end
614
616
  MU::Config.addDependency(server, server['name']+"vpc", "vpc")
615
- MU::Config.addDependency(server, server['name']+"vpc-natstion", "server", phase: "groom")
617
+ MU::Config.addDependency(server, server['name']+"vpc-natstion", "server", their_phase: "groom")
616
618
  server['vpc'] = {
617
619
  "name" => server['name']+"vpc",
618
620
  "subnet_pref" => "private"
@@ -636,6 +638,7 @@ module MU
636
638
  ok
637
639
  end
638
640
 
641
+ # stub
639
642
  def self.diskConfig(config, create = true, disk_as_url = true, credentials: nil)
640
643
  end
641
644
 
@@ -769,10 +772,23 @@ module MU
769
772
 
770
773
  os_obj = MU::Cloud::Azure.compute(:OSProfile).new
771
774
  if windows?
775
+ winrm_listen = MU::Cloud::Azure.compute(:WinRMListener).new
776
+ winrm_listen.certificate_url = "goddamn stupid ass thing"
777
+ winrm_listen.protocol = "https"
778
+ winrm = MU::Cloud::Azure.compute(:WinRMConfiguration).new
779
+ winrm.listeners = [winrm_listen]
780
+
772
781
  win_obj = MU::Cloud::Azure.compute(:WindowsConfiguration).new
782
+ win_obj.win_rmconfiguration = winrm
773
783
  os_obj.windows_configuration = win_obj
774
784
  os_obj.admin_username = @config['windows_admin_username']
775
- os_obj.admin_password = MU.generateWindowsPassword
785
+ os_obj.admin_password = begin
786
+ @deploy.fetchSecret(@mu_name, "windows_admin_password")
787
+ rescue MU::MommaCat::SecretError
788
+ pw = MU.generateWindowsPassword
789
+ @deploy.saveNodeSecret(@mu_name, pw, "windows_admin_password")
790
+ pw
791
+ end
776
792
  os_obj.computer_name = @deploy.getResourceName(@config["name"], max_length: 15, disallowed_chars: /[~!@#$%^&*()=+_\[\]{}\\\|;:\.'",<>\/\?]/)
777
793
  else
778
794
  os_obj.admin_username = @config['ssh_user']
@@ -304,7 +304,7 @@ module MU
304
304
  role_name: baserole.role_name,
305
305
  policy_name: name
306
306
  )
307
- policies[name] = URI.decode(resp.policy_document)
307
+ policies[name] = CGI.unescape(resp.policy_document)
308
308
  }
309
309
  }
310
310
  end
@@ -236,7 +236,7 @@ module MU
236
236
  # @param sibling_only [Boolean]
237
237
  # @return [MU::Config::Habitat,nil]
238
238
  def self.projectLookup(name, deploy = MU.mommacat, raise_on_fail: true, sibling_only: false)
239
- project_obj = deploy.findLitterMate(type: "habitats", name: name) if deploy if !caller.grep(/`findLitterMate'/) # XXX the dumbest
239
+ project_obj = deploy.findLitterMate(type: "habitats", name: name) if deploy and caller.grep(/`findLitterMate'/).empty? # XXX the dumbest
240
240
 
241
241
  if !project_obj and !sibling_only
242
242
  resp = MU::MommaCat.findStray(
@@ -348,7 +348,8 @@ module MU
348
348
  # Plant a Mu deploy secret into a storage bucket somewhere for so our kittens can consume it
349
349
  # @param deploy_id [String]: The deploy for which we're writing the secret
350
350
  # @param value [String]: The contents of the secret
351
- def self.writeDeploySecret(deploy_id, value, name = nil, credentials: nil)
351
+ def self.writeDeploySecret(deploy, value, name = nil, credentials: nil)
352
+ deploy_id = deploy.deploy_id
352
353
  name ||= deploy_id+"-secret"
353
354
  begin
354
355
  MU.log "Writing #{name} to Cloud Storage bucket #{adminBucketName(credentials)}"
@@ -487,7 +488,7 @@ MU.log e.message, MU::WARN, details: e.inspect
487
488
  base_url = "http://metadata.google.internal/computeMetadata/v1"
488
489
  begin
489
490
  Timeout.timeout(2) do
490
- response = open(
491
+ response = URI.open(
491
492
  "#{base_url}/#{param}",
492
493
  "Metadata-Flavor" => "Google"
493
494
  ).read
@@ -981,7 +982,20 @@ MU.log e.message, MU::WARN, details: e.inspect
981
982
  end
982
983
 
983
984
  # Google's Cloud Billing Service API
984
- # @param subclass [<Google::Apis::LoggingV2>]: If specified, will return the class ::Google::Apis::LoggingV2::subclass instead of an API client instance
985
+ # @param subclass [<Google::Apis::CloudbillingV1>]: If specified, will return the class ::Google::Apis::CloudbillingV1::subclass instead of an API client instance
986
+ def self.budgets(subclass = nil, credentials: nil)
987
+ require 'google/apis/billingbudgets_v1'
988
+
989
+ if subclass.nil?
990
+ @@budgets_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "BillingbudgetsV1::CloudBillingBudgetService", scopes: ['cloud-platform', 'cloud-billing'], credentials: credentials, masquerade: MU::Cloud::Google.credConfig(credentials)['masquerade_as'])
991
+ return @@budgets_api[credentials]
992
+ elsif subclass.is_a?(Symbol)
993
+ return Object.const_get("::Google").const_get("Apis").const_get("BillingbudgetsV1").const_get(subclass)
994
+ end
995
+ end
996
+
997
+ # Google's Cloud Billing Budget Service API
998
+ # @param subclass [<Google::Apis::CloudbillingV1>]: If specified, will return the class ::Google::Apis::CloudbillingV1::subclass instead of an API client instance
985
999
  def self.billing(subclass = nil, credentials: nil)
986
1000
  require 'google/apis/cloudbilling_v1'
987
1001
 
@@ -1024,6 +1038,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1024
1038
  # @return [Array<OpenStruct>],nil]
1025
1039
  def self.getOrg(credentials = nil, with_id: nil)
1026
1040
  creds = MU::Cloud::Google.credConfig(credentials)
1041
+ return nil if !creds
1027
1042
  credname = if creds and creds['name']
1028
1043
  creds['name']
1029
1044
  else
@@ -1309,7 +1324,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1309
1324
 
1310
1325
  svc_name = Regexp.last_match[1]
1311
1326
  save_verbosity = MU.verbosity
1312
- if svc_name != "servicemanagement.googleapis.com" and method_sym != :delete
1327
+ if !["servicemanagement.googleapis.com", "billingbudgets.googleapis.com"].include?(svc_name) and method_sym != :delete
1313
1328
  retries += 1
1314
1329
  @@enable_semaphores[project].synchronize {
1315
1330
  MU.setLogging(MU::Logger::NORMAL)
@@ -1564,6 +1579,7 @@ MU.log e.message, MU::WARN, details: e.inspect
1564
1579
  @@firestore_api = {}
1565
1580
  @@admin_directory_api = {}
1566
1581
  @@billing_api = {}
1582
+ @@budgets_api = {}
1567
1583
  @@function_api = {}
1568
1584
  end
1569
1585
  end