cloud-mu 3.4.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.
- checksums.yaml +4 -4
- data/ansible/roles/mu-nat/tasks/main.yml +3 -0
- data/bin/mu-aws-setup +41 -7
- data/bin/mu-azure-setup +34 -0
- data/bin/mu-configure +214 -119
- data/bin/mu-gcp-setup +37 -2
- data/bin/mu-node-manage +3 -0
- data/bin/mu-refresh-ssl +67 -0
- data/bin/mu-run-tests +14 -4
- data/bin/mu-self-update +30 -10
- data/bin/mu-upload-chef-artifacts +30 -26
- data/cloud-mu.gemspec +8 -6
- data/cookbooks/mu-master/attributes/default.rb +5 -1
- data/cookbooks/mu-master/metadata.rb +2 -2
- data/cookbooks/mu-master/recipes/default.rb +81 -26
- data/cookbooks/mu-master/recipes/init.rb +197 -62
- data/cookbooks/mu-master/recipes/update_nagios_only.rb +1 -1
- data/cookbooks/mu-master/recipes/vault.rb +78 -77
- data/cookbooks/mu-master/templates/default/mods/rewrite.conf.erb +1 -0
- data/cookbooks/mu-master/templates/default/nagios.conf.erb +103 -0
- data/cookbooks/mu-master/templates/default/web_app.conf.erb +14 -30
- data/cookbooks/mu-tools/attributes/default.rb +5 -0
- data/cookbooks/mu-tools/files/centos-6/CentOS-Base.repo +47 -0
- data/cookbooks/mu-tools/libraries/helper.rb +12 -2
- data/cookbooks/mu-tools/libraries/monkey.rb +1 -1
- data/cookbooks/mu-tools/recipes/apply_security.rb +6 -0
- data/cookbooks/mu-tools/recipes/aws_api.rb +6 -4
- data/cookbooks/mu-tools/recipes/base_repositories.rb +1 -1
- data/cookbooks/mu-tools/recipes/gcloud.rb +2 -9
- data/cookbooks/mu-tools/recipes/google_api.rb +5 -2
- data/cookbooks/mu-tools/resources/disk.rb +108 -58
- data/extras/Gemfile.lock.bootstrap +394 -0
- data/extras/bucketstubs/error.html +0 -0
- data/extras/bucketstubs/index.html +0 -0
- data/extras/clean-stock-amis +9 -9
- data/extras/git_rpm/build.sh +20 -0
- data/extras/git_rpm/mugit.spec +53 -0
- data/extras/image-generators/VMWare/centos8.yaml +15 -0
- data/extras/openssl_rpm/build.sh +19 -0
- data/extras/openssl_rpm/mussl.spec +46 -0
- data/extras/python_rpm/muthon.spec +14 -4
- data/extras/ruby_rpm/muby.spec +9 -5
- data/extras/sqlite_rpm/build.sh +19 -0
- data/extras/sqlite_rpm/muqlite.spec +47 -0
- data/install/installer +7 -5
- data/modules/mu.rb +12 -5
- data/modules/mu/cloud/machine_images.rb +1 -1
- data/modules/mu/cloud/providers.rb +6 -1
- data/modules/mu/cloud/resource_base.rb +1 -1
- data/modules/mu/cloud/ssh_sessions.rb +4 -0
- data/modules/mu/config.rb +28 -12
- data/modules/mu/config/database.rb +2 -2
- data/modules/mu/config/firewall_rule.rb +1 -1
- data/modules/mu/config/ref.rb +2 -2
- data/modules/mu/config/schema_helpers.rb +12 -3
- data/modules/mu/config/server.rb +10 -4
- data/modules/mu/config/server_pool.rb +2 -2
- data/modules/mu/config/vpc.rb +10 -10
- data/modules/mu/defaults/AWS.yaml +32 -32
- data/modules/mu/deploy.rb +23 -10
- data/modules/mu/groomers/chef.rb +2 -2
- data/modules/mu/master.rb +49 -3
- data/modules/mu/mommacat.rb +8 -5
- data/modules/mu/mommacat/naming.rb +2 -2
- data/modules/mu/mommacat/storage.rb +22 -27
- data/modules/mu/providers/aws.rb +142 -48
- data/modules/mu/providers/aws/alarm.rb +3 -3
- data/modules/mu/providers/aws/bucket.rb +19 -19
- data/modules/mu/providers/aws/cache_cluster.rb +22 -22
- data/modules/mu/providers/aws/cdn.rb +2 -2
- data/modules/mu/providers/aws/collection.rb +14 -14
- data/modules/mu/providers/aws/container_cluster.rb +27 -27
- data/modules/mu/providers/aws/database.rb +40 -39
- data/modules/mu/providers/aws/dnszone.rb +5 -5
- data/modules/mu/providers/aws/endpoint.rb +35 -35
- data/modules/mu/providers/aws/firewall_rule.rb +26 -23
- data/modules/mu/providers/aws/function.rb +28 -28
- data/modules/mu/providers/aws/group.rb +7 -7
- data/modules/mu/providers/aws/habitat.rb +2 -2
- data/modules/mu/providers/aws/job.rb +6 -6
- data/modules/mu/providers/aws/loadbalancer.rb +34 -34
- data/modules/mu/providers/aws/log.rb +14 -14
- data/modules/mu/providers/aws/msg_queue.rb +10 -10
- data/modules/mu/providers/aws/nosqldb.rb +8 -8
- data/modules/mu/providers/aws/notifier.rb +7 -7
- data/modules/mu/providers/aws/role.rb +17 -15
- data/modules/mu/providers/aws/search_domain.rb +10 -10
- data/modules/mu/providers/aws/server.rb +176 -95
- data/modules/mu/providers/aws/server_pool.rb +65 -105
- data/modules/mu/providers/aws/storage_pool.rb +17 -9
- data/modules/mu/providers/aws/user.rb +1 -1
- data/modules/mu/providers/aws/vpc.rb +103 -51
- data/modules/mu/providers/aws/vpc_subnet.rb +43 -39
- data/modules/mu/providers/azure.rb +78 -12
- data/modules/mu/providers/azure/server.rb +18 -3
- data/modules/mu/providers/cloudformation/server.rb +1 -1
- data/modules/mu/providers/google.rb +19 -4
- data/modules/mu/providers/google/folder.rb +6 -2
- data/modules/mu/providers/google/function.rb +65 -30
- data/modules/mu/providers/google/role.rb +1 -1
- data/modules/mu/providers/google/vpc.rb +27 -2
- data/modules/tests/aws-servers-with-handrolled-iam.yaml +37 -0
- data/modules/tests/k8s.yaml +1 -1
- metadata +24 -8
|
@@ -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
|
|
292
|
-
|
|
293
|
-
|
|
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
|
|
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(
|
|
413
|
-
|
|
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
|
|
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
|
|
@@ -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",
|
|
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"
|
|
@@ -770,10 +772,23 @@ module MU
|
|
|
770
772
|
|
|
771
773
|
os_obj = MU::Cloud::Azure.compute(:OSProfile).new
|
|
772
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
|
+
|
|
773
781
|
win_obj = MU::Cloud::Azure.compute(:WindowsConfiguration).new
|
|
782
|
+
win_obj.win_rmconfiguration = winrm
|
|
774
783
|
os_obj.windows_configuration = win_obj
|
|
775
784
|
os_obj.admin_username = @config['windows_admin_username']
|
|
776
|
-
os_obj.admin_password =
|
|
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
|
|
777
792
|
os_obj.computer_name = @deploy.getResourceName(@config["name"], max_length: 15, disallowed_chars: /[~!@#$%^&*()=+_\[\]{}\\\|;:\.'",<>\/\?]/)
|
|
778
793
|
else
|
|
779
794
|
os_obj.admin_username = @config['ssh_user']
|
|
@@ -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(
|
|
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::
|
|
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
|
|
|
@@ -1310,7 +1324,7 @@ MU.log e.message, MU::WARN, details: e.inspect
|
|
|
1310
1324
|
|
|
1311
1325
|
svc_name = Regexp.last_match[1]
|
|
1312
1326
|
save_verbosity = MU.verbosity
|
|
1313
|
-
if
|
|
1327
|
+
if !["servicemanagement.googleapis.com", "billingbudgets.googleapis.com"].include?(svc_name) and method_sym != :delete
|
|
1314
1328
|
retries += 1
|
|
1315
1329
|
@@enable_semaphores[project].synchronize {
|
|
1316
1330
|
MU.setLogging(MU::Logger::NORMAL)
|
|
@@ -1565,6 +1579,7 @@ MU.log e.message, MU::WARN, details: e.inspect
|
|
|
1565
1579
|
@@firestore_api = {}
|
|
1566
1580
|
@@admin_directory_api = {}
|
|
1567
1581
|
@@billing_api = {}
|
|
1582
|
+
@@budgets_api = {}
|
|
1568
1583
|
@@function_api = {}
|
|
1569
1584
|
end
|
|
1570
1585
|
end
|
|
@@ -265,8 +265,12 @@ module MU
|
|
|
265
265
|
|
|
266
266
|
if args[:cloud_id]
|
|
267
267
|
raw_id = args[:cloud_id].sub(/^folders\//, "")
|
|
268
|
-
|
|
269
|
-
|
|
268
|
+
begin
|
|
269
|
+
resp = MU::Cloud::Google.folder(credentials: args[:credentials]).get_folder("folders/"+raw_id)
|
|
270
|
+
found[resp.name] = resp if resp
|
|
271
|
+
rescue ::Google::Apis::ClientError => e
|
|
272
|
+
raise e if e.message !~ /forbidden: /
|
|
273
|
+
end
|
|
270
274
|
|
|
271
275
|
elsif args[:flags] and args[:flags]['display_name']
|
|
272
276
|
|
|
@@ -119,6 +119,9 @@ module example.com/cloudfunction
|
|
|
119
119
|
# Called automatically by {MU::Deploy#createResources}
|
|
120
120
|
def groom
|
|
121
121
|
desc = {}
|
|
122
|
+
|
|
123
|
+
func_obj = buildDesc
|
|
124
|
+
|
|
122
125
|
labels = Hash[@tags.keys.map { |k|
|
|
123
126
|
[k.downcase, @tags[k].downcase.gsub(/[^-_a-z0-9]/, '-')] }
|
|
124
127
|
]
|
|
@@ -140,6 +143,10 @@ module example.com/cloudfunction
|
|
|
140
143
|
if cloud_desc.available_memory_mb != @config['memory']
|
|
141
144
|
need_update = true
|
|
142
145
|
end
|
|
146
|
+
if cloud_desc.service_account_email != func_obj.service_account_email
|
|
147
|
+
need_update = true
|
|
148
|
+
end
|
|
149
|
+
|
|
143
150
|
if @config['environment_variable']
|
|
144
151
|
@config['environment_variable'].each { |var|
|
|
145
152
|
if !cloud_desc.environment_variables or
|
|
@@ -161,7 +168,17 @@ module example.com/cloudfunction
|
|
|
161
168
|
File.read("#{dir}/current.zip")
|
|
162
169
|
}
|
|
163
170
|
|
|
164
|
-
|
|
171
|
+
tempfile = nil
|
|
172
|
+
new = if @config['code']['zip_file'] or @config['code']['path']
|
|
173
|
+
if @config['code']['path']
|
|
174
|
+
tempfile = Tempfile.new(["function", ".zip"])
|
|
175
|
+
MU.log "#{@mu_name} using code at #{@config['code']['path']}"
|
|
176
|
+
MU::Master.zipDir(@config['code']['path'], tempfile.path)
|
|
177
|
+
@config['code']['zip_file'] = tempfile.path
|
|
178
|
+
else
|
|
179
|
+
MU.log "#{@mu_name} using code packaged at #{@config['code']['zip_file']}"
|
|
180
|
+
end
|
|
181
|
+
# @code_sha256 = Base64.encode64(Digest::SHA256.digest(zip)).chomp
|
|
165
182
|
File.read(@config['code']['zip_file'])
|
|
166
183
|
elsif @config['code']['gs_url']
|
|
167
184
|
@config['code']['gs_url'].match(/^gs:\/\/([^\/]+)\/(.*)/)
|
|
@@ -172,25 +189,31 @@ module example.com/cloudfunction
|
|
|
172
189
|
File.read(dir+"/new.zip")
|
|
173
190
|
}
|
|
174
191
|
end
|
|
192
|
+
|
|
175
193
|
if @config['code']['gs_url'] and
|
|
176
194
|
(@config['code']['gs_url'] != cloud_desc.source_archive_url or
|
|
177
195
|
current != new)
|
|
178
196
|
need_update = true
|
|
179
|
-
elsif @config['code']['zip_file'] and current != new
|
|
197
|
+
elsif (@config['code']['zip_file'] or @config['code']['path']) and current != new
|
|
180
198
|
need_update = true
|
|
181
|
-
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
if @config['vpc_connector']
|
|
202
|
+
if cloud_desc.vpc_connector != @config['vpc_connector'] or
|
|
203
|
+
cloud_desc.vpc_connector_egress_settings != (@config['vpc_connector_allow_all_egress'] ? "ALL_TRAFFIC" : "PRIVATE_RANGES_ONLY")
|
|
204
|
+
need_update = true
|
|
205
|
+
end
|
|
182
206
|
end
|
|
183
207
|
|
|
184
208
|
if need_update
|
|
185
|
-
|
|
186
|
-
MU.log "Updating Cloud Function #{@mu_name}", MU::NOTICE, details: func_obj
|
|
209
|
+
MU.log "Updating Cloud Function #{@cloud_id}", MU::NOTICE, details: func_obj
|
|
187
210
|
begin
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
rescue ::Google::Apis::ClientError
|
|
193
|
-
MU.log "Error updating Cloud Function #{@mu_name}.", MU::ERR
|
|
211
|
+
MU::Cloud::Google.function(credentials: @credentials).patch_project_location_function(
|
|
212
|
+
@cloud_id,
|
|
213
|
+
func_obj
|
|
214
|
+
)
|
|
215
|
+
rescue ::Google::Apis::ClientError => e
|
|
216
|
+
MU.log "Error updating Cloud Function #{@mu_name}.", MU::ERR, e.message
|
|
194
217
|
if desc[:source_archive_url]
|
|
195
218
|
main_file = nil
|
|
196
219
|
HELLO_WORLDS.each_pair { |runtime, code|
|
|
@@ -207,6 +230,11 @@ module example.com/cloudfunction
|
|
|
207
230
|
# service_account_email: sa.kitten.cloud_desc.email,
|
|
208
231
|
# labels: labels,
|
|
209
232
|
|
|
233
|
+
if tempfile
|
|
234
|
+
tempfile.close
|
|
235
|
+
tempfile.unlink
|
|
236
|
+
end
|
|
237
|
+
|
|
210
238
|
end
|
|
211
239
|
|
|
212
240
|
# Return the metadata for this project's configuration
|
|
@@ -354,6 +382,7 @@ module example.com/cloudfunction
|
|
|
354
382
|
def self.schema(config)
|
|
355
383
|
toplevel_required = ["runtime"]
|
|
356
384
|
schema = {
|
|
385
|
+
"roles" => MU::Cloud.resourceClass("Google", "User").schema(config)[1]["roles"],
|
|
357
386
|
"triggers" => {
|
|
358
387
|
"type" => "array",
|
|
359
388
|
"items" => {
|
|
@@ -448,6 +477,7 @@ module example.com/cloudfunction
|
|
|
448
477
|
content_type: "application/zip",
|
|
449
478
|
name: filename
|
|
450
479
|
)
|
|
480
|
+
|
|
451
481
|
MU::Cloud::Google.storage(credentials: credentials).insert_object(
|
|
452
482
|
bucket,
|
|
453
483
|
obj_obj,
|
|
@@ -487,7 +517,7 @@ module example.com/cloudfunction
|
|
|
487
517
|
end
|
|
488
518
|
# XXX list_project_locations
|
|
489
519
|
|
|
490
|
-
if !function['code'] or (!function['code']['zip_file'] and !function['code']['gs_url'])
|
|
520
|
+
if !function['code'] or (!function['code']['zip_file'] and !function['code']['gs_url'] and !function['code']['path'])
|
|
491
521
|
MU.log "Must specify a code source in Cloud Function #{function['name']}", MU::ERR
|
|
492
522
|
ok = false
|
|
493
523
|
elsif function['code']['zip_file']
|
|
@@ -557,22 +587,14 @@ module example.com/cloudfunction
|
|
|
557
587
|
|
|
558
588
|
location = "projects/"+@config['project']+"/locations/"+@config['region']
|
|
559
589
|
sa = nil
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
end
|
|
567
|
-
rescue ::Google::Apis::ClientError => e
|
|
568
|
-
if e.message.match(/notFound:/)
|
|
569
|
-
sleep 10
|
|
570
|
-
retries += 1
|
|
571
|
-
retry
|
|
572
|
-
end
|
|
573
|
-
end while !sa or !sa.cloud_desc and retries < 5
|
|
590
|
+
need_sa = Proc.new {
|
|
591
|
+
!sa or !sa.kitten or !sa.kitten.cloud_desc
|
|
592
|
+
}
|
|
593
|
+
MU.retrier(loop_if: need_sa, wait: 10, max: 6) { |retries, _wait|
|
|
594
|
+
sa = MU::Config::Ref.get(@config['service_account'])
|
|
595
|
+
}
|
|
574
596
|
|
|
575
|
-
if
|
|
597
|
+
if need_sa.call()
|
|
576
598
|
raise MuError, "Failed to get service account cloud id from #{@config['service_account'].to_s}"
|
|
577
599
|
end
|
|
578
600
|
|
|
@@ -583,7 +605,7 @@ module example.com/cloudfunction
|
|
|
583
605
|
# entry_point: "hello_world",
|
|
584
606
|
entry_point: @config['handler'],
|
|
585
607
|
description: @deploy.deploy_id,
|
|
586
|
-
service_account_email: sa.cloud_desc.email,
|
|
608
|
+
service_account_email: sa.kitten.cloud_desc.email,
|
|
587
609
|
labels: labels,
|
|
588
610
|
available_memory_mb: @config['memory']
|
|
589
611
|
}
|
|
@@ -596,7 +618,6 @@ module example.com/cloudfunction
|
|
|
596
618
|
if @config['vpc_connector']
|
|
597
619
|
desc[:vpc_connector] = @config['vpc_connector']
|
|
598
620
|
desc[:vpc_connector_egress_settings] = @config['vpc_connector_allow_all_egress'] ? "ALL_TRAFFIC" : "PRIVATE_RANGES_ONLY"
|
|
599
|
-
pp desc
|
|
600
621
|
elsif @vpc
|
|
601
622
|
desc[:network] = @vpc.url.sub(/^.*?\/projects\//, 'projects/')
|
|
602
623
|
end
|
|
@@ -627,8 +648,22 @@ module example.com/cloudfunction
|
|
|
627
648
|
# }
|
|
628
649
|
if @config['code']['gs_url']
|
|
629
650
|
desc[:source_archive_url] = @config['code']['gs_url']
|
|
630
|
-
elsif @config['code']['zip_file']
|
|
651
|
+
elsif @config['code']['zip_file'] or @config['code']['path']
|
|
652
|
+
tempfile = nil
|
|
653
|
+
if @config['code']['path']
|
|
654
|
+
tempfile = Tempfile.new(["function", ".zip"])
|
|
655
|
+
MU.log "#{@mu_name} using code at #{@config['code']['path']}"
|
|
656
|
+
MU::Master.zipDir(@config['code']['path'], tempfile.path)
|
|
657
|
+
@config['code']['zip_file'] = tempfile.path
|
|
658
|
+
else
|
|
659
|
+
MU.log "#{@mu_name} using code packaged at #{@config['code']['zip_file']}"
|
|
660
|
+
end
|
|
631
661
|
desc[:source_archive_url] = MU::Cloud::Google::Function.uploadPackage(@config['code']['zip_file'], @mu_name+"-cloudfunction.zip", credentials: @credentials)
|
|
662
|
+
|
|
663
|
+
if tempfile
|
|
664
|
+
tempfile.close
|
|
665
|
+
tempfile.unlink
|
|
666
|
+
end
|
|
632
667
|
end
|
|
633
668
|
|
|
634
669
|
# Dir.mktmpdir(@mu_name) { |dir|
|