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
@@ -19,6 +19,7 @@ module MU
|
|
19
19
|
class Bucket < MU::Cloud::Bucket
|
20
20
|
@deploy = nil
|
21
21
|
@config = nil
|
22
|
+
@project_id = nil
|
22
23
|
|
23
24
|
attr_reader :mu_name
|
24
25
|
attr_reader :config
|
@@ -30,17 +31,28 @@ module MU
|
|
30
31
|
@deploy = mommacat
|
31
32
|
@config = MU::Config.manxify(kitten_cfg)
|
32
33
|
@cloud_id ||= cloud_id
|
34
|
+
if mu_name
|
35
|
+
@mu_name = mu_name
|
36
|
+
@config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
|
37
|
+
if !@project_id
|
38
|
+
project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
|
39
|
+
@project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
|
40
|
+
end
|
41
|
+
end
|
33
42
|
@mu_name ||= @deploy.getResourceName(@config["name"])
|
34
43
|
end
|
35
44
|
|
36
45
|
# Called automatically by {MU::Deploy#createResources}
|
37
46
|
def create
|
38
|
-
MU::Cloud::Google.
|
47
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
|
48
|
+
MU::Cloud::Google.storage(credentials: credentials).insert_bucket(@project_id, bucket_descriptor)
|
39
49
|
@cloud_id = @mu_name.downcase
|
40
50
|
end
|
41
51
|
|
42
52
|
# Called automatically by {MU::Deploy#createResources}
|
43
53
|
def groom
|
54
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
|
55
|
+
|
44
56
|
current = cloud_desc
|
45
57
|
changed = false
|
46
58
|
|
@@ -63,6 +75,52 @@ module MU
|
|
63
75
|
if changed
|
64
76
|
MU::Cloud::Google.storage(credentials: credentials).patch_bucket(@cloud_id, bucket_descriptor)
|
65
77
|
end
|
78
|
+
|
79
|
+
if @config['policies']
|
80
|
+
@config['policies'].each { |pol|
|
81
|
+
pol['grant_to'].each { |grantee|
|
82
|
+
entity = if grantee["type"]
|
83
|
+
sibling = deploy_obj.findLitterMate(
|
84
|
+
name: grantee["identifier"],
|
85
|
+
type: grantee["type"]
|
86
|
+
)
|
87
|
+
if sibling
|
88
|
+
sibling.cloudobj.cloud_id
|
89
|
+
else
|
90
|
+
raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["identifier"]} when generating Cloud Storage access policy"
|
91
|
+
end
|
92
|
+
else
|
93
|
+
pol['grant_to'].first['identifier']
|
94
|
+
end
|
95
|
+
|
96
|
+
if entity.match(/@/) and !entity.match(/^(group|user)\-/)
|
97
|
+
entity = "user-"+entity if entity.match(/@/)
|
98
|
+
end
|
99
|
+
|
100
|
+
bucket_acl_obj = MU::Cloud::Google.storage(:BucketAccessControl).new(
|
101
|
+
bucket: @cloud_id,
|
102
|
+
role: pol['permissions'].first,
|
103
|
+
entity: entity
|
104
|
+
)
|
105
|
+
MU.log "Adding Cloud Storage policy to bucket #{@cloud_id}", MU::NOTICE, details: bucket_acl_obj
|
106
|
+
MU::Cloud::Google.storage(credentials: credentials).insert_bucket_access_control(
|
107
|
+
@cloud_id,
|
108
|
+
bucket_acl_obj
|
109
|
+
)
|
110
|
+
|
111
|
+
acl_obj = MU::Cloud::Google.storage(:ObjectAccessControl).new(
|
112
|
+
bucket: @cloud_id,
|
113
|
+
role: pol['permissions'].first,
|
114
|
+
entity: entity
|
115
|
+
)
|
116
|
+
MU::Cloud::Google.storage(credentials: credentials).insert_default_object_access_control(
|
117
|
+
@cloud_id,
|
118
|
+
acl_obj
|
119
|
+
)
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
123
|
+
end
|
66
124
|
end
|
67
125
|
|
68
126
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
@@ -72,6 +130,12 @@ module MU
|
|
72
130
|
true
|
73
131
|
end
|
74
132
|
|
133
|
+
# Denote whether this resource implementation is experiment, ready for
|
134
|
+
# testing, or ready for production use.
|
135
|
+
def self.quality
|
136
|
+
MU::Cloud::BETA
|
137
|
+
end
|
138
|
+
|
75
139
|
# Remove all buckets associated with the currently loaded deployment.
|
76
140
|
# @param noop [Boolean]: If true, will only print what would be done
|
77
141
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -96,7 +160,9 @@ module MU
|
|
96
160
|
# Return the metadata for this user cofiguration
|
97
161
|
# @return [Hash]
|
98
162
|
def notify
|
99
|
-
MU.structToHash(cloud_desc)
|
163
|
+
desc = MU.structToHash(cloud_desc)
|
164
|
+
desc["project_id"] = @project_id
|
165
|
+
desc
|
100
166
|
end
|
101
167
|
|
102
168
|
# Locate an existing bucket.
|
@@ -104,7 +170,7 @@ module MU
|
|
104
170
|
# @param region [String]: The cloud provider region.
|
105
171
|
# @param flags [Hash]: Optional flags
|
106
172
|
# @return [OpenStruct]: The cloud provider's complete descriptions of matching bucket.
|
107
|
-
def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {})
|
173
|
+
def self.find(cloud_id: nil, region: MU.curRegion, credentials: nil, flags: {}, tag_key: nil, tag_value: nil)
|
108
174
|
found = {}
|
109
175
|
if cloud_id
|
110
176
|
found[cloud_id] = MU::Cloud::Google.storage(credentials: credentials).get_bucket(cloud_id)
|
@@ -135,6 +201,15 @@ module MU
|
|
135
201
|
def self.validateConfig(bucket, configurator)
|
136
202
|
ok = true
|
137
203
|
|
204
|
+
if bucket['policies']
|
205
|
+
bucket['policies'].each { |pol|
|
206
|
+
if !pol['permissions'] or pol['permissions'].empty?
|
207
|
+
pol['permissions'] = ["READER"]
|
208
|
+
end
|
209
|
+
}
|
210
|
+
# XXX validate READER OWNER EDITOR w/e
|
211
|
+
end
|
212
|
+
|
138
213
|
ok
|
139
214
|
end
|
140
215
|
|
@@ -38,6 +38,11 @@ module MU
|
|
38
38
|
@mu_name = mu_name
|
39
39
|
deploydata = describe[2]
|
40
40
|
@config['availability_zone'] = deploydata['zone']
|
41
|
+
@config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
|
42
|
+
if !@project_id
|
43
|
+
project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
|
44
|
+
@project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
|
45
|
+
end
|
41
46
|
else
|
42
47
|
@mu_name ||= @deploy.getResourceName(@config["name"], max_length: 40)
|
43
48
|
end
|
@@ -185,6 +190,7 @@ puts @config['credentials']
|
|
185
190
|
desc = MU.structToHash(MU::Cloud::Google.container(credentials: @config['credentials']).get_zone_cluster(@config["project"], @config['availability_zone'], @mu_name.downcase))
|
186
191
|
desc["project"] = @config['project']
|
187
192
|
desc["cloud_id"] = @cloud_id
|
193
|
+
desc["project_id"] = @project_id
|
188
194
|
desc["mu_name"] = @mu_name.downcase
|
189
195
|
desc
|
190
196
|
end
|
@@ -196,6 +202,12 @@ puts @config['credentials']
|
|
196
202
|
false
|
197
203
|
end
|
198
204
|
|
205
|
+
# Denote whether this resource implementation is experiment, ready for
|
206
|
+
# testing, or ready for production use.
|
207
|
+
def self.quality
|
208
|
+
MU::Cloud::ALPHA
|
209
|
+
end
|
210
|
+
|
199
211
|
# Called by {MU::Cleanup}. Locates resources that were created by the
|
200
212
|
# currently-loaded deployment, and purges them.
|
201
213
|
# @param noop [Boolean]: If true, will only print what would be done
|
@@ -18,6 +18,7 @@ module MU
|
|
18
18
|
# A database as configured in {MU::Config::BasketofKittens::databases}
|
19
19
|
class Database < MU::Cloud::Database
|
20
20
|
@deploy = nil
|
21
|
+
@project_id = nil
|
21
22
|
@config = nil
|
22
23
|
attr_reader :mu_name
|
23
24
|
attr_reader :cloud_id
|
@@ -36,6 +37,11 @@ module MU
|
|
36
37
|
|
37
38
|
if !mu_name.nil?
|
38
39
|
@mu_name = mu_name
|
40
|
+
@config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
|
41
|
+
if !@project_id
|
42
|
+
project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
|
43
|
+
@project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
|
44
|
+
end
|
39
45
|
else
|
40
46
|
@mu_name ||=
|
41
47
|
if @config and @config['engine'] and @config["engine"].match(/^sqlserver/)
|
@@ -51,6 +57,7 @@ module MU
|
|
51
57
|
# Called automatically by {MU::Deploy#createResources}
|
52
58
|
# @return [String]: The cloud provider's identifier for this database instance.
|
53
59
|
def create
|
60
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
|
54
61
|
labels = {}
|
55
62
|
MU::MommaCat.listStandardTags.each_pair { |name, value|
|
56
63
|
if !value.nil?
|
@@ -74,7 +81,7 @@ module MU
|
|
74
81
|
instance_type: "CLOUD_SQL_INSTANCE" # TODO: READ_REPLICA_INSTANCE
|
75
82
|
)
|
76
83
|
pp instance_desc
|
77
|
-
pp MU::Cloud::Google.sql(credentials: @config['credentials']).insert_instance(@
|
84
|
+
pp MU::Cloud::Google.sql(credentials: @config['credentials']).insert_instance(@project_id, instance_desc)
|
78
85
|
end
|
79
86
|
|
80
87
|
# Locate an existing Database or Databases and return an array containing matching GCP resource descriptors for those that match.
|
@@ -90,6 +97,7 @@ module MU
|
|
90
97
|
|
91
98
|
# Called automatically by {MU::Deploy#createResources}
|
92
99
|
def groom
|
100
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
|
93
101
|
end
|
94
102
|
|
95
103
|
# Register a description of this database instance with this deployment's metadata.
|
@@ -111,6 +119,12 @@ module MU
|
|
111
119
|
false
|
112
120
|
end
|
113
121
|
|
122
|
+
# Denote whether this resource implementation is experiment, ready for
|
123
|
+
# testing, or ready for production use.
|
124
|
+
def self.quality
|
125
|
+
MU::Cloud::ALPHA
|
126
|
+
end
|
127
|
+
|
114
128
|
# Called by {MU::Cleanup}. Locates resources that were created by the
|
115
129
|
# currently-loaded deployment, and purges them.
|
116
130
|
# @param noop [Boolean]: If true, will only print what would be done
|
@@ -21,6 +21,7 @@ module MU
|
|
21
21
|
|
22
22
|
@deploy = nil
|
23
23
|
@config = nil
|
24
|
+
@project_id = nil
|
24
25
|
@admin_sgs = Hash.new
|
25
26
|
@admin_sg_semaphore = Mutex.new
|
26
27
|
|
@@ -38,6 +39,11 @@ module MU
|
|
38
39
|
@mu_name = mu_name
|
39
40
|
# This is really a placeholder, since we "own" multiple rule sets
|
40
41
|
@cloud_id ||= MU::Cloud::Google.nameStr(@mu_name+"-ingress-allow")
|
42
|
+
@config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
|
43
|
+
if !@project_id
|
44
|
+
project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
|
45
|
+
@project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
|
46
|
+
end
|
41
47
|
else
|
42
48
|
if !@vpc.nil?
|
43
49
|
@mu_name = @deploy.getResourceName(@config['name'], need_unique_string: true, max_length: 61)
|
@@ -52,6 +58,8 @@ module MU
|
|
52
58
|
|
53
59
|
# Called by {MU::Deploy#createResources}
|
54
60
|
def create
|
61
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
|
62
|
+
|
55
63
|
vpc_id = @vpc.cloudobj.url if !@vpc.nil? and !@vpc.cloudobj.nil?
|
56
64
|
vpc_id ||= @config['vpc']['vpc_id'] if @config['vpc'] and @config['vpc']['vpc_id']
|
57
65
|
|
@@ -109,8 +117,8 @@ module MU
|
|
109
117
|
allrules.each_value { |fwdesc|
|
110
118
|
threads << Thread.new {
|
111
119
|
fwobj = MU::Cloud::Google.compute(:Firewall).new(fwdesc)
|
112
|
-
MU.log "Creating firewall #{fwdesc[:name]} in project #{@
|
113
|
-
resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_firewall(@
|
120
|
+
MU.log "Creating firewall #{fwdesc[:name]} in project #{@project_id}", details: fwobj
|
121
|
+
resp = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_firewall(@project_id, fwobj)
|
114
122
|
# XXX Check for empty (no hosts) sets
|
115
123
|
# MU.log "Can't create empty firewalls in Google Cloud, skipping #{@mu_name}", MU::WARN
|
116
124
|
}
|
@@ -123,6 +131,7 @@ module MU
|
|
123
131
|
|
124
132
|
# Called by {MU::Deploy#createResources}
|
125
133
|
def groom
|
134
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloudobj.cloud_id
|
126
135
|
end
|
127
136
|
|
128
137
|
# Log metadata about this ruleset to the currently running deployment
|
@@ -132,6 +141,7 @@ module MU
|
|
132
141
|
)
|
133
142
|
sg_data ||= {}
|
134
143
|
sg_data["group_id"] = @cloud_id
|
144
|
+
sg_data["project_id"] = @project_id
|
135
145
|
sg_data["cloud_id"] = @cloud_id
|
136
146
|
|
137
147
|
return sg_data
|
@@ -176,6 +186,12 @@ module MU
|
|
176
186
|
true
|
177
187
|
end
|
178
188
|
|
189
|
+
# Denote whether this resource implementation is experiment, ready for
|
190
|
+
# testing, or ready for production use.
|
191
|
+
def self.quality
|
192
|
+
MU::Cloud::RELEASE
|
193
|
+
end
|
194
|
+
|
179
195
|
# Remove all security groups (firewall rulesets) associated with the currently loaded deployment.
|
180
196
|
# @param noop [Boolean]: If true, will only print what would be done
|
181
197
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
@@ -19,6 +19,7 @@ module MU
|
|
19
19
|
class Folder < MU::Cloud::Folder
|
20
20
|
@deploy = nil
|
21
21
|
@config = nil
|
22
|
+
@parent = nil
|
22
23
|
|
23
24
|
attr_reader :mu_name
|
24
25
|
attr_reader :config
|
@@ -30,23 +31,95 @@ module MU
|
|
30
31
|
@deploy = mommacat
|
31
32
|
@config = MU::Config.manxify(kitten_cfg)
|
32
33
|
@cloud_id ||= cloud_id
|
33
|
-
|
34
|
+
|
35
|
+
if !mu_name.nil?
|
36
|
+
@mu_name = mu_name
|
37
|
+
elsif @config['scrub_mu_isms']
|
38
|
+
@mu_name = @config['name']
|
39
|
+
else
|
40
|
+
@mu_name = @deploy.getResourceName(@config['name'])
|
41
|
+
end
|
34
42
|
end
|
35
43
|
|
36
44
|
# Called automatically by {MU::Deploy#createResources}
|
37
45
|
def create
|
38
46
|
|
39
|
-
name_string = @
|
47
|
+
name_string = if @config['scrub_mu_isms']
|
48
|
+
@config["name"]
|
49
|
+
else
|
50
|
+
@deploy.getResourceName(@config["name"], max_length: 30).downcase
|
51
|
+
end
|
40
52
|
|
41
|
-
|
42
|
-
name: name_string,
|
53
|
+
params = {
|
43
54
|
display_name: name_string
|
44
|
-
|
45
|
-
|
46
|
-
MU.
|
47
|
-
|
55
|
+
}
|
56
|
+
|
57
|
+
parent = MU::Cloud::Google::Folder.resolveParent(@config['parent'], credentials: @config['credentials'])
|
58
|
+
|
59
|
+
folder_obj = MU::Cloud::Google.folder(:Folder).new(params)
|
60
|
+
|
61
|
+
MU.log "Creating folder #{name_string} under #{parent}", details: folder_obj
|
62
|
+
resp = MU::Cloud::Google.folder(credentials: @config['credentials']).create_folder(folder_obj, parent: parent)
|
63
|
+
|
64
|
+
# Wait for list_folders output to be consistent (for the folder we
|
65
|
+
# just created to show up)
|
66
|
+
retries = 0
|
67
|
+
begin
|
68
|
+
found = MU::Cloud::Google::Folder.find(credentials: credentials, flags: { 'display_name' => name_string, 'parent_id' => parent })
|
69
|
+
if found.size > 0
|
70
|
+
@cloud_id = found.keys.first
|
71
|
+
@parent = found.values.first.parent
|
72
|
+
MU.log "Folder #{name_string} has identifier #{@cloud_id}"
|
73
|
+
else
|
74
|
+
if retries > 0 and (retries % 3) == 0
|
75
|
+
MU.log "Waiting for Google Cloud folder #{name_string} to appear in list_folder results...", MU::NOTICE
|
76
|
+
end
|
77
|
+
retries += 1
|
78
|
+
sleep 15
|
79
|
+
end
|
80
|
+
end while found.size == 0
|
48
81
|
|
49
|
-
|
82
|
+
end
|
83
|
+
|
84
|
+
# Given a {MU::Config::Folder.reference} configuration block, resolve
|
85
|
+
# to a GCP resource id and type suitable for use in API calls to manage
|
86
|
+
# projects and folders.
|
87
|
+
# @param parentblock [Hash]
|
88
|
+
# @return [String]
|
89
|
+
def self.resolveParent(parentblock, credentials: nil)
|
90
|
+
my_org = MU::Cloud::Google.getOrg(credentials)
|
91
|
+
if !parentblock or parentblock['id'] == my_org.name or
|
92
|
+
parentblock['name'] == my_org.display_name or (parentblock['id'] and
|
93
|
+
"organizations/"+parentblock['id'] == my_org.name)
|
94
|
+
return my_org.name
|
95
|
+
end
|
96
|
+
|
97
|
+
if parentblock['name']
|
98
|
+
sib_folder = MU::MommaCat.findStray(
|
99
|
+
"Google",
|
100
|
+
"folders",
|
101
|
+
deploy_id: parentblock['deploy_id'],
|
102
|
+
credentials: credentials,
|
103
|
+
name: parentblock['name']
|
104
|
+
).first
|
105
|
+
if sib_folder
|
106
|
+
return "folders/"+sib_folder.cloudobj.cloud_id
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
begin
|
111
|
+
found = MU::Cloud::Google::Folder.find(cloud_id: parentblock['id'], credentials: credentials, flags: { 'display_name' => parentblock['name'] })
|
112
|
+
rescue ::Google::Apis::ClientError => e
|
113
|
+
if !e.message.match(/Invalid request status_code: 404/)
|
114
|
+
raise e
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if found and found.size > 0
|
119
|
+
return found.values.first.name
|
120
|
+
end
|
121
|
+
|
122
|
+
nil
|
50
123
|
end
|
51
124
|
|
52
125
|
# Return the cloud descriptor for the Folder
|
@@ -57,8 +130,9 @@ pp folder_obj
|
|
57
130
|
# Return the metadata for this project's configuration
|
58
131
|
# @return [Hash]
|
59
132
|
def notify
|
60
|
-
desc = MU.structToHash(MU::Cloud::Google.folder(credentials: credentials).get_folder(
|
133
|
+
desc = MU.structToHash(MU::Cloud::Google.folder(credentials: @config['credentials']).get_folder("folders/"+@cloud_id))
|
61
134
|
desc["mu_name"] = @mu_name
|
135
|
+
desc["parent"] = @parent
|
62
136
|
desc["cloud_id"] = @cloud_id
|
63
137
|
desc
|
64
138
|
end
|
@@ -70,24 +144,105 @@ pp folder_obj
|
|
70
144
|
true
|
71
145
|
end
|
72
146
|
|
147
|
+
# Denote whether this resource implementation is experiment, ready for
|
148
|
+
# testing, or ready for production use.
|
149
|
+
def self.quality
|
150
|
+
MU::Cloud::BETA
|
151
|
+
end
|
152
|
+
|
73
153
|
# Remove all Google projects associated with the currently loaded deployment. Try to, anyway.
|
74
154
|
# @param noop [Boolean]: If true, will only print what would be done
|
75
155
|
# @param ignoremaster [Boolean]: If true, will remove resources not flagged as originating from this Mu server
|
76
|
-
# @param region [String]: The cloud provider region
|
77
156
|
# @return [void]
|
78
|
-
def self.cleanup(noop: false, ignoremaster: false,
|
157
|
+
def self.cleanup(noop: false, ignoremaster: false, credentials: nil, flags: {}, region: MU.myRegion)
|
158
|
+
# We can't label GCP folders, and their names are too short to encode
|
159
|
+
# Mu deploy IDs, so all we can do is rely on flags['known'] passed in
|
160
|
+
# from cleanup, which relies on our metadata to know what's ours.
|
161
|
+
|
162
|
+
if flags and flags['known']
|
163
|
+
flags['known'].each { |cloud_id|
|
164
|
+
found = self.find(cloud_id: cloud_id, credentials: credentials)
|
165
|
+
if found.size > 0 and found.values.first.lifecycle_state == "ACTIVE"
|
166
|
+
MU.log "Deleting folder #{found.values.first.display_name} (#{found.keys.first})"
|
167
|
+
if !noop
|
168
|
+
max_retries = 10
|
169
|
+
retries = 0
|
170
|
+
success = false
|
171
|
+
begin
|
172
|
+
MU::Cloud::Google.folder(credentials: credentials).delete_folder(
|
173
|
+
"folders/"+found.keys.first
|
174
|
+
)
|
175
|
+
found = self.find(cloud_id: cloud_id, credentials: credentials)
|
176
|
+
if found and found.size > 0 and found.values.first.lifecycle_state != "DELETE_REQUESTED"
|
177
|
+
if retries < max_retries
|
178
|
+
sleep 30
|
179
|
+
retries += 1
|
180
|
+
puts retries
|
181
|
+
else
|
182
|
+
MU.log "Folder #{cloud_id} still exists after #{max_retries.to_s} attempts to delete", MU::ERR
|
183
|
+
break
|
184
|
+
end
|
185
|
+
else
|
186
|
+
success = true
|
187
|
+
end
|
188
|
+
|
189
|
+
rescue ::Google::Apis::ClientError => e
|
190
|
+
if e.message.match(/failedPrecondition/) and retries < max_retries
|
191
|
+
sleep 30
|
192
|
+
retries += 1
|
193
|
+
retry
|
194
|
+
else
|
195
|
+
raise e
|
196
|
+
end
|
197
|
+
end while !success
|
198
|
+
end
|
199
|
+
end
|
200
|
+
}
|
201
|
+
end
|
79
202
|
end
|
80
203
|
|
81
204
|
# Locate an existing project
|
82
205
|
# @param cloud_id [String]: The cloud provider's identifier for this resource.
|
83
|
-
# @param region [String]: The cloud provider region.
|
84
206
|
# @param flags [Hash]: Optional flags
|
85
207
|
# @return [OpenStruct]: The cloud provider's complete descriptions of matching project
|
86
|
-
def self.find(cloud_id: nil,
|
208
|
+
def self.find(cloud_id: nil, credentials: nil, flags: {}, tag_key: nil, tag_value: nil)
|
87
209
|
found = {}
|
210
|
+
|
211
|
+
# Recursively search a GCP folder hierarchy for a folder matching our
|
212
|
+
# supplied name or identifier.
|
213
|
+
def self.find_matching_folder(parent, name: nil, id: nil, credentials: nil)
|
214
|
+
resp = MU::Cloud::Google.folder(credentials: credentials).list_folders(parent: parent)
|
215
|
+
if resp and resp.folders
|
216
|
+
resp.folders.each { |f|
|
217
|
+
if name and name.downcase == f.display_name.downcase
|
218
|
+
return f
|
219
|
+
elsif id and "folders/"+id== f.name
|
220
|
+
return f
|
221
|
+
else
|
222
|
+
found = self.find_matching_folder(f.name, name: name, id: id, credentials: credentials)
|
223
|
+
return found if found
|
224
|
+
end
|
225
|
+
}
|
226
|
+
end
|
227
|
+
nil
|
228
|
+
end
|
229
|
+
|
88
230
|
if cloud_id
|
89
|
-
found[cloud_id] = MU::Cloud::Google.folder(credentials: credentials).get_folder(cloud_id)
|
90
|
-
|
231
|
+
found[cloud_id.sub(/^folders\//, "")] = MU::Cloud::Google.folder(credentials: credentials).get_folder("folders/"+cloud_id.sub(/^folders\//, ""))
|
232
|
+
elsif flags['display_name']
|
233
|
+
parent = if flags['parent_id']
|
234
|
+
flags['parent_id']
|
235
|
+
else
|
236
|
+
my_org = MU::Cloud::Google.getOrg(credentials)
|
237
|
+
my_org.name
|
238
|
+
end
|
239
|
+
|
240
|
+
if parent
|
241
|
+
resp = self.find_matching_folder(parent, name: flags['display_name'], credentials: credentials)
|
242
|
+
if resp
|
243
|
+
found[resp.name.sub(/^folders\//, "")] = resp
|
244
|
+
end
|
245
|
+
end
|
91
246
|
end
|
92
247
|
|
93
248
|
found
|
@@ -110,6 +265,18 @@ pp folder_obj
|
|
110
265
|
def self.validateConfig(folder, configurator)
|
111
266
|
ok = true
|
112
267
|
|
268
|
+
if !MU::Cloud::Google.getOrg(folder['credentials'])
|
269
|
+
MU.log "Cannot manage Google Cloud projects in environments without an organization. See also: https://cloud.google.com/resource-manager/docs/creating-managing-organization", MU::ERR
|
270
|
+
ok = false
|
271
|
+
end
|
272
|
+
|
273
|
+
if folder['parent'] and folder['parent']['name'] and !folder['parent']['deploy_id'] and configurator.haveLitterMate?(folder['parent']['name'], "folders")
|
274
|
+
folder["dependencies"] ||= []
|
275
|
+
folder["dependencies"] << {
|
276
|
+
"type" => "folder",
|
277
|
+
"name" => folder['parent']['name']
|
278
|
+
}
|
279
|
+
end
|
113
280
|
|
114
281
|
ok
|
115
282
|
end
|