cloud-mu 2.0.0.pre.beta2 → 2.0.0.pre.beta3
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/Berksfile.lock +1 -1
- data/cloud-mu.gemspec +4 -3
- data/cookbooks/mu-master/templates/default/mu.rc.erb +2 -2
- data/cookbooks/mu-tools/files/default/Mu_CA.pem +18 -19
- data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -1
- data/modules/mu/cleanup.rb +14 -1
- data/modules/mu/cloud.rb +40 -22
- data/modules/mu/clouds/aws/alarm.rb +6 -0
- data/modules/mu/clouds/aws/bucket.rb +29 -0
- data/modules/mu/clouds/aws/cache_cluster.rb +6 -0
- data/modules/mu/clouds/aws/container_cluster.rb +6 -0
- data/modules/mu/clouds/aws/database.rb +6 -0
- data/modules/mu/clouds/aws/dnszone.rb +6 -0
- data/modules/mu/clouds/aws/endpoint.rb +6 -0
- data/modules/mu/clouds/aws/firewall_rule.rb +6 -0
- data/modules/mu/clouds/aws/folder.rb +6 -0
- data/modules/mu/clouds/aws/function.rb +6 -0
- data/modules/mu/clouds/aws/group.rb +6 -0
- data/modules/mu/clouds/aws/loadbalancer.rb +6 -0
- data/modules/mu/clouds/aws/log.rb +6 -0
- data/modules/mu/clouds/aws/msg_queue.rb +6 -0
- data/modules/mu/clouds/aws/nosqldb.rb +6 -0
- data/modules/mu/clouds/aws/notifier.rb +6 -0
- data/modules/mu/clouds/aws/role.rb +97 -11
- data/modules/mu/clouds/aws/search_domain.rb +6 -0
- data/modules/mu/clouds/aws/server.rb +6 -0
- data/modules/mu/clouds/aws/server_pool.rb +6 -0
- data/modules/mu/clouds/aws/storage_pool.rb +6 -0
- data/modules/mu/clouds/aws/user.rb +6 -0
- data/modules/mu/clouds/aws/vpc.rb +25 -1
- data/modules/mu/clouds/google.rb +86 -16
- data/modules/mu/clouds/google/bucket.rb +78 -3
- data/modules/mu/clouds/google/container_cluster.rb +12 -0
- data/modules/mu/clouds/google/database.rb +15 -1
- data/modules/mu/clouds/google/firewall_rule.rb +18 -2
- data/modules/mu/clouds/google/folder.rb +183 -16
- data/modules/mu/clouds/google/group.rb +7 -1
- data/modules/mu/clouds/google/habitat.rb +139 -24
- data/modules/mu/clouds/google/loadbalancer.rb +26 -12
- data/modules/mu/clouds/google/server.rb +25 -10
- data/modules/mu/clouds/google/server_pool.rb +16 -3
- data/modules/mu/clouds/google/user.rb +7 -1
- data/modules/mu/clouds/google/vpc.rb +87 -76
- data/modules/mu/config.rb +12 -0
- data/modules/mu/config/bucket.rb +4 -0
- data/modules/mu/config/folder.rb +1 -0
- data/modules/mu/config/habitat.rb +1 -1
- data/modules/mu/config/role.rb +78 -34
- data/modules/mu/config/vpc.rb +1 -0
- data/modules/mu/groomers/chef.rb +1 -1
- data/modules/mu/kittens.rb +689 -283
- metadata +5 -4
@@ -216,6 +216,12 @@ module MU
|
|
216
216
|
false
|
217
217
|
end
|
218
218
|
|
219
|
+
# Denote whether this resource implementation is experiment, ready for
|
220
|
+
# testing, or ready for production use.
|
221
|
+
def self.quality
|
222
|
+
MU::Cloud::BETA
|
223
|
+
end
|
224
|
+
|
219
225
|
# Remove all logs associated with the currently loaded deployment.
|
220
226
|
# @param noop [Boolean]: If true, will only print what would be done
|
221
227
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -133,6 +133,12 @@ module MU
|
|
133
133
|
false
|
134
134
|
end
|
135
135
|
|
136
|
+
# Denote whether this resource implementation is experiment, ready for
|
137
|
+
# testing, or ready for production use.
|
138
|
+
def self.quality
|
139
|
+
MU::Cloud::RELEASE
|
140
|
+
end
|
141
|
+
|
136
142
|
# Remove all msg_queues associated with the currently loaded deployment.
|
137
143
|
# @param noop [Boolean]: If true, will only print what would be done
|
138
144
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -160,6 +160,12 @@ pp params
|
|
160
160
|
false
|
161
161
|
end
|
162
162
|
|
163
|
+
# Denote whether this resource implementation is experiment, ready for
|
164
|
+
# testing, or ready for production use.
|
165
|
+
def self.quality
|
166
|
+
MU::Cloud::BETA
|
167
|
+
end
|
168
|
+
|
163
169
|
# Remove all buckets associated with the currently loaded deployment.
|
164
170
|
# @param noop [Boolean]: If true, will only print what would be done
|
165
171
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -62,6 +62,12 @@ module MU
|
|
62
62
|
false
|
63
63
|
end
|
64
64
|
|
65
|
+
# Denote whether this resource implementation is experiment, ready for
|
66
|
+
# testing, or ready for production use.
|
67
|
+
def self.quality
|
68
|
+
MU::Cloud::BETA
|
69
|
+
end
|
70
|
+
|
65
71
|
# Remove all notifiers associated with the currently loaded deployment.
|
66
72
|
# @param noop [Boolean]: If true, will only print what would be done
|
67
73
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -292,6 +292,12 @@ module MU
|
|
292
292
|
true
|
293
293
|
end
|
294
294
|
|
295
|
+
# Denote whether this resource implementation is experiment, ready for
|
296
|
+
# testing, or ready for production use.
|
297
|
+
def self.quality
|
298
|
+
MU::Cloud::BETA
|
299
|
+
end
|
300
|
+
|
295
301
|
# Remove all roles associated with the currently loaded deployment.
|
296
302
|
# @param noop [Boolean]: If true, will only print what would be done
|
297
303
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -485,6 +491,42 @@ module MU
|
|
485
491
|
resp.instance_profile.arn
|
486
492
|
end
|
487
493
|
|
494
|
+
# Schema fragment for IAM policy conditions, which some other resource
|
495
|
+
# types may need to import.
|
496
|
+
def self.condition_schema
|
497
|
+
{
|
498
|
+
"items" => {
|
499
|
+
"properties" => {
|
500
|
+
"conditions" => {
|
501
|
+
"type" => "array",
|
502
|
+
"items" => {
|
503
|
+
"type" => "object",
|
504
|
+
"description" => "One or more conditions under which to apply this policy. See also: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_condition.html",
|
505
|
+
"required" => ["comparison", "variable", "values"],
|
506
|
+
"properties" => {
|
507
|
+
"comparison" => {
|
508
|
+
"type" => "string",
|
509
|
+
"description" => "A comparison to make, like +DateGreaterThan+ or +IpAddress+."
|
510
|
+
},
|
511
|
+
"variable" => {
|
512
|
+
"type" => "string",
|
513
|
+
"description" => "The variable which we will compare, like +aws:CurrentTime+ or +aws:SourceIp+."
|
514
|
+
},
|
515
|
+
"values" => {
|
516
|
+
"type" => "array",
|
517
|
+
"items" => {
|
518
|
+
"type" => "string",
|
519
|
+
"description" => "Value(s) to which we will compare our variable, like +2013-08-16T15:00:00Z+ or +192.0.2.0/24+."
|
520
|
+
}
|
521
|
+
}
|
522
|
+
}
|
523
|
+
}
|
524
|
+
}
|
525
|
+
}
|
526
|
+
}
|
527
|
+
}
|
528
|
+
end
|
529
|
+
|
488
530
|
# Cloud-specific configuration properties.
|
489
531
|
# @param config [MU::Config]: The calling MU::Config object
|
490
532
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
@@ -499,9 +541,11 @@ module MU
|
|
499
541
|
end
|
500
542
|
}.map { |t| MU::Cloud.resource_types[t][:cfg_name] }.sort
|
501
543
|
|
544
|
+
|
502
545
|
schema = {
|
503
546
|
"tags" => MU::Config.tags_primitive,
|
504
547
|
"optional_tags" => MU::Config.optional_tags_primitive,
|
548
|
+
"policies" => self.condition_schema,
|
505
549
|
"import" => {
|
506
550
|
"items" => {
|
507
551
|
"description" => "Can be a shorthand reference to a canned IAM policy like +AdministratorAccess+, or a full ARN like +arn:aws:iam::aws:policy/AmazonESCognitoAccess+"
|
@@ -611,14 +655,15 @@ module MU
|
|
611
655
|
ok
|
612
656
|
end
|
613
657
|
|
614
|
-
|
615
|
-
|
616
|
-
#
|
617
|
-
#
|
618
|
-
|
658
|
+
# Convert our generic internal representation of access policies into
|
659
|
+
# structures suitable for AWS IAM policy documents.
|
660
|
+
# @param policies [Array<Hash>]: One or more policy chunks
|
661
|
+
# @param deploy_obj [MU::MommaCat]: Deployment object to use when looking up sibling Mu resources
|
662
|
+
# @return [Array<Hash>]
|
663
|
+
def self.genPolicyDocument(policies, deploy_obj: nil)
|
619
664
|
iam_policies = []
|
620
|
-
if
|
621
|
-
|
665
|
+
if policies
|
666
|
+
policies.each { |policy|
|
622
667
|
doc = {
|
623
668
|
"Version" => "2012-10-17",
|
624
669
|
"Statement" => [
|
@@ -633,19 +678,52 @@ module MU
|
|
633
678
|
policy["permissions"].each { |perm|
|
634
679
|
doc["Statement"].first["Action"] << perm
|
635
680
|
}
|
681
|
+
if policy["conditions"]
|
682
|
+
doc["Statement"].first["Condition"] ||= {}
|
683
|
+
policy["conditions"].each { |cond|
|
684
|
+
doc["Statement"].first["Condition"][cond['comparison']] = {
|
685
|
+
cond["variable"] => cond["values"]
|
686
|
+
}
|
687
|
+
}
|
688
|
+
end
|
689
|
+
if policy["grant_to"] # XXX factor this with target, they're too similar
|
690
|
+
doc["Statement"].first["Principal"] ||= []
|
691
|
+
policy["grant_to"].each { |grantee|
|
692
|
+
if grantee["type"] and deploy_obj
|
693
|
+
sibling = deploy_obj.findLitterMate(
|
694
|
+
name: grantee["identifier"],
|
695
|
+
type: grantee["type"]
|
696
|
+
)
|
697
|
+
if sibling
|
698
|
+
id = sibling.cloudobj.arn
|
699
|
+
doc["Statement"].first["Principal"] << id
|
700
|
+
else
|
701
|
+
raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["identifier"]} when generating IAM policy"
|
702
|
+
end
|
703
|
+
else
|
704
|
+
doc["Statement"].first["Principal"] << grantee["identifier"]
|
705
|
+
end
|
706
|
+
}
|
707
|
+
if policy["grant_to"].size == 1
|
708
|
+
doc["Statement"].first["Principal"] = doc["Statement"].first["Principal"].first
|
709
|
+
end
|
710
|
+
end
|
636
711
|
if policy["targets"]
|
637
712
|
policy["targets"].each { |target|
|
638
|
-
if target["type"]
|
639
|
-
sibling =
|
713
|
+
if target["type"] and deploy_obj
|
714
|
+
sibling = deploy_obj.findLitterMate(
|
640
715
|
name: target["identifier"],
|
641
716
|
type: target["type"]
|
642
717
|
)
|
643
718
|
if sibling
|
644
|
-
|
719
|
+
id = sibling.cloudobj.arn
|
720
|
+
id += target["path"] if target["path"]
|
721
|
+
doc["Statement"].first["Resource"] << id
|
645
722
|
else
|
646
|
-
raise MuError, "Couldn't find a #{target["entity_type"]} named #{target["identifier"]} when generating IAM policy
|
723
|
+
raise MuError, "Couldn't find a #{target["entity_type"]} named #{target["identifier"]} when generating IAM policy"
|
647
724
|
end
|
648
725
|
else
|
726
|
+
target["identifier"] += target["path"] if target["path"]
|
649
727
|
doc["Statement"].first["Resource"] << target["identifier"]
|
650
728
|
end
|
651
729
|
}
|
@@ -657,6 +735,14 @@ module MU
|
|
657
735
|
iam_policies
|
658
736
|
end
|
659
737
|
|
738
|
+
private
|
739
|
+
|
740
|
+
# Convert entries from the cloud-neutral @config['policies'] list into
|
741
|
+
# AWS syntax.
|
742
|
+
def convert_policies_to_iam
|
743
|
+
MU::Cloud::AWS::Role.genPolicyDocument(@config['policies'], deploy_obj: @deploy)
|
744
|
+
end
|
745
|
+
|
660
746
|
def get_tag_params(strip_std = false)
|
661
747
|
@config['tags'] ||= []
|
662
748
|
|
@@ -108,6 +108,12 @@ module MU
|
|
108
108
|
false
|
109
109
|
end
|
110
110
|
|
111
|
+
# Denote whether this resource implementation is experiment, ready for
|
112
|
+
# testing, or ready for production use.
|
113
|
+
def self.quality
|
114
|
+
MU::Cloud::RELEASE
|
115
|
+
end
|
116
|
+
|
111
117
|
# Remove all search_domains associated with the currently loaded deployment.
|
112
118
|
# @param noop [Boolean]: If true, will only print what would be done
|
113
119
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -1794,6 +1794,12 @@ module MU
|
|
1794
1794
|
false
|
1795
1795
|
end
|
1796
1796
|
|
1797
|
+
# Denote whether this resource implementation is experiment, ready for
|
1798
|
+
# testing, or ready for production use.
|
1799
|
+
def self.quality
|
1800
|
+
MU::Cloud::RELEASE
|
1801
|
+
end
|
1802
|
+
|
1797
1803
|
# Remove all instances associated with the currently loaded deployment. Also cleans up associated volumes, droppings in the MU master's /etc/hosts and ~/.ssh, and in whatever Groomer was used.
|
1798
1804
|
# @param noop [Boolean]: If true, will only print what would be done
|
1799
1805
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -989,6 +989,12 @@ module MU
|
|
989
989
|
false
|
990
990
|
end
|
991
991
|
|
992
|
+
# Denote whether this resource implementation is experiment, ready for
|
993
|
+
# testing, or ready for production use.
|
994
|
+
def self.quality
|
995
|
+
MU::Cloud::RELEASE
|
996
|
+
end
|
997
|
+
|
992
998
|
# Remove all autoscale groups associated with the currently loaded deployment.
|
993
999
|
# @param noop [Boolean]: If true, will only print what would be done
|
994
1000
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -349,6 +349,12 @@ module MU
|
|
349
349
|
false
|
350
350
|
end
|
351
351
|
|
352
|
+
# Denote whether this resource implementation is experiment, ready for
|
353
|
+
# testing, or ready for production use.
|
354
|
+
def self.quality
|
355
|
+
MU::Cloud::RELEASE
|
356
|
+
end
|
357
|
+
|
352
358
|
# Called by {MU::Cleanup}. Locates resources that were created by the
|
353
359
|
# currently-loaded deployment, and purges them.
|
354
360
|
# @param noop [Boolean]: If true, will only print what would be done
|
@@ -136,6 +136,12 @@ module MU
|
|
136
136
|
true
|
137
137
|
end
|
138
138
|
|
139
|
+
# Denote whether this resource implementation is experiment, ready for
|
140
|
+
# testing, or ready for production use.
|
141
|
+
def self.quality
|
142
|
+
MU::Cloud::BETA
|
143
|
+
end
|
144
|
+
|
139
145
|
# Remove all users associated with the currently loaded deployment.
|
140
146
|
# @param noop [Boolean]: If true, will only print what would be done
|
141
147
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -1129,6 +1129,12 @@ module MU
|
|
1129
1129
|
false
|
1130
1130
|
end
|
1131
1131
|
|
1132
|
+
# Denote whether this resource implementation is experiment, ready for
|
1133
|
+
# testing, or ready for production use.
|
1134
|
+
def self.quality
|
1135
|
+
MU::Cloud::RELEASE
|
1136
|
+
end
|
1137
|
+
|
1132
1138
|
# Remove all VPC resources associated with the currently loaded deployment.
|
1133
1139
|
# @param noop [Boolean]: If true, will only print what would be done
|
1134
1140
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -1440,10 +1446,28 @@ module MU
|
|
1440
1446
|
ok
|
1441
1447
|
end
|
1442
1448
|
|
1449
|
+
# Remove all network interfaces associated with the currently loaded deployment.
|
1450
|
+
# @param noop [Boolean]: If true, will only print what would be done
|
1451
|
+
# @param tagfilters [Array<Hash>]: EC2 tags to filter against when search for resources to purge
|
1452
|
+
# @param region [String]: The cloud provider region
|
1453
|
+
# @return [void]
|
1454
|
+
def self.purge_interfaces(noop = false, tagfilters = [{name: "tag:MU-ID", values: [MU.deploy_id]}], region: MU.curRegion, credentials: nil)
|
1455
|
+
resp = MU::Cloud::AWS.ec2(credentials: credentials, region: region).describe_network_interfaces(
|
1456
|
+
filters: tagfilters
|
1457
|
+
)
|
1458
|
+
ifaces = resp.data.network_interfaces
|
1459
|
+
|
1460
|
+
return if ifaces.nil? or ifaces.size == 0
|
1461
|
+
|
1462
|
+
ifaces.each { |iface|
|
1463
|
+
MU.log "Deleting Network Interface #{iface.network_interface_id}"
|
1464
|
+
MU::Cloud::AWS.ec2(credentials: credentials, region: region).delete_network_interface(network_interface_id: iface.network_interface_id)
|
1465
|
+
}
|
1466
|
+
end
|
1467
|
+
|
1443
1468
|
|
1444
1469
|
private
|
1445
1470
|
|
1446
|
-
|
1447
1471
|
# List the route tables for each subnet in the given VPC
|
1448
1472
|
def self.listAllSubnetRouteTables(vpc_id, region: MU.curRegion, credentials: nil)
|
1449
1473
|
resp = MU::Cloud::AWS.ec2(region: region, credentials: credentials).describe_subnets(
|
data/modules/mu/clouds/google.rb
CHANGED
@@ -28,6 +28,7 @@ module MU
|
|
28
28
|
@@my_hosted_cfg = nil
|
29
29
|
@@authorizers = {}
|
30
30
|
@@acct_to_profile_map = {}
|
31
|
+
@@enable_semaphores = {}
|
31
32
|
|
32
33
|
# Any cloud-specific instance methods we require our resource
|
33
34
|
# implementations to have, above and beyond the ones specified by
|
@@ -71,6 +72,36 @@ module MU
|
|
71
72
|
$MU_CFG['google'].keys
|
72
73
|
end
|
73
74
|
|
75
|
+
# A shortcut for {MU::MommaCat.findStray} to resolve a shorthand project
|
76
|
+
# name into a cloud object, whether it refers to a sibling by internal
|
77
|
+
# name or by cloud identifier.
|
78
|
+
# @param name [String]
|
79
|
+
# @param deploy [String]
|
80
|
+
# @param raise_on_fail [Boolean]
|
81
|
+
# @param sibling_only [Boolean]
|
82
|
+
# @return [MU::Cloud::Habitat,nil]
|
83
|
+
def self.projectLookup(name, deploy = MU.mommacat, raise_on_fail: true, sibling_only: false)
|
84
|
+
project_obj = deploy.findLitterMate(type: "habitats", name: name)
|
85
|
+
|
86
|
+
if !project_obj and !sibling_only
|
87
|
+
resp = MU::MommaCat.findStray(
|
88
|
+
"Google",
|
89
|
+
"habitats",
|
90
|
+
deploy_id: deploy.deploy_id,
|
91
|
+
cloud_id: name,
|
92
|
+
name: name,
|
93
|
+
dummy_ok: true
|
94
|
+
)
|
95
|
+
project_obj = resp.first if resp
|
96
|
+
end
|
97
|
+
|
98
|
+
if (!project_obj or !project_obj.cloud_id) and raise_on_fail
|
99
|
+
raise MuError, "Failed to find project '#{name}' in deploy #{deploy.deploy_id}"
|
100
|
+
end
|
101
|
+
|
102
|
+
project_obj
|
103
|
+
end
|
104
|
+
|
74
105
|
# Resolve the administrative Cloud Storage bucket for a given credential
|
75
106
|
# set, or return a default.
|
76
107
|
# @param credentials [String]
|
@@ -211,7 +242,7 @@ module MU
|
|
211
242
|
|
212
243
|
[name, "log_vol_ebs_key"].each { |obj|
|
213
244
|
MU.log "Granting #{acct} access to #{obj} in Cloud Storage bucket #{adminBucketName(credentials)}"
|
214
|
-
|
245
|
+
|
215
246
|
MU::Cloud::Google.storage(credentials: credentials).insert_object_access_control(
|
216
247
|
adminBucketName(credentials),
|
217
248
|
obj,
|
@@ -323,6 +354,10 @@ module MU
|
|
323
354
|
|
324
355
|
cfg = credConfig(credentials)
|
325
356
|
|
357
|
+
if cfg['project']
|
358
|
+
@@enable_semaphores[cfg['project']] ||= Mutex.new
|
359
|
+
end
|
360
|
+
|
326
361
|
if cfg
|
327
362
|
data = nil
|
328
363
|
@@authorizers[credentials] ||= {}
|
@@ -597,7 +632,8 @@ module MU
|
|
597
632
|
require 'google/apis/cloudresourcemanager_v1'
|
598
633
|
|
599
634
|
if subclass.nil?
|
600
|
-
@@resource_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "CloudresourcemanagerV1::CloudResourceManagerService", scopes: ['https://www.googleapis.com/auth/cloud-platform'], credentials: credentials)
|
635
|
+
# @@resource_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "CloudresourcemanagerV1::CloudResourceManagerService", scopes: ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/cloudplatformprojects'], masquerade: MU::Cloud::Google.credConfig(credentials)['masquerade_as'], credentials: credentials)
|
636
|
+
@@resource_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "CloudresourcemanagerV1::CloudResourceManagerService", scopes: ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/cloudplatformprojects'], credentials: credentials)
|
601
637
|
return @@resource_api[credentials]
|
602
638
|
elsif subclass.is_a?(Symbol)
|
603
639
|
return Object.const_get("::Google").const_get("Apis").const_get("CloudresourcemanagerV1").const_get(subclass)
|
@@ -605,15 +641,15 @@ module MU
|
|
605
641
|
end
|
606
642
|
|
607
643
|
# Google's Cloud Resource Manager API V2, which apparently has all the folder bits
|
608
|
-
# @param subclass [<Google::Apis::
|
644
|
+
# @param subclass [<Google::Apis::CloudresourcemanagerV2>]: If specified, will return the class ::Google::Apis::CloudresourcemanagerV2::subclass instead of an API client instance
|
609
645
|
def self.folder(subclass = nil, credentials: nil)
|
610
|
-
require 'google/apis/
|
646
|
+
require 'google/apis/cloudresourcemanager_v2'
|
611
647
|
|
612
648
|
if subclass.nil?
|
613
|
-
@@resource2_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "
|
649
|
+
@@resource2_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "CloudresourcemanagerV2::CloudResourceManagerService", scopes: ['https://www.googleapis.com/auth/cloud-platform', 'https://www.googleapis.com/auth/cloudplatformfolders'], credentials: credentials)
|
614
650
|
return @@resource2_api[credentials]
|
615
651
|
elsif subclass.is_a?(Symbol)
|
616
|
-
return Object.const_get("::Google").const_get("Apis").const_get("
|
652
|
+
return Object.const_get("::Google").const_get("Apis").const_get("CloudresourcemanagerV2").const_get(subclass)
|
617
653
|
end
|
618
654
|
end
|
619
655
|
|
@@ -682,6 +718,31 @@ module MU
|
|
682
718
|
end
|
683
719
|
end
|
684
720
|
|
721
|
+
# Google's Cloud Billing Service API
|
722
|
+
# @param subclass [<Google::Apis::LoggingV2>]: If specified, will return the class ::Google::Apis::LoggingV2::subclass instead of an API client instance
|
723
|
+
def self.billing(subclass = nil, credentials: nil)
|
724
|
+
require 'google/apis/cloudbilling_v1'
|
725
|
+
|
726
|
+
if subclass.nil?
|
727
|
+
@@billing_api[credentials] ||= MU::Cloud::Google::GoogleEndpoint.new(api: "CloudbillingV1::CloudbillingService", scopes: ['https://www.googleapis.com/auth/cloud-platform'], credentials: credentials)
|
728
|
+
return @@billing_api[credentials]
|
729
|
+
elsif subclass.is_a?(Symbol)
|
730
|
+
return Object.const_get("::Google").const_get("Apis").const_get("CloudbillingV1").const_get(subclass)
|
731
|
+
end
|
732
|
+
end
|
733
|
+
|
734
|
+
|
735
|
+
# Retrieve the organization, if any, to which these credentials belong.
|
736
|
+
# @param credentials [String]
|
737
|
+
# @return [Array<OpenStruct>],nil]
|
738
|
+
def self.getOrg(credentials = nil)
|
739
|
+
resp = MU::Cloud::Google.resource_manager(credentials: credentials).search_organizations
|
740
|
+
if resp and resp.organizations
|
741
|
+
# XXX no idea if it's possible to be a member of multiple orgs
|
742
|
+
return resp.organizations.first
|
743
|
+
end
|
744
|
+
nil
|
745
|
+
end
|
685
746
|
|
686
747
|
private
|
687
748
|
|
@@ -762,7 +823,7 @@ module MU
|
|
762
823
|
end
|
763
824
|
# TODO validate that the resource actually went away, because it seems not to do so very reliably
|
764
825
|
rescue ::Google::Apis::ClientError => e
|
765
|
-
raise e if !e.message.match(
|
826
|
+
raise e if !e.message.match(/(^notFound: |operation in progress)/)
|
766
827
|
end while failed and retries < 6
|
767
828
|
end
|
768
829
|
}
|
@@ -798,30 +859,37 @@ module MU
|
|
798
859
|
else
|
799
860
|
raise MU::MuError, "Service account #{MU::Cloud::Google.svc_account_name} has insufficient privileges to call #{method_sym}"
|
800
861
|
end
|
801
|
-
rescue ::Google::Apis::ClientError => e
|
862
|
+
rescue ::Google::Apis::ClientError, OpenSSL::SSL::SSLError => e
|
802
863
|
if e.message.match(/^invalidParameter:/)
|
803
864
|
MU.log "#{method_sym.to_s}: "+e.message, MU::ERR, details: arguments
|
804
865
|
# uncomment for debugging stuff; this can occur in benign situations so we don't normally want it logging
|
805
866
|
elsif e.message.match(/^forbidden:/)
|
806
867
|
MU.log "Using credentials #{@credentials}: #{method_sym.to_s}: "+e.message, MU::ERR, details: caller
|
807
868
|
end
|
808
|
-
|
869
|
+
@@enable_semaphores ||= {}
|
870
|
+
max_retries = 3
|
871
|
+
wait_time = 90
|
872
|
+
if retries <= max_retries and e.message.match(/^accessNotConfigured/)
|
809
873
|
enable_obj = nil
|
810
874
|
project = arguments.size > 0 ? arguments.first.to_s : MU::Cloud::Google.defaultProject(@credentials)
|
875
|
+
@@enable_semaphores[project] ||= Mutex.new
|
811
876
|
enable_obj = MU::Cloud::Google.service_manager(:EnableServiceRequest).new(
|
812
877
|
consumer_id: "project:"+project
|
813
878
|
)
|
814
879
|
# XXX dumbass way to get this string
|
815
|
-
e.message.match(/
|
880
|
+
e.message.match(/by visiting https:\/\/console\.developers\.google\.com\/apis\/api\/(.+?)\//)
|
881
|
+
|
816
882
|
svc_name = Regexp.last_match[1]
|
817
883
|
save_verbosity = MU.verbosity
|
818
884
|
if svc_name != "servicemanagement.googleapis.com"
|
819
|
-
MU.setLogging(MU::Logger::NORMAL)
|
820
|
-
MU.log "Attempting to enable #{svc_name} in project #{project}, then waiting for 30s", MU::WARN
|
821
|
-
MU.setLogging(save_verbosity)
|
822
|
-
MU::Cloud::Google.service_manager(credentials: @credentials).enable_service(svc_name, enable_obj)
|
823
|
-
sleep 30
|
824
885
|
retries += 1
|
886
|
+
@@enable_semaphores[project].synchronize {
|
887
|
+
MU.setLogging(MU::Logger::NORMAL)
|
888
|
+
MU.log "Attempting to enable #{svc_name} in project #{project}; will retry #{method_sym.to_s} in #{(wait_time/retries).to_s}s (#{retries.to_s}/#{max_retries.to_s})", MU::NOTICE
|
889
|
+
MU.setLogging(save_verbosity)
|
890
|
+
MU::Cloud::Google.service_manager(credentials: @credentials).enable_service(svc_name, enable_obj)
|
891
|
+
}
|
892
|
+
sleep wait_time/retries
|
825
893
|
retry
|
826
894
|
else
|
827
895
|
MU.setLogging(MU::Logger::NORMAL)
|
@@ -831,7 +899,8 @@ module MU
|
|
831
899
|
end
|
832
900
|
elsif retries <= 10 and
|
833
901
|
e.message.match(/^resourceNotReady:/) or
|
834
|
-
(e.message.match(/^resourceInUseByAnotherResource:/) and method_sym.to_s.match(/^delete_/))
|
902
|
+
(e.message.match(/^resourceInUseByAnotherResource:/) and method_sym.to_s.match(/^delete_/)) or
|
903
|
+
e.message.match(/SSL_connect/)
|
835
904
|
if retries > 0 and retries % 3 == 0
|
836
905
|
MU.log "Will retry #{method_sym} after #{e.message} (retry #{retries})", MU::NOTICE, details: arguments
|
837
906
|
else
|
@@ -968,6 +1037,7 @@ module MU
|
|
968
1037
|
@@service_api = {}
|
969
1038
|
@@firestore_api = {}
|
970
1039
|
@@admin_directory_api = {}
|
1040
|
+
@@billing_api = {}
|
971
1041
|
end
|
972
1042
|
end
|
973
1043
|
end
|