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