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