cloud-mu 2.1.0beta → 3.0.0beta
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 +5 -5
- data/Berksfile +4 -5
- data/Berksfile.lock +179 -0
- data/README.md +1 -6
- data/ansible/roles/geerlingguy.firewall/templates/firewall.bash.j2 +0 -0
- data/ansible/roles/mu-installer/README.md +33 -0
- data/ansible/roles/mu-installer/defaults/main.yml +2 -0
- data/ansible/roles/mu-installer/handlers/main.yml +2 -0
- data/ansible/roles/mu-installer/meta/main.yml +60 -0
- data/ansible/roles/mu-installer/tasks/main.yml +13 -0
- data/ansible/roles/mu-installer/tests/inventory +2 -0
- data/ansible/roles/mu-installer/tests/test.yml +5 -0
- data/ansible/roles/mu-installer/vars/main.yml +2 -0
- data/bin/mu-adopt +125 -0
- data/bin/mu-aws-setup +4 -4
- data/bin/mu-azure-setup +265 -0
- data/bin/mu-azure-tests +43 -0
- data/bin/mu-cleanup +20 -8
- data/bin/mu-configure +224 -98
- data/bin/mu-deploy +8 -3
- data/bin/mu-gcp-setup +16 -8
- data/bin/mu-gen-docs +92 -8
- data/bin/mu-load-config.rb +52 -12
- data/bin/mu-momma-cat +36 -0
- data/bin/mu-node-manage +34 -27
- data/bin/mu-self-update +2 -2
- data/bin/mu-ssh +12 -8
- data/bin/mu-upload-chef-artifacts +11 -4
- data/bin/mu-user-manage +3 -0
- data/cloud-mu.gemspec +8 -11
- data/cookbooks/firewall/libraries/helpers_iptables.rb +2 -2
- data/cookbooks/firewall/metadata.json +1 -1
- data/cookbooks/firewall/recipes/default.rb +5 -9
- data/cookbooks/mu-firewall/attributes/default.rb +2 -0
- data/cookbooks/mu-firewall/metadata.rb +1 -1
- data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +0 -0
- data/cookbooks/mu-master/Berksfile +2 -2
- data/cookbooks/mu-master/files/default/check_mem.pl +0 -0
- data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
- data/cookbooks/mu-master/metadata.rb +5 -4
- data/cookbooks/mu-master/recipes/389ds.rb +1 -1
- data/cookbooks/mu-master/recipes/basepackages.rb +30 -10
- data/cookbooks/mu-master/recipes/default.rb +59 -7
- data/cookbooks/mu-master/recipes/firewall-holes.rb +1 -1
- data/cookbooks/mu-master/recipes/init.rb +65 -47
- data/cookbooks/mu-master/recipes/{eks-kubectl.rb → kubectl.rb} +4 -10
- data/cookbooks/mu-master/recipes/sssd.rb +2 -1
- data/cookbooks/mu-master/recipes/update_nagios_only.rb +6 -6
- data/cookbooks/mu-master/templates/default/web_app.conf.erb +2 -2
- data/cookbooks/mu-master/templates/mods/ldap.conf.erb +4 -0
- data/cookbooks/mu-php54/Berksfile +1 -2
- data/cookbooks/mu-php54/metadata.rb +4 -5
- data/cookbooks/mu-php54/recipes/default.rb +1 -1
- data/cookbooks/mu-splunk/templates/default/splunk-init.erb +0 -0
- data/cookbooks/mu-tools/Berksfile +3 -2
- data/cookbooks/mu-tools/files/default/Mu_CA.pem +33 -0
- data/cookbooks/mu-tools/libraries/helper.rb +20 -8
- data/cookbooks/mu-tools/metadata.rb +5 -2
- data/cookbooks/mu-tools/recipes/apply_security.rb +2 -3
- data/cookbooks/mu-tools/recipes/eks.rb +1 -1
- data/cookbooks/mu-tools/recipes/gcloud.rb +5 -30
- data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
- data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -0
- data/cookbooks/mu-tools/recipes/selinux.rb +19 -0
- data/cookbooks/mu-tools/recipes/split_var_partitions.rb +0 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +256 -122
- data/cookbooks/mu-tools/resources/disk.rb +3 -1
- data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +1 -1
- data/cookbooks/mu-tools/templates/default/etc_hosts.erb +1 -1
- data/cookbooks/mu-tools/templates/default/{kubeconfig.erb → kubeconfig-eks.erb} +0 -0
- data/cookbooks/mu-tools/templates/default/kubeconfig-gke.erb +27 -0
- data/cookbooks/mu-tools/templates/windows-10/sshd_config.erb +137 -0
- data/cookbooks/mu-utility/recipes/nat.rb +4 -0
- data/extras/alpha.png +0 -0
- data/extras/beta.png +0 -0
- data/extras/clean-stock-amis +2 -2
- data/extras/generate-stock-images +131 -0
- data/extras/git-fix-permissions-hook +0 -0
- data/extras/image-generators/AWS/centos6.yaml +17 -0
- data/extras/image-generators/{aws → AWS}/centos7-govcloud.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/centos7.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/rhel7.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/win2k12.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/win2k16.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/windows.yaml +0 -0
- data/extras/image-generators/{gcp → Google}/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +18 -0
- data/extras/python_rpm/build.sh +0 -0
- data/extras/release.png +0 -0
- data/extras/ruby_rpm/build.sh +0 -0
- data/extras/ruby_rpm/muby.spec +1 -1
- data/install/README.md +43 -5
- data/install/deprecated-bash-library.sh +0 -0
- data/install/installer +1 -1
- data/install/jenkinskeys.rb +0 -0
- data/install/mu-master.yaml +55 -0
- data/modules/mommacat.ru +41 -7
- data/modules/mu.rb +444 -149
- data/modules/mu/adoption.rb +500 -0
- data/modules/mu/cleanup.rb +235 -158
- data/modules/mu/cloud.rb +675 -138
- data/modules/mu/clouds/aws.rb +156 -24
- data/modules/mu/clouds/aws/alarm.rb +4 -14
- data/modules/mu/clouds/aws/bucket.rb +60 -18
- data/modules/mu/clouds/aws/cache_cluster.rb +8 -20
- data/modules/mu/clouds/aws/collection.rb +12 -22
- data/modules/mu/clouds/aws/container_cluster.rb +209 -118
- data/modules/mu/clouds/aws/database.rb +120 -45
- data/modules/mu/clouds/aws/dnszone.rb +7 -18
- data/modules/mu/clouds/aws/endpoint.rb +5 -15
- data/modules/mu/clouds/aws/firewall_rule.rb +144 -72
- data/modules/mu/clouds/aws/folder.rb +4 -11
- data/modules/mu/clouds/aws/function.rb +6 -16
- data/modules/mu/clouds/aws/group.rb +4 -12
- data/modules/mu/clouds/aws/habitat.rb +11 -13
- data/modules/mu/clouds/aws/loadbalancer.rb +40 -28
- data/modules/mu/clouds/aws/log.rb +5 -13
- data/modules/mu/clouds/aws/msg_queue.rb +9 -24
- data/modules/mu/clouds/aws/nosqldb.rb +4 -12
- data/modules/mu/clouds/aws/notifier.rb +6 -13
- data/modules/mu/clouds/aws/role.rb +69 -40
- data/modules/mu/clouds/aws/search_domain.rb +17 -20
- data/modules/mu/clouds/aws/server.rb +184 -94
- data/modules/mu/clouds/aws/server_pool.rb +33 -38
- data/modules/mu/clouds/aws/storage_pool.rb +5 -12
- data/modules/mu/clouds/aws/user.rb +59 -33
- data/modules/mu/clouds/aws/userdata/linux.erb +18 -30
- data/modules/mu/clouds/aws/userdata/windows.erb +9 -9
- data/modules/mu/clouds/aws/vpc.rb +214 -145
- data/modules/mu/clouds/azure.rb +978 -44
- data/modules/mu/clouds/azure/container_cluster.rb +413 -0
- data/modules/mu/clouds/azure/firewall_rule.rb +500 -0
- data/modules/mu/clouds/azure/habitat.rb +167 -0
- data/modules/mu/clouds/azure/loadbalancer.rb +205 -0
- data/modules/mu/clouds/azure/role.rb +211 -0
- data/modules/mu/clouds/azure/server.rb +810 -0
- data/modules/mu/clouds/azure/user.rb +257 -0
- data/modules/mu/clouds/azure/userdata/README.md +4 -0
- data/modules/mu/clouds/azure/userdata/linux.erb +137 -0
- data/modules/mu/clouds/azure/userdata/windows.erb +275 -0
- data/modules/mu/clouds/azure/vpc.rb +782 -0
- data/modules/mu/clouds/cloudformation.rb +12 -9
- data/modules/mu/clouds/cloudformation/firewall_rule.rb +5 -13
- data/modules/mu/clouds/cloudformation/server.rb +10 -1
- data/modules/mu/clouds/cloudformation/server_pool.rb +1 -0
- data/modules/mu/clouds/cloudformation/vpc.rb +0 -2
- data/modules/mu/clouds/google.rb +554 -117
- data/modules/mu/clouds/google/bucket.rb +173 -32
- data/modules/mu/clouds/google/container_cluster.rb +1112 -157
- data/modules/mu/clouds/google/database.rb +24 -47
- data/modules/mu/clouds/google/firewall_rule.rb +344 -89
- data/modules/mu/clouds/google/folder.rb +156 -79
- data/modules/mu/clouds/google/group.rb +272 -82
- data/modules/mu/clouds/google/habitat.rb +177 -52
- data/modules/mu/clouds/google/loadbalancer.rb +9 -34
- data/modules/mu/clouds/google/role.rb +1211 -0
- data/modules/mu/clouds/google/server.rb +491 -227
- data/modules/mu/clouds/google/server_pool.rb +233 -48
- data/modules/mu/clouds/google/user.rb +479 -125
- data/modules/mu/clouds/google/userdata/linux.erb +3 -3
- data/modules/mu/clouds/google/userdata/windows.erb +9 -9
- data/modules/mu/clouds/google/vpc.rb +381 -223
- data/modules/mu/config.rb +689 -214
- data/modules/mu/config/bucket.rb +1 -1
- data/modules/mu/config/cache_cluster.rb +1 -1
- data/modules/mu/config/cache_cluster.yml +0 -4
- data/modules/mu/config/container_cluster.rb +18 -9
- data/modules/mu/config/database.rb +6 -23
- data/modules/mu/config/firewall_rule.rb +9 -15
- data/modules/mu/config/folder.rb +22 -21
- data/modules/mu/config/habitat.rb +22 -21
- data/modules/mu/config/loadbalancer.rb +2 -2
- data/modules/mu/config/role.rb +9 -40
- data/modules/mu/config/server.rb +26 -5
- data/modules/mu/config/server_pool.rb +1 -1
- data/modules/mu/config/storage_pool.rb +2 -2
- data/modules/mu/config/user.rb +4 -0
- data/modules/mu/config/vpc.rb +350 -110
- data/modules/mu/defaults/{amazon_images.yaml → AWS.yaml} +37 -39
- data/modules/mu/defaults/Azure.yaml +17 -0
- data/modules/mu/defaults/Google.yaml +24 -0
- data/modules/mu/defaults/README.md +1 -1
- data/modules/mu/deploy.rb +168 -125
- data/modules/mu/groomer.rb +2 -1
- data/modules/mu/groomers/ansible.rb +104 -32
- data/modules/mu/groomers/chef.rb +96 -44
- data/modules/mu/kittens.rb +20602 -0
- data/modules/mu/logger.rb +38 -11
- data/modules/mu/master.rb +90 -8
- data/modules/mu/master/chef.rb +2 -3
- data/modules/mu/master/ldap.rb +0 -1
- data/modules/mu/master/ssl.rb +250 -0
- data/modules/mu/mommacat.rb +917 -513
- data/modules/scratchpad.erb +1 -1
- data/modules/tests/super_complex_bok.yml +0 -0
- data/modules/tests/super_simple_bok.yml +0 -0
- data/roles/mu-master.json +2 -1
- data/spec/azure_creds +5 -0
- data/spec/mu.yaml +56 -0
- data/spec/mu/clouds/azure_spec.rb +164 -27
- data/spec/spec_helper.rb +5 -0
- data/test/clean_up.py +0 -0
- data/test/exec_inspec.py +0 -0
- data/test/exec_mu_install.py +0 -0
- data/test/exec_retry.py +0 -0
- data/test/smoke_test.rb +0 -0
- metadata +90 -118
- data/cookbooks/mu-jenkins/Berksfile +0 -14
- data/cookbooks/mu-jenkins/CHANGELOG.md +0 -13
- data/cookbooks/mu-jenkins/LICENSE +0 -37
- data/cookbooks/mu-jenkins/README.md +0 -105
- data/cookbooks/mu-jenkins/attributes/default.rb +0 -42
- data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +0 -73
- data/cookbooks/mu-jenkins/files/default/deploy_config.xml +0 -44
- data/cookbooks/mu-jenkins/metadata.rb +0 -21
- data/cookbooks/mu-jenkins/recipes/default.rb +0 -195
- data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +0 -54
- data/cookbooks/mu-jenkins/recipes/public_key.rb +0 -24
- data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +0 -24
- data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +0 -14
- data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +0 -6
- data/cookbooks/nagios/Berksfile +0 -11
- data/cookbooks/nagios/CHANGELOG.md +0 -589
- data/cookbooks/nagios/CONTRIBUTING.md +0 -11
- data/cookbooks/nagios/LICENSE +0 -37
- data/cookbooks/nagios/README.md +0 -328
- data/cookbooks/nagios/TESTING.md +0 -2
- data/cookbooks/nagios/attributes/config.rb +0 -171
- data/cookbooks/nagios/attributes/default.rb +0 -228
- data/cookbooks/nagios/chefignore +0 -102
- data/cookbooks/nagios/definitions/command.rb +0 -33
- data/cookbooks/nagios/definitions/contact.rb +0 -33
- data/cookbooks/nagios/definitions/contactgroup.rb +0 -33
- data/cookbooks/nagios/definitions/host.rb +0 -33
- data/cookbooks/nagios/definitions/hostdependency.rb +0 -33
- data/cookbooks/nagios/definitions/hostescalation.rb +0 -34
- data/cookbooks/nagios/definitions/hostgroup.rb +0 -33
- data/cookbooks/nagios/definitions/nagios_conf.rb +0 -38
- data/cookbooks/nagios/definitions/resource.rb +0 -33
- data/cookbooks/nagios/definitions/service.rb +0 -33
- data/cookbooks/nagios/definitions/servicedependency.rb +0 -33
- data/cookbooks/nagios/definitions/serviceescalation.rb +0 -34
- data/cookbooks/nagios/definitions/servicegroup.rb +0 -33
- data/cookbooks/nagios/definitions/timeperiod.rb +0 -33
- data/cookbooks/nagios/libraries/base.rb +0 -314
- data/cookbooks/nagios/libraries/command.rb +0 -91
- data/cookbooks/nagios/libraries/contact.rb +0 -230
- data/cookbooks/nagios/libraries/contactgroup.rb +0 -112
- data/cookbooks/nagios/libraries/custom_option.rb +0 -36
- data/cookbooks/nagios/libraries/data_bag_helper.rb +0 -23
- data/cookbooks/nagios/libraries/default.rb +0 -90
- data/cookbooks/nagios/libraries/host.rb +0 -412
- data/cookbooks/nagios/libraries/hostdependency.rb +0 -181
- data/cookbooks/nagios/libraries/hostescalation.rb +0 -173
- data/cookbooks/nagios/libraries/hostgroup.rb +0 -119
- data/cookbooks/nagios/libraries/nagios.rb +0 -282
- data/cookbooks/nagios/libraries/resource.rb +0 -59
- data/cookbooks/nagios/libraries/service.rb +0 -455
- data/cookbooks/nagios/libraries/servicedependency.rb +0 -215
- data/cookbooks/nagios/libraries/serviceescalation.rb +0 -195
- data/cookbooks/nagios/libraries/servicegroup.rb +0 -144
- data/cookbooks/nagios/libraries/timeperiod.rb +0 -160
- data/cookbooks/nagios/libraries/users_helper.rb +0 -54
- data/cookbooks/nagios/metadata.rb +0 -25
- data/cookbooks/nagios/recipes/_load_databag_config.rb +0 -153
- data/cookbooks/nagios/recipes/_load_default_config.rb +0 -241
- data/cookbooks/nagios/recipes/apache.rb +0 -48
- data/cookbooks/nagios/recipes/default.rb +0 -204
- data/cookbooks/nagios/recipes/nginx.rb +0 -82
- data/cookbooks/nagios/recipes/pagerduty.rb +0 -143
- data/cookbooks/nagios/recipes/server_package.rb +0 -40
- data/cookbooks/nagios/recipes/server_source.rb +0 -164
- data/cookbooks/nagios/templates/default/apache2.conf.erb +0 -96
- data/cookbooks/nagios/templates/default/cgi.cfg.erb +0 -266
- data/cookbooks/nagios/templates/default/commands.cfg.erb +0 -13
- data/cookbooks/nagios/templates/default/contacts.cfg.erb +0 -37
- data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +0 -25
- data/cookbooks/nagios/templates/default/hosts.cfg.erb +0 -15
- data/cookbooks/nagios/templates/default/htpasswd.users.erb +0 -6
- data/cookbooks/nagios/templates/default/nagios.cfg.erb +0 -22
- data/cookbooks/nagios/templates/default/nginx.conf.erb +0 -62
- data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +0 -185
- data/cookbooks/nagios/templates/default/resource.cfg.erb +0 -27
- data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +0 -15
- data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +0 -14
- data/cookbooks/nagios/templates/default/services.cfg.erb +0 -14
- data/cookbooks/nagios/templates/default/templates.cfg.erb +0 -31
- data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +0 -13
- data/extras/image-generators/aws/centos6.yaml +0 -18
- data/modules/mu/defaults/google_images.yaml +0 -16
- data/roles/mu-master-jenkins.json +0 -24
|
@@ -15,30 +15,16 @@
|
|
|
15
15
|
module MU
|
|
16
16
|
class Cloud
|
|
17
17
|
class Google
|
|
18
|
-
# Creates
|
|
18
|
+
# Creates a Google folder as configured in {MU::Config::BasketofKittens::folders}
|
|
19
19
|
class Folder < MU::Cloud::Folder
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
# @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
|
|
29
|
-
# @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::folders}
|
|
30
|
-
def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
|
|
31
|
-
@deploy = mommacat
|
|
32
|
-
@config = MU::Config.manxify(kitten_cfg)
|
|
33
|
-
@cloud_id ||= cloud_id
|
|
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
|
|
20
|
+
|
|
21
|
+
# Initialize this cloud resource object. Calling +super+ will invoke the initializer defined under {MU::Cloud}, which should set the attribtues listed in {MU::Cloud::PUBLIC_ATTRS} as well as applicable dependency shortcuts, like <tt>@vpc</tt>, for us.
|
|
22
|
+
# @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
|
|
23
|
+
def initialize(**args)
|
|
24
|
+
super
|
|
25
|
+
cloud_desc if @cloud_id # XXX this maybe isn't my job
|
|
26
|
+
|
|
27
|
+
@mu_name ||= @deploy.getResourceName(@config['name'])
|
|
42
28
|
end
|
|
43
29
|
|
|
44
30
|
# Called automatically by {MU::Deploy#createResources}
|
|
@@ -54,6 +40,9 @@ module MU
|
|
|
54
40
|
display_name: name_string
|
|
55
41
|
}
|
|
56
42
|
|
|
43
|
+
if @config['parent']['name'] and !@config['parent']['id']
|
|
44
|
+
@config['parent']['deploy_id'] = @deploy.deploy_id
|
|
45
|
+
end
|
|
57
46
|
parent = MU::Cloud::Google::Folder.resolveParent(@config['parent'], credentials: @config['credentials'])
|
|
58
47
|
|
|
59
48
|
folder_obj = MU::Cloud::Google.folder(:Folder).new(params)
|
|
@@ -79,6 +68,20 @@ module MU
|
|
|
79
68
|
end
|
|
80
69
|
end while found.size == 0
|
|
81
70
|
|
|
71
|
+
@habitat = parent
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Retrieve the IAM bindings for this folder (associates between IAM roles and groups/users)
|
|
76
|
+
def bindings
|
|
77
|
+
MU::Cloud::Google::Folder.bindings(@cloud_id, credentials: @config['credentials'])
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Retrieve the IAM bindings for this folder (associates between IAM roles and groups/users)
|
|
81
|
+
# @param folder [String]:
|
|
82
|
+
# @param credentials [String]:
|
|
83
|
+
def self.bindings(folder, credentials: nil)
|
|
84
|
+
MU::Cloud::Google.folder(credentials: credentials).get_folder_iam_policy(folder).bindings
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
# Given a {MU::Config::Folder.reference} configuration block, resolve
|
|
@@ -88,9 +91,9 @@ module MU
|
|
|
88
91
|
# @return [String]
|
|
89
92
|
def self.resolveParent(parentblock, credentials: nil)
|
|
90
93
|
my_org = MU::Cloud::Google.getOrg(credentials)
|
|
91
|
-
if !parentblock or parentblock['id'] == my_org.name or
|
|
94
|
+
if my_org and (!parentblock or parentblock['id'] == my_org.name or
|
|
92
95
|
parentblock['name'] == my_org.display_name or (parentblock['id'] and
|
|
93
|
-
"organizations/"+parentblock['id'] == my_org.name)
|
|
96
|
+
"organizations/"+parentblock['id'] == my_org.name))
|
|
94
97
|
return my_org.name
|
|
95
98
|
end
|
|
96
99
|
|
|
@@ -103,12 +106,12 @@ module MU
|
|
|
103
106
|
name: parentblock['name']
|
|
104
107
|
).first
|
|
105
108
|
if sib_folder
|
|
106
|
-
return
|
|
109
|
+
return sib_folder.cloud_desc.name
|
|
107
110
|
end
|
|
108
111
|
end
|
|
109
112
|
|
|
110
113
|
begin
|
|
111
|
-
|
|
114
|
+
found = MU::Cloud::Google::Folder.find(cloud_id: parentblock['id'], credentials: credentials, flags: { 'display_name' => parentblock['name'] })
|
|
112
115
|
rescue ::Google::Apis::ClientError => e
|
|
113
116
|
if !e.message.match(/Invalid request status_code: 404/)
|
|
114
117
|
raise e
|
|
@@ -123,11 +126,14 @@ module MU
|
|
|
123
126
|
end
|
|
124
127
|
|
|
125
128
|
# Return the cloud descriptor for the Folder
|
|
129
|
+
# @return [Google::Apis::Core::Hashable]
|
|
126
130
|
def cloud_desc
|
|
127
|
-
MU::Cloud::Google::Folder.find(cloud_id: @cloud_id).values.first
|
|
131
|
+
@cached_cloud_desc ||= MU::Cloud::Google::Folder.find(cloud_id: @cloud_id, credentials: @config['credentials']).values.first
|
|
132
|
+
@habitat_id ||= @cached_cloud_desc.parent.sub(/^(folders|organizations)\//, "")
|
|
133
|
+
@cached_cloud_desc
|
|
128
134
|
end
|
|
129
135
|
|
|
130
|
-
# Return the metadata for this
|
|
136
|
+
# Return the metadata for this folders's configuration
|
|
131
137
|
# @return [Hash]
|
|
132
138
|
def notify
|
|
133
139
|
desc = MU.structToHash(MU::Cloud::Google.folder(credentials: @config['credentials']).get_folder("folders/"+@cloud_id))
|
|
@@ -147,7 +153,7 @@ module MU
|
|
|
147
153
|
# Denote whether this resource implementation is experiment, ready for
|
|
148
154
|
# testing, or ready for production use.
|
|
149
155
|
def self.quality
|
|
150
|
-
MU::Cloud::
|
|
156
|
+
MU::Cloud::RELEASE
|
|
151
157
|
end
|
|
152
158
|
|
|
153
159
|
# Remove all Google projects associated with the currently loaded deployment. Try to, anyway.
|
|
@@ -158,54 +164,70 @@ module MU
|
|
|
158
164
|
# We can't label GCP folders, and their names are too short to encode
|
|
159
165
|
# Mu deploy IDs, so all we can do is rely on flags['known'] passed in
|
|
160
166
|
# from cleanup, which relies on our metadata to know what's ours.
|
|
161
|
-
|
|
167
|
+
#noop = true
|
|
162
168
|
if flags and flags['known']
|
|
169
|
+
threads = []
|
|
163
170
|
flags['known'].each { |cloud_id|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
171
|
+
threads << Thread.new {
|
|
172
|
+
|
|
173
|
+
found = self.find(cloud_id: cloud_id, credentials: credentials)
|
|
174
|
+
|
|
175
|
+
if found.size > 0 and found.values.first.lifecycle_state == "ACTIVE"
|
|
176
|
+
MU.log "Deleting folder #{found.values.first.display_name} (#{found.keys.first})"
|
|
177
|
+
if !noop
|
|
178
|
+
max_retries = 10
|
|
179
|
+
retries = 0
|
|
180
|
+
success = false
|
|
181
|
+
begin
|
|
182
|
+
MU::Cloud::Google.folder(credentials: credentials).delete_folder(
|
|
183
|
+
"folders/"+found.keys.first
|
|
184
|
+
)
|
|
185
|
+
found = self.find(cloud_id: cloud_id, credentials: credentials)
|
|
186
|
+
if found and found.size > 0 and found.values.first.lifecycle_state != "DELETE_REQUESTED"
|
|
187
|
+
if retries < max_retries
|
|
188
|
+
sleep 30
|
|
189
|
+
retries += 1
|
|
190
|
+
puts retries
|
|
191
|
+
else
|
|
192
|
+
MU.log "Folder #{cloud_id} still exists after #{max_retries.to_s} attempts to delete", MU::ERR
|
|
193
|
+
break
|
|
194
|
+
end
|
|
195
|
+
else
|
|
196
|
+
success = true
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
rescue ::Google::Apis::ClientError => e
|
|
200
|
+
# XXX maybe see if the folder has disappeared already?
|
|
201
|
+
# XXX look for child folders that haven't been deleted, that's what this tends
|
|
202
|
+
# to mean
|
|
203
|
+
if e.message.match(/failedPrecondition/) and retries < max_retries
|
|
178
204
|
sleep 30
|
|
179
205
|
retries += 1
|
|
180
|
-
|
|
206
|
+
retry
|
|
181
207
|
else
|
|
182
|
-
MU.log "
|
|
208
|
+
MU.log "Got 'failedPrecondition' a bunch while trying to delete #{found.values.first.display_name} (#{found.keys.first})", MU::ERR
|
|
183
209
|
break
|
|
184
210
|
end
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
211
|
+
end while !success
|
|
212
|
+
end
|
|
198
213
|
end
|
|
199
|
-
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
threads.each { |t|
|
|
217
|
+
t.join
|
|
200
218
|
}
|
|
201
219
|
end
|
|
202
220
|
end
|
|
203
221
|
|
|
204
|
-
# Locate
|
|
205
|
-
#
|
|
206
|
-
#
|
|
207
|
-
#
|
|
208
|
-
|
|
222
|
+
# Locate and return cloud provider descriptors of this resource type
|
|
223
|
+
# which match the provided parameters, or all visible resources if no
|
|
224
|
+
# filters are specified. At minimum, implementations of +find+ must
|
|
225
|
+
# honor +credentials+ and +cloud_id+ arguments. We may optionally
|
|
226
|
+
# support other search methods, such as +tag_key+ and +tag_value+, or
|
|
227
|
+
# cloud-specific arguments like +project+. See also {MU::MommaCat.findStray}.
|
|
228
|
+
# @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
|
|
229
|
+
# @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching resources
|
|
230
|
+
def self.find(**args)
|
|
209
231
|
found = {}
|
|
210
232
|
|
|
211
233
|
# Recursively search a GCP folder hierarchy for a folder matching our
|
|
@@ -227,33 +249,88 @@ module MU
|
|
|
227
249
|
nil
|
|
228
250
|
end
|
|
229
251
|
|
|
230
|
-
if
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
252
|
+
parent = if args[:flags] and args[:flags]['parent_id']
|
|
253
|
+
args[:flags]['parent_id']
|
|
254
|
+
else
|
|
255
|
+
my_org = MU::Cloud::Google.getOrg(args[:credentials])
|
|
256
|
+
my_org.name
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
if args[:cloud_id]
|
|
260
|
+
raw_id = args[:cloud_id].sub(/^folders\//, "")
|
|
261
|
+
resp = MU::Cloud::Google.folder(credentials: args[:credentials]).get_folder("folders/"+raw_id)
|
|
262
|
+
found[resp.name] = resp if resp
|
|
263
|
+
|
|
264
|
+
elsif args[:flags] and args[:flags]['display_name']
|
|
239
265
|
|
|
240
266
|
if parent
|
|
241
|
-
resp = self.find_matching_folder(parent, name: flags['display_name'], credentials: credentials)
|
|
267
|
+
resp = self.find_matching_folder(parent, name: args[:flags]['display_name'], credentials: args[:credentials])
|
|
242
268
|
if resp
|
|
243
|
-
found[resp.name
|
|
269
|
+
found[resp.name] = resp
|
|
244
270
|
end
|
|
245
271
|
end
|
|
272
|
+
else
|
|
273
|
+
resp = MU::Cloud::Google.folder(credentials: args[:credentials]).list_folders(parent: parent)
|
|
274
|
+
if resp and resp.folders
|
|
275
|
+
resp.folders.each { |folder|
|
|
276
|
+
next if folder.lifecycle_state == "DELETE_REQUESTED"
|
|
277
|
+
found[folder.name] = folder
|
|
278
|
+
# recurse so that we'll pick up child folders
|
|
279
|
+
children = self.find(
|
|
280
|
+
credentials: args[:credentials],
|
|
281
|
+
flags: { 'parent_id' => folder.name }
|
|
282
|
+
)
|
|
283
|
+
if !children.nil? and !children.empty?
|
|
284
|
+
found.merge!(children)
|
|
285
|
+
end
|
|
286
|
+
}
|
|
287
|
+
end
|
|
246
288
|
end
|
|
247
|
-
|
|
289
|
+
|
|
248
290
|
found
|
|
249
291
|
end
|
|
250
292
|
|
|
293
|
+
# Reverse-map our cloud description into a runnable config hash.
|
|
294
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
|
295
|
+
# calculate our own accordingly based on what's live in the cloud.
|
|
296
|
+
def toKitten(rootparent: nil, billing: nil, habitats: nil)
|
|
297
|
+
bok = {
|
|
298
|
+
"cloud" => "Google",
|
|
299
|
+
"credentials" => @config['credentials']
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
bok['display_name'] = cloud_desc.display_name
|
|
303
|
+
bok['cloud_id'] = cloud_desc.name
|
|
304
|
+
bok['name'] = cloud_desc.display_name#+bok['cloud_id'] # only way to guarantee uniqueness
|
|
305
|
+
if cloud_desc.parent.match(/^folders\/(.*)/)
|
|
306
|
+
MU.log bok['display_name']+" generating reference", MU::NOTICE, details: cloud_desc.parent
|
|
307
|
+
bok['parent'] = MU::Config::Ref.get(
|
|
308
|
+
id: cloud_desc.parent,
|
|
309
|
+
cloud: "Google",
|
|
310
|
+
credentials: @config['credentials'],
|
|
311
|
+
type: "folders"
|
|
312
|
+
)
|
|
313
|
+
elsif rootparent
|
|
314
|
+
bok['parent'] = {
|
|
315
|
+
'id' => rootparent.is_a?(String) ? rootparent : rootparent.cloud_desc.name
|
|
316
|
+
}
|
|
317
|
+
else
|
|
318
|
+
bok['parent'] = { 'id' => cloud_desc.parent }
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
bok
|
|
322
|
+
end
|
|
323
|
+
|
|
251
324
|
# Cloud-specific configuration properties.
|
|
252
325
|
# @param config [MU::Config]: The calling MU::Config object
|
|
253
326
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
254
327
|
def self.schema(config)
|
|
255
328
|
toplevel_required = []
|
|
256
329
|
schema = {
|
|
330
|
+
"display_name" => {
|
|
331
|
+
"type" => "string",
|
|
332
|
+
"description" => "The +display_name+ field of this folder, specified only if we want it to be something other than the automatically-generated string derived from the +name+ field.",
|
|
333
|
+
}
|
|
257
334
|
}
|
|
258
335
|
[toplevel_required, schema]
|
|
259
336
|
end
|
|
@@ -266,7 +343,7 @@ module MU
|
|
|
266
343
|
ok = true
|
|
267
344
|
|
|
268
345
|
if !MU::Cloud::Google.getOrg(folder['credentials'])
|
|
269
|
-
MU.log "Cannot manage Google Cloud
|
|
346
|
+
MU.log "Cannot manage Google Cloud folders in environments without an organization", MU::ERR
|
|
270
347
|
ok = false
|
|
271
348
|
end
|
|
272
349
|
|
|
@@ -17,36 +17,102 @@ module MU
|
|
|
17
17
|
class Google
|
|
18
18
|
# A group as configured in {MU::Config::BasketofKittens::groups}
|
|
19
19
|
class Group < MU::Cloud::Group
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
# @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
|
|
27
|
-
# @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::groups}
|
|
28
|
-
def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
|
|
29
|
-
@deploy = mommacat
|
|
30
|
-
@config = MU::Config.manxify(kitten_cfg)
|
|
31
|
-
@cloud_id ||= cloud_id
|
|
20
|
+
|
|
21
|
+
# Initialize this cloud resource object. Calling +super+ will invoke the initializer defined under {MU::Cloud}, which should set the attribtues listed in {MU::Cloud::PUBLIC_ATTRS} as well as applicable dependency shortcuts, like <tt>@vpc</tt>, for us.
|
|
22
|
+
# @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
|
|
23
|
+
def initialize(**args)
|
|
24
|
+
super
|
|
25
|
+
|
|
32
26
|
@mu_name ||= @deploy.getResourceName(@config["name"])
|
|
33
27
|
end
|
|
34
28
|
|
|
35
29
|
# Called automatically by {MU::Deploy#createResources}
|
|
36
30
|
def create
|
|
37
|
-
|
|
31
|
+
if !@config['external']
|
|
32
|
+
if !@config['email']
|
|
33
|
+
domains = MU::Cloud::Google.admin_directory(credentials: @credentials).list_domains(@customer)
|
|
34
|
+
@config['email'] = @mu_name.downcase+"@"+domains.domains.first.domain_name
|
|
35
|
+
end
|
|
36
|
+
group_obj = MU::Cloud::Google.admin_directory(:Group).new(
|
|
37
|
+
name: @mu_name,
|
|
38
|
+
email: @config['email'],
|
|
39
|
+
description: @deploy.deploy_id
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
MU.log "Creating group #{@mu_name}", details: group_obj
|
|
43
|
+
|
|
44
|
+
resp = MU::Cloud::Google.admin_directory(credentials: @credentials).insert_group(group_obj)
|
|
45
|
+
@cloud_id = resp.email
|
|
46
|
+
|
|
47
|
+
MU::Cloud::Google::Role.bindFromConfig("group", @cloud_id, @config['roles'], credentials: @config['credentials'])
|
|
48
|
+
else
|
|
49
|
+
@cloud_id = @config['name'].sub(/@.*/, "")+"@"+@config['domain']
|
|
50
|
+
end
|
|
38
51
|
end
|
|
39
52
|
|
|
40
53
|
# Called automatically by {MU::Deploy#createResources}
|
|
41
54
|
def groom
|
|
42
|
-
|
|
55
|
+
MU::Cloud::Google::Role.bindFromConfig("group", @cloud_id, @config['roles'], credentials: @config['credentials'], debug: true)
|
|
56
|
+
|
|
57
|
+
if @config['members']
|
|
58
|
+
resolved_desired = []
|
|
59
|
+
@config['members'].each { |m|
|
|
60
|
+
sibling_user = @deploy.findLitterMate(name: m, type: "users")
|
|
61
|
+
usermail = if sibling_user
|
|
62
|
+
sibling_user.cloud_id
|
|
63
|
+
elsif !m.match(/@/)
|
|
64
|
+
domains = MU::Cloud::Google.admin_directory(credentials: @credentials).list_domains(@customer)
|
|
65
|
+
m+"@"+domains.domains.first.domain_name
|
|
66
|
+
else
|
|
67
|
+
m
|
|
68
|
+
end
|
|
69
|
+
resolved_desired << usermail
|
|
70
|
+
next if members.include?(usermail)
|
|
71
|
+
MU.log "Adding user #{usermail} to group #{@mu_name}"
|
|
72
|
+
MU::Cloud::Google.admin_directory(credentials: @credentials).insert_member(
|
|
73
|
+
@cloud_id,
|
|
74
|
+
MU::Cloud::Google.admin_directory(:Member).new(
|
|
75
|
+
email: usermail
|
|
76
|
+
)
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
deletia = members - resolved_desired
|
|
81
|
+
deletia.each { |m|
|
|
82
|
+
MU.log "Removing user #{m} from group #{@mu_name}", MU::NOTICE
|
|
83
|
+
MU::Cloud::Google.admin_directory(credentials: @credentials).delete_member(@cloud_id, m)
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# Theoretically there can be a delay
|
|
87
|
+
begin
|
|
88
|
+
if members.sort != resolved_desired.sort
|
|
89
|
+
sleep 3
|
|
90
|
+
end
|
|
91
|
+
end while members.sort != resolved_desired.sort
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Retrieve a list of users (by cloud id) of this group
|
|
97
|
+
def members
|
|
98
|
+
resp = MU::Cloud::Google.admin_directory(credentials: @credentials).list_members(@cloud_id)
|
|
99
|
+
members = []
|
|
100
|
+
if resp and resp.members
|
|
101
|
+
members = resp.members.map { |m| m.email }
|
|
102
|
+
# XXX reject status != "ACTIVE" ?
|
|
103
|
+
end
|
|
104
|
+
members
|
|
43
105
|
end
|
|
44
106
|
|
|
45
107
|
# Return the metadata for this group configuration
|
|
46
108
|
# @return [Hash]
|
|
47
109
|
def notify
|
|
48
|
-
|
|
49
|
-
|
|
110
|
+
if !@config['external']
|
|
111
|
+
base = MU.structToHash(cloud_desc)
|
|
112
|
+
end
|
|
113
|
+
base ||= {}
|
|
114
|
+
|
|
115
|
+
base
|
|
50
116
|
end
|
|
51
117
|
|
|
52
118
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
|
@@ -56,10 +122,18 @@ module MU
|
|
|
56
122
|
true
|
|
57
123
|
end
|
|
58
124
|
|
|
125
|
+
# Return the list of "container" resource types in which this resource
|
|
126
|
+
# can reside. The list will include an explicit nil if this resource
|
|
127
|
+
# can exist outside of any container.
|
|
128
|
+
# @return [Array<Symbol,nil>]
|
|
129
|
+
def self.canLiveIn
|
|
130
|
+
[nil]
|
|
131
|
+
end
|
|
132
|
+
|
|
59
133
|
# Denote whether this resource implementation is experiment, ready for
|
|
60
134
|
# testing, or ready for production use.
|
|
61
135
|
def self.quality
|
|
62
|
-
MU::Cloud::
|
|
136
|
+
MU::Cloud::BETA
|
|
63
137
|
end
|
|
64
138
|
|
|
65
139
|
# Remove all groups associated with the currently loaded deployment.
|
|
@@ -68,19 +142,90 @@ module MU
|
|
|
68
142
|
# @param region [String]: The cloud provider region
|
|
69
143
|
# @return [void]
|
|
70
144
|
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
145
|
+
my_domains = MU::Cloud::Google.getDomains(credentials)
|
|
146
|
+
my_org = MU::Cloud::Google.getOrg(credentials)
|
|
147
|
+
|
|
148
|
+
if my_org
|
|
149
|
+
groups = MU::Cloud::Google.admin_directory(credentials: credentials).list_groups(customer: MU::Cloud::Google.customerID(credentials)).groups
|
|
150
|
+
if groups
|
|
151
|
+
groups.each { |group|
|
|
152
|
+
if group.description == MU.deploy_id
|
|
153
|
+
MU.log "Deleting group #{group.name} from #{my_org.display_name}", details: group
|
|
154
|
+
if !noop
|
|
155
|
+
MU::Cloud::Google.admin_directory(credentials: credentials).delete_group(group.id)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
}
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
if flags['known']
|
|
163
|
+
flags['known'].each { |group|
|
|
164
|
+
MU::Cloud::Google::Role.removeBindings("group", group, credentials: credentials, noop: noop)
|
|
165
|
+
}
|
|
166
|
+
end
|
|
71
167
|
end
|
|
72
168
|
|
|
73
|
-
# Locate
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
169
|
+
# Locate and return cloud provider descriptors of this resource type
|
|
170
|
+
# which match the provided parameters, or all visible resources if no
|
|
171
|
+
# filters are specified. At minimum, implementations of +find+ must
|
|
172
|
+
# honor +credentials+ and +cloud_id+ arguments. We may optionally
|
|
173
|
+
# support other search methods, such as +tag_key+ and +tag_value+, or
|
|
174
|
+
# cloud-specific arguments like +project+. See also {MU::MommaCat.findStray}.
|
|
175
|
+
# @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
|
|
176
|
+
# @return [Hash<String,OpenStruct>]: The cloud provider's complete descriptions of matching resources
|
|
177
|
+
def self.find(**args)
|
|
178
|
+
found = {}
|
|
179
|
+
|
|
180
|
+
# The API treats the email address field as its main identifier, so
|
|
181
|
+
# we'll go ahead and respect that.
|
|
182
|
+
if args[:cloud_id]
|
|
183
|
+
begin
|
|
184
|
+
resp = MU::Cloud::Google.admin_directory(credentials: args[:credentials]).get_group(args[:cloud_id])
|
|
185
|
+
found[resp.email] = resp if resp
|
|
186
|
+
rescue ::Google::Apis::ClientError => e
|
|
187
|
+
raise e if !e.message.match(/forbidden: /)
|
|
188
|
+
end
|
|
189
|
+
else
|
|
190
|
+
resp = MU::Cloud::Google.admin_directory(credentials: args[:credentials]).list_groups(customer: MU::Cloud::Google.customerID(args[:credentials]))
|
|
191
|
+
if resp and resp.groups
|
|
192
|
+
found = Hash[resp.groups.map { |g| [g.email, g] }]
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
# XXX what about Google Groups groups and other external groups? Where do we fish for those? Do we even need to?
|
|
81
196
|
found
|
|
82
197
|
end
|
|
83
198
|
|
|
199
|
+
# Reverse-map our cloud description into a runnable config hash.
|
|
200
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
|
201
|
+
# calculate our own accordingly based on what's live in the cloud.
|
|
202
|
+
def toKitten(rootparent: nil, billing: nil, habitats: nil)
|
|
203
|
+
|
|
204
|
+
bok = {
|
|
205
|
+
"cloud" => "Google",
|
|
206
|
+
"credentials" => @config['credentials']
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
bok['name'] = cloud_desc.name
|
|
210
|
+
bok['cloud_id'] = cloud_desc.email
|
|
211
|
+
bok['members'] = members
|
|
212
|
+
bok['members'].each { |m|
|
|
213
|
+
m = MU::Config::Ref.get(
|
|
214
|
+
id: m,
|
|
215
|
+
cloud: "Google",
|
|
216
|
+
credentials: @config['credentials'],
|
|
217
|
+
type: "users"
|
|
218
|
+
)
|
|
219
|
+
}
|
|
220
|
+
group_roles = MU::Cloud::Google::Role.getAllBindings(@config['credentials'])["by_entity"]
|
|
221
|
+
if group_roles["group"] and group_roles["group"][bok['cloud_id']] and
|
|
222
|
+
group_roles["group"][bok['cloud_id']].size > 0
|
|
223
|
+
bok['roles'] = MU::Cloud::Google::Role.entityBindingsToSchema(group_roles["group"][bok['cloud_id']], credentials: @config['credentials'])
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
bok
|
|
227
|
+
end
|
|
228
|
+
|
|
84
229
|
# Cloud-specific configuration properties.
|
|
85
230
|
# @param config [MU::Config]: The calling MU::Config object
|
|
86
231
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
|
@@ -89,20 +234,32 @@ module MU
|
|
|
89
234
|
schema = {
|
|
90
235
|
"name" => {
|
|
91
236
|
"type" => "string",
|
|
92
|
-
"description" => "This
|
|
237
|
+
"description" => "This can include an optional @domain component (<tt>foo@example.com</tt>).
|
|
238
|
+
|
|
239
|
+
If the domain portion is not specified, and we manage exactly one GSuite or Cloud Identity domain, we will attempt to create the group in that domain.
|
|
240
|
+
|
|
241
|
+
If we do not manage any domains, and none are specified, we will assume <tt>@googlegroups.com</tt> for the domain and attempt to bind an existing external Google Group to roles under our jurisdiction.
|
|
242
|
+
|
|
243
|
+
If the domain portion is specified, and our credentials can manage that domain via GSuite or Cloud Identity, we will attempt to create the group in that domain.
|
|
244
|
+
|
|
245
|
+
If it is a domain we do not manage, we will attempt to bind an existing external group from that domain to roles under our jurisdiction.
|
|
246
|
+
|
|
247
|
+
If we are binding (rather than creating) a group and no roles are specified, we will default to +roles/viewer+ at the organization scope. If our credentials do not manage an organization, we will grant this role in our default project.
|
|
248
|
+
|
|
249
|
+
"
|
|
250
|
+
},
|
|
251
|
+
"domain" => {
|
|
252
|
+
"type" => "string",
|
|
253
|
+
"description" => "The domain from which the group originates or in which it should be created. This can instead be embedded in the {name} field: +foo@example.com+."
|
|
93
254
|
},
|
|
255
|
+
"external" => {
|
|
256
|
+
"type" => "boolean",
|
|
257
|
+
"description" => "Explicitly flag this group as originating from an external domain. This should always autodetect correctly."
|
|
258
|
+
},
|
|
259
|
+
|
|
94
260
|
"roles" => {
|
|
95
261
|
"type" => "array",
|
|
96
|
-
"
|
|
97
|
-
"default" => ["roles/viewer"],
|
|
98
|
-
"items" => {
|
|
99
|
-
"type" => "string",
|
|
100
|
-
"description" => "One or more Google IAM roles to associate with this group. Google Cloud groups are not created directly; pre-existing Google Groups are associated with a project by being bound to one or more roles in that project. If no roles are specified, we default to +roles/viewer+, which permits read-only access project-wide."
|
|
101
|
-
}
|
|
102
|
-
},
|
|
103
|
-
"project" => {
|
|
104
|
-
"type" => "string",
|
|
105
|
-
"description" => "The project into which to deploy resources"
|
|
262
|
+
"items" => MU::Cloud::Google::Role.ref_schema
|
|
106
263
|
}
|
|
107
264
|
}
|
|
108
265
|
[toplevel_required, schema]
|
|
@@ -114,64 +271,97 @@ module MU
|
|
|
114
271
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
|
115
272
|
def self.validateConfig(group, configurator)
|
|
116
273
|
ok = true
|
|
117
|
-
if group['members'] and group['members'].size > 0 and
|
|
118
|
-
!$MU_CFG['google']['masquerade_as']
|
|
119
|
-
MU.log "Cannot change Google group memberships in non-GSuite environments.\nVisit https://groups.google.com to manage groups.", MU::ERR
|
|
120
|
-
ok = false
|
|
121
|
-
end
|
|
122
274
|
|
|
123
|
-
|
|
124
|
-
|
|
275
|
+
my_domains = MU::Cloud::Google.getDomains(group['credentials'])
|
|
276
|
+
my_org = MU::Cloud::Google.getOrg(group['credentials'])
|
|
125
277
|
|
|
126
|
-
|
|
278
|
+
if group['name'].match(/@(.*+)$/)
|
|
279
|
+
domain = Regexp.last_match[1].downcase
|
|
280
|
+
if domain and group['domain'] and domain != group['domain'].downcase
|
|
281
|
+
MU.log "Group #{group['name']} had a domain component, but the domain field was also specified (#{group['domain']}) and they don't match."
|
|
282
|
+
ok = false
|
|
283
|
+
end
|
|
284
|
+
group['domain'] = domain
|
|
285
|
+
|
|
286
|
+
if !my_domains or !my_domains.include?(domain)
|
|
287
|
+
group['external'] = true
|
|
127
288
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
if
|
|
141
|
-
|
|
142
|
-
|
|
289
|
+
if !["googlegroups.com", "google.com"].include?(domain)
|
|
290
|
+
MU.log "#{group['name']} appears to be a member of a domain that our credentials (#{group['credentials']}) do not manage; attempts to grant access for this group may fail!", MU::WARN
|
|
291
|
+
end
|
|
292
|
+
|
|
293
|
+
if !group['roles'] or group['roles'].empty?
|
|
294
|
+
group['roles'] = [
|
|
295
|
+
{
|
|
296
|
+
"role" => {
|
|
297
|
+
"id" => "roles/viewer"
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
]
|
|
301
|
+
if my_org
|
|
302
|
+
group['roles'][0]["organizations"] = [my_org.name]
|
|
303
|
+
else
|
|
304
|
+
group['roles'][0]["projects"] = {
|
|
305
|
+
"id" => group["project"]
|
|
306
|
+
}
|
|
143
307
|
end
|
|
308
|
+
MU.log "External Google group specified with no role binding, will grant 'viewer' in #{my_org ? "organization #{my_org.display_name}" : "project #{group['project']}"}", MU::WARN
|
|
144
309
|
end
|
|
145
|
-
}
|
|
146
|
-
if !seen
|
|
147
|
-
ext_policy.bindings << MU::Cloud::Google.resource_manager(:Binding).new(
|
|
148
|
-
role: role,
|
|
149
|
-
members: ["group:"+@config['name']]
|
|
150
|
-
)
|
|
151
|
-
change_needed = true
|
|
152
310
|
end
|
|
153
|
-
|
|
311
|
+
else
|
|
312
|
+
if !group['domain']
|
|
313
|
+
if my_domains.size == 1
|
|
314
|
+
group['domain'] = my_domains.first
|
|
315
|
+
elsif my_domains.size > 1
|
|
316
|
+
MU.log "Google interactive User #{group['name']} did not specify a domain, and we have multiple defaults available. Must specify exactly one.", MU::ERR, details: my_domains
|
|
317
|
+
ok = false
|
|
318
|
+
else
|
|
319
|
+
group['domain'] = "googlegroups.com"
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
154
323
|
|
|
155
|
-
if change_needed
|
|
156
|
-
req_obj = MU::Cloud::Google.resource_manager(:SetIamPolicyRequest).new(
|
|
157
|
-
policy: ext_policy
|
|
158
|
-
)
|
|
159
|
-
MU.log "Adding group #{@config['name']} to Google Cloud project #{@config['project']}", details: @config['roles']
|
|
160
324
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if e.message.match(/does not exist/i) and !MU::Cloud::Google.credConfig(@config['credentials'])['masquerade_as']
|
|
168
|
-
raise MuError, "Group #{@config['name']} does not exist, and we cannot create Google groups in non-GSuite environments.\nVisit https://groups.google.com to manage groups."
|
|
169
|
-
end
|
|
170
|
-
raise e
|
|
325
|
+
credcfg = MU::Cloud::Google.credConfig(group['credentials'])
|
|
326
|
+
|
|
327
|
+
if group['external'] and group['members']
|
|
328
|
+
MU.log "Cannot manage memberships for external group #{group['name']}", MU::ERR
|
|
329
|
+
if group['domain'] == "googlegroups.com"
|
|
330
|
+
MU.log "Visit https://groups.google.com to manage Google Groups.", MU::ERR
|
|
171
331
|
end
|
|
332
|
+
ok = false
|
|
172
333
|
end
|
|
334
|
+
|
|
335
|
+
if group['members']
|
|
336
|
+
group['members'].each { |m|
|
|
337
|
+
if configurator.haveLitterMate?(m, "users")
|
|
338
|
+
group['dependencies'] ||= []
|
|
339
|
+
group['dependencies'] << {
|
|
340
|
+
"name" => m,
|
|
341
|
+
"type" => "user"
|
|
342
|
+
}
|
|
343
|
+
end
|
|
344
|
+
}
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
if group['roles']
|
|
348
|
+
group['roles'].each { |r|
|
|
349
|
+
if r['role'] and r['role']['name'] and
|
|
350
|
+
(!r['role']['deploy_id'] and !r['role']['id'])
|
|
351
|
+
group['dependencies'] ||= []
|
|
352
|
+
group['dependencies'] << {
|
|
353
|
+
"type" => "role",
|
|
354
|
+
"name" => r['role']['name']
|
|
355
|
+
}
|
|
356
|
+
end
|
|
357
|
+
}
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
ok
|
|
173
361
|
end
|
|
174
362
|
|
|
363
|
+
private
|
|
364
|
+
|
|
175
365
|
end
|
|
176
366
|
end
|
|
177
367
|
end
|