cloud-mu 2.1.0beta → 3.0.0beta
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -17,34 +17,17 @@ module MU
|
|
17
17
|
class Google
|
18
18
|
# Support for Google Cloud Storage
|
19
19
|
class Bucket < MU::Cloud::Bucket
|
20
|
-
|
21
|
-
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
attr_reader :config
|
26
|
-
attr_reader :cloud_id
|
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::logs}
|
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
|
-
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
|
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
|
42
25
|
@mu_name ||= @deploy.getResourceName(@config["name"])
|
43
26
|
end
|
44
27
|
|
45
28
|
# Called automatically by {MU::Deploy#createResources}
|
46
29
|
def create
|
47
|
-
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).
|
30
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloud_id
|
48
31
|
MU::Cloud::Google.storage(credentials: credentials).insert_bucket(@project_id, bucket_descriptor)
|
49
32
|
@cloud_id = @mu_name.downcase
|
50
33
|
end
|
@@ -72,6 +55,18 @@ module MU
|
|
72
55
|
changed = true
|
73
56
|
end
|
74
57
|
|
58
|
+
if @config['bucket_wide_acls'] and (!current.iam_configuration or
|
59
|
+
!current.iam_configuration.bucket_policy_only or
|
60
|
+
!current.iam_configuration.bucket_policy_only.enabled)
|
61
|
+
MU.log "Converting Cloud Storage bucket #{@cloud_id} to use bucket-wide ACLs only", MU::NOTICE
|
62
|
+
changed = true
|
63
|
+
elsif !@config['bucket_wide_acls'] and current.iam_configuration and
|
64
|
+
current.iam_configuration.bucket_policy_only and
|
65
|
+
current.iam_configuration.bucket_policy_only.enabled
|
66
|
+
MU.log "Converting Cloud Storage bucket #{@cloud_id} to use bucket and object ACLs", MU::NOTICE
|
67
|
+
changed = true
|
68
|
+
end
|
69
|
+
|
75
70
|
if changed
|
76
71
|
MU::Cloud::Google.storage(credentials: credentials).patch_bucket(@cloud_id, bucket_descriptor)
|
77
72
|
end
|
@@ -79,18 +74,19 @@ module MU
|
|
79
74
|
if @config['policies']
|
80
75
|
@config['policies'].each { |pol|
|
81
76
|
pol['grant_to'].each { |grantee|
|
77
|
+
grantee['id'] ||= grantee["identifier"]
|
82
78
|
entity = if grantee["type"]
|
83
79
|
sibling = deploy_obj.findLitterMate(
|
84
|
-
name: grantee["
|
80
|
+
name: grantee["id"],
|
85
81
|
type: grantee["type"]
|
86
82
|
)
|
87
83
|
if sibling
|
88
84
|
sibling.cloudobj.cloud_id
|
89
85
|
else
|
90
|
-
raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["
|
86
|
+
raise MuError, "Couldn't find a #{grantee["type"]} named #{grantee["id"]} when generating Cloud Storage access policy"
|
91
87
|
end
|
92
88
|
else
|
93
|
-
pol['grant_to'].first['
|
89
|
+
pol['grant_to'].first['id']
|
94
90
|
end
|
95
91
|
|
96
92
|
if entity.match(/@/) and !entity.match(/^(group|user)\-/)
|
@@ -123,6 +119,14 @@ module MU
|
|
123
119
|
end
|
124
120
|
end
|
125
121
|
|
122
|
+
# Upload a file to a bucket.
|
123
|
+
# @param url [String]: Target URL, of the form gs://bucket/folder/file
|
124
|
+
# @param acl [String]: Canned ACL permission to assign to the object we upload
|
125
|
+
# @param file [String]: Path to a local file to write to our target location. One of +file+ or +data+ must be specified.
|
126
|
+
# @param data [String]: Data to write to our target location. One of +file+ or +data+ must be specified.
|
127
|
+
def self.upload(url, acl: "private", file: nil, data: nil, credentials: nil)
|
128
|
+
end
|
129
|
+
|
126
130
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
127
131
|
# is it localized to a region/zone?
|
128
132
|
# @return [Boolean]
|
@@ -166,18 +170,135 @@ module MU
|
|
166
170
|
end
|
167
171
|
|
168
172
|
# Locate an existing bucket.
|
169
|
-
# @param cloud_id [String]: The cloud provider's identifier for this resource.
|
170
|
-
# @param region [String]: The cloud provider region.
|
171
|
-
# @param flags [Hash]: Optional flags
|
172
173
|
# @return [OpenStruct]: The cloud provider's complete descriptions of matching bucket.
|
173
|
-
def self.find(
|
174
|
+
def self.find(**args)
|
175
|
+
args[:project] ||= args[:habitat]
|
176
|
+
args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
|
177
|
+
|
174
178
|
found = {}
|
175
|
-
if cloud_id
|
176
|
-
found[cloud_id] = MU::Cloud::Google.storage(credentials: credentials).get_bucket(cloud_id)
|
179
|
+
if args[:cloud_id]
|
180
|
+
found[args[:cloud_id]] = MU::Cloud::Google.storage(credentials: args[:credentials]).get_bucket(args[:cloud_id])
|
181
|
+
else
|
182
|
+
resp = begin
|
183
|
+
MU::Cloud::Google.storage(credentials: args[:credentials]).list_buckets(args[:project])
|
184
|
+
rescue ::Google::Apis::ClientError => e
|
185
|
+
raise e if !e.message.match(/forbidden:/)
|
186
|
+
end
|
187
|
+
|
188
|
+
if resp and resp.items
|
189
|
+
resp.items.each { |bucket|
|
190
|
+
found[bucket.id] = bucket
|
191
|
+
}
|
192
|
+
end
|
177
193
|
end
|
194
|
+
|
178
195
|
found
|
179
196
|
end
|
180
197
|
|
198
|
+
# Reverse-map our cloud description into a runnable config hash.
|
199
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
200
|
+
# calculate our own accordingly based on what's live in the cloud.
|
201
|
+
def toKitten(rootparent: nil, billing: nil, habitats: nil)
|
202
|
+
bok = {
|
203
|
+
"cloud" => "Google",
|
204
|
+
"credentials" => @config['credentials'],
|
205
|
+
"cloud_id" => @cloud_id
|
206
|
+
}
|
207
|
+
|
208
|
+
bok['name'] = cloud_desc.name
|
209
|
+
bok['project'] = @project_id
|
210
|
+
bok['storage_class'] = cloud_desc.storage_class
|
211
|
+
if cloud_desc.versioning and cloud_desc.versioning.enabled
|
212
|
+
bok['versioning'] = true
|
213
|
+
end
|
214
|
+
if cloud_desc.website
|
215
|
+
bok['web'] = true
|
216
|
+
if cloud_desc.website.not_found_page
|
217
|
+
bok['web_error_object'] = cloud_desc.website.not_found_page
|
218
|
+
end
|
219
|
+
if cloud_desc.website.main_page_suffix
|
220
|
+
bok['web_index_object'] = cloud_desc.website.main_page_suffix
|
221
|
+
end
|
222
|
+
pp cloud_desc
|
223
|
+
end
|
224
|
+
|
225
|
+
# MU.log "get_bucket_iam_policy", MU::NOTICE, details: MU::Cloud::Google.storage(credentials: @credentials).get_bucket_iam_policy(@cloud_id)
|
226
|
+
pols = MU::Cloud::Google.storage(credentials: @credentials).get_bucket_iam_policy(@cloud_id)
|
227
|
+
|
228
|
+
if pols and pols.bindings and pols.bindings.size > 0
|
229
|
+
bok['policies'] = []
|
230
|
+
count = 0
|
231
|
+
grantees = {}
|
232
|
+
pols.bindings.each { |binding|
|
233
|
+
grantees[binding.role] ||= []
|
234
|
+
binding.members.each { |grantee|
|
235
|
+
if grantee.match(/^(user|group):(.*)/)
|
236
|
+
grantees[binding.role] << MU::Config::Ref.get(
|
237
|
+
id: Regexp.last_match[2],
|
238
|
+
type: Regexp.last_match[1]+"s",
|
239
|
+
cloud: "Google",
|
240
|
+
credentials: @credentials
|
241
|
+
)
|
242
|
+
elsif grantee == "allUsers" or
|
243
|
+
grantee == "allAuthenticatedUsers" or
|
244
|
+
grantee.match(/^project(?:Owner|Editor|Viewer):/)
|
245
|
+
grantees[binding.role] << { "id" => grantee }
|
246
|
+
elsif grantee.match(/^serviceAccount:(.*)/)
|
247
|
+
sa_name = Regexp.last_match[1]
|
248
|
+
if MU::Cloud::Google::User.cannedServiceAcctName?(sa_name)
|
249
|
+
grantees[binding.role] << { "id" => grantee }
|
250
|
+
else
|
251
|
+
grantees[binding.role] << MU::Config::Ref.get(
|
252
|
+
id: sa_name,
|
253
|
+
type: "users",
|
254
|
+
cloud: "Google",
|
255
|
+
credentials: @credentials
|
256
|
+
)
|
257
|
+
end
|
258
|
+
else
|
259
|
+
# *shrug*
|
260
|
+
grantees[binding.role] << { "id" => grantee }
|
261
|
+
end
|
262
|
+
}
|
263
|
+
}
|
264
|
+
|
265
|
+
# munge together roles that apply to the exact same set of
|
266
|
+
# principals
|
267
|
+
reverse_map = {}
|
268
|
+
grantees.each_pair { |perm, grant_to|
|
269
|
+
reverse_map[grant_to] ||= []
|
270
|
+
reverse_map[grant_to] << perm
|
271
|
+
}
|
272
|
+
already_done = []
|
273
|
+
|
274
|
+
grantees.each_pair { |perm, grant_to|
|
275
|
+
if already_done.include?(perm+grant_to.to_s)
|
276
|
+
next
|
277
|
+
end
|
278
|
+
bok['policies'] << {
|
279
|
+
"name" => "policy"+count.to_s,
|
280
|
+
"grant_to" => grant_to,
|
281
|
+
"permissions" => reverse_map[grant_to]
|
282
|
+
}
|
283
|
+
reverse_map[grant_to].each { |doneperm|
|
284
|
+
already_done << doneperm+grant_to.to_s
|
285
|
+
}
|
286
|
+
count = count+1
|
287
|
+
}
|
288
|
+
end
|
289
|
+
|
290
|
+
if cloud_desc.iam_configuration and
|
291
|
+
cloud_desc.iam_configuration.bucket_policy_only and
|
292
|
+
cloud_desc.iam_configuration.bucket_policy_only.enabled
|
293
|
+
bok['bucket_wide_acls'] = true
|
294
|
+
else
|
295
|
+
# MU.log "list_bucket_access_controls", MU::NOTICE, details: MU::Cloud::Google.storage(credentials: @credentials).list_bucket_access_controls(@cloud_id)
|
296
|
+
# MU.log "list_default_object_access_controls", MU::NOTICE, details: MU::Cloud::Google.storage(credentials: @credentials).list_default_object_access_controls(@cloud_id)
|
297
|
+
end
|
298
|
+
|
299
|
+
bok
|
300
|
+
end
|
301
|
+
|
181
302
|
# Cloud-specific configuration properties.
|
182
303
|
# @param config [MU::Config]: The calling MU::Config object
|
183
304
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
@@ -188,6 +309,11 @@ module MU
|
|
188
309
|
"type" => "string",
|
189
310
|
"enum" => ["MULTI_REGIONAL", "REGIONAL", "STANDARD", "NEARLINE", "COLDLINE", "DURABLE_REDUCED_AVAILABILITY"],
|
190
311
|
"default" => "STANDARD"
|
312
|
+
},
|
313
|
+
"bucket_wide_acls" => {
|
314
|
+
"type" => "boolean",
|
315
|
+
"default" => false,
|
316
|
+
"description" => "Disables object-level access controls in favor of bucket-wide policies"
|
191
317
|
}
|
192
318
|
}
|
193
319
|
[toplevel_required, schema]
|
@@ -200,6 +326,7 @@ module MU
|
|
200
326
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
201
327
|
def self.validateConfig(bucket, configurator)
|
202
328
|
ok = true
|
329
|
+
bucket['project'] ||= MU::Cloud::Google.defaultProject(bucket['credentials'])
|
203
330
|
|
204
331
|
if bucket['policies']
|
205
332
|
bucket['policies'].each { |pol|
|
@@ -245,6 +372,20 @@ module MU
|
|
245
372
|
params[:versioning] = MU::Cloud::Google.storage(:Bucket)::Versioning.new(enabled: false)
|
246
373
|
end
|
247
374
|
|
375
|
+
if @config['bucket_wide_acls']
|
376
|
+
params[:iam_configuration] = MU::Cloud::Google.storage(:Bucket)::IamConfiguration.new(
|
377
|
+
bucket_policy_only: MU::Cloud::Google.storage(:Bucket)::IamConfiguration::BucketPolicyOnly.new(
|
378
|
+
enabled: @config['bucket_wide_acls']
|
379
|
+
)
|
380
|
+
)
|
381
|
+
else
|
382
|
+
params[:iam_configuration] = MU::Cloud::Google.storage(:Bucket)::IamConfiguration.new(
|
383
|
+
bucket_policy_only: MU::Cloud::Google.storage(:Bucket)::IamConfiguration::BucketPolicyOnly.new(
|
384
|
+
enabled: false
|
385
|
+
)
|
386
|
+
)
|
387
|
+
end
|
388
|
+
|
248
389
|
MU::Cloud::Google.storage(:Bucket).new(params)
|
249
390
|
end
|
250
391
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright:: Copyright (c)
|
1
|
+
# Copyright:: Copyright (c) 2019 eGlobalTech, Inc., all rights reserved
|
2
2
|
#
|
3
3
|
# Licensed under the BSD-3 license (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -17,33 +17,13 @@ module MU
|
|
17
17
|
class Google
|
18
18
|
# A Kubernetes cluster as configured in {MU::Config::BasketofKittens::container_clusters}
|
19
19
|
class ContainerCluster < MU::Cloud::ContainerCluster
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
# @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
|
28
|
-
# @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::container_clusters}
|
29
|
-
def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
|
30
|
-
@deploy = mommacat
|
31
|
-
@config = MU::Config.manxify(kitten_cfg)
|
32
|
-
@cloud_id ||= cloud_id
|
33
|
-
# @mu_name = mu_name ? mu_name : @deploy.getResourceName(@config["name"])
|
34
|
-
@config["groomer"] = MU::Config.defaultGroomer unless @config["groomer"]
|
35
|
-
@groomclass = MU::Groomer.loadGroomer(@config["groomer"])
|
36
|
-
|
37
|
-
if !mu_name.nil?
|
38
|
-
@mu_name = mu_name
|
39
|
-
deploydata = describe[2]
|
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
|
46
|
-
else
|
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
|
+
|
26
|
+
if !@mu_name
|
47
27
|
@mu_name ||= @deploy.getResourceName(@config["name"], max_length: 40)
|
48
28
|
end
|
49
29
|
end
|
@@ -52,16 +32,11 @@ module MU
|
|
52
32
|
# Called automatically by {MU::Deploy#createResources}
|
53
33
|
# @return [String]: The cloud provider's identifier for this GKE instance.
|
54
34
|
def create
|
55
|
-
labels = {
|
56
|
-
|
57
|
-
|
58
|
-
labels[name.downcase] = value.downcase.gsub(/[^a-z0-9\-\_]/i, "_")
|
59
|
-
end
|
60
|
-
}
|
35
|
+
labels = Hash[@tags.keys.map { |k|
|
36
|
+
[k.downcase, @tags[k].downcase.gsub(/[^-_a-z0-9]/, '-')] }
|
37
|
+
]
|
61
38
|
labels["name"] = MU::Cloud::Google.nameStr(@mu_name)
|
62
39
|
|
63
|
-
@config['availability_zone'] ||= MU::Cloud::Google.listAZs(@config['region']).sample
|
64
|
-
|
65
40
|
if @vpc.nil? and @config['vpc'] and @config['vpc']['vpc_name']
|
66
41
|
@vpc = @deploy.findLitterMate(name: @config['vpc']['vpc_name'], type: "vpcs")
|
67
42
|
end
|
@@ -70,124 +45,673 @@ module MU
|
|
70
45
|
raise MuError, "ContainerCluster #{@config['name']} unable to locate its resident VPC from #{@config['vpc']}"
|
71
46
|
end
|
72
47
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
48
|
+
|
49
|
+
sa = MU::Config::Ref.get(@config['service_account'])
|
50
|
+
if sa.name and @deploy.findLitterMate(name: sa.name, type: "users")
|
51
|
+
@service_acct = @deploy.findLitterMate(name: sa.name, type: "users").cloud_desc
|
52
|
+
else
|
53
|
+
if !sa or !sa.kitten or !sa.kitten.cloud_desc
|
54
|
+
raise MuError, "Failed to get service account cloud id from #{@config['service_account'].to_s}"
|
78
55
|
end
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
@
|
83
|
-
|
84
|
-
project: @config['project'],
|
85
|
-
credentials: @config['credentials']
|
86
|
-
)
|
87
|
-
MU::Cloud::Google.grantDeploySecretAccess(service_acct.email, credentials: @config['credentials'])
|
56
|
+
@service_acct = sa.kitten.cloud_desc
|
57
|
+
end
|
58
|
+
if !@config['scrub_mu_isms']
|
59
|
+
MU::Cloud::Google.grantDeploySecretAccess(@service_acct.email, credentials: @config['credentials'])
|
60
|
+
end
|
88
61
|
|
89
|
-
@config['ssh_user'] ||= "
|
62
|
+
@config['ssh_user'] ||= "muadmin"
|
90
63
|
|
91
|
-
node_desc = {
|
92
|
-
:machine_type => @config['instance_type'],
|
93
|
-
:preemptible => @config['preemptible'],
|
94
|
-
:disk_size_gb => @config['disk_size_gb'],
|
95
|
-
:labels => labels,
|
96
|
-
:tags => [@mu_name.downcase],
|
97
|
-
:service_account => service_acct.email,
|
98
|
-
:oauth_scopes => ["https://www.googleapis.com/auth/compute", "https://www.googleapis.com/auth/devstorage.read_only"],
|
99
|
-
:metadata => {
|
100
|
-
"ssh-keys" => @config['ssh_user']+":"+@deploy.ssh_public_key
|
101
|
-
}
|
102
|
-
}
|
103
|
-
[:local_ssd_count, :min_cpu_platform, :image_type].each { |field|
|
104
|
-
if @config[field.to_s]
|
105
|
-
node_desc[field] = @config[field.to_s]
|
106
|
-
end
|
107
|
-
}
|
108
64
|
|
109
|
-
nodeobj =
|
65
|
+
nodeobj = if @config['min_size'] and @config['max_size']
|
66
|
+
MU::Cloud::Google.container(:NodePool).new(
|
67
|
+
name: @mu_name.downcase,
|
68
|
+
initial_node_count: @config['instance_count'] || @config['min_size'],
|
69
|
+
autoscaling: MU::Cloud::Google.container(:NodePoolAutoscaling).new(
|
70
|
+
enabled: true,
|
71
|
+
min_node_count: @config['min_size'],
|
72
|
+
max_node_count: @config['max_size'],
|
73
|
+
),
|
74
|
+
management: MU::Cloud::Google.container(:NodeManagement).new(
|
75
|
+
auto_upgrade: @config['auto_upgrade'],
|
76
|
+
auto_repair: @config['auto_repair']
|
77
|
+
),
|
78
|
+
config: MU::Cloud::Google.container(:NodeConfig).new(node_desc)
|
79
|
+
)
|
80
|
+
else
|
81
|
+
MU::Cloud::Google.container(:NodeConfig).new(node_desc)
|
82
|
+
end
|
83
|
+
locations = if @config['availability_zone']
|
84
|
+
[@config['availability_zone']]
|
85
|
+
else
|
86
|
+
MU::Cloud::Google.listAZs(@config['region'])
|
87
|
+
end
|
88
|
+
|
89
|
+
master_user = @config['master_user']
|
90
|
+
# We'll create a temporary basic auth config so that we can grant
|
91
|
+
# useful permissions to the Client Certificate user
|
92
|
+
master_user ||= "master_user"
|
93
|
+
master_pw = Password.pronounceable(18)
|
110
94
|
|
111
95
|
desc = {
|
112
96
|
:name => @mu_name.downcase,
|
113
97
|
:description => @deploy.deploy_id,
|
114
98
|
:network => @vpc.cloud_id,
|
115
|
-
:
|
116
|
-
:labels => labels,
|
99
|
+
:enable_tpu => @config['tpu'],
|
117
100
|
:resource_labels => labels,
|
118
|
-
:
|
119
|
-
:
|
120
|
-
|
121
|
-
|
101
|
+
:locations => locations,
|
102
|
+
:master_auth => MU::Cloud::Google.container(:MasterAuth).new(
|
103
|
+
:client_certificate_config => MU::Cloud::Google.container(:ClientCertificateConfig).new(
|
104
|
+
:issue_client_certificate => true
|
105
|
+
),
|
106
|
+
:username => master_user,
|
107
|
+
:password => master_pw
|
108
|
+
),
|
122
109
|
}
|
123
110
|
|
111
|
+
if @config['kubernetes']
|
112
|
+
desc[:addons_config] = MU::Cloud::Google.container(:AddonsConfig).new(
|
113
|
+
horizontal_pod_autoscaling: MU::Cloud::Google.container(:HorizontalPodAutoscaling).new(
|
114
|
+
disabled: !@config['kubernetes']['horizontal_pod_autoscaling']
|
115
|
+
),
|
116
|
+
http_load_balancing: MU::Cloud::Google.container(:HttpLoadBalancing).new(
|
117
|
+
disabled: !@config['kubernetes']['http_load_balancing']
|
118
|
+
),
|
119
|
+
kubernetes_dashboard: MU::Cloud::Google.container(:KubernetesDashboard).new(
|
120
|
+
disabled: !@config['kubernetes']['dashboard']
|
121
|
+
),
|
122
|
+
network_policy_config: MU::Cloud::Google.container(:NetworkPolicyConfig).new(
|
123
|
+
disabled: !@config['kubernetes']['network_policy_addon']
|
124
|
+
)
|
125
|
+
)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Pick an existing subnet from our VPC, if we're not going to create
|
129
|
+
# one.
|
130
|
+
if !@config['custom_subnet']
|
131
|
+
@vpc.subnets.each { |s|
|
132
|
+
if s.az == @config['region']
|
133
|
+
desc[:subnetwork] = s.cloud_id
|
134
|
+
break
|
135
|
+
end
|
136
|
+
}
|
137
|
+
end
|
138
|
+
if @config['log_facility'] == "kubernetes"
|
139
|
+
desc[:logging_service] = "logging.googleapis.com/kubernetes"
|
140
|
+
desc[:monitoring_service] = "monitoring.googleapis.com/kubernetes"
|
141
|
+
elsif @config['log_facility'] == "basic"
|
142
|
+
desc[:logging_service] = "logging.googleapis.com"
|
143
|
+
desc[:monitoring_service] = "monitoring.googleapis.com"
|
144
|
+
else
|
145
|
+
desc[:logging_service] = "none"
|
146
|
+
desc[:monitoring_service] = "none"
|
147
|
+
end
|
148
|
+
|
149
|
+
if nodeobj.is_a?(::Google::Apis::ContainerV1::NodeConfig)
|
150
|
+
desc[:node_config] = nodeobj
|
151
|
+
desc[:initial_node_count] = @config['instance_count']
|
152
|
+
else
|
153
|
+
desc[:node_pools] = [nodeobj]
|
154
|
+
end
|
155
|
+
|
156
|
+
if @config['kubernetes']
|
157
|
+
if @config['kubernetes']['version']
|
158
|
+
desc[:initial_cluster_version] = @config['kubernetes']['version']
|
159
|
+
end
|
160
|
+
if @config['kubernetes']['alpha']
|
161
|
+
desc[:enable_kubernetes_alpha] = @config['kubernetes']['alpha']
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
if @config['preferred_maintenance_window']
|
166
|
+
desc[:maintenance_policy] = MU::Cloud::Google.container(:MaintenancePolicy).new(
|
167
|
+
window: MU::Cloud::Google.container(:MaintenanceWindow).new(
|
168
|
+
daily_maintenance_window: MU::Cloud::Google.container(:DailyMaintenanceWindow).new(
|
169
|
+
start_time: @config['preferred_maintenance_window']
|
170
|
+
)
|
171
|
+
)
|
172
|
+
)
|
173
|
+
end
|
174
|
+
|
175
|
+
if @config['private_cluster']
|
176
|
+
desc[:private_cluster_config] = MU::Cloud::Google.container(:PrivateClusterConfig).new(
|
177
|
+
enable_private_endpoint: @config['private_cluster']['private_master'],
|
178
|
+
enable_private_nodes: @config['private_cluster']['private_nodes'],
|
179
|
+
master_ipv4_cidr_block: @config['private_cluster']['master_ip_block']
|
180
|
+
)
|
181
|
+
desc[:ip_allocation_policy] = MU::Cloud::Google.container(:IpAllocationPolicy).new(
|
182
|
+
use_ip_aliases: true
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
if @config['ip_aliases'] or @config['custom_subnet'] or
|
187
|
+
@config['services_ip_block'] or @config['services_ip_block_name'] or
|
188
|
+
@config['pod_ip_block'] or @config['pod_ip_block_name'] or
|
189
|
+
@config['tpu_ip_block']
|
190
|
+
alloc_desc = { :use_ip_aliases => @config['ip_aliases'] }
|
191
|
+
|
192
|
+
if @config['custom_subnet']
|
193
|
+
alloc_desc[:create_subnetwork] = true
|
194
|
+
alloc_desc[:subnetwork_name] = if @config['custom_subnet']['name']
|
195
|
+
@config['custom_subnet']['name']
|
196
|
+
else
|
197
|
+
@mu_name.downcase
|
198
|
+
end
|
199
|
+
|
200
|
+
if @config['custom_subnet']['node_ip_block']
|
201
|
+
alloc_desc[:node_ipv4_cidr_block] = @config['custom_subnet']['node_ip_block']
|
202
|
+
end
|
203
|
+
else
|
204
|
+
if @config['pod_ip_block_name']
|
205
|
+
alloc_desc[:cluster_secondary_range_name] = @config['pod_ip_block_name']
|
206
|
+
end
|
207
|
+
if @config['services_ip_block_name']
|
208
|
+
alloc_desc[:services_secondary_range_name] = @config['services_ip_block_name']
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
if @config['services_ip_block']
|
213
|
+
alloc_desc[:services_ipv4_cidr_block] = @config['services_ip_block']
|
214
|
+
end
|
215
|
+
if @config['tpu_ip_block']
|
216
|
+
alloc_desc[:tpu_ipv4_cidr_block] = @config['tpu_ip_block']
|
217
|
+
end
|
218
|
+
if @config['pod_ip_block']
|
219
|
+
alloc_desc[:cluster_ipv4_cidr_block] = @config['pod_ip_block']
|
220
|
+
end
|
221
|
+
|
222
|
+
desc[:ip_allocation_policy] = MU::Cloud::Google.container(:IpAllocationPolicy).new(alloc_desc)
|
223
|
+
pp alloc_desc
|
224
|
+
end
|
225
|
+
|
226
|
+
if @config['authorized_networks'] and @config['authorized_networks'].size > 0
|
227
|
+
desc[:master_authorized_networks_config] = MU::Cloud::Google.container(:MasterAuthorizedNetworksConfig).new(
|
228
|
+
enabled: true,
|
229
|
+
cidr_blocks: @config['authorized_networks'].map { |n|
|
230
|
+
MU::Cloud::Google.container(:CidrBlock).new(
|
231
|
+
cidr_block: n['ip_block'],
|
232
|
+
display_name: n['label']
|
233
|
+
)
|
234
|
+
}
|
235
|
+
)
|
236
|
+
end
|
237
|
+
|
238
|
+
if @config['kubernetes'] and @config['kubernetes']['max_pods'] and
|
239
|
+
@config['ip_aliases']
|
240
|
+
desc[:default_max_pods_constraint] = MU::Cloud::Google.container(:MaxPodsConstraint).new(
|
241
|
+
max_pods_per_node: @config['kubernetes']['max_pods']
|
242
|
+
)
|
243
|
+
end
|
244
|
+
|
124
245
|
requestobj = MU::Cloud::Google.container(:CreateClusterRequest).new(
|
125
|
-
:cluster => MU::Cloud::Google.container(:Cluster).new(desc)
|
246
|
+
:cluster => MU::Cloud::Google.container(:Cluster).new(desc),
|
126
247
|
)
|
127
248
|
|
128
|
-
MU.log "Creating GKE cluster #{@mu_name.downcase}", details:
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
249
|
+
MU.log "Creating GKE cluster #{@mu_name.downcase}", details: requestobj
|
250
|
+
@config['master_az'] = @config['region']
|
251
|
+
parent_arg = "projects/"+@config['project']+"/locations/"+@config['master_az']
|
252
|
+
|
253
|
+
cluster = MU::Cloud::Google.container(credentials: @config['credentials']).create_project_location_cluster(
|
254
|
+
parent_arg,
|
134
255
|
requestobj
|
135
256
|
)
|
257
|
+
@cloud_id = parent_arg+"/clusters/"+@mu_name.downcase
|
136
258
|
|
137
259
|
resp = nil
|
138
260
|
begin
|
139
|
-
resp = MU::Cloud::Google.container(credentials: @config['credentials']).
|
261
|
+
resp = MU::Cloud::Google.container(credentials: @config['credentials']).get_project_location_cluster(@cloud_id)
|
262
|
+
if resp.status == "ERROR"
|
263
|
+
MU.log "GKE cluster #{@cloud_id} failed", MU::ERR, details: resp.status_message
|
264
|
+
raise MuError, "GKE cluster #{@cloud_id} failed: #{resp.status_message}"
|
265
|
+
end
|
140
266
|
sleep 30 if resp.status != "RUNNING"
|
141
267
|
end while resp.nil? or resp.status != "RUNNING"
|
142
|
-
# labelCluster # XXX need newer API release
|
143
|
-
@cloud_id = @mu_name.downcase
|
144
268
|
|
145
|
-
|
269
|
+
writeKubeConfig
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
|
274
|
+
# Called automatically by {MU::Deploy#createResources}
|
275
|
+
def groom
|
276
|
+
labelCluster
|
277
|
+
|
278
|
+
me = cloud_desc
|
279
|
+
|
280
|
+
parent_arg = "projects/"+@config['project']+"/locations/"+me.location
|
281
|
+
|
282
|
+
# Enable/disable basic auth
|
283
|
+
authcfg = {}
|
284
|
+
action = nil
|
285
|
+
if @config['master_user'] and (me.master_auth.username != @config['master_user'] or !me.master_auth.password)
|
286
|
+
authcfg[:username] = @config['master_user']
|
287
|
+
authcfg[:password] = Password.pronounceable(16..18)
|
288
|
+
MU.log "Enabling basic auth for GKE cluster #{@mu_name.downcase}", MU::NOTICE, details: authcfg
|
289
|
+
elsif !@config['master_user'] and me.master_auth.username
|
290
|
+
authcfg[:username] = ""
|
291
|
+
MU.log "Disabling basic auth for GKE cluster #{@mu_name.downcase}", MU::NOTICE
|
292
|
+
end
|
293
|
+
if authcfg.size > 0
|
294
|
+
MU::Cloud::Google.container(credentials: @config['credentials']).set_project_location_cluster_master_auth(
|
295
|
+
@cloud_id,
|
296
|
+
MU::Cloud::Google.container(:SetMasterAuthRequest).new(
|
297
|
+
name: @cloud_id,
|
298
|
+
action: "SET_USERNAME",
|
299
|
+
update: MU::Cloud::Google.container(:MasterAuth).new(
|
300
|
+
authcfg
|
301
|
+
)
|
302
|
+
)
|
303
|
+
)
|
304
|
+
me = cloud_desc(use_cache: false)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Now go through all the things that use update_project_location_cluster
|
308
|
+
updates = []
|
309
|
+
|
310
|
+
locations = if @config['availability_zone']
|
311
|
+
[@config['availability_zone']]
|
312
|
+
else
|
313
|
+
MU::Cloud::Google.listAZs(@config['region'])
|
314
|
+
end
|
315
|
+
if me.locations != locations
|
316
|
+
updates << { :desired_locations => locations }
|
317
|
+
end
|
318
|
+
|
319
|
+
if @config['min_size'] and @config['max_size'] and
|
320
|
+
(me.node_pools.first.autoscaling.min_node_count != @config['min_size'] or
|
321
|
+
me.node_pools.first.autoscaling.max_node_count != @config['max_size'])
|
322
|
+
updates << {
|
323
|
+
:desired_node_pool_autoscaling => MU::Cloud::Google.container(:NodePoolAutoscaling).new(
|
324
|
+
enabled: true,
|
325
|
+
max_node_count: @config['max_size'],
|
326
|
+
min_node_count: @config['min_size']
|
327
|
+
)
|
328
|
+
}
|
329
|
+
end
|
330
|
+
|
331
|
+
if @config['authorized_networks'] and @config['authorized_networks'].size > 0
|
332
|
+
desired = @config['authorized_networks'].map { |n|
|
333
|
+
MU::Cloud::Google.container(:CidrBlock).new(
|
334
|
+
cidr_block: n['ip_block'],
|
335
|
+
display_name: n['label']
|
336
|
+
)
|
337
|
+
}
|
338
|
+
if !me.master_authorized_networks_config or
|
339
|
+
!me.master_authorized_networks_config.enabled or
|
340
|
+
!me.master_authorized_networks_config.cidr_blocks or
|
341
|
+
me.master_authorized_networks_config.cidr_blocks.map {|n| n.cidr_block+n.display_name }.sort != desired.map {|n| n.cidr_block+n.display_name }.sort
|
342
|
+
updates << { :desired_master_authorized_networks_config => MU::Cloud::Google.container(:MasterAuthorizedNetworksConfig).new(
|
343
|
+
enabled: true,
|
344
|
+
cidr_blocks: desired
|
345
|
+
)}
|
346
|
+
end
|
347
|
+
elsif me.master_authorized_networks_config and
|
348
|
+
me.master_authorized_networks_config.enabled
|
349
|
+
updates << { :desired_master_authorized_networks_config => MU::Cloud::Google.container(:MasterAuthorizedNetworksConfig).new(
|
350
|
+
enabled: false
|
351
|
+
)}
|
352
|
+
end
|
353
|
+
|
354
|
+
if @config['log_facility'] == "kubernetes" and me.logging_service != "logging.googleapis.com/kubernetes"
|
355
|
+
updates << {
|
356
|
+
:desired_logging_service => "logging.googleapis.com/kubernetes",
|
357
|
+
:desired_monitoring_service => "monitoring.googleapis.com/kubernetes"
|
358
|
+
}
|
359
|
+
elsif @config['log_facility'] == "basic" and me.logging_service != "logging.googleapis.com"
|
360
|
+
updates << {
|
361
|
+
:desired_logging_service => "logging.googleapis.com",
|
362
|
+
:desired_monitoring_service => "monitoring.googleapis.com"
|
363
|
+
}
|
364
|
+
elsif @config['log_facility'] == "none" and me.logging_service != "none"
|
365
|
+
updates << {
|
366
|
+
:desired_logging_service => "none",
|
367
|
+
:desired_monitoring_service => "none"
|
368
|
+
}
|
369
|
+
end
|
370
|
+
|
371
|
+
if @config['kubernetes']
|
372
|
+
if (me.addons_config.horizontal_pod_autoscaling.disabled and @config['kubernetes']['horizontal_pod_autoscaling']) or
|
373
|
+
(!me.addons_config.horizontal_pod_autoscaling and !@config['kubernetes']['horizontal_pod_autoscaling']) or
|
374
|
+
(me.addons_config.http_load_balancing.disabled and @config['kubernetes']['http_load_balancing']) or
|
375
|
+
(!me.addons_config.http_load_balancing and !@config['kubernetes']['http_load_balancing']) or
|
376
|
+
(me.addons_config.kubernetes_dashboard.disabled and @config['kubernetes']['dashboard']) or
|
377
|
+
(!me.addons_config.kubernetes_dashboard and !@config['kubernetes']['dashboard']) or
|
378
|
+
(me.addons_config.network_policy_config.disabled and @config['kubernetes']['network_policy_addon']) or
|
379
|
+
(!me.addons_config.network_policy_config and !@config['kubernetes']['network_policy_addon'])
|
380
|
+
updates << { :desired_addons_config => MU::Cloud::Google.container(:AddonsConfig).new(
|
381
|
+
horizontal_pod_autoscaling: MU::Cloud::Google.container(:HorizontalPodAutoscaling).new(
|
382
|
+
disabled: !@config['kubernetes']['horizontal_pod_autoscaling']
|
383
|
+
),
|
384
|
+
http_load_balancing: MU::Cloud::Google.container(:HttpLoadBalancing).new(
|
385
|
+
disabled: !@config['kubernetes']['http_load_balancing']
|
386
|
+
),
|
387
|
+
kubernetes_dashboard: MU::Cloud::Google.container(:KubernetesDashboard).new(
|
388
|
+
disabled: !@config['kubernetes']['dashboard']
|
389
|
+
),
|
390
|
+
network_policy_config: MU::Cloud::Google.container(:NetworkPolicyConfig).new(
|
391
|
+
disabled: !@config['kubernetes']['network_policy_addon']
|
392
|
+
)
|
393
|
+
)}
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
if @config['kubernetes'] and @config['kubernetes']['version']
|
398
|
+
if MU.version_sort(@config['kubernetes']['version'], me.current_master_version) > 0
|
399
|
+
updates << { :desired_master_version => @config['kubernetes']['version'] }
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
if @config['kubernetes'] and @config['kubernetes']['nodeversion']
|
404
|
+
if MU.version_sort(@config['kubernetes']['nodeversion'], me.current_node_version) > 0
|
405
|
+
updates << { :desired_node_version => @config['kubernetes']['nodeversion'] }
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
if updates.size > 0
|
410
|
+
updates.each { |mapping|
|
411
|
+
requestobj = MU::Cloud::Google.container(:UpdateClusterRequest).new(
|
412
|
+
:name => @cloud_id,
|
413
|
+
:update => MU::Cloud::Google.container(:ClusterUpdate).new(
|
414
|
+
mapping
|
415
|
+
)
|
416
|
+
)
|
417
|
+
MU.log "Updating GKE Cluster #{@mu_name.downcase}", MU::NOTICE, details: mapping
|
418
|
+
begin
|
419
|
+
MU::Cloud::Google.container(credentials: @config['credentials']).update_project_location_cluster(
|
420
|
+
@cloud_id,
|
421
|
+
requestobj
|
422
|
+
)
|
423
|
+
rescue ::Google::Apis::ClientError => e
|
424
|
+
MU.log e.message, MU::WARN
|
425
|
+
end
|
426
|
+
}
|
427
|
+
me = cloud_desc(use_cache: false)
|
428
|
+
end
|
429
|
+
|
430
|
+
if @config['preferred_maintenance_window'] and
|
431
|
+
(!me.maintenance_policy.window or
|
432
|
+
!me.maintenance_policy.window.daily_maintenance_window or
|
433
|
+
me.maintenance_policy.window.daily_maintenance_window.start_time != @config['preferred_maintenance_window'])
|
434
|
+
MU.log "Setting GKE Cluster #{@mu_name.downcase} maintenance time to #{@config['preferred_maintenance_window']}", MU::NOTICE
|
435
|
+
MU::Cloud::Google.container(credentials: @config['credentials']).set_project_location_cluster_maintenance_policy(
|
436
|
+
@cloud_id,
|
437
|
+
MU::Cloud::Google.container(:SetMaintenancePolicyRequest).new(
|
438
|
+
maintenance_policy: MU::Cloud::Google.container(:MaintenancePolicy).new(
|
439
|
+
window: MU::Cloud::Google.container(:MaintenanceWindow).new(
|
440
|
+
daily_maintenance_window: MU::Cloud::Google.container(:DailyMaintenanceWindow).new(
|
441
|
+
start_time: @config['preferred_maintenance_window']
|
442
|
+
)
|
443
|
+
)
|
444
|
+
)
|
445
|
+
)
|
446
|
+
)
|
447
|
+
elsif !@config['preferred_maintenance_window'] and me.maintenance_policy.window
|
448
|
+
MU.log "Unsetting GKE Cluster #{@mu_name.downcase} maintenance time to #{@config['preferred_maintenance_window']}", MU::NOTICE
|
449
|
+
MU::Cloud::Google.container(credentials: @config['credentials']).set_project_location_cluster_maintenance_policy(
|
450
|
+
@cloud_id,
|
451
|
+
nil
|
452
|
+
)
|
453
|
+
end
|
454
|
+
|
455
|
+
|
456
|
+
kube_conf = writeKubeConfig
|
457
|
+
|
458
|
+
if @config['kubernetes_resources']
|
459
|
+
MU::Master.applyKubernetesResources(
|
460
|
+
@config['name'],
|
461
|
+
@config['kubernetes_resources'],
|
462
|
+
kubeconfig: kube_conf,
|
463
|
+
outputdir: @deploy.deploy_dir
|
464
|
+
)
|
465
|
+
end
|
466
|
+
|
467
|
+
MU.log %Q{How to interact with your Kubernetes cluster\nkubectl --kubeconfig "#{kube_conf}" get events --all-namespaces\nkubectl --kubeconfig "#{kube_conf}" get all\nkubectl --kubeconfig "#{kube_conf}" create -f some_k8s_deploy.yml\nkubectl --kubeconfig "#{kube_conf}" get nodes}, MU::SUMMARY
|
146
468
|
end
|
147
469
|
|
470
|
+
|
148
471
|
# Locate an existing ContainerCluster or ContainerClusters and return an array containing matching GCP resource descriptors for those that match.
|
149
|
-
# @param cloud_id [String]: The cloud provider's identifier for this resource.
|
150
|
-
# @param region [String]: The cloud provider region
|
151
|
-
# @param tag_key [String]: A tag key to search.
|
152
|
-
# @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
|
153
|
-
# @param flags [Hash]: Optional flags
|
154
472
|
# @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching ContainerClusters
|
155
|
-
def self.find(
|
156
|
-
|
473
|
+
def self.find(**args)
|
474
|
+
args[:project] ||= args[:habitat]
|
475
|
+
args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
|
476
|
+
location = args[:region] || args[:availability_zone] || "-"
|
477
|
+
|
478
|
+
found = {}
|
479
|
+
|
480
|
+
if args[:cloud_id]
|
481
|
+
resp = begin
|
482
|
+
MU::Cloud::Google.container(credentials: args[:credentials]).get_project_location_cluster(args[:cloud_id])
|
483
|
+
rescue ::Google::Apis::ClientError => e
|
484
|
+
raise e if !e.message.match(/forbidden:/)
|
485
|
+
end
|
486
|
+
found[args[:cloud_id]] = resp if resp
|
487
|
+
else
|
488
|
+
resp = begin
|
489
|
+
MU::Cloud::Google.container(credentials: args[:credentials]).list_project_location_clusters("projects/#{args[:project]}/locations/#{location}")
|
490
|
+
rescue ::Google::Apis::ClientError => e
|
491
|
+
raise e if !e.message.match(/forbidden:/)
|
492
|
+
end
|
493
|
+
if resp and resp.clusters and !resp.clusters.empty?
|
494
|
+
resp.clusters.each { |c|
|
495
|
+
found[c.self_link.sub(/.*?\/projects\//, 'projects/')] = c
|
496
|
+
}
|
497
|
+
end
|
498
|
+
end
|
499
|
+
|
500
|
+
found
|
157
501
|
end
|
158
502
|
|
159
|
-
#
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
503
|
+
# Reverse-map our cloud description into a runnable config hash.
|
504
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
505
|
+
# calculate our own accordingly based on what's live in the cloud.
|
506
|
+
def toKitten(rootparent: nil, billing: nil, habitats: nil)
|
507
|
+
|
508
|
+
bok = {
|
509
|
+
"cloud" => "Google",
|
510
|
+
"project" => @config['project'],
|
511
|
+
"credentials" => @config['credentials'],
|
512
|
+
"cloud_id" => @cloud_id,
|
513
|
+
"name" => cloud_desc.name.dup
|
514
|
+
}
|
515
|
+
|
516
|
+
bok['region'] = cloud_desc.location.sub(/\-[a-z]$/, "")
|
517
|
+
if cloud_desc.locations.size == 1
|
518
|
+
bok['availability_zone'] = cloud_desc.locations.first
|
519
|
+
end
|
520
|
+
bok["instance_count"] = cloud_desc.current_node_count
|
521
|
+
cloud_desc.network_config.network.match(/^projects\/(.*?)\/.*?\/networks\/([^\/]+)(?:$|\/)/)
|
522
|
+
vpc_proj = Regexp.last_match[1]
|
523
|
+
vpc_id = Regexp.last_match[2]
|
524
|
+
|
525
|
+
bok['vpc'] = MU::Config::Ref.get(
|
526
|
+
id: vpc_id,
|
527
|
+
cloud: "Google",
|
528
|
+
habitat: MU::Config::Ref.get(
|
529
|
+
id: vpc_proj,
|
530
|
+
cloud: "Google",
|
531
|
+
credentials: @credentials,
|
532
|
+
type: "habitats"
|
533
|
+
),
|
534
|
+
credentials: @config['credentials'],
|
535
|
+
type: "vpcs"
|
536
|
+
)
|
537
|
+
|
538
|
+
|
539
|
+
bok['kubernetes'] = {
|
540
|
+
"version" => cloud_desc.current_master_version,
|
541
|
+
"nodeversion" => cloud_desc.current_node_version
|
542
|
+
}
|
543
|
+
if cloud_desc.default_max_pods_constraint and
|
544
|
+
cloud_desc.default_max_pods_constraint.max_pods_per_node
|
545
|
+
bok['kubernetes']['max_pods'] = cloud_desc.default_max_pods_constraint.max_pods_per_node
|
546
|
+
end
|
547
|
+
|
548
|
+
if cloud_desc.addons_config.horizontal_pod_autoscaling and
|
549
|
+
cloud_desc.addons_config.horizontal_pod_autoscaling.disabled
|
550
|
+
bok['kubernetes']['horizontal_pod_autoscaling'] = false
|
551
|
+
end
|
552
|
+
if cloud_desc.addons_config.http_load_balancing and
|
553
|
+
cloud_desc.addons_config.http_load_balancing.disabled
|
554
|
+
bok['kubernetes']['http_load_balancing'] = false
|
555
|
+
end
|
556
|
+
if !cloud_desc.addons_config.kubernetes_dashboard or
|
557
|
+
!cloud_desc.addons_config.kubernetes_dashboard.disabled
|
558
|
+
bok['kubernetes']['dashboard'] = true
|
559
|
+
end
|
560
|
+
if !cloud_desc.addons_config.network_policy_config or
|
561
|
+
!cloud_desc.addons_config.network_policy_config.disabled
|
562
|
+
bok['kubernetes']['network_policy_addon'] = true
|
563
|
+
end
|
564
|
+
|
565
|
+
if cloud_desc.ip_allocation_policy.use_ip_aliases
|
566
|
+
bok['ip_aliases'] = true
|
567
|
+
end
|
568
|
+
if cloud_desc.ip_allocation_policy.cluster_ipv4_cidr_block
|
569
|
+
bok['pod_ip_block'] = cloud_desc.ip_allocation_policy.cluster_ipv4_cidr_block
|
570
|
+
end
|
571
|
+
if cloud_desc.ip_allocation_policy.services_ipv4_cidr_block
|
572
|
+
bok['services_ip_block'] = cloud_desc.ip_allocation_policy.services_ipv4_cidr_block
|
573
|
+
end
|
574
|
+
|
575
|
+
if cloud_desc.ip_allocation_policy.create_subnetwork
|
576
|
+
bok['custom_subnet'] = {
|
577
|
+
"name" => (cloud_desc.ip_allocation_policy.subnetwork_name || cloud_desc.subnetwork)
|
578
|
+
}
|
579
|
+
if cloud_desc.ip_allocation_policy.node_ipv4_cidr_block
|
580
|
+
bok['custom_subnet']['node_ip_block'] = cloud_desc.ip_allocation_policy.node_ipv4_cidr_block
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
bok['log_facility'] = if cloud_desc.logging_service == "logging.googleapis.com"
|
585
|
+
"basic"
|
586
|
+
elsif cloud_desc.logging_service == "logging.googleapis.com/kubernetes"
|
587
|
+
"kubernetes"
|
588
|
+
else
|
589
|
+
"none"
|
590
|
+
end
|
591
|
+
|
592
|
+
if cloud_desc.master_auth and cloud_desc.master_auth.username
|
593
|
+
bok['master_user'] = cloud_desc.master_auth.username
|
594
|
+
end
|
595
|
+
|
596
|
+
if cloud_desc.maintenance_policy and
|
597
|
+
cloud_desc.maintenance_policy.window and
|
598
|
+
cloud_desc.maintenance_policy.window.daily_maintenance_window and
|
599
|
+
cloud_desc.maintenance_policy.window.daily_maintenance_window.start_time
|
600
|
+
bok['preferred_maintenance_window'] = cloud_desc.maintenance_policy.window.daily_maintenance_window.start_time
|
601
|
+
end
|
602
|
+
|
603
|
+
if cloud_desc.enable_tpu
|
604
|
+
bok['tpu'] = true
|
605
|
+
end
|
606
|
+
if cloud_desc.enable_kubernetes_alpha
|
607
|
+
bok['kubernetes'] ||= {}
|
608
|
+
bok['kubernetes']['alpha'] = true
|
609
|
+
end
|
610
|
+
|
611
|
+
if cloud_desc.node_pools and cloud_desc.node_pools.size > 0
|
612
|
+
pool = cloud_desc.node_pools.first # we don't really support multiples atm
|
613
|
+
bok["instance_type"] = pool.config.machine_type
|
614
|
+
bok["instance_count"] = pool.initial_node_count
|
615
|
+
bok['scopes'] = pool.config.oauth_scopes
|
616
|
+
if pool.config.metadata
|
617
|
+
bok["metadata"] = pool.config.metadata.keys.map { |k|
|
618
|
+
{ "key" => k, "value" => pool.config.metadata[k] }
|
619
|
+
}
|
620
|
+
end
|
621
|
+
if pool.autoscaling and pool.autoscaling.enabled
|
622
|
+
bok['max_size'] = pool.autoscaling.max_node_count
|
623
|
+
bok['min_size'] = pool.autoscaling.min_node_count
|
624
|
+
end
|
625
|
+
bok['auto_repair'] = false
|
626
|
+
bok['auto_upgrade'] = false
|
627
|
+
if pool.management
|
628
|
+
bok['auto_repair'] = true if pool.management.auto_repair
|
629
|
+
bok['auto_upgrade'] = true if pool.management.auto_upgrade
|
630
|
+
end
|
631
|
+
[:local_ssd_count, :min_cpu_platform, :image_type, :disk_size_gb, :preemptible, :service_account].each { |field|
|
632
|
+
if pool.config.respond_to?(field)
|
633
|
+
bok[field.to_s] = pool.config.method(field).call
|
634
|
+
bok.delete(field.to_s) if bok[field.to_s].nil?
|
635
|
+
end
|
636
|
+
}
|
637
|
+
else
|
638
|
+
bok["instance_type"] = cloud_desc.node_config.machine_type
|
639
|
+
bok['scopes'] = cloud_desc.node_config.oauth_scopes
|
640
|
+
if cloud_desc.node_config.metadata
|
641
|
+
bok["metadata"] = cloud_desc.node_config.metadata.keys.map { |k|
|
642
|
+
{ "key" => k, "value" => pool.config.metadata[k] }
|
643
|
+
}
|
644
|
+
end
|
645
|
+
[:local_ssd_count, :min_cpu_platform, :image_type, :disk_size_gb, :preemptible, :service_account].each { |field|
|
646
|
+
if cloud_desc.node_config.respond_to?(field)
|
647
|
+
bok[field.to_s] = cloud_desc.node_config.method(field).call
|
648
|
+
bok.delete(field.to_s) if bok[field.to_s].nil?
|
649
|
+
end
|
650
|
+
}
|
651
|
+
end
|
652
|
+
|
653
|
+
if bok['service_account']
|
654
|
+
found = MU::Cloud::Google::User.find(
|
655
|
+
credentials: bok['credentials'],
|
656
|
+
project: bok['project'],
|
657
|
+
cloud_id: bok['service_account']
|
658
|
+
)
|
659
|
+
if found and found.size == 1
|
660
|
+
sa = found.values.first
|
661
|
+
# Ignore generic Mu service accounts
|
662
|
+
if cloud_desc.resource_labels and
|
663
|
+
cloud_desc.resource_labels["mu-id"] and
|
664
|
+
sa.description and
|
665
|
+
cloud_desc.resource_labels["mu-id"].downcase == sa.description.downcase
|
666
|
+
bok.delete("service_account")
|
667
|
+
else
|
668
|
+
bok['service_account'] = MU::Config::Ref.get(
|
669
|
+
id: found.values.first.name,
|
670
|
+
cloud: "Google",
|
671
|
+
credentials: @config['credentials'],
|
672
|
+
type: "users"
|
673
|
+
)
|
674
|
+
end
|
675
|
+
else
|
676
|
+
bok.delete("service_account")
|
677
|
+
end
|
678
|
+
end
|
679
|
+
|
680
|
+
if cloud_desc.private_cluster_config
|
681
|
+
if cloud_desc.private_cluster_config.enable_private_nodes?
|
682
|
+
bok["private_cluster"] ||= {}
|
683
|
+
bok["private_cluster"]["private_nodes"] = true
|
684
|
+
end
|
685
|
+
if cloud_desc.private_cluster_config.enable_private_endpoint?
|
686
|
+
bok["private_cluster"] ||= {}
|
687
|
+
bok["private_cluster"]["private_master"] = true
|
688
|
+
end
|
689
|
+
if cloud_desc.private_cluster_config.master_ipv4_cidr_block
|
690
|
+
bok["private_cluster"] ||= {}
|
691
|
+
bok["private_cluster"]["master_ip_block"] = cloud_desc.private_cluster_config.master_ipv4_cidr_block
|
692
|
+
end
|
693
|
+
end
|
694
|
+
|
695
|
+
if cloud_desc.master_authorized_networks_config and
|
696
|
+
cloud_desc.master_authorized_networks_config.cidr_blocks and
|
697
|
+
cloud_desc.master_authorized_networks_config.cidr_blocks.size > 0
|
698
|
+
bok['authorized_networks'] = []
|
699
|
+
cloud_desc.master_authorized_networks_config.cidr_blocks.each { |c|
|
700
|
+
bok['authorized_networks'] << {
|
701
|
+
"ip_block" => c.cidr_block,
|
702
|
+
"label" => c.display_name
|
703
|
+
}
|
704
|
+
}
|
705
|
+
end
|
706
|
+
|
707
|
+
bok
|
186
708
|
end
|
187
709
|
|
710
|
+
|
188
711
|
# Register a description of this cluster instance with this deployment's metadata.
|
189
712
|
def notify
|
190
|
-
|
713
|
+
resp = MU::Cloud::Google.container(credentials: @config['credentials']).get_project_location_cluster(@cloud_id)
|
714
|
+
desc = MU.structToHash(resp)
|
191
715
|
desc["project"] = @config['project']
|
192
716
|
desc["cloud_id"] = @cloud_id
|
193
717
|
desc["project_id"] = @project_id
|
@@ -205,7 +729,7 @@ puts @config['credentials']
|
|
205
729
|
# Denote whether this resource implementation is experiment, ready for
|
206
730
|
# testing, or ready for production use.
|
207
731
|
def self.quality
|
208
|
-
MU::Cloud::
|
732
|
+
MU::Cloud::RELEASE
|
209
733
|
end
|
210
734
|
|
211
735
|
# Called by {MU::Cleanup}. Locates resources that were created by the
|
@@ -218,35 +742,51 @@ puts @config['credentials']
|
|
218
742
|
skipsnapshots = flags["skipsnapshots"]
|
219
743
|
|
220
744
|
flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
|
745
|
+
return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
|
746
|
+
clusters = []
|
747
|
+
|
748
|
+
# Make sure we catch regional *and* zone clusters
|
749
|
+
found = MU::Cloud::Google.container(credentials: credentials).list_project_location_clusters("projects/#{flags['project']}/locations/#{region}")
|
750
|
+
clusters.concat(found.clusters) if found and found.clusters
|
221
751
|
MU::Cloud::Google.listAZs(region).each { |az|
|
222
|
-
found = MU::Cloud::Google.container(credentials: credentials).
|
223
|
-
if found and found.clusters
|
224
|
-
|
752
|
+
found = MU::Cloud::Google.container(credentials: credentials).list_project_location_clusters("projects/#{flags['project']}/locations/#{az}")
|
753
|
+
clusters.concat(found.clusters) if found and found.clusters
|
754
|
+
}
|
225
755
|
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
756
|
+
clusters.uniq.each { |cluster|
|
757
|
+
if !cluster.resource_labels or (
|
758
|
+
!cluster.name.match(/^#{Regexp.quote(MU.deploy_id)}\-/i) and
|
759
|
+
cluster.resource_labels['mu-id'] != MU.deploy_id.downcase
|
760
|
+
)
|
761
|
+
next
|
762
|
+
end
|
763
|
+
MU.log "Deleting GKE cluster #{cluster.name}"
|
764
|
+
if !noop
|
765
|
+
cloud_id = cluster.self_link.sub(/.*?\/projects\//, 'projects/')
|
766
|
+
retries = 0
|
767
|
+
begin
|
768
|
+
MU::Cloud::Google.container(credentials: credentials).delete_project_location_cluster(cloud_id)
|
769
|
+
MU::Cloud::Google.container(credentials: credentials).get_project_location_cluster(cloud_id)
|
770
|
+
sleep 60
|
771
|
+
rescue ::Google::Apis::ClientError => e
|
772
|
+
if e.message.match(/notFound: /)
|
773
|
+
MU.log cloud_id, MU::WARN, details: e.inspect
|
774
|
+
break
|
775
|
+
elsif e.message.match(/failedPrecondition: /)
|
776
|
+
if (retries % 5) == 0
|
777
|
+
MU.log "Waiting to delete GKE cluster #{cluster.name}: #{e.message}", MU::NOTICE
|
778
|
+
end
|
779
|
+
sleep 60
|
780
|
+
retries += 1
|
781
|
+
retry
|
782
|
+
else
|
783
|
+
MU.log cloud_id, MU::WARN, details: e.inspect
|
784
|
+
raise e
|
246
785
|
end
|
247
|
-
|
786
|
+
end while true
|
248
787
|
end
|
249
788
|
}
|
789
|
+
|
250
790
|
end
|
251
791
|
|
252
792
|
# Cloud-specific configuration properties.
|
@@ -254,16 +794,104 @@ puts @config['credentials']
|
|
254
794
|
# @return [Array<Array,Hash>]: List of required fields, and json-schema Hash of cloud-specific configuration parameters for this resource
|
255
795
|
def self.schema(config)
|
256
796
|
toplevel_required = []
|
797
|
+
gke_defaults = defaults
|
257
798
|
schema = {
|
799
|
+
"auto_upgrade" => {
|
800
|
+
"type" => "boolean",
|
801
|
+
"description" => "Automatically upgrade worker nodes during maintenance windows",
|
802
|
+
"default" => true
|
803
|
+
},
|
804
|
+
"auto_repair" => {
|
805
|
+
"type" => "boolean",
|
806
|
+
"description" => "Automatically replace worker nodes which fail health checks",
|
807
|
+
"default" => true
|
808
|
+
},
|
258
809
|
"local_ssd_count" => {
|
259
810
|
"type" => "integer",
|
260
811
|
"description" => "The number of local SSD disks to be attached to workers. See https://cloud.google.com/compute/docs/disks/local-ssd#local_ssd_limits"
|
261
812
|
},
|
813
|
+
"ssh_user" => MU::Cloud::Google::Server.schema(config)[1]["ssh_user"],
|
814
|
+
"metadata" => MU::Cloud::Google::Server.schema(config)[1]["metadata"],
|
815
|
+
"service_account" => MU::Cloud::Google::Server.schema(config)[1]["service_account"],
|
816
|
+
"scopes" => MU::Cloud::Google::Server.schema(config)[1]["scopes"],
|
817
|
+
"private_cluster" => {
|
818
|
+
"description" => "Set a GKE cluster to be private, that is segregated into its own hidden VPC.",
|
819
|
+
"type" => "object",
|
820
|
+
"properties" => {
|
821
|
+
"private_nodes" => {
|
822
|
+
"type" => "boolean",
|
823
|
+
"default" => true,
|
824
|
+
"description" => "Whether GKE worker nodes have internal IP addresses only."
|
825
|
+
},
|
826
|
+
"private_master" => {
|
827
|
+
"type" => "boolean",
|
828
|
+
"default" => false,
|
829
|
+
"description" => "Whether the GKE Kubernetes master's internal IP address is used as the cluster endpoint."
|
830
|
+
},
|
831
|
+
"master_ip_block" => {
|
832
|
+
"type" => "string",
|
833
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
834
|
+
"default" => "172.20.0.0/28",
|
835
|
+
"description" => "The private IP address range to use for the GKE master's network"
|
836
|
+
}
|
837
|
+
}
|
838
|
+
},
|
839
|
+
"custom_subnet" => {
|
840
|
+
"type" => "object",
|
841
|
+
"description" => "If set, GKE will create a new subnetwork specifically for this cluster",
|
842
|
+
"properties" => {
|
843
|
+
"name" => {
|
844
|
+
"type" => "string",
|
845
|
+
"description" => "Set a custom name for the generated subnet"
|
846
|
+
},
|
847
|
+
"node_ip_block" => {
|
848
|
+
"type" => "string",
|
849
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
850
|
+
"description" => "The IP address range of the worker nodes in this cluster, in CIDR notation"
|
851
|
+
}
|
852
|
+
}
|
853
|
+
},
|
854
|
+
"pod_ip_block" => {
|
855
|
+
"type" => "string",
|
856
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
857
|
+
"description" => "The IP address range of the container pods in this cluster, in CIDR notation"
|
858
|
+
},
|
859
|
+
"pod_ip_block_name" => {
|
860
|
+
"type" => "string",
|
861
|
+
"description" => "The name of the secondary range to be used for the pod CIDR block"
|
862
|
+
},
|
863
|
+
"services_ip_block" => {
|
864
|
+
"type" => "string",
|
865
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
866
|
+
"description" => "The IP address range of the services in this cluster, in CIDR notation"
|
867
|
+
},
|
868
|
+
"services_ip_block_name" => {
|
869
|
+
"type" => "string",
|
870
|
+
"description" => "The name of the secondary range to be used for the services CIDR block"
|
871
|
+
},
|
872
|
+
"ip_aliases" => {
|
873
|
+
"type" => "boolean",
|
874
|
+
"description" => "Whether alias IPs will be used for pod IPs in the cluster. Will be automatically enabled for functionality, such as +private_cluster+, which requires it."
|
875
|
+
},
|
876
|
+
"tpu_ip_block" => {
|
877
|
+
"type" => "string",
|
878
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
879
|
+
"description" => "The IP address range of any Cloud TPUs in this cluster, in CIDR notation"
|
880
|
+
},
|
262
881
|
"disk_size_gb" => {
|
263
882
|
"type" => "integer",
|
264
883
|
"description" => "Size of the disk attached to each worker, specified in GB. The smallest allowed disk size is 10GB",
|
265
884
|
"default" => 100
|
266
885
|
},
|
886
|
+
"min_size" => {
|
887
|
+
"description" => "In GKE, this is the minimum number of nodes *per availability zone*, when scaling is enabled. Setting +min_size+ and +max_size+ enables scaling."
|
888
|
+
},
|
889
|
+
"max_size" => {
|
890
|
+
"description" => "In GKE, this is the maximum number of nodes *per availability zone*, when scaling is enabled. Setting +min_size+ and +max_size+ enables scaling."
|
891
|
+
},
|
892
|
+
"instance_count" => {
|
893
|
+
"description" => "In GKE, this value is ignored if +min_size+ and +max_size+ are set."
|
894
|
+
},
|
267
895
|
"min_cpu_platform" => {
|
268
896
|
"type" => "string",
|
269
897
|
"description" => "Minimum CPU platform to be used by workers. The instances may be scheduled on the specified or newer CPU platform. Applicable values are the friendly names of CPU platforms, such as minCpuPlatform: 'Intel Haswell' or minCpuPlatform: 'Intel Sandy Bridge'."
|
@@ -275,7 +903,95 @@ puts @config['credentials']
|
|
275
903
|
},
|
276
904
|
"image_type" => {
|
277
905
|
"type" => "string",
|
278
|
-
"
|
906
|
+
"enum" => gke_defaults ? gke_defaults.valid_image_types : ["COS"],
|
907
|
+
"description" => "The image type to use for workers. Note that for a given image type, the latest version of it will be used.",
|
908
|
+
"default" => gke_defaults ? gke_defaults.default_image_type : "COS"
|
909
|
+
},
|
910
|
+
"availability_zone" => {
|
911
|
+
"type" => "string",
|
912
|
+
"description" => "Target a specific availability zone for this cluster"
|
913
|
+
},
|
914
|
+
"preferred_maintenance_window" => {
|
915
|
+
"type" => "string",
|
916
|
+
"description" => "The preferred daily time to perform node maintenance. Time format should be in [RFC3339](http://www.ietf.org/rfc/rfc3339.txt) format +HH:MM+ GMT.",
|
917
|
+
"pattern" => '^\d\d:\d\d$'
|
918
|
+
},
|
919
|
+
"kubernetes" => {
|
920
|
+
"description" => "Kubernetes-specific options",
|
921
|
+
"properties" => {
|
922
|
+
"version" => {
|
923
|
+
"type" => "string"
|
924
|
+
},
|
925
|
+
"nodeversion" => {
|
926
|
+
"type" => "string",
|
927
|
+
"description" => "The version of Kubernetes to install on GKE worker nodes."
|
928
|
+
},
|
929
|
+
"alpha" => {
|
930
|
+
"type" => "boolean",
|
931
|
+
"default" => false,
|
932
|
+
"description" => "Enable alpha-quality Kubernetes features on this cluster"
|
933
|
+
},
|
934
|
+
"dashboard" => {
|
935
|
+
"type" => "boolean",
|
936
|
+
"default" => false,
|
937
|
+
"description" => "Enable the Kubernetes Dashboard"
|
938
|
+
},
|
939
|
+
"horizontal_pod_autoscaling" => {
|
940
|
+
"type" => "boolean",
|
941
|
+
"default" => true,
|
942
|
+
"description" => "Increases or decreases the number of replica pods a replication controller has based on the resource usage of the existing pods."
|
943
|
+
},
|
944
|
+
"http_load_balancing" => {
|
945
|
+
"type" => "boolean",
|
946
|
+
"default" => true,
|
947
|
+
"description" => "HTTP (L7) load balancing controller addon, which makes it easy to set up HTTP load balancers for services in a cluster."
|
948
|
+
},
|
949
|
+
"network_policy_addon" => {
|
950
|
+
"type" => "boolean",
|
951
|
+
"default" => false,
|
952
|
+
"description" => "Enable the Network Policy addon"
|
953
|
+
}
|
954
|
+
}
|
955
|
+
},
|
956
|
+
"pod_ip_range" => {
|
957
|
+
"type" => "string",
|
958
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
959
|
+
"description" => "The IP address range of the container pods in this cluster, in CIDR notation"
|
960
|
+
},
|
961
|
+
"tpu" => {
|
962
|
+
"type" => "boolean",
|
963
|
+
"default" => false,
|
964
|
+
"description" => "Enable the ability to use Cloud TPUs in this cluster."
|
965
|
+
},
|
966
|
+
"log_facility" => {
|
967
|
+
"type" => "string",
|
968
|
+
"default" => "kubernetes",
|
969
|
+
"description" => "The +logging.googleapis.com+ and +monitoring.googleapis.com+ facilities that this cluster should use to write logs and metrics.",
|
970
|
+
"enum" => ["basic", "kubernetes", "none"]
|
971
|
+
},
|
972
|
+
"master_user" => {
|
973
|
+
"type" => "string",
|
974
|
+
"description" => "Enables Basic Auth for a GKE cluster with string as the master username"
|
975
|
+
},
|
976
|
+
"authorized_networks" => {
|
977
|
+
"type" => "array",
|
978
|
+
"items" => {
|
979
|
+
"description" => "GKE's Master authorized networks functionality",
|
980
|
+
"type" => "object",
|
981
|
+
"ip_block" => {
|
982
|
+
"type" => "string",
|
983
|
+
"description" => "CIDR block to allow",
|
984
|
+
"pattern" => MU::Config::CIDR_PATTERN,
|
985
|
+
},
|
986
|
+
"label" =>{
|
987
|
+
"description" => "Label for this CIDR block",
|
988
|
+
"type" => "string",
|
989
|
+
}
|
990
|
+
}
|
991
|
+
},
|
992
|
+
"master_az" => {
|
993
|
+
"type" => "string",
|
994
|
+
"description" => "Target a specific Availability Zone for the GKE master. If not set, we will choose one which has the most current versions of Kubernetes available."
|
279
995
|
}
|
280
996
|
}
|
281
997
|
[toplevel_required, schema]
|
@@ -287,11 +1003,148 @@ puts @config['credentials']
|
|
287
1003
|
# @return [Boolean]: True if validation succeeded, False otherwise
|
288
1004
|
def self.validateConfig(cluster, configurator)
|
289
1005
|
ok = true
|
290
|
-
|
291
|
-
|
292
|
-
|
1006
|
+
cluster['project'] ||= MU::Cloud::Google.defaultProject(cluster['credentials'])
|
1007
|
+
|
1008
|
+
cluster['master_az'] ||= cluster['availability_zone'] if cluster['availability_zone']
|
1009
|
+
|
1010
|
+
if cluster['private_cluster'] or cluster['custom_subnet'] or
|
1011
|
+
cluster['services_ip_block'] or cluster['services_ip_block_name'] or
|
1012
|
+
cluster['pod_ip_block'] or cluster['pod_ip_block_name'] or
|
1013
|
+
cluster['tpu_ip_block']
|
1014
|
+
cluster['ip_aliases'] = true
|
1015
|
+
end
|
1016
|
+
|
1017
|
+
if cluster['service_account']
|
1018
|
+
cluster['service_account']['cloud'] = "Google"
|
1019
|
+
cluster['service_account']['habitat'] ||= MU::Config::Ref.get(
|
1020
|
+
id: cluster['project'],
|
1021
|
+
cloud: "Google",
|
1022
|
+
credentials: cluster['credentials'],
|
1023
|
+
type: "habitats"
|
1024
|
+
)
|
1025
|
+
if cluster['service_account']['name'] and
|
1026
|
+
!cluster['service_account']['id']
|
1027
|
+
cluster['dependencies'] ||= []
|
1028
|
+
cluster['dependencies'] << {
|
1029
|
+
"type" => "user",
|
1030
|
+
"name" => cluster['service_account']['name']
|
1031
|
+
}
|
1032
|
+
end
|
1033
|
+
found = MU::Config::Ref.get(cluster['service_account'])
|
1034
|
+
# XXX verify that found.kitten fails when it's supposed to
|
1035
|
+
if cluster['service_account']['id'] and !found.kitten
|
1036
|
+
MU.log "GKE cluster #{cluster['name']} failed to locate service account #{cluster['service_account']} in project #{cluster['project']}", MU::ERR
|
1037
|
+
ok = false
|
1038
|
+
end
|
1039
|
+
else
|
1040
|
+
user = {
|
1041
|
+
"name" => cluster['name'],
|
1042
|
+
"cloud" => "Google",
|
1043
|
+
"project" => cluster["project"],
|
1044
|
+
"credentials" => cluster["credentials"],
|
1045
|
+
"type" => "service"
|
1046
|
+
}
|
1047
|
+
configurator.insertKitten(user, "users", true)
|
1048
|
+
cluster['dependencies'] ||= []
|
1049
|
+
cluster['service_account'] = MU::Config::Ref.get(
|
1050
|
+
type: "users",
|
1051
|
+
cloud: "Google",
|
1052
|
+
name: cluster["name"],
|
1053
|
+
project: cluster["project"],
|
1054
|
+
credentials: cluster["credentials"]
|
1055
|
+
)
|
1056
|
+
cluster['dependencies'] << {
|
1057
|
+
"type" => "user",
|
1058
|
+
"name" => cluster["name"]
|
1059
|
+
}
|
1060
|
+
end
|
1061
|
+
|
1062
|
+
if (cluster['pod_ip_block_name'] or cluster['services_ip_block_name']) and
|
1063
|
+
cluster['custom_subnet']
|
1064
|
+
MU.log "GKE cluster #{cluster['name']} cannot specify pod_ip_block_name or services_ip_block_name when using a custom subnet", MU::ERR
|
1065
|
+
ok = false
|
1066
|
+
end
|
1067
|
+
|
1068
|
+
# If we've enabled master authorized networks, make sure our Mu
|
1069
|
+
# Master is one of the things allowed in.
|
1070
|
+
if cluster['authorized_networks']
|
1071
|
+
found_me = false
|
1072
|
+
my_cidr = NetAddr::IPv4.parse(MU.mu_public_ip)
|
1073
|
+
cluster['authorized_networks'].each { |block|
|
1074
|
+
cidr_obj = NetAddr::IPv4Net.parse(block['ip_block'])
|
1075
|
+
if cidr_obj.contains(my_cidr)
|
1076
|
+
found_me = true
|
1077
|
+
break
|
1078
|
+
end
|
1079
|
+
}
|
1080
|
+
if !found_me
|
1081
|
+
cluster['authorized_networks'] << {
|
1082
|
+
"ip_block" => MU.mu_public_ip+"/32",
|
1083
|
+
"label" => "Mu Master #{$MU_CFG['hostname']}"
|
1084
|
+
}
|
1085
|
+
end
|
1086
|
+
end
|
1087
|
+
|
1088
|
+
master_versions = defaults(az: cluster['master_az']).valid_master_versions.sort { |a, b| MU.version_sort(a, b) }
|
1089
|
+
if cluster['kubernetes'] and cluster['kubernetes']['version']
|
1090
|
+
if cluster['kubernetes']['version'] == "latest"
|
1091
|
+
cluster['kubernetes']['version'] = master_versions.last
|
1092
|
+
elsif !master_versions.include?(cluster['kubernetes']['version'])
|
1093
|
+
match = false
|
1094
|
+
master_versions.each { |v|
|
1095
|
+
if v.match(/^#{Regexp.quote(cluster['kubernetes']['version'])}/)
|
1096
|
+
match = true
|
1097
|
+
break
|
1098
|
+
end
|
1099
|
+
}
|
1100
|
+
if !match
|
1101
|
+
MU.log "No version matching #{cluster['kubernetes']['version']} available, will try floating minor revision", MU::WARN
|
1102
|
+
cluster['kubernetes']['version'].sub!(/^(\d+\.\d+\.).*/i, '\1')
|
1103
|
+
master_versions.each { |v|
|
1104
|
+
if v.match(/^#{Regexp.quote(cluster['kubernetes']['version'])}/)
|
1105
|
+
match = true
|
1106
|
+
break
|
1107
|
+
end
|
1108
|
+
}
|
1109
|
+
if !match
|
1110
|
+
MU.log "Failed to find a GKE master version matching #{cluster['kubernetes']['version']} among available versions in #{cluster['master_az'] || cluster['region']}.", MU::ERR, details: master_versions
|
1111
|
+
ok = false
|
1112
|
+
end
|
1113
|
+
end
|
1114
|
+
end
|
1115
|
+
end
|
1116
|
+
|
1117
|
+
node_versions = defaults(az: cluster['master_az']).valid_node_versions.sort { |a, b| MU.version_sort(a, b) }
|
1118
|
+
|
1119
|
+
if cluster['kubernetes'] and cluster['kubernetes']['nodeversion']
|
1120
|
+
if cluster['kubernetes']['nodeversion'] == "latest"
|
1121
|
+
cluster['kubernetes']['nodeversion'] = node_versions.last
|
1122
|
+
elsif !node_versions.include?(cluster['kubernetes']['nodeversion'])
|
1123
|
+
match = false
|
1124
|
+
node_versions.each { |v|
|
1125
|
+
if v.match(/^#{Regexp.quote(cluster['kubernetes']['nodeversion'])}/)
|
1126
|
+
match = true
|
1127
|
+
break
|
1128
|
+
end
|
1129
|
+
}
|
1130
|
+
if !match
|
1131
|
+
MU.log "No version matching #{cluster['kubernetes']['nodeversion']} available, will try floating minor revision", MU::WARN
|
1132
|
+
cluster['kubernetes']['nodeversion'].sub!(/^(\d+\.\d+\.).*/i, '\1')
|
1133
|
+
node_versions.each { |v|
|
1134
|
+
if v.match(/^#{Regexp.quote(cluster['kubernetes']['nodeversion'])}/)
|
1135
|
+
match = true
|
1136
|
+
break
|
1137
|
+
end
|
1138
|
+
}
|
1139
|
+
if !match
|
1140
|
+
MU.log "Failed to find a GKE node version matching #{cluster['kubernetes']['nodeversion']} among available versions in #{cluster['master_az'] || cluster['region']}.", MU::ERR, details: node_versions
|
1141
|
+
ok = false
|
1142
|
+
end
|
1143
|
+
end
|
1144
|
+
end
|
1145
|
+
end
|
293
1146
|
|
294
|
-
cluster['instance_type'] = MU::Cloud::Google::Server.validateInstanceType(cluster["instance_type"], cluster["region"])
|
1147
|
+
cluster['instance_type'] = MU::Cloud::Google::Server.validateInstanceType(cluster["instance_type"], cluster["region"], project: cluster['project'], credentials: cluster['credentials'])
|
295
1148
|
ok = false if cluster['instance_type'].nil?
|
296
1149
|
|
297
1150
|
ok
|
@@ -299,19 +1152,121 @@ puts @config['credentials']
|
|
299
1152
|
|
300
1153
|
private
|
301
1154
|
|
302
|
-
def
|
303
|
-
labels = {
|
304
|
-
|
305
|
-
|
306
|
-
|
1155
|
+
def node_desc
|
1156
|
+
labels = Hash[@tags.keys.map { |k|
|
1157
|
+
[k.downcase, @tags[k].downcase.gsub(/[^-_a-z0-9]/, '-')] }
|
1158
|
+
]
|
1159
|
+
labels["name"] = MU::Cloud::Google.nameStr(@mu_name)
|
1160
|
+
desc = {
|
1161
|
+
:machine_type => @config['instance_type'],
|
1162
|
+
:preemptible => @config['preemptible'],
|
1163
|
+
:disk_size_gb => @config['disk_size_gb'],
|
1164
|
+
:labels => labels,
|
1165
|
+
:tags => [@mu_name.downcase],
|
1166
|
+
:service_account => @service_acct.email,
|
1167
|
+
:oauth_scopes => @config['scopes']
|
1168
|
+
}
|
1169
|
+
desc[:metadata] = {}
|
1170
|
+
deploykey = @config['ssh_user']+":"+@deploy.ssh_public_key
|
1171
|
+
if @config['metadata']
|
1172
|
+
desc[:metadata] = Hash[@config['metadata'].map { |m|
|
1173
|
+
[m["key"], m["value"]]
|
1174
|
+
}]
|
1175
|
+
end
|
1176
|
+
if desc[:metadata]["ssh-keys"]
|
1177
|
+
desc[:metadata]["ssh-keys"] += "\n"+deploykey
|
1178
|
+
else
|
1179
|
+
desc[:metadata]["ssh-keys"] = deploykey
|
1180
|
+
end
|
1181
|
+
[:local_ssd_count, :min_cpu_platform, :image_type].each { |field|
|
1182
|
+
if @config[field.to_s]
|
1183
|
+
desc[field] = @config[field.to_s]
|
307
1184
|
end
|
308
1185
|
}
|
1186
|
+
desc
|
1187
|
+
end
|
1188
|
+
|
1189
|
+
def labelCluster
|
1190
|
+
labels = Hash[@tags.keys.map { |k|
|
1191
|
+
[k.downcase, @tags[k].downcase.gsub(/[^-_a-z0-9]/, '-')] }
|
1192
|
+
]
|
309
1193
|
labels["name"] = MU::Cloud::Google.nameStr(@mu_name)
|
310
1194
|
|
311
1195
|
labelset = MU::Cloud::Google.container(:SetLabelsRequest).new(
|
312
1196
|
resource_labels: labels
|
313
1197
|
)
|
314
|
-
MU::Cloud::Google.container(credentials: @config['credentials']).
|
1198
|
+
MU::Cloud::Google.container(credentials: @config['credentials']).set_project_location_cluster_resource_labels(@cloud_id, labelset)
|
1199
|
+
end
|
1200
|
+
|
1201
|
+
@@server_config = {}
|
1202
|
+
def self.defaults(credentials = nil, az: nil)
|
1203
|
+
az ||= MU::Cloud::Google.listAZs.sample
|
1204
|
+
return nil if az.nil?
|
1205
|
+
@@server_config[credentials] ||= {}
|
1206
|
+
if @@server_config[credentials][az]
|
1207
|
+
return @@server_config[credentials][az]
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
parent_arg = "projects/"+MU::Cloud::Google.defaultProject(credentials)+"/locations/"+az
|
1211
|
+
|
1212
|
+
@@server_config[credentials][az] = MU::Cloud::Google.container(credentials: credentials).get_project_location_server_config(parent_arg)
|
1213
|
+
@@server_config[credentials][az]
|
1214
|
+
end
|
1215
|
+
|
1216
|
+
def writeKubeConfig
|
1217
|
+
kube_conf = @deploy.deploy_dir+"/kubeconfig-#{@config['name']}"
|
1218
|
+
client_binding = @deploy.deploy_dir+"/k8s-client-user-admin-binding.yaml"
|
1219
|
+
@endpoint = "https://"+cloud_desc.endpoint
|
1220
|
+
@cacert = cloud_desc.master_auth.cluster_ca_certificate
|
1221
|
+
@cluster = cloud_desc.name
|
1222
|
+
@clientcert = cloud_desc.master_auth.client_certificate
|
1223
|
+
@clientkey = cloud_desc.master_auth.client_key
|
1224
|
+
if cloud_desc.master_auth.username
|
1225
|
+
@username = cloud_desc.master_auth.username
|
1226
|
+
end
|
1227
|
+
if cloud_desc.master_auth.password
|
1228
|
+
@password = cloud_desc.master_auth.password
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
kube = ERB.new(File.read(MU.myRoot+"/cookbooks/mu-tools/templates/default/kubeconfig-gke.erb"))
|
1232
|
+
File.open(kube_conf, "w"){ |k|
|
1233
|
+
k.puts kube.result(binding)
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
# Take this opportunity to ensure that the 'client' service account
|
1237
|
+
# used by certificate authentication exists and has appropriate
|
1238
|
+
# privilege
|
1239
|
+
if @username and @password
|
1240
|
+
File.open(client_binding, "w"){ |k|
|
1241
|
+
k.puts <<-EOF
|
1242
|
+
kind: ClusterRoleBinding
|
1243
|
+
apiVersion: rbac.authorization.k8s.io/v1
|
1244
|
+
metadata:
|
1245
|
+
name: client-binding
|
1246
|
+
namespace: kube-system
|
1247
|
+
roleRef:
|
1248
|
+
kind: ClusterRole
|
1249
|
+
name: cluster-admin
|
1250
|
+
apiGroup: rbac.authorization.k8s.io
|
1251
|
+
subjects:
|
1252
|
+
- kind: User
|
1253
|
+
name: client
|
1254
|
+
namespace: kube-system
|
1255
|
+
EOF
|
1256
|
+
}
|
1257
|
+
bind_cmd = %Q{#{MU::Master.kubectl} create serviceaccount client --namespace=kube-system --kubeconfig "#{kube_conf}" ; #{MU::Master.kubectl} --kubeconfig "#{kube_conf}" apply -f #{client_binding}}
|
1258
|
+
MU.log bind_cmd
|
1259
|
+
system(bind_cmd)
|
1260
|
+
end
|
1261
|
+
# unset the variables we set just for ERB
|
1262
|
+
[:@endpoint, :@cacert, :@cluster, :@clientcert, :@clientkey, :@username, :@password].each { |var|
|
1263
|
+
begin
|
1264
|
+
remove_instance_variable(var)
|
1265
|
+
rescue NameError
|
1266
|
+
end
|
1267
|
+
}
|
1268
|
+
|
1269
|
+
kube_conf
|
315
1270
|
end
|
316
1271
|
|
317
1272
|
end #class
|