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
|
@@ -28,27 +28,19 @@ module MU
|
|
|
28
28
|
# Google Cloud, this amounts to a single Instance in an Unmanaged
|
|
29
29
|
# Instance Group.
|
|
30
30
|
class Server < MU::Cloud::Server
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
# @param mommacat [MU::MommaCat]: A {MU::Mommacat} object containing the deploy of which this resource is/will be a member.
|
|
42
|
-
# @param kitten_cfg [Hash]: The fully parsed and resolved {MU::Config} resource descriptor as defined in {MU::Config::BasketofKittens::servers}
|
|
43
|
-
def initialize(mommacat: nil, kitten_cfg: nil, mu_name: nil, cloud_id: nil)
|
|
44
|
-
@deploy = mommacat
|
|
45
|
-
@config = MU::Config.manxify(kitten_cfg)
|
|
46
|
-
@cloud_id = cloud_id
|
|
47
|
-
|
|
48
|
-
if @deploy
|
|
49
|
-
@userdata = MU::Cloud.fetchUserdata(
|
|
31
|
+
|
|
32
|
+
# 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.
|
|
33
|
+
# @param args [Hash]: Hash of named arguments passed via Ruby's double-splat
|
|
34
|
+
def initialize(**args)
|
|
35
|
+
super
|
|
36
|
+
|
|
37
|
+
@userdata = if @config['userdata_script']
|
|
38
|
+
@config['userdata_script']
|
|
39
|
+
elsif @deploy and !@config['scrub_mu_isms']
|
|
40
|
+
MU::Cloud.fetchUserdata(
|
|
50
41
|
platform: @config["platform"],
|
|
51
|
-
cloud: "
|
|
42
|
+
cloud: "Google",
|
|
43
|
+
credentials: @config['credentials'],
|
|
52
44
|
template_variables: {
|
|
53
45
|
"deployKey" => Base64.urlsafe_encode64(@deploy.public_key),
|
|
54
46
|
"deploySSHKey" => @deploy.ssh_public_key,
|
|
@@ -57,6 +49,9 @@ module MU
|
|
|
57
49
|
"publicIP" => MU.mu_public_ip,
|
|
58
50
|
"skipApplyUpdates" => @config['skipinitialupdates'],
|
|
59
51
|
"windowsAdminName" => @config['windows_admin_username'],
|
|
52
|
+
"adminBucketName" => MU::Cloud::Google.adminBucketName(@credentials),
|
|
53
|
+
"chefVersion" => MU.chefVersion,
|
|
54
|
+
"mommaCatPort" => MU.mommaCatPort,
|
|
60
55
|
"resourceName" => @config["name"],
|
|
61
56
|
"resourceType" => "server",
|
|
62
57
|
"platform" => @config["platform"]
|
|
@@ -64,17 +59,11 @@ module MU
|
|
|
64
59
|
custom_append: @config['userdata_script']
|
|
65
60
|
)
|
|
66
61
|
end
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
@mu_name = mu_name
|
|
70
|
-
@config['mu_name'] = @mu_name
|
|
62
|
+
# XXX writing things into @config at runtime is a bad habit and we should stop
|
|
63
|
+
if !@mu_name.nil?
|
|
64
|
+
@config['mu_name'] = @mu_name # XXX whyyyy
|
|
71
65
|
# describe
|
|
72
66
|
@mu_windows_name = @deploydata['mu_windows_name'] if @mu_windows_name.nil? and @deploydata
|
|
73
|
-
@config['project'] ||= MU::Cloud::Google.defaultProject(@config['credentials'])
|
|
74
|
-
if !@project_id
|
|
75
|
-
project = MU::Cloud::Google.projectLookup(@config['project'], @deploy, sibling_only: true, raise_on_fail: false)
|
|
76
|
-
@project_id = project.nil? ? @config['project'] : project.cloudobj.cloud_id
|
|
77
|
-
end
|
|
78
67
|
else
|
|
79
68
|
if kitten_cfg.has_key?("basis")
|
|
80
69
|
@mu_name = @deploy.getResourceName(@config['name'], need_unique_string: true)
|
|
@@ -83,60 +72,88 @@ module MU
|
|
|
83
72
|
end
|
|
84
73
|
@config['mu_name'] = @mu_name
|
|
85
74
|
|
|
86
|
-
@config['instance_secret'] = Password.random(50)
|
|
87
75
|
end
|
|
88
|
-
@config['
|
|
89
|
-
@
|
|
76
|
+
@config['instance_secret'] ||= Password.random(50)
|
|
77
|
+
@config['ssh_user'] ||= "muadmin"
|
|
90
78
|
|
|
91
79
|
end
|
|
92
80
|
|
|
93
|
-
#
|
|
94
|
-
#
|
|
95
|
-
# @param
|
|
96
|
-
# @
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
saobj = MU::Cloud::Google.iam(:CreateServiceAccountRequest).new(
|
|
106
|
-
account_id: name.gsub(/[^a-z]/, ""), # XXX this mangling isn't required in the console, so why is it here?
|
|
107
|
-
service_account: MU::Cloud::Google.iam(:ServiceAccount).new(
|
|
108
|
-
display_name: rolename,
|
|
109
|
-
# do NOT specify project_id or name, we know that much
|
|
110
|
-
)
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
resp = MU::Cloud::Google.iam(credentials: credentials).create_service_account(
|
|
114
|
-
"projects/#{project}",
|
|
115
|
-
saobj
|
|
116
|
-
)
|
|
81
|
+
# Return the date/time a machine image was created.
|
|
82
|
+
# @param image_id [String]: URL to a Google disk image
|
|
83
|
+
# @param credentials [String]
|
|
84
|
+
# @return [DateTime]
|
|
85
|
+
def self.imageTimeStamp(image_id, credentials: nil)
|
|
86
|
+
begin
|
|
87
|
+
img = fetchImage(image_id, credentials: credentials)
|
|
88
|
+
return DateTime.new if img.nil?
|
|
89
|
+
return DateTime.parse(img.creation_timestamp)
|
|
90
|
+
rescue ::Google::Apis::ClientError => e
|
|
91
|
+
end
|
|
117
92
|
|
|
118
|
-
|
|
119
|
-
email: resp.email,
|
|
120
|
-
scopes: scopes
|
|
121
|
-
)
|
|
93
|
+
return DateTime.new
|
|
122
94
|
end
|
|
123
95
|
|
|
96
|
+
@@image_id_map = {}
|
|
97
|
+
|
|
124
98
|
# Retrieve the cloud descriptor for this machine image, which can be
|
|
125
99
|
# a whole or partial URL. Will follow deprecation notices and retrieve
|
|
126
100
|
# the latest version, if applicable.
|
|
127
101
|
# @param image_id [String]: URL to a Google disk image
|
|
128
|
-
# @
|
|
102
|
+
# @param credentials [String]
|
|
103
|
+
# @return [Google::Apis::ComputeV1::Image]
|
|
129
104
|
def self.fetchImage(image_id, credentials: nil)
|
|
105
|
+
return @@image_id_map[image_id] if @@image_id_map[image_id]
|
|
106
|
+
|
|
130
107
|
img_proj = img_name = nil
|
|
131
|
-
|
|
132
|
-
img_proj = image_id.gsub(
|
|
108
|
+
if image_id.match(/\//)
|
|
109
|
+
img_proj = image_id.gsub(/(?:https?:\/\/.*?\.googleapis\.com\/compute\/.*?\/)?.*?\/?(?:projects\/)?([^\/]+)\/.*/, '\1')
|
|
133
110
|
img_name = image_id.gsub(/.*?([^\/]+)$/, '\1')
|
|
111
|
+
else
|
|
112
|
+
img_name = image_id
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
begin
|
|
116
|
+
@@image_id_map[image_id] = MU::Cloud::Google.compute(credentials: credentials).get_image_from_family(img_proj, img_name)
|
|
117
|
+
return @@image_id_map[image_id]
|
|
118
|
+
rescue ::Google::Apis::ClientError
|
|
119
|
+
# This is fine- we don't know that what we asked for is really an
|
|
120
|
+
# image family name, instead of just an image.
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
begin
|
|
134
124
|
img = MU::Cloud::Google.compute(credentials: credentials).get_image(img_proj, img_name)
|
|
135
125
|
if !img.deprecated.nil? and !img.deprecated.replacement.nil?
|
|
136
126
|
image_id = img.deprecated.replacement
|
|
127
|
+
img_proj = image_id.gsub(/(?:https?:\/\/.*?\.googleapis\.com\/compute\/.*?\/)?.*?\/?(?:projects\/)?([^\/]+)\/.*/, '\1')
|
|
128
|
+
img_name = image_id.gsub(/.*?([^\/]+)$/, '\1')
|
|
137
129
|
end
|
|
130
|
+
rescue ::Google::Apis::ClientError => e
|
|
131
|
+
# SOME people *cough* don't use deprecation or image family names
|
|
132
|
+
# and just spew out images with a version appended to the name, so
|
|
133
|
+
# let's try some crude semantic versioning list.
|
|
134
|
+
if e.message.match(/^notFound: /) and img_name.match(/-[^\-]+$/)
|
|
135
|
+
list = MU::Cloud::Google.compute(credentials: credentials).list_images(img_proj, filter: "name eq #{img_name.sub(/-[^\-]+$/, '')}-.*")
|
|
136
|
+
if list and list.items
|
|
137
|
+
latest = nil
|
|
138
|
+
list.items.each { |candidate|
|
|
139
|
+
created = DateTime.parse(candidate.creation_timestamp)
|
|
140
|
+
if latest.nil? or created > latest
|
|
141
|
+
latest = created
|
|
142
|
+
img = candidate
|
|
143
|
+
end
|
|
144
|
+
}
|
|
145
|
+
if latest
|
|
146
|
+
MU.log "Mapped #{image_id} to #{img.name} with semantic versioning guesswork", MU::WARN
|
|
147
|
+
@@image_id_map[image_id] = img
|
|
148
|
+
return @@image_id_map[image_id]
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
raise e # if our little semantic versioning party trick failed
|
|
138
153
|
end while !img.deprecated.nil? and img.deprecated.state == "DEPRECATED" and !img.deprecated.replacement.nil?
|
|
139
|
-
MU::Cloud::Google.compute(credentials: credentials).get_image(img_proj, img_name)
|
|
154
|
+
final = MU::Cloud::Google.compute(credentials: credentials).get_image(img_proj, img_name)
|
|
155
|
+
@@image_id_map[image_id] = final
|
|
156
|
+
@@image_id_map[image_id]
|
|
140
157
|
end
|
|
141
158
|
|
|
142
159
|
# Generator for disk configuration parameters for a Compute instance
|
|
@@ -230,7 +247,7 @@ next if !create
|
|
|
230
247
|
end
|
|
231
248
|
subnet = vpc.getSubnet(name: subnet_cfg['subnet_name'], cloud_id: subnet_cfg['subnet_id'])
|
|
232
249
|
if subnet.nil?
|
|
233
|
-
raise MuError, "Couldn't find subnet details while configuring Server #{config['name']} (VPC: #{vpc.mu_name})"
|
|
250
|
+
raise MuError, "Couldn't find subnet details for #{subnet_cfg['subnet_name'] || subnet_cfg['subnet_id']} while configuring Server #{config['name']} (VPC: #{vpc.mu_name})"
|
|
234
251
|
end
|
|
235
252
|
base_iface_obj = {
|
|
236
253
|
:network => vpc.url,
|
|
@@ -249,15 +266,21 @@ next if !create
|
|
|
249
266
|
|
|
250
267
|
# Called automatically by {MU::Deploy#createResources}
|
|
251
268
|
def create
|
|
252
|
-
@project_id = MU::Cloud::Google.projectLookup(@config['
|
|
269
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloud_id
|
|
270
|
+
|
|
271
|
+
sa = MU::Config::Ref.get(@config['service_account'])
|
|
253
272
|
|
|
254
|
-
|
|
255
|
-
@
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
273
|
+
if !sa or !sa.kitten or !sa.kitten.cloud_desc
|
|
274
|
+
raise MuError, "Failed to get service account cloud id from #{@config['service_account'].to_s}"
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
@service_acct = MU::Cloud::Google.compute(:ServiceAccount).new(
|
|
278
|
+
email: sa.kitten.cloud_desc.email,
|
|
279
|
+
scopes: @config['scopes']
|
|
259
280
|
)
|
|
260
|
-
|
|
281
|
+
if !@config['scrub_mu_isms']
|
|
282
|
+
MU::Cloud::Google.grantDeploySecretAccess(@service_acct.email, credentials: @config['credentials'])
|
|
283
|
+
end
|
|
261
284
|
|
|
262
285
|
begin
|
|
263
286
|
disks = MU::Cloud::Google::Server.diskConfig(@config, credentials: @config['credentials'])
|
|
@@ -273,25 +296,36 @@ next if !create
|
|
|
273
296
|
:name => MU::Cloud::Google.nameStr(@mu_name),
|
|
274
297
|
:can_ip_forward => !@config['src_dst_check'],
|
|
275
298
|
:description => @deploy.deploy_id,
|
|
276
|
-
:service_accounts => [service_acct],
|
|
299
|
+
:service_accounts => [@service_acct],
|
|
277
300
|
:network_interfaces => interfaces,
|
|
278
301
|
:machine_type => "zones/"+@config['availability_zone']+"/machineTypes/"+@config['size'],
|
|
279
|
-
:metadata => {
|
|
280
|
-
:items => [
|
|
281
|
-
{
|
|
282
|
-
:key => "ssh-keys",
|
|
283
|
-
:value => @config['ssh_user']+":"+@deploy.ssh_public_key
|
|
284
|
-
},
|
|
285
|
-
{
|
|
286
|
-
:key => "startup-script",
|
|
287
|
-
:value => @userdata
|
|
288
|
-
}
|
|
289
|
-
]
|
|
290
|
-
},
|
|
291
302
|
:tags => MU::Cloud::Google.compute(:Tags).new(items: [MU::Cloud::Google.nameStr(@mu_name)])
|
|
292
303
|
}
|
|
293
304
|
desc[:disks] = disks if disks.size > 0
|
|
294
305
|
|
|
306
|
+
metadata = {}
|
|
307
|
+
if @config['metadata']
|
|
308
|
+
metadata = Hash[@config['metadata'].map { |m|
|
|
309
|
+
[m["key"], m["value"]]
|
|
310
|
+
}]
|
|
311
|
+
end
|
|
312
|
+
metadata["startup-script"] = @userdata if @userdata and !@userdata.empty?
|
|
313
|
+
|
|
314
|
+
deploykey = @config['ssh_user']+":"+@deploy.ssh_public_key
|
|
315
|
+
if metadata["ssh-keys"]
|
|
316
|
+
metadata["ssh-keys"] += "\n"+deploykey
|
|
317
|
+
else
|
|
318
|
+
metadata["ssh-keys"] = deploykey
|
|
319
|
+
end
|
|
320
|
+
desc[:metadata] = MU::Cloud::Google.compute(:Metadata).new(
|
|
321
|
+
:items => metadata.keys.map { |k|
|
|
322
|
+
MU::Cloud::Google.compute(:Metadata)::Item.new(
|
|
323
|
+
key: k,
|
|
324
|
+
value: metadata[k]
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
)
|
|
328
|
+
|
|
295
329
|
# Tags in GCP means something other than what we think of;
|
|
296
330
|
# labels are the thing you think you mean
|
|
297
331
|
desc[:labels] = {}
|
|
@@ -302,10 +336,16 @@ next if !create
|
|
|
302
336
|
}
|
|
303
337
|
desc[:labels]["name"] = @mu_name.downcase
|
|
304
338
|
|
|
339
|
+
if @config['network_tags'] and @config['network_tags'].size > 0
|
|
340
|
+
desc[:tags] = U::Cloud::Google.compute(:Tags).new(
|
|
341
|
+
items: @config['network_tags']
|
|
342
|
+
)
|
|
343
|
+
end
|
|
305
344
|
|
|
306
345
|
instanceobj = MU::Cloud::Google.compute(:Instance).new(desc)
|
|
307
346
|
|
|
308
|
-
MU.log "Creating instance #{@mu_name}"
|
|
347
|
+
MU.log "Creating instance #{@mu_name}", MU::NOTICE, details: instanceobj
|
|
348
|
+
|
|
309
349
|
begin
|
|
310
350
|
instance = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_instance(
|
|
311
351
|
@project_id,
|
|
@@ -351,7 +391,7 @@ next if !create
|
|
|
351
391
|
parent_thread_id = Thread.current.object_id
|
|
352
392
|
Thread.new {
|
|
353
393
|
MU.dupGlobals(parent_thread_id)
|
|
354
|
-
MU::Cloud::Google::Server.cleanup(noop: false, ignoremaster: false, flags: { "skipsnapshots" => true } )
|
|
394
|
+
MU::Cloud::Google::Server.cleanup(noop: false, ignoremaster: false, flags: { "skipsnapshots" => true }, region: @config['region'] )
|
|
355
395
|
}
|
|
356
396
|
end
|
|
357
397
|
end
|
|
@@ -467,7 +507,7 @@ next if !create
|
|
|
467
507
|
return false if !MU::MommaCat.lock(@cloud_id+"-orchestrate", true)
|
|
468
508
|
return false if !MU::MommaCat.lock(@cloud_id+"-groom", true)
|
|
469
509
|
|
|
470
|
-
# MU::
|
|
510
|
+
# MU::Cloud::AWS.createStandardTags(@cloud_id, region: @config['region'])
|
|
471
511
|
# MU::MommaCat.createTag(@cloud_id, "Name", node, region: @config['region'])
|
|
472
512
|
#
|
|
473
513
|
# if @config['optional_tags']
|
|
@@ -586,80 +626,82 @@ next if !create
|
|
|
586
626
|
end #postBoot
|
|
587
627
|
|
|
588
628
|
# Locate an existing instance or instances and return an array containing matching AWS resource descriptors for those that match.
|
|
589
|
-
# @param cloud_id [String]: The cloud provider's identifier for this resource.
|
|
590
|
-
# @param region [String]: The cloud provider region
|
|
591
|
-
# @param tag_key [String]: A tag key to search.
|
|
592
|
-
# @param tag_value [String]: The value of the tag specified by tag_key to match when searching by tag.
|
|
593
|
-
# @param ip [String]: An IP address associated with the instance
|
|
594
|
-
# @param flags [Hash]: Optional flags
|
|
595
629
|
# @return [Array<Hash<String,OpenStruct>>]: The cloud provider's complete descriptions of matching instances
|
|
596
|
-
def self.find(
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
regions = [region]
|
|
630
|
+
def self.find(**args)
|
|
631
|
+
args[:project] ||= args[:habitat]
|
|
632
|
+
args[:project] ||= MU::Cloud::Google.defaultProject(args[:credentials])
|
|
633
|
+
if !args[:region].nil? and MU::Cloud::Google.listRegions.include?(args[:region])
|
|
634
|
+
regions = [args[:region]]
|
|
602
635
|
else
|
|
603
636
|
regions = MU::Cloud::Google.listRegions
|
|
604
637
|
end
|
|
605
638
|
|
|
606
|
-
|
|
639
|
+
found = {}
|
|
607
640
|
search_semaphore = Mutex.new
|
|
608
641
|
search_threads = []
|
|
609
642
|
|
|
610
643
|
# If we got an instance id, go get it
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
flags["project"],
|
|
644
|
+
parent_thread_id = Thread.current.object_id
|
|
645
|
+
regions.each { |r|
|
|
646
|
+
search_threads << Thread.new(r) { |region|
|
|
647
|
+
Thread.abort_on_exception = false
|
|
648
|
+
MU.dupGlobals(parent_thread_id)
|
|
649
|
+
MU.log "Hunting for instance with cloud id '#{args[:cloud_id]}' in #{region}", MU::DEBUG
|
|
650
|
+
MU::Cloud::Google.listAZs(region).each { |az|
|
|
651
|
+
begin
|
|
652
|
+
if !args[:cloud_id].nil? and !args[:cloud_id].empty?
|
|
653
|
+
resp = MU::Cloud::Google.compute(credentials: args[:credentials]).get_instance(
|
|
654
|
+
args[:project],
|
|
623
655
|
az,
|
|
624
|
-
cloud_id
|
|
656
|
+
args[:cloud_id]
|
|
657
|
+
)
|
|
658
|
+
search_semaphore.synchronize {
|
|
659
|
+
found[args[:cloud_id]] = resp if !resp.nil?
|
|
660
|
+
}
|
|
661
|
+
else
|
|
662
|
+
resp = MU::Cloud::Google.compute(credentials: args[:credentials]).list_instances(
|
|
663
|
+
args[:project],
|
|
664
|
+
az
|
|
625
665
|
)
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
666
|
+
if resp and resp.items
|
|
667
|
+
resp.items.each { |instance|
|
|
668
|
+
search_semaphore.synchronize {
|
|
669
|
+
found[instance.name] = instance
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
end
|
|
630
673
|
end
|
|
631
|
-
|
|
632
|
-
|
|
674
|
+
rescue ::OpenSSL::SSL::SSLError => e
|
|
675
|
+
MU.log "Got #{e.message} looking for instance #{args[:cloud_id]} in project #{args[:project]} (#{az}). Usually this means we've tried to query a non-functional region.", MU::DEBUG
|
|
676
|
+
rescue ::Google::Apis::ClientError => e
|
|
677
|
+
raise e if !e.message.match(/^(?:notFound|forbidden): /)
|
|
678
|
+
end
|
|
633
679
|
}
|
|
634
680
|
}
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
if found_instances.size > 0
|
|
645
|
-
return found_instances
|
|
646
|
-
end
|
|
647
|
-
|
|
681
|
+
}
|
|
682
|
+
done_threads = []
|
|
683
|
+
begin
|
|
684
|
+
search_threads.reject! { |t| t.nil? }
|
|
685
|
+
search_threads.each { |t|
|
|
686
|
+
joined = t.join(2)
|
|
687
|
+
done_threads << joined if !joined.nil?
|
|
688
|
+
}
|
|
689
|
+
end while found.size < 1 and done_threads.size != search_threads.size
|
|
648
690
|
# Ok, well, let's try looking it up by IP then
|
|
649
|
-
if instance.nil? and !ip.nil?
|
|
650
|
-
MU.log "Hunting for instance by IP '#{ip}'", MU::DEBUG
|
|
651
|
-
end
|
|
691
|
+
# if instance.nil? and !args[:ip].nil?
|
|
692
|
+
# MU.log "Hunting for instance by IP '#{args[:ip]}'", MU::DEBUG
|
|
693
|
+
# end
|
|
652
694
|
|
|
653
|
-
if !instance.nil?
|
|
654
|
-
return {instance.name => instance} if !instance.nil?
|
|
655
|
-
end
|
|
695
|
+
# if !instance.nil?
|
|
696
|
+
# return {instance.name => instance} if !instance.nil?
|
|
697
|
+
# end
|
|
656
698
|
|
|
657
699
|
# Fine, let's try it by tag.
|
|
658
|
-
if !tag_value.nil?
|
|
659
|
-
MU.log "Searching for instance by tag '#{tag_key}=#{tag_value}'", MU::DEBUG
|
|
660
|
-
end
|
|
700
|
+
# if !args[:tag_value].nil?
|
|
701
|
+
# MU.log "Searching for instance by tag '#{args[:tag_key]}=#{args[:tag_value]}'", MU::DEBUG
|
|
702
|
+
# end
|
|
661
703
|
|
|
662
|
-
return
|
|
704
|
+
return found
|
|
663
705
|
end
|
|
664
706
|
|
|
665
707
|
# Return a description of this resource appropriate for deployment
|
|
@@ -724,7 +766,7 @@ next if !create
|
|
|
724
766
|
|
|
725
767
|
# Called automatically by {MU::Deploy#createResources}
|
|
726
768
|
def groom
|
|
727
|
-
@project_id = MU::Cloud::Google.projectLookup(@config['
|
|
769
|
+
@project_id = MU::Cloud::Google.projectLookup(@config['project'], @deploy).cloud_id
|
|
728
770
|
|
|
729
771
|
MU::MommaCat.lock(@cloud_id+"-groom")
|
|
730
772
|
|
|
@@ -801,12 +843,12 @@ next if !create
|
|
|
801
843
|
instance_id: @cloud_id,
|
|
802
844
|
region: @config['region'],
|
|
803
845
|
storage: @config['storage'],
|
|
804
|
-
family: ("mu-"+@config['platform']+"-"+MU.environment).downcase,
|
|
805
846
|
project: @project_id,
|
|
806
847
|
exclude_storage: img_cfg['image_exclude_storage'],
|
|
807
848
|
make_public: img_cfg['public'],
|
|
808
849
|
tags: @config['tags'],
|
|
809
850
|
zone: @config['availability_zone'],
|
|
851
|
+
family: @config['family'],
|
|
810
852
|
credentials: @config['credentials']
|
|
811
853
|
)
|
|
812
854
|
@deploy.notify("images", @config['name'], {"image_id" => image_id})
|
|
@@ -835,7 +877,7 @@ next if !create
|
|
|
835
877
|
# @param region [String]: The cloud provider region
|
|
836
878
|
# @param tags [Array<String>]: Extra/override tags to apply to the image.
|
|
837
879
|
# @return [String]: The cloud provider identifier of the new machine image.
|
|
838
|
-
def self.createImage(name: nil, instance_id: nil, storage: {}, exclude_storage: false, project: nil, make_public: false, tags: [], region: nil, family:
|
|
880
|
+
def self.createImage(name: nil, instance_id: nil, storage: {}, exclude_storage: false, project: nil, make_public: false, tags: [], region: nil, family: nil, zone: MU::Cloud::Google.listAZs.sample, credentials: nil)
|
|
839
881
|
project ||= MU::Cloud::Google.defaultProject(credentials)
|
|
840
882
|
instance = MU::Cloud::Server.find(cloud_id: instance_id, region: region)
|
|
841
883
|
if instance.nil?
|
|
@@ -891,46 +933,21 @@ next if !create
|
|
|
891
933
|
end
|
|
892
934
|
|
|
893
935
|
labels["name"] = instance_id.downcase
|
|
894
|
-
|
|
895
|
-
name
|
|
896
|
-
source_disk
|
|
897
|
-
description
|
|
898
|
-
labels
|
|
899
|
-
|
|
900
|
-
|
|
936
|
+
image_desc = {
|
|
937
|
+
:name => name,
|
|
938
|
+
:source_disk => bootdisk,
|
|
939
|
+
:description => "Mu image created from #{name}",
|
|
940
|
+
:labels => labels
|
|
941
|
+
}
|
|
942
|
+
image_desc[:family] = family if family
|
|
901
943
|
|
|
902
944
|
newimage = MU::Cloud::Google.compute(credentials: @config['credentials']).insert_image(
|
|
903
945
|
project,
|
|
904
|
-
|
|
946
|
+
MU::Cloud::Google.compute(:Image).new(image_desc)
|
|
905
947
|
)
|
|
906
948
|
newimage.name
|
|
907
949
|
end
|
|
908
950
|
|
|
909
|
-
# def cloud_desc
|
|
910
|
-
# max_retries = 5
|
|
911
|
-
# retries = 0
|
|
912
|
-
# if !@cloud_id.nil?
|
|
913
|
-
# begin
|
|
914
|
-
# return MU::Cloud::Google.compute(credentials: @config['credentials']).get_instance(
|
|
915
|
-
# @project_id,
|
|
916
|
-
# @config['availability_zone'],
|
|
917
|
-
# @cloud_id
|
|
918
|
-
# )
|
|
919
|
-
# rescue ::Google::Apis::ClientError => e
|
|
920
|
-
# if e.message.match(/^notFound: /)
|
|
921
|
-
# return nil
|
|
922
|
-
# else
|
|
923
|
-
# raise e
|
|
924
|
-
# end
|
|
925
|
-
# end
|
|
926
|
-
# end
|
|
927
|
-
# nil
|
|
928
|
-
# end
|
|
929
|
-
|
|
930
|
-
def cloud_desc
|
|
931
|
-
MU::Cloud::Google::Server.find(cloud_id: @cloud_id, credentials: @config['credentials']).values.first
|
|
932
|
-
end
|
|
933
|
-
|
|
934
951
|
# Return the IP address that we, the Mu server, should be using to access
|
|
935
952
|
# this host via the network. Note that this does not factor in SSH
|
|
936
953
|
# bastion hosts that may be in the path, see getSSHConfig if that's what
|
|
@@ -974,7 +991,8 @@ next if !create
|
|
|
974
991
|
# @param dev [String]: Device name to use when attaching to instance
|
|
975
992
|
# @param size [String]: Size (in gb) of the new volume
|
|
976
993
|
# @param type [String]: Cloud storage type of the volume, if applicable
|
|
977
|
-
|
|
994
|
+
# @param delete_on_termination [Boolean]: Value of delete_on_termination flag to set
|
|
995
|
+
def addVolume(dev, size, type: "pd-standard", delete_on_termination: false)
|
|
978
996
|
devname = dev.gsub(/.*?\/([^\/]+)$/, '\1')
|
|
979
997
|
resname = MU::Cloud::Google.nameStr(@mu_name+"-"+devname)
|
|
980
998
|
MU.log "Creating disk #{resname}"
|
|
@@ -1007,11 +1025,13 @@ next if !create
|
|
|
1007
1025
|
end
|
|
1008
1026
|
|
|
1009
1027
|
attachobj = MU::Cloud::Google.compute(:AttachedDisk).new(
|
|
1010
|
-
auto_delete: true,
|
|
1011
1028
|
device_name: devname,
|
|
1012
1029
|
source: newdisk.self_link,
|
|
1013
|
-
type: "PERSISTENT"
|
|
1030
|
+
type: "PERSISTENT",
|
|
1031
|
+
auto_delete: delete_on_termination
|
|
1014
1032
|
)
|
|
1033
|
+
|
|
1034
|
+
MU.log "Attaching disk #{resname} to #{@cloud_id} at #{devname}"
|
|
1015
1035
|
attachment = MU::Cloud::Google.compute(credentials: @config['credentials']).attach_disk(
|
|
1016
1036
|
@project_id,
|
|
1017
1037
|
@config['availability_zone'],
|
|
@@ -1028,6 +1048,113 @@ next if !create
|
|
|
1028
1048
|
true
|
|
1029
1049
|
end
|
|
1030
1050
|
|
|
1051
|
+
# Reverse-map our cloud description into a runnable config hash.
|
|
1052
|
+
# We assume that any values we have in +@config+ are placeholders, and
|
|
1053
|
+
# calculate our own accordingly based on what's live in the cloud.
|
|
1054
|
+
def toKitten(rootparent: nil, billing: nil, habitats: nil)
|
|
1055
|
+
bok = {
|
|
1056
|
+
"cloud" => "Google",
|
|
1057
|
+
"credentials" => @config['credentials'],
|
|
1058
|
+
"cloud_id" => @cloud_id,
|
|
1059
|
+
"project" => @project_id
|
|
1060
|
+
}
|
|
1061
|
+
if !cloud_desc
|
|
1062
|
+
MU.log "toKitten failed to load a cloud_desc from #{@cloud_id}", MU::ERR, details: @config
|
|
1063
|
+
return nil
|
|
1064
|
+
end
|
|
1065
|
+
bok['name'] = cloud_desc.name
|
|
1066
|
+
|
|
1067
|
+
# XXX we can have multiple network interfaces, and often do; need
|
|
1068
|
+
# language to account for this
|
|
1069
|
+
iface = cloud_desc.network_interfaces.first
|
|
1070
|
+
iface.network.match(/(?:^|\/)projects\/(.*?)\/.*?\/networks\/([^\/]+)(?:$|\/)/)
|
|
1071
|
+
vpc_proj = Regexp.last_match[1]
|
|
1072
|
+
vpc_id = Regexp.last_match[2]
|
|
1073
|
+
|
|
1074
|
+
bok['vpc'] = MU::Config::Ref.get(
|
|
1075
|
+
id: vpc_id,
|
|
1076
|
+
cloud: "Google",
|
|
1077
|
+
habitat: MU::Config::Ref.get(
|
|
1078
|
+
id: vpc_proj,
|
|
1079
|
+
cloud: "Google",
|
|
1080
|
+
credentials: @credentials,
|
|
1081
|
+
type: "habitats"
|
|
1082
|
+
),
|
|
1083
|
+
credentials: @credentials,
|
|
1084
|
+
type: "vpcs",
|
|
1085
|
+
subnet_id: iface.subnetwork.sub(/.*?\/([^\/]+)$/, '\1')
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
cloud_desc.disks.each { |disk|
|
|
1089
|
+
next if !disk.source
|
|
1090
|
+
disk.source.match(/\/projects\/([^\/]+)\/zones\/([^\/]+)\/disks\/(.*)/)
|
|
1091
|
+
proj = Regexp.last_match[1]
|
|
1092
|
+
az = Regexp.last_match[2]
|
|
1093
|
+
name = Regexp.last_match[3]
|
|
1094
|
+
begin
|
|
1095
|
+
disk_desc = MU::Cloud::Google.compute(credentials: @credentials).get_disk(proj, az, name)
|
|
1096
|
+
if disk_desc.source_image and disk.boot
|
|
1097
|
+
bok['image_id'] ||= disk_desc.source_image.sub(/^https:\/\/www\.googleapis\.com\/compute\/[^\/]+\//, '')
|
|
1098
|
+
else
|
|
1099
|
+
bok['storage'] ||= []
|
|
1100
|
+
storage_blob = {
|
|
1101
|
+
"size" => disk_desc.size_gb,
|
|
1102
|
+
"device" => "/dev/xvd"+(disk.index+97).chr.downcase
|
|
1103
|
+
}
|
|
1104
|
+
bok['storage'] << storage_blob
|
|
1105
|
+
end
|
|
1106
|
+
rescue ::Google::Apis::ClientError => e
|
|
1107
|
+
MU.log "Failed to retrieve disk #{name} attached to server #{@cloud_id} in #{proj}/#{az}", MU::WARN, details: e.message
|
|
1108
|
+
next
|
|
1109
|
+
end
|
|
1110
|
+
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
if cloud_desc.labels
|
|
1114
|
+
bok['tags'] = cloud_desc.labels.keys.map { |k| { "key" => k, "value" => cloud_desc.labels[k] } }
|
|
1115
|
+
end
|
|
1116
|
+
if cloud_desc.tags and cloud_desc.tags.items and cloud_desc.tags.items.size > 0
|
|
1117
|
+
bok['network_tags'] = cloud_desc.tags.items
|
|
1118
|
+
end
|
|
1119
|
+
bok['src_dst_check'] = !cloud_desc.can_ip_forward
|
|
1120
|
+
bok['size'] = cloud_desc.machine_type.sub(/.*?\/([^\/]+)$/, '\1')
|
|
1121
|
+
bok['project'] = @project_id
|
|
1122
|
+
if cloud_desc.service_accounts
|
|
1123
|
+
bok['scopes'] = cloud_desc.service_accounts.map { |sa| sa.scopes }.flatten.uniq
|
|
1124
|
+
end
|
|
1125
|
+
if cloud_desc.metadata and cloud_desc.metadata.items
|
|
1126
|
+
bok['metadata'] = cloud_desc.metadata.items.map { |m| MU.structToHash(m) }
|
|
1127
|
+
end
|
|
1128
|
+
|
|
1129
|
+
# Skip nodes that are just members of GKE clusters
|
|
1130
|
+
if bok['name'].match(/^gke-.*?-[a-f0-9]+-[a-z0-9]+$/) and
|
|
1131
|
+
bok['image_id'].match(/(:?^|\/)projects\/gke-node-images\//)
|
|
1132
|
+
found_gke_tag = false
|
|
1133
|
+
bok['network_tags'].each { |tag|
|
|
1134
|
+
if tag.match(/^gke-/)
|
|
1135
|
+
found_gke_tag = true
|
|
1136
|
+
break
|
|
1137
|
+
end
|
|
1138
|
+
}
|
|
1139
|
+
if found_gke_tag
|
|
1140
|
+
MU.log "Server #{bok['name']} appears to belong to a ContainerCluster, skipping adoption", MU::DEBUG
|
|
1141
|
+
return nil
|
|
1142
|
+
end
|
|
1143
|
+
end
|
|
1144
|
+
|
|
1145
|
+
if bok['metadata']
|
|
1146
|
+
bok['metadata'].each { |item|
|
|
1147
|
+
if item[:key] == "created-by" and item[:value].match(/\/instanceGroupManagers\//)
|
|
1148
|
+
MU.log "Server #{bok['name']} appears to belong to a ServerPool, skipping adoption", MU::DEBUG, details: item[:value]
|
|
1149
|
+
return nil
|
|
1150
|
+
end
|
|
1151
|
+
}
|
|
1152
|
+
end
|
|
1153
|
+
|
|
1154
|
+
|
|
1155
|
+
bok
|
|
1156
|
+
end
|
|
1157
|
+
|
|
1031
1158
|
# Does this resource type exist as a global (cloud-wide) artifact, or
|
|
1032
1159
|
# is it localized to a region/zone?
|
|
1033
1160
|
# @return [Boolean]
|
|
@@ -1048,6 +1175,7 @@ next if !create
|
|
|
1048
1175
|
# @return [void]
|
|
1049
1176
|
def self.cleanup(noop: false, ignoremaster: false, region: MU.curRegion, credentials: nil, flags: {})
|
|
1050
1177
|
flags["project"] ||= MU::Cloud::Google.defaultProject(credentials)
|
|
1178
|
+
return if !MU::Cloud::Google::Habitat.isLive?(flags["project"], credentials)
|
|
1051
1179
|
skipsnapshots = flags["skipsnapshots"]
|
|
1052
1180
|
onlycloud = flags["onlycloud"]
|
|
1053
1181
|
# XXX make damn sure MU.deploy_id is set
|
|
@@ -1105,51 +1233,140 @@ next if !create
|
|
|
1105
1233
|
def self.schema(config)
|
|
1106
1234
|
toplevel_required = []
|
|
1107
1235
|
schema = {
|
|
1108
|
-
"
|
|
1236
|
+
"roles" => MU::Cloud::Google::User.schema(config)[1]["roles"],
|
|
1237
|
+
"create_image" => {
|
|
1238
|
+
"properties" => {
|
|
1239
|
+
"family" => {
|
|
1240
|
+
"type" => "string",
|
|
1241
|
+
"description" => "Add a GCP image +family+ string to the created image(s)"
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
},
|
|
1245
|
+
"availability_zone" => {
|
|
1246
|
+
"type" => "string",
|
|
1247
|
+
"description" => "Target this instance to a specific Availability Zone"
|
|
1248
|
+
},
|
|
1249
|
+
"ssh_user" => {
|
|
1109
1250
|
"type" => "string",
|
|
1110
|
-
"description" => "
|
|
1251
|
+
"description" => "Account to use when connecting via ssh. Google Cloud images don't come with predefined remote access users, and some don't work with our usual default of +root+, so we recommend using some other (non-root) username.",
|
|
1252
|
+
"default" => "muadmin"
|
|
1253
|
+
},
|
|
1254
|
+
"network_tags" => {
|
|
1255
|
+
"type" => "array",
|
|
1256
|
+
"items" => {
|
|
1257
|
+
"type" => "string",
|
|
1258
|
+
"description" => "Add a network tag to this host, which can be used to selectively apply routes or firewall rules."
|
|
1259
|
+
}
|
|
1260
|
+
},
|
|
1261
|
+
"service_account" => MU::Config::Ref.schema(
|
|
1262
|
+
type: "users",
|
|
1263
|
+
desc: "An existing service account to use instead of the default one generated by Mu during the deployment process."
|
|
1264
|
+
),
|
|
1265
|
+
"metadata" => {
|
|
1266
|
+
"type" => "array",
|
|
1267
|
+
"items" => {
|
|
1268
|
+
"type" => "object",
|
|
1269
|
+
"description" => "Custom key-value pairs to be added to the metadata of Google Cloud virtual machines",
|
|
1270
|
+
"required" => ["key", "value"],
|
|
1271
|
+
"properties" => {
|
|
1272
|
+
"key" => {
|
|
1273
|
+
"type" => "string"
|
|
1274
|
+
},
|
|
1275
|
+
"value" => {
|
|
1276
|
+
"type" => "string"
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1111
1280
|
},
|
|
1112
1281
|
"routes" => {
|
|
1113
1282
|
"type" => "array",
|
|
1114
1283
|
"items" => MU::Config::VPC.routeschema
|
|
1284
|
+
},
|
|
1285
|
+
"scopes" => {
|
|
1286
|
+
"type" => "array",
|
|
1287
|
+
"items" => {
|
|
1288
|
+
"type" => "string",
|
|
1289
|
+
"description" => "API scopes to make available to this resource's service account."
|
|
1290
|
+
},
|
|
1291
|
+
"default" => ["https://www.googleapis.com/auth/compute.readonly", "https://www.googleapis.com/auth/logging.write", "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/monitoring.write", "https://www.googleapis.com/auth/devstorage.read_only"]
|
|
1115
1292
|
}
|
|
1116
1293
|
}
|
|
1117
1294
|
[toplevel_required, schema]
|
|
1118
1295
|
end
|
|
1119
1296
|
|
|
1297
|
+
@@instance_type_cache = {}
|
|
1298
|
+
|
|
1120
1299
|
# Confirm that the given instance size is valid for the given region.
|
|
1121
1300
|
# If someone accidentally specified an equivalent size from some other cloud provider, return something that makes sense. If nothing makes sense, return nil.
|
|
1122
1301
|
# @param size [String]: Instance type to check
|
|
1123
1302
|
# @param region [String]: Region to check against
|
|
1124
1303
|
# @return [String,nil]
|
|
1125
|
-
def self.validateInstanceType(size, region)
|
|
1126
|
-
|
|
1127
|
-
if
|
|
1304
|
+
def self.validateInstanceType(size, region, project: nil, credentials: nil)
|
|
1305
|
+
size = size.dup.to_s
|
|
1306
|
+
if @@instance_type_cache[project] and
|
|
1307
|
+
@@instance_type_cache[project][region] and
|
|
1308
|
+
@@instance_type_cache[project][region][size]
|
|
1309
|
+
return @@instance_type_cache[project][region][size]
|
|
1310
|
+
end
|
|
1311
|
+
|
|
1312
|
+
if size.match(/\/?custom-(\d+)-(\d+)(?:-ext)?$/)
|
|
1313
|
+
cpus = Regexp.last_match[1].to_i
|
|
1314
|
+
mem = Regexp.last_match[2].to_i
|
|
1315
|
+
ok = true
|
|
1316
|
+
if cpus < 1 or cpus > 32 or (cpus % 2 != 0 and cpus != 1)
|
|
1317
|
+
MU.log "Custom instance type #{size} illegal: CPU count must be 1 or an even number between 2 and 32", MU::ERR
|
|
1318
|
+
ok = false
|
|
1319
|
+
end
|
|
1320
|
+
if (mem % 256) != 0
|
|
1321
|
+
MU.log "Custom instance type #{size} illegal: Memory must be a multiple of 256 (MB)", MU::ERR
|
|
1322
|
+
ok = false
|
|
1323
|
+
end
|
|
1324
|
+
if ok
|
|
1325
|
+
return "custom-#{cpus.to_s}-#{mem.to_s}"
|
|
1326
|
+
else
|
|
1327
|
+
return nil
|
|
1328
|
+
end
|
|
1329
|
+
end
|
|
1330
|
+
|
|
1331
|
+
@@instance_type_cache[project] ||= {}
|
|
1332
|
+
@@instance_type_cache[project][region] ||= {}
|
|
1333
|
+
types = (MU::Cloud::Google.listInstanceTypes(region, project: project, credentials: credentials))[project][region]
|
|
1334
|
+
realsize = size.dup
|
|
1335
|
+
|
|
1336
|
+
if types and (realsize.nil? or !types.has_key?(realsize))
|
|
1128
1337
|
# See if it's a type we can approximate from one of the other clouds
|
|
1129
|
-
atypes = (MU::Cloud::AWS.listInstanceTypes)[MU::Cloud::AWS.myRegion]
|
|
1130
1338
|
foundmatch = false
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1339
|
+
MU::Cloud.availableClouds.each { |cloud|
|
|
1340
|
+
next if cloud == "Google"
|
|
1341
|
+
cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
|
|
1342
|
+
foreign_types = (cloudbase.listInstanceTypes)[cloudbase.myRegion]
|
|
1343
|
+
if foreign_types and foreign_types.size > 0 and foreign_types.has_key?(size)
|
|
1344
|
+
vcpu = foreign_types[size]["vcpu"]
|
|
1345
|
+
mem = foreign_types[size]["memory"]
|
|
1346
|
+
ecu = foreign_types[size]["ecu"]
|
|
1347
|
+
types.keys.sort.reverse.each { |type|
|
|
1348
|
+
features = types[type]
|
|
1349
|
+
next if ecu == "Variable" and ecu != features["ecu"]
|
|
1350
|
+
next if features["vcpu"] != vcpu
|
|
1351
|
+
if (features["memory"] - mem.to_f).abs < 0.10*mem
|
|
1352
|
+
foundmatch = true
|
|
1353
|
+
MU.log "You specified #{cloud} instance type '#{realsize}.' Approximating with Google Compute type '#{type}.'", MU::WARN
|
|
1354
|
+
realsize = type
|
|
1355
|
+
break
|
|
1356
|
+
end
|
|
1357
|
+
}
|
|
1358
|
+
end
|
|
1359
|
+
break if foundmatch
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1147
1362
|
if !foundmatch
|
|
1148
|
-
MU.log "Invalid size '#{
|
|
1363
|
+
MU.log "Invalid size '#{realsize}' for Google Compute instance in #{region} (checked project #{project}). Supported types:", MU::ERR, details: types.keys.sort.join(", ")
|
|
1364
|
+
@@instance_type_cache[project][region][size] = nil
|
|
1149
1365
|
return nil
|
|
1150
1366
|
end
|
|
1151
1367
|
end
|
|
1152
|
-
size
|
|
1368
|
+
@@instance_type_cache[project][region][size] = realsize
|
|
1369
|
+
@@instance_type_cache[project][region][size]
|
|
1153
1370
|
end
|
|
1154
1371
|
|
|
1155
1372
|
|
|
@@ -1160,17 +1377,57 @@ next if !create
|
|
|
1160
1377
|
def self.validateConfig(server, configurator)
|
|
1161
1378
|
ok = true
|
|
1162
1379
|
|
|
1163
|
-
server['
|
|
1164
|
-
|
|
1380
|
+
server['project'] ||= MU::Cloud::Google.defaultProject(server['credentials'])
|
|
1381
|
+
size = validateInstanceType(server["size"], server["region"], project: server['project'], credentials: server['credentials'])
|
|
1382
|
+
|
|
1383
|
+
if size.nil?
|
|
1384
|
+
MU.log "Failed to verify instance size #{server["size"]} for Server #{server['name']}", MU::WARN
|
|
1385
|
+
else
|
|
1386
|
+
server["size"] = size
|
|
1387
|
+
end
|
|
1165
1388
|
|
|
1166
1389
|
# If we're not targeting an availability zone, pick one randomly
|
|
1167
1390
|
if !server['availability_zone']
|
|
1168
1391
|
server['availability_zone'] = MU::Cloud::Google.listAZs(server['region']).sample
|
|
1169
1392
|
end
|
|
1170
1393
|
|
|
1394
|
+
if server['service_account']
|
|
1395
|
+
server['service_account']['cloud'] = "Google"
|
|
1396
|
+
server['service_account']['habitat'] ||= server['project']
|
|
1397
|
+
found = MU::Config::Ref.get(server['service_account'])
|
|
1398
|
+
if found.id and !found.kitten
|
|
1399
|
+
MU.log "GKE server #{server['name']} failed to locate service account #{server['service_account']} in project #{server['project']}", MU::ERR
|
|
1400
|
+
ok = false
|
|
1401
|
+
end
|
|
1402
|
+
else
|
|
1403
|
+
user = {
|
|
1404
|
+
"name" => server['name'],
|
|
1405
|
+
"cloud" => "Google",
|
|
1406
|
+
"project" => server["project"],
|
|
1407
|
+
"credentials" => server["credentials"],
|
|
1408
|
+
"type" => "service"
|
|
1409
|
+
}
|
|
1410
|
+
if server['roles']
|
|
1411
|
+
user['roles'] = server['roles'].dup
|
|
1412
|
+
end
|
|
1413
|
+
configurator.insertKitten(user, "users", true)
|
|
1414
|
+
server['dependencies'] ||= []
|
|
1415
|
+
server['service_account'] = MU::Config::Ref.get(
|
|
1416
|
+
type: "users",
|
|
1417
|
+
cloud: "Google",
|
|
1418
|
+
name: server["name"],
|
|
1419
|
+
project: server["project"],
|
|
1420
|
+
credentials: server["credentials"]
|
|
1421
|
+
)
|
|
1422
|
+
server['dependencies'] << {
|
|
1423
|
+
"type" => "user",
|
|
1424
|
+
"name" => server["name"]
|
|
1425
|
+
}
|
|
1426
|
+
end
|
|
1427
|
+
|
|
1171
1428
|
subnets = nil
|
|
1172
1429
|
if !server['vpc']
|
|
1173
|
-
vpcs = MU::Cloud::Google::VPC.find
|
|
1430
|
+
vpcs = MU::Cloud::Google::VPC.find(credentials: server['credentials'])
|
|
1174
1431
|
if vpcs["default"]
|
|
1175
1432
|
server["vpc"] ||= {}
|
|
1176
1433
|
server["vpc"]["vpc_id"] = vpcs["default"].self_link
|
|
@@ -1202,8 +1459,9 @@ next if !create
|
|
|
1202
1459
|
end
|
|
1203
1460
|
|
|
1204
1461
|
if server['image_id'].nil?
|
|
1205
|
-
|
|
1206
|
-
|
|
1462
|
+
img_id = MU::Cloud.getStockImage("Google", platform: server['platform'])
|
|
1463
|
+
if img_id
|
|
1464
|
+
server['image_id'] = configurator.getTail("server"+server['name']+"Image", value: img_id, prettyname: "server"+server['name']+"Image", cloudtype: "Google::Apis::ComputeV1::Image")
|
|
1207
1465
|
else
|
|
1208
1466
|
MU.log "No image specified for #{server['name']} and no default available for platform #{server['platform']}", MU::ERR, details: server
|
|
1209
1467
|
ok = false
|
|
@@ -1214,7 +1472,6 @@ next if !create
|
|
|
1214
1472
|
begin
|
|
1215
1473
|
real_image = MU::Cloud::Google::Server.fetchImage(server['image_id'].to_s, credentials: server['credentials'])
|
|
1216
1474
|
rescue ::Google::Apis::ClientError => e
|
|
1217
|
-
MU.log e.inspect, MU::WARN
|
|
1218
1475
|
end
|
|
1219
1476
|
|
|
1220
1477
|
if real_image.nil?
|
|
@@ -1226,31 +1483,38 @@ next if !create
|
|
|
1226
1483
|
img_project = Regexp.last_match[1]
|
|
1227
1484
|
img_name = Regexp.last_match[2]
|
|
1228
1485
|
begin
|
|
1486
|
+
img = MU::Cloud::Google.compute(credentials: server['credentials']).get_image(img_project, img_name)
|
|
1229
1487
|
snaps = MU::Cloud::Google.compute(credentials: server['credentials']).list_snapshots(
|
|
1230
1488
|
img_project,
|
|
1231
1489
|
filter: "name eq #{img_name}-.*"
|
|
1232
1490
|
)
|
|
1233
1491
|
server['storage'] ||= []
|
|
1234
1492
|
used_devs = server['storage'].map { |disk| disk['device'].gsub(/.*?\//, "") }
|
|
1235
|
-
snaps.items
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1493
|
+
if snaps and snaps.items
|
|
1494
|
+
snaps.items.each { |snap|
|
|
1495
|
+
next if !snap.labels.is_a?(Hash) or !snap.labels["mu-device-name"] or snap.labels["mu-parent-image"] != img_name
|
|
1496
|
+
devname = snap.labels["mu-device-name"]
|
|
1497
|
+
|
|
1498
|
+
if used_devs.include?(devname)
|
|
1499
|
+
MU.log "Device name #{devname} already declared in server #{server['name']} (snapshot #{snap.name} wants the name)", MU::ERR
|
|
1500
|
+
ok = false
|
|
1501
|
+
end
|
|
1502
|
+
server['storage'] << {
|
|
1503
|
+
"snapshot_id" => snap.self_link,
|
|
1504
|
+
"size" => snap.disk_size_gb,
|
|
1505
|
+
"delete_on_termination" => true,
|
|
1506
|
+
"device" => devname
|
|
1507
|
+
}
|
|
1508
|
+
used_devs << devname
|
|
1248
1509
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1510
|
+
if snaps.items.size > 0
|
|
1511
|
+
# MU.log img_name, MU::WARN, details: snaps.items
|
|
1512
|
+
end
|
|
1513
|
+
end
|
|
1251
1514
|
rescue ::Google::Apis::ClientError => e
|
|
1252
1515
|
# it's ok, sometimes we don't have permission to list snapshots
|
|
1253
1516
|
# in other peoples' projects
|
|
1517
|
+
# MU.log img_name, MU::WARN, details: img
|
|
1254
1518
|
raise e if !e.message.match(/^forbidden: /)
|
|
1255
1519
|
end
|
|
1256
1520
|
end
|