cloud-mu 3.2.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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