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
data/modules/mu/cloud.rb
CHANGED
|
@@ -39,12 +39,40 @@ module MU
|
|
|
39
39
|
class MuCloudFlagNotImplemented < StandardError;
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
# Exception we throw when we attempt to make an API call against a project
|
|
43
|
+
# that is already deleted.
|
|
44
|
+
class MuDefunctHabitat < StandardError;
|
|
45
|
+
end
|
|
46
|
+
|
|
42
47
|
# Methods which a cloud resource implementation, e.g. Server, must implement
|
|
43
48
|
generic_class_methods = [:find, :cleanup, :validateConfig, :schema, :isGlobal?]
|
|
44
49
|
generic_instance_methods = [:create, :notify, :mu_name, :cloud_id, :config]
|
|
45
50
|
|
|
46
51
|
# Class methods which the base of a cloud implementation must implement
|
|
47
|
-
generic_class_methods_toplevel = [:required_instance_methods, :myRegion, :listRegions, :listAZs, :hosted?, :hosted_config, :config_example, :writeDeploySecret, :listCredentials, :credConfig, :listInstanceTypes, :adminBucketName, :adminBucketUrl]
|
|
52
|
+
generic_class_methods_toplevel = [:required_instance_methods, :myRegion, :listRegions, :listAZs, :hosted?, :hosted_config, :config_example, :writeDeploySecret, :listCredentials, :credConfig, :listInstanceTypes, :adminBucketName, :adminBucketUrl, :habitat]
|
|
53
|
+
|
|
54
|
+
# Public attributes which will be available on all instantiated cloud resource objects
|
|
55
|
+
#
|
|
56
|
+
# +:config+: The fully-resolved {MU::Config} hash describing the object, aka the Basket of Kittens entry
|
|
57
|
+
#
|
|
58
|
+
# +:mu_name+: The unique internal name of the object, if one already exists
|
|
59
|
+
#
|
|
60
|
+
# +:cloud+: The cloud in which this object is resident
|
|
61
|
+
#
|
|
62
|
+
# +:cloud_id+: The cloud provider's official identifier for this object
|
|
63
|
+
#
|
|
64
|
+
# +:environment+: The declared environment string for the deployment of which this object is a member
|
|
65
|
+
#
|
|
66
|
+
# +:deploy:+ The {MU::MommaCat} object representing the deployment of which this object is a member
|
|
67
|
+
#
|
|
68
|
+
# +:deploy_id:+ The unique string which identifies the deployment of which this object is a member
|
|
69
|
+
#
|
|
70
|
+
# +:deploydata:+ A Hash containing all metadata reported by resources in this deploy method, via their +notify+ methods
|
|
71
|
+
#
|
|
72
|
+
# +:appname:+ The declared application name of this deployment
|
|
73
|
+
#
|
|
74
|
+
# +:credentials:+ The name of the cloud provider credential set from +mu.yaml+ which is used to manage this object
|
|
75
|
+
PUBLIC_ATTRS = [:config, :mu_name, :cloud, :cloud_id, :environment, :deploy, :deploy_id, :deploydata, :appname, :credentials]
|
|
48
76
|
|
|
49
77
|
# Initialize empty classes for each of these. We'll fill them with code
|
|
50
78
|
# later; we're doing this here because otherwise the parser yells about
|
|
@@ -161,7 +189,7 @@ module MU
|
|
|
161
189
|
:interface => self.const_get("Habitat"),
|
|
162
190
|
:deps_wait_on_my_creation => true,
|
|
163
191
|
:waits_on_parent_completion => true,
|
|
164
|
-
:class => generic_class_methods,
|
|
192
|
+
:class => generic_class_methods + [:isLive?],
|
|
165
193
|
:instance => generic_instance_methods + [:groom]
|
|
166
194
|
},
|
|
167
195
|
:Collection => {
|
|
@@ -217,7 +245,7 @@ module MU
|
|
|
217
245
|
:deps_wait_on_my_creation => true,
|
|
218
246
|
:waits_on_parent_completion => false,
|
|
219
247
|
:class => generic_class_methods,
|
|
220
|
-
:instance => generic_instance_methods + [:registerNode]
|
|
248
|
+
:instance => generic_instance_methods + [:groom, :registerNode]
|
|
221
249
|
},
|
|
222
250
|
:Server => {
|
|
223
251
|
:has_multiples => true,
|
|
@@ -227,8 +255,8 @@ module MU
|
|
|
227
255
|
:interface => self.const_get("Server"),
|
|
228
256
|
:deps_wait_on_my_creation => false,
|
|
229
257
|
:waits_on_parent_completion => false,
|
|
230
|
-
:class => generic_class_methods + [:validateInstanceType],
|
|
231
|
-
:instance => generic_instance_methods + [:groom, :postBoot, :getSSHConfig, :canonicalIP, :getWindowsAdminPassword, :active?, :groomer, :mu_windows_name, :mu_windows_name=, :reboot, :addVolume]
|
|
258
|
+
:class => generic_class_methods + [:validateInstanceType, :imageTimeStamp],
|
|
259
|
+
:instance => generic_instance_methods + [:groom, :postBoot, :getSSHConfig, :canonicalIP, :getWindowsAdminPassword, :active?, :groomer, :mu_windows_name, :mu_windows_name=, :reboot, :addVolume, :genericNAT]
|
|
232
260
|
},
|
|
233
261
|
:ServerPool => {
|
|
234
262
|
:has_multiples => false,
|
|
@@ -403,8 +431,8 @@ module MU
|
|
|
403
431
|
:interface => self.const_get("Bucket"),
|
|
404
432
|
:deps_wait_on_my_creation => true,
|
|
405
433
|
:waits_on_parent_completion => true,
|
|
406
|
-
:class => generic_class_methods,
|
|
407
|
-
:instance => generic_instance_methods + [:groom]
|
|
434
|
+
:class => generic_class_methods + [:upload],
|
|
435
|
+
:instance => generic_instance_methods + [:groom, :upload]
|
|
408
436
|
},
|
|
409
437
|
:NoSQLDB => {
|
|
410
438
|
:has_multiples => false,
|
|
@@ -419,6 +447,195 @@ module MU
|
|
|
419
447
|
}
|
|
420
448
|
}.freeze
|
|
421
449
|
|
|
450
|
+
# The public AWS S3 bucket where we expect to find YAML files listing our
|
|
451
|
+
# standard base images for various platforms.
|
|
452
|
+
BASE_IMAGE_BUCKET = "cloudamatic"
|
|
453
|
+
# The path in the AWS S3 bucket where we expect to find YAML files listing
|
|
454
|
+
# our standard base images for various platforms.
|
|
455
|
+
BASE_IMAGE_PATH = "/images"
|
|
456
|
+
|
|
457
|
+
# Aliases for platform names, in case we don't have actual images built for
|
|
458
|
+
# them.
|
|
459
|
+
PLATFORM_ALIASES = {
|
|
460
|
+
"linux" => "centos7",
|
|
461
|
+
"windows" => "win2k12r2",
|
|
462
|
+
"win2k12" => "win2k12r2",
|
|
463
|
+
"ubuntu" => "ubuntu16",
|
|
464
|
+
"centos" => "centos7",
|
|
465
|
+
"rhel7" => "rhel71",
|
|
466
|
+
"rhel" => "rhel71",
|
|
467
|
+
"amazon" => "amazon2016"
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
@@image_fetch_cache = {}
|
|
471
|
+
@@platform_cache = []
|
|
472
|
+
@@image_fetch_semaphore = Mutex.new
|
|
473
|
+
|
|
474
|
+
# Rifle our image lists from {MU::Cloud.getStockImage} and return a list
|
|
475
|
+
# of valid +platform+ names.
|
|
476
|
+
# @return [Array<String>]
|
|
477
|
+
def self.listPlatforms
|
|
478
|
+
return @@platform_cache if @@platform_cache and !@@platform_cache.empty?
|
|
479
|
+
@@platform_cache = MU::Cloud.supportedClouds.map { |cloud|
|
|
480
|
+
begin
|
|
481
|
+
loadCloudType(cloud, :Server)
|
|
482
|
+
rescue MU::Cloud::MuCloudResourceNotImplemented, MU::MuError => e
|
|
483
|
+
next
|
|
484
|
+
end
|
|
485
|
+
|
|
486
|
+
images = MU::Cloud.getStockImage(cloud, quiet: true)
|
|
487
|
+
if images
|
|
488
|
+
images.keys
|
|
489
|
+
else
|
|
490
|
+
nil
|
|
491
|
+
end
|
|
492
|
+
}.flatten.uniq
|
|
493
|
+
@@platform_cache.delete(nil)
|
|
494
|
+
@@platform_cache.sort
|
|
495
|
+
@@platform_cache
|
|
496
|
+
end
|
|
497
|
+
|
|
498
|
+
# Locate a base image for a {MU::Cloud::Server} resource. First we check
|
|
499
|
+
# Mu's public bucket, which should list the latest and greatest. If we can't
|
|
500
|
+
# fetch that, then we fall back to a YAML file that's bundled as part of Mu,
|
|
501
|
+
# but which will typically be less up-to-date.
|
|
502
|
+
# @param cloud [String]: The cloud provider for which to return an image list
|
|
503
|
+
# @param platform [String]: The supported platform for which to return an image or images. If not specified, we'll return our entire library for the appropriate cloud provider.
|
|
504
|
+
# @param region [String]: The region for which the returned image or images should be supported, for cloud providers which require it (such as AWS).
|
|
505
|
+
# @param fail_hard [Boolean]: Raise an exception on most errors, such as an inability to reach our public listing, lack of matching images, etc.
|
|
506
|
+
# @return [Hash,String,nil]
|
|
507
|
+
def self.getStockImage(cloud = MU::Config.defaultCloud, platform: nil, region: nil, fail_hard: false, quiet: false)
|
|
508
|
+
|
|
509
|
+
if !MU::Cloud.supportedClouds.include?(cloud)
|
|
510
|
+
MU.log "'#{cloud}' is not a supported cloud provider! Available providers:", MU::ERR, details: MU::Cloud.supportedClouds
|
|
511
|
+
raise MuError, "'#{cloud}' is not a supported cloud provider!"
|
|
512
|
+
end
|
|
513
|
+
|
|
514
|
+
urls = ["http://"+BASE_IMAGE_BUCKET+".s3-website-us-east-1.amazonaws.com"+BASE_IMAGE_PATH]
|
|
515
|
+
if $MU_CFG and $MU_CFG['custom_images_url']
|
|
516
|
+
urls << $MU_CFG['custom_images_url']
|
|
517
|
+
end
|
|
518
|
+
|
|
519
|
+
images = nil
|
|
520
|
+
urls.each { |base_url|
|
|
521
|
+
@@image_fetch_semaphore.synchronize {
|
|
522
|
+
if @@image_fetch_cache[cloud] and (Time.now - @@image_fetch_cache[cloud]['time']) < 30
|
|
523
|
+
images = @@image_fetch_cache[cloud]['contents'].dup
|
|
524
|
+
else
|
|
525
|
+
begin
|
|
526
|
+
Timeout.timeout(2) do
|
|
527
|
+
response = open("#{base_url}/#{cloud}.yaml").read
|
|
528
|
+
images ||= {}
|
|
529
|
+
images.deep_merge!(YAML.load(response))
|
|
530
|
+
break
|
|
531
|
+
end
|
|
532
|
+
rescue Exception => e
|
|
533
|
+
if fail_hard
|
|
534
|
+
raise MuError, "Failed to fetch stock images from #{base_url}/#{cloud}.yaml (#{e.message})"
|
|
535
|
+
else
|
|
536
|
+
MU.log "Failed to fetch stock images from #{base_url}/#{cloud}.yaml (#{e.message})", MU::WARN if !quiet
|
|
537
|
+
end
|
|
538
|
+
end
|
|
539
|
+
end
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
@@image_fetch_semaphore.synchronize {
|
|
544
|
+
@@image_fetch_cache[cloud] = {
|
|
545
|
+
'contents' => images.dup,
|
|
546
|
+
'time' => Time.now
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
backwards_compat = {
|
|
551
|
+
"AWS" => "amazon_images",
|
|
552
|
+
"Google" => "google_images",
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
# Load from inside our repository, if we didn't get images elsewise
|
|
556
|
+
if images.nil?
|
|
557
|
+
[backwards_compat[cloud], cloud].each { |file|
|
|
558
|
+
next if file.nil?
|
|
559
|
+
if File.exist?("#{MU.myRoot}/modules/mu/defaults/#{file}.yaml")
|
|
560
|
+
images = YAML.load(File.read("#{MU.myRoot}/modules/mu/defaults/#{file}.yaml"))
|
|
561
|
+
break
|
|
562
|
+
end
|
|
563
|
+
}
|
|
564
|
+
end
|
|
565
|
+
|
|
566
|
+
# Now overlay local overrides, both of the systemwide (/opt/mu/etc) and
|
|
567
|
+
# per-user (~/.mu/etc) variety.
|
|
568
|
+
[backwards_compat[cloud], cloud].each { |file|
|
|
569
|
+
next if file.nil?
|
|
570
|
+
if File.exist?("#{MU.etcDir}/#{file}.yaml")
|
|
571
|
+
images ||= {}
|
|
572
|
+
images.deep_merge!(YAML.load(File.read("#{MU.etcDir}/#{file}.yaml")))
|
|
573
|
+
end
|
|
574
|
+
if Process.uid != 0
|
|
575
|
+
basepath = Etc.getpwuid(Process.uid).dir+"/.mu/etc"
|
|
576
|
+
if File.exist?("#{basepath}/#{file}.yaml")
|
|
577
|
+
images ||= {}
|
|
578
|
+
images.deep_merge!(YAML.load(File.read("#{basepath}/#{file}.yaml")))
|
|
579
|
+
end
|
|
580
|
+
end
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if images.nil?
|
|
584
|
+
if fail_hard
|
|
585
|
+
raise MuError, "Failed to find any base images for #{cloud}"
|
|
586
|
+
else
|
|
587
|
+
MU.log "Failed to find any base images for #{cloud}", MU::WARN if !quiet
|
|
588
|
+
return nil
|
|
589
|
+
end
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
PLATFORM_ALIASES.each_pair { |a, t|
|
|
593
|
+
if images[t] and !images[a]
|
|
594
|
+
images[a] = images[t]
|
|
595
|
+
end
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if platform
|
|
599
|
+
if !images[platform]
|
|
600
|
+
if fail_hard
|
|
601
|
+
raise MuError, "No base image for platform #{platform} in cloud #{cloud}"
|
|
602
|
+
else
|
|
603
|
+
MU.log "No base image for platform #{platform} in cloud #{cloud}", MU::WARN if !quiet
|
|
604
|
+
return nil
|
|
605
|
+
end
|
|
606
|
+
end
|
|
607
|
+
images = images[platform]
|
|
608
|
+
|
|
609
|
+
if region
|
|
610
|
+
# We won't fuss about the region argument if this isn't a cloud that
|
|
611
|
+
# has regions, just quietly don't bother.
|
|
612
|
+
if images.is_a?(Hash)
|
|
613
|
+
if images[region]
|
|
614
|
+
images = images[region]
|
|
615
|
+
else
|
|
616
|
+
if fail_hard
|
|
617
|
+
raise MuError, "No base image for platform #{platform} in cloud #{cloud} region #{region} found"
|
|
618
|
+
else
|
|
619
|
+
MU.log "No base image for platform #{platform} in cloud #{cloud} region #{region} found", MU::WARN if !quiet
|
|
620
|
+
return nil
|
|
621
|
+
end
|
|
622
|
+
end
|
|
623
|
+
end
|
|
624
|
+
end
|
|
625
|
+
else
|
|
626
|
+
if region
|
|
627
|
+
images.each_pair { |p, regions|
|
|
628
|
+
# Filter to match our requested region, but for all the platforms,
|
|
629
|
+
# since we didn't specify one.
|
|
630
|
+
if regions.is_a?(Hash)
|
|
631
|
+
regions.delete_if { |r| r != region }
|
|
632
|
+
end
|
|
633
|
+
}
|
|
634
|
+
end
|
|
635
|
+
end
|
|
636
|
+
|
|
637
|
+
images
|
|
638
|
+
end
|
|
422
639
|
|
|
423
640
|
# A list of supported cloud resource types as Mu classes
|
|
424
641
|
def self.resource_types;
|
|
@@ -429,6 +646,7 @@ module MU
|
|
|
429
646
|
# @param type [String]: A string that looks like our short or full class name or singular or plural configuration names.
|
|
430
647
|
# @return [Array]: Class name (Symbol), singular config name (String), plural config name (String), full class name (Object)
|
|
431
648
|
def self.getResourceNames(type)
|
|
649
|
+
return [nil, nil, nil, nil, {}] if !type
|
|
432
650
|
@@resource_types.each_pair { |name, cloudclass|
|
|
433
651
|
if name == type.to_sym or
|
|
434
652
|
cloudclass[:cfg_name] == type or
|
|
@@ -462,10 +680,28 @@ module MU
|
|
|
462
680
|
@@supportedCloudList = ['AWS', 'CloudFormation', 'Google', 'Azure']
|
|
463
681
|
|
|
464
682
|
# List of known/supported Cloud providers
|
|
683
|
+
# @return [Array<String>]
|
|
465
684
|
def self.supportedClouds
|
|
466
685
|
@@supportedCloudList
|
|
467
686
|
end
|
|
468
687
|
|
|
688
|
+
# List of known/supported Cloud providers for which we have at least one
|
|
689
|
+
# set of credentials configured.
|
|
690
|
+
# @return [Array<String>]
|
|
691
|
+
def self.availableClouds
|
|
692
|
+
available = []
|
|
693
|
+
MU::Cloud.supportedClouds.each { |cloud|
|
|
694
|
+
begin
|
|
695
|
+
cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
|
|
696
|
+
next if cloudbase.listCredentials.nil? or cloudbase.listCredentials.empty?
|
|
697
|
+
available << cloud
|
|
698
|
+
rescue NameError
|
|
699
|
+
end
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
available
|
|
703
|
+
end
|
|
704
|
+
|
|
469
705
|
# Load the container class for each cloud we know about, and inject autoload
|
|
470
706
|
# code for each of its supported resource type classes.
|
|
471
707
|
failed = []
|
|
@@ -495,7 +731,7 @@ module MU
|
|
|
495
731
|
# @param template_variables [Hash]: A list of variable substitutions to pass as globals to the ERB parser when loading the userdata script.
|
|
496
732
|
# @param custom_append [String]: Arbitrary extra code to append to our default userdata behavior.
|
|
497
733
|
# @return [String]
|
|
498
|
-
def self.fetchUserdata(platform: "linux", template_variables: {}, custom_append: nil, cloud: "
|
|
734
|
+
def self.fetchUserdata(platform: "linux", template_variables: {}, custom_append: nil, cloud: "AWS", scrub_mu_isms: false, credentials: nil)
|
|
499
735
|
return nil if platform.nil? or platform.empty?
|
|
500
736
|
userdata_mutex.synchronize {
|
|
501
737
|
script = ""
|
|
@@ -503,10 +739,16 @@ module MU
|
|
|
503
739
|
if template_variables.nil? or !template_variables.is_a?(Hash)
|
|
504
740
|
raise MuError, "My second argument should be a hash of variables to pass into ERB templates"
|
|
505
741
|
end
|
|
742
|
+
template_variables["credentials"] ||= credentials
|
|
506
743
|
$mu = OpenStruct.new(template_variables)
|
|
507
|
-
userdata_dir = File.expand_path(MU.myRoot+"/modules/mu/clouds/#{cloud}/userdata")
|
|
508
|
-
|
|
509
|
-
platform =
|
|
744
|
+
userdata_dir = File.expand_path(MU.myRoot+"/modules/mu/clouds/#{cloud.downcase}/userdata")
|
|
745
|
+
|
|
746
|
+
platform = if %w{win2k12r2 win2k12 win2k8 win2k8r2 win2k16 windows win2k19}.include?(platform)
|
|
747
|
+
"windows"
|
|
748
|
+
else
|
|
749
|
+
"linux"
|
|
750
|
+
end
|
|
751
|
+
|
|
510
752
|
erbfile = "#{userdata_dir}/#{platform}.erb"
|
|
511
753
|
if !File.exist?(erbfile)
|
|
512
754
|
MU.log "No such userdata template '#{erbfile}'", MU::WARN, details: caller
|
|
@@ -590,12 +832,12 @@ module MU
|
|
|
590
832
|
}
|
|
591
833
|
@@resource_types[type.to_sym][:instance].each { |instance_method|
|
|
592
834
|
if !myclass.public_instance_methods.include?(instance_method)
|
|
593
|
-
raise
|
|
835
|
+
raise MuCloudResourceNotImplemented, "MU::Cloud::#{cloud}::#{type} has not implemented required instance method #{instance_method}"
|
|
594
836
|
end
|
|
595
837
|
}
|
|
596
838
|
cloudclass.required_instance_methods.each { |instance_method|
|
|
597
839
|
if !myclass.public_instance_methods.include?(instance_method)
|
|
598
|
-
|
|
840
|
+
MU.log "MU::Cloud::#{cloud}::#{type} has not implemented required instance method #{instance_method}, will declare as attr_accessor", MU::DEBUG
|
|
599
841
|
end
|
|
600
842
|
}
|
|
601
843
|
|
|
@@ -603,7 +845,7 @@ module MU
|
|
|
603
845
|
return myclass
|
|
604
846
|
rescue NameError => e
|
|
605
847
|
@cloud_class_cache[cloud][type] = nil
|
|
606
|
-
raise
|
|
848
|
+
raise MuCloudResourceNotImplemented, "The '#{type}' resource is not supported in cloud #{cloud} (tried MU::#{cloud}::#{type})", e.backtrace
|
|
607
849
|
end
|
|
608
850
|
end
|
|
609
851
|
|
|
@@ -624,20 +866,9 @@ module MU
|
|
|
624
866
|
|
|
625
867
|
@@resource_types.each_pair { |name, attrs|
|
|
626
868
|
Object.const_get("MU").const_get("Cloud").const_get(name).class_eval {
|
|
627
|
-
attr_reader :cloud
|
|
628
|
-
attr_reader :environment
|
|
629
869
|
attr_reader :cloudclass
|
|
630
870
|
attr_reader :cloudobj
|
|
631
|
-
attr_reader :deploy_id
|
|
632
|
-
attr_reader :mu_name
|
|
633
|
-
attr_reader :cloud_id
|
|
634
|
-
attr_reader :credentials
|
|
635
|
-
attr_reader :url
|
|
636
|
-
attr_reader :config
|
|
637
|
-
attr_reader :deploydata
|
|
638
871
|
attr_reader :destroyed
|
|
639
|
-
attr_reader :cfm_template
|
|
640
|
-
attr_reader :cfm_name
|
|
641
872
|
attr_reader :delayed_save
|
|
642
873
|
|
|
643
874
|
def self.shortname
|
|
@@ -668,11 +899,6 @@ module MU
|
|
|
668
899
|
MU::Cloud.resource_types[shortname.to_sym][:deps_wait_on_my_creation]
|
|
669
900
|
end
|
|
670
901
|
|
|
671
|
-
def groomer
|
|
672
|
-
return @cloudobj.groomer if !@cloudobj.nil?
|
|
673
|
-
nil
|
|
674
|
-
end
|
|
675
|
-
|
|
676
902
|
# Print something palatable when we're called in a string context.
|
|
677
903
|
def to_s
|
|
678
904
|
fullname = "#{self.class.shortname}"
|
|
@@ -688,86 +914,232 @@ module MU
|
|
|
688
914
|
return fullname
|
|
689
915
|
end
|
|
690
916
|
|
|
917
|
+
# Set our +deploy+ and +deploy_id+ attributes, optionally doing so even
|
|
918
|
+
# if they have already been set.
|
|
919
|
+
#
|
|
920
|
+
# @param mommacat [MU::MommaCat]: The deploy to which we're being told we belong
|
|
921
|
+
# @param force [Boolean]: Set even if we already have a deploy object
|
|
922
|
+
# @return [String]: Our new +deploy_id+
|
|
923
|
+
def intoDeploy(mommacat, force: false)
|
|
924
|
+
if force or (!@deploy)
|
|
925
|
+
MU.log "Inserting #{self} (#{self.object_id}) into #{mommacat.deploy_id}", MU::DEBUG
|
|
926
|
+
@deploy = mommacat
|
|
927
|
+
@deploy_id = @deploy.deploy_id
|
|
928
|
+
@cloudobj.intoDeploy(mommacat, force: force) if @cloudobj
|
|
929
|
+
end
|
|
930
|
+
@deploy_id
|
|
931
|
+
end
|
|
691
932
|
|
|
692
933
|
# @param mommacat [MU::MommaCat]: The deployment containing this cloud resource
|
|
693
934
|
# @param mu_name [String]: Optional- specify the full Mu resource name of an existing resource to load, instead of creating a new one
|
|
694
935
|
# @param cloud_id [String]: Optional- specify the cloud provider's identifier for an existing resource to load, instead of creating a new one
|
|
695
936
|
# @param kitten_cfg [Hash]: The parse configuration for this object from {MU::Config}
|
|
696
|
-
def initialize(
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
937
|
+
def initialize(**args)
|
|
938
|
+
raise MuError, "Cannot invoke Cloud objects without a configuration" if args[:kitten_cfg].nil?
|
|
939
|
+
|
|
940
|
+
# We are a parent wrapper object. Initialize our child object and
|
|
941
|
+
# housekeeping bits accordingly.
|
|
942
|
+
if self.class.name.match(/^MU::Cloud::([^:]+)$/)
|
|
943
|
+
@live = true
|
|
944
|
+
@delayed_save = args[:delayed_save]
|
|
945
|
+
@method_semaphore = Mutex.new
|
|
946
|
+
@method_locks = {}
|
|
947
|
+
if args[:mommacat]
|
|
948
|
+
MU.log "Initializing an instance of #{self.class.name} in #{args[:mommacat].deploy_id} #{mu_name}", MU::DEBUG, details: args[:kitten_cfg]
|
|
949
|
+
elsif args[:mu_name].nil?
|
|
950
|
+
raise MuError, "Can't instantiate a MU::Cloud object with a live deploy or giving us a mu_name"
|
|
951
|
+
else
|
|
952
|
+
MU.log "Initializing a detached #{self.class.name} named #{args[:mu_name]}", MU::DEBUG, details: args[:kitten_cfg]
|
|
953
|
+
end
|
|
710
954
|
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
955
|
+
my_cloud = args[:kitten_cfg]['cloud'].to_s || MU::Config.defaultCloud
|
|
956
|
+
if my_cloud.nil? or !MU::Cloud.supportedClouds.include?(my_cloud)
|
|
957
|
+
raise MuError, "Can't instantiate a MU::Cloud object without a valid cloud (saw '#{my_cloud}')"
|
|
958
|
+
end
|
|
959
|
+
|
|
960
|
+
@cloudclass = MU::Cloud.loadCloudType(my_cloud, self.class.shortname)
|
|
961
|
+
@cloudparentclass = Object.const_get("MU").const_get("Cloud").const_get(my_cloud)
|
|
962
|
+
@cloudobj = @cloudclass.new(
|
|
963
|
+
mommacat: args[:mommacat],
|
|
964
|
+
kitten_cfg: args[:kitten_cfg],
|
|
965
|
+
cloud_id: args[:cloud_id],
|
|
966
|
+
mu_name: args[:mu_name]
|
|
967
|
+
)
|
|
968
|
+
raise MuError, "Unknown error instantiating #{self}" if @cloudobj.nil?
|
|
969
|
+
# These should actually call the method live instead of caching a static value
|
|
970
|
+
PUBLIC_ATTRS.each { |a|
|
|
971
|
+
instance_variable_set(("@"+a.to_s).to_sym, @cloudobj.send(a))
|
|
972
|
+
}
|
|
973
|
+
@deploy ||= args[:mommacat]
|
|
974
|
+
@deploy_id ||= @deploy.deploy_id if @deploy
|
|
975
|
+
|
|
976
|
+
# Register with the containing deployment
|
|
977
|
+
if !@deploy.nil? and !@cloudobj.mu_name.nil? and
|
|
978
|
+
!@cloudobj.mu_name.empty? and !args[:delay_descriptor_load]
|
|
979
|
+
describe # XXX is this actually safe here?
|
|
980
|
+
@deploy.addKitten(self.class.cfg_name, @config['name'], self)
|
|
981
|
+
elsif !@deploy.nil? and @cloudobj.mu_name.nil?
|
|
982
|
+
MU.log "#{self} in #{@deploy.deploy_id} didn't generate a mu_name after being loaded/initialized, dependencies on this resource will probably be confused!", MU::ERR, details: [caller, args.keys]
|
|
983
|
+
end
|
|
984
|
+
|
|
985
|
+
|
|
986
|
+
# We are actually a child object invoking this via super() from its
|
|
987
|
+
# own initialize(), so initialize all the attributes and instance
|
|
988
|
+
# variables we know to be universal.
|
|
716
989
|
else
|
|
717
|
-
MU.log "Initializing an independent instance of #{self.class.name} named #{mu_name}", MU::DEBUG, details: kitten_cfg
|
|
718
|
-
end
|
|
719
|
-
if !kitten_cfg.has_key?("cloud")
|
|
720
|
-
kitten_cfg['cloud'] = MU::Config.defaultCloud
|
|
721
|
-
end
|
|
722
|
-
@cloud = kitten_cfg['cloud']
|
|
723
|
-
@cloudclass = MU::Cloud.loadCloudType(@cloud, self.class.shortname)
|
|
724
|
-
@environment = kitten_cfg['environment']
|
|
725
|
-
@method_semaphore = Mutex.new
|
|
726
|
-
@method_locks = {}
|
|
727
|
-
# XXX require subclass to provide attr_readers of @config and @deploy
|
|
728
990
|
|
|
729
|
-
|
|
991
|
+
# Declare the attributes that everyone should have
|
|
992
|
+
class << self
|
|
993
|
+
PUBLIC_ATTRS.each { |a|
|
|
994
|
+
attr_reader a
|
|
995
|
+
}
|
|
996
|
+
end
|
|
730
997
|
|
|
731
|
-
|
|
998
|
+
# XXX this butchers ::Id and ::Ref objects that might be used by dependencies() to good effect, but we also can't expect our implementations to cope with knowing when a .to_s has to be appended to things at random
|
|
999
|
+
@config = MU::Config.manxify(args[:kitten_cfg]) || MU::Config.manxify(args[:config])
|
|
732
1000
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
@cloud_id ||= @cloudobj.cloud_id
|
|
738
|
-
end
|
|
1001
|
+
if !@config
|
|
1002
|
+
MU.log "Missing config arguments in setInstanceVariables, can't initialize a cloud object without it", MU::ERR, details: args.keys
|
|
1003
|
+
raise MuError, "Missing config arguments in setInstanceVariables"
|
|
1004
|
+
end
|
|
739
1005
|
|
|
740
|
-
|
|
741
|
-
@config = @cloudobj.config
|
|
1006
|
+
@deploy = args[:mommacat] || args[:deploy]
|
|
742
1007
|
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
@cloudobj.mu_windows_name = @deploy.getResourceName(@config['name'], max_length: 15, need_unique_string: true, use_unique_string: unq, reuse_unique_string: true)
|
|
754
|
-
else
|
|
755
|
-
@cloudobj.mu_windows_name = @deploy.getResourceName(@config['name'], max_length: 15, need_unique_string: true)
|
|
1008
|
+
@credentials = args[:credentials]
|
|
1009
|
+
@credentials ||= @config['credentials']
|
|
1010
|
+
|
|
1011
|
+
@cloud = @config['cloud']
|
|
1012
|
+
if !@cloud
|
|
1013
|
+
if self.class.name.match(/^MU::Cloud::([^:]+)(?:::.+|$)/)
|
|
1014
|
+
cloudclass_name = Regexp.last_match[1]
|
|
1015
|
+
if MU::Cloud.supportedClouds.include?(cloudclass_name)
|
|
1016
|
+
@cloud = cloudclass_name
|
|
1017
|
+
end
|
|
756
1018
|
end
|
|
757
1019
|
end
|
|
758
|
-
|
|
1020
|
+
if !@cloud
|
|
1021
|
+
raise MuError, "Failed to determine what cloud #{self} should be in!"
|
|
1022
|
+
end
|
|
1023
|
+
|
|
1024
|
+
@environment = @config['environment']
|
|
1025
|
+
if @deploy
|
|
1026
|
+
@deploy_id = @deploy.deploy_id
|
|
1027
|
+
@appname = @deploy.appname
|
|
1028
|
+
end
|
|
1029
|
+
|
|
1030
|
+
@cloudclass = MU::Cloud.loadCloudType(@cloud, self.class.shortname)
|
|
1031
|
+
@cloudparentclass = Object.const_get("MU").const_get("Cloud").const_get(@cloud)
|
|
1032
|
+
|
|
1033
|
+
# A pre-existing object, you say?
|
|
1034
|
+
if args[:cloud_id]
|
|
1035
|
+
|
|
1036
|
+
# TODO implement ::Id for every cloud... and they should know how to get from
|
|
1037
|
+
# cloud_desc to a fully-resolved ::Id object, not just the short string
|
|
1038
|
+
|
|
1039
|
+
@cloud_id = args[:cloud_id]
|
|
1040
|
+
describe(cloud_id: @cloud_id)
|
|
1041
|
+
@habitat_id = habitat_id # effectively, cache this
|
|
1042
|
+
|
|
1043
|
+
# If we can build us an ::Id object for @cloud_id instead of a
|
|
1044
|
+
# string, do so.
|
|
1045
|
+
begin
|
|
1046
|
+
idclass = Object.const_get("MU").const_get("Cloud").const_get(@cloud).const_get("Id")
|
|
1047
|
+
long_id = if @deploydata and @deploydata[idclass.idattr.to_s]
|
|
1048
|
+
@deploydata[idclass.idattr.to_s]
|
|
1049
|
+
elsif self.respond_to?(idclass.idattr)
|
|
1050
|
+
self.send(idclass.idattr)
|
|
1051
|
+
end
|
|
1052
|
+
|
|
1053
|
+
@cloud_id = idclass.new(long_id) if !long_id.nil? and !long_id.empty?
|
|
1054
|
+
# 1 see if we have the value on the object directly or in deploy data
|
|
1055
|
+
# 2 set an attr_reader with the value
|
|
1056
|
+
# 3 rewrite our @cloud_id attribute with a ::Id object
|
|
1057
|
+
rescue NameError, MU::Cloud::MuCloudResourceNotImplemented
|
|
1058
|
+
end
|
|
1059
|
+
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
# Use pre-existing mu_name (we're probably loading an extant deploy)
|
|
1063
|
+
# if available
|
|
1064
|
+
if args[:mu_name]
|
|
1065
|
+
@mu_name = args[:mu_name].dup
|
|
1066
|
+
# If scrub_mu_isms is set, our mu_name is always just the bare name
|
|
1067
|
+
# field of the resource.
|
|
1068
|
+
elsif @config['scrub_mu_isms']
|
|
1069
|
+
@mu_name = @config['name'].dup
|
|
1070
|
+
# XXX feck it insert an inheritable method right here? Set a default? How should resource implementations determine whether they're instantiating a new object?
|
|
1071
|
+
end
|
|
1072
|
+
|
|
1073
|
+
@tags = {}
|
|
1074
|
+
if !@config['scrub_mu_isms']
|
|
1075
|
+
@tags = @deploy ? @deploy.listStandardTags : MU::MommaCat.listStandardTags
|
|
1076
|
+
end
|
|
1077
|
+
if @config['tags']
|
|
1078
|
+
@config['tags'].each { |tag|
|
|
1079
|
+
@tags[tag['key']] = tag['value']
|
|
1080
|
+
}
|
|
1081
|
+
end
|
|
1082
|
+
|
|
1083
|
+
if @cloudparentclass.respond_to?(:resourceInitHook)
|
|
1084
|
+
@cloudparentclass.resourceInitHook(self, @deploy)
|
|
1085
|
+
end
|
|
1086
|
+
|
|
1087
|
+
# Add cloud-specific instance methods for our resource objects to
|
|
1088
|
+
# inherit.
|
|
1089
|
+
if @cloudparentclass.const_defined?(:AdditionalResourceMethods)
|
|
1090
|
+
self.extend @cloudparentclass.const_get(:AdditionalResourceMethods)
|
|
1091
|
+
end
|
|
759
1092
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
1093
|
+
if ["Server", "ServerPool"].include?(self.class.shortname) and @deploy
|
|
1094
|
+
@mu_name ||= @deploy.getResourceName(@config['name'], need_unique_string: @config.has_key?("basis"))
|
|
1095
|
+
if self.class.shortname == "Server"
|
|
1096
|
+
@groomer = MU::Groomer.new(self)
|
|
1097
|
+
end
|
|
1098
|
+
|
|
1099
|
+
@groomclass = MU::Groomer.loadGroomer(@config["groomer"])
|
|
1100
|
+
|
|
1101
|
+
if windows? or @config['active_directory'] and !@mu_windows_name
|
|
1102
|
+
if !@deploydata.nil? and !@deploydata['mu_windows_name'].nil?
|
|
1103
|
+
@mu_windows_name = @deploydata['mu_windows_name']
|
|
1104
|
+
else
|
|
1105
|
+
# Use the same random differentiator as the "real" name if we're
|
|
1106
|
+
# from a ServerPool. Helpful for admin sanity.
|
|
1107
|
+
unq = @mu_name.sub(/^.*?-(...)$/, '\1')
|
|
1108
|
+
if @config['basis'] and !unq.nil? and !unq.empty?
|
|
1109
|
+
@mu_windows_name = @deploy.getResourceName(@config['name'], max_length: 15, need_unique_string: true, use_unique_string: unq, reuse_unique_string: true)
|
|
1110
|
+
else
|
|
1111
|
+
@mu_windows_name = @deploy.getResourceName(@config['name'], max_length: 15, need_unique_string: true)
|
|
1112
|
+
end
|
|
1113
|
+
end
|
|
1114
|
+
end
|
|
1115
|
+
class << self
|
|
1116
|
+
attr_reader :groomer
|
|
1117
|
+
attr_reader :groomerclass
|
|
1118
|
+
attr_accessor :mu_windows_name # XXX might be ok as reader now
|
|
1119
|
+
end
|
|
1120
|
+
end
|
|
767
1121
|
end
|
|
768
1122
|
|
|
769
1123
|
end
|
|
770
1124
|
|
|
1125
|
+
def cloud
|
|
1126
|
+
if @cloud
|
|
1127
|
+
@cloud
|
|
1128
|
+
elsif @config and @config['cloud']
|
|
1129
|
+
@config['cloud']
|
|
1130
|
+
elsif self.class.name.match(/^MU::Cloud::([^:]+)::.+/)
|
|
1131
|
+
cloudclass_name = Regexp.last_match[1]
|
|
1132
|
+
if MU::Cloud.supportedClouds.include?(cloudclass_name)
|
|
1133
|
+
cloudclass_name
|
|
1134
|
+
else
|
|
1135
|
+
nil
|
|
1136
|
+
end
|
|
1137
|
+
else
|
|
1138
|
+
nil
|
|
1139
|
+
end
|
|
1140
|
+
end
|
|
1141
|
+
|
|
1142
|
+
|
|
771
1143
|
# Remove all metadata and cloud resources associated with this object
|
|
772
1144
|
def destroy
|
|
773
1145
|
if !@cloudobj.nil? and !@cloudobj.groomer.nil?
|
|
@@ -796,23 +1168,99 @@ module MU
|
|
|
796
1168
|
end
|
|
797
1169
|
end
|
|
798
1170
|
end
|
|
1171
|
+
|
|
1172
|
+
# Return the cloud object's idea of where it lives (project, account,
|
|
1173
|
+
# etc) in the form of an identifier. If not applicable for this object,
|
|
1174
|
+
# we expect to return +nil+.
|
|
1175
|
+
# @return [String,nil]
|
|
1176
|
+
def habitat(nolookup: true)
|
|
1177
|
+
return nil if ["folder", "habitat"].include?(self.class.cfg_name)
|
|
1178
|
+
if @cloudobj
|
|
1179
|
+
@cloudparentclass.habitat(@cloudobj, nolookup: nolookup, deploy: @deploy)
|
|
1180
|
+
else
|
|
1181
|
+
@cloudparentclass.habitat(self, nolookup: nolookup, deploy: @deploy)
|
|
1182
|
+
end
|
|
1183
|
+
end
|
|
1184
|
+
|
|
1185
|
+
def habitat_id(nolookup: false)
|
|
1186
|
+
@habitat_id ||= habitat(nolookup: nolookup)
|
|
1187
|
+
@habitat_id
|
|
1188
|
+
end
|
|
1189
|
+
|
|
1190
|
+
# We're fundamentally a wrapper class, so go ahead and reroute requests
|
|
1191
|
+
# that are meant for our wrapped object.
|
|
1192
|
+
def method_missing(method_sym, *arguments)
|
|
1193
|
+
if @cloudobj
|
|
1194
|
+
MU.log "INVOKING #{method_sym.to_s} FROM PARENT CLOUD OBJECT #{self}", MU::DEBUG, details: arguments
|
|
1195
|
+
@cloudobj.method(method_sym).call(*arguments)
|
|
1196
|
+
else
|
|
1197
|
+
raise NoMethodError, "No such instance method #{method_sym.to_s} available on #{self.class.name}"
|
|
1198
|
+
end
|
|
1199
|
+
end
|
|
1200
|
+
|
|
1201
|
+
# Merge the passed hash into the existing configuration hash of this
|
|
1202
|
+
# cloud object. Currently this is only used by the {MU::Adoption}
|
|
1203
|
+
# module. I don't love exposing this to the whole internal API, but I'm
|
|
1204
|
+
# probably overthinking that.
|
|
1205
|
+
# @param newcfg [Hash]
|
|
1206
|
+
def config!(newcfg)
|
|
1207
|
+
@config.merge!(newcfg)
|
|
1208
|
+
end
|
|
799
1209
|
|
|
800
|
-
def cloud_desc()
|
|
1210
|
+
def cloud_desc(use_cache: true)
|
|
801
1211
|
describe
|
|
1212
|
+
|
|
802
1213
|
if !@cloudobj.nil?
|
|
803
|
-
|
|
804
|
-
|
|
1214
|
+
if @cloudobj.class.instance_methods(false).include?(:cloud_desc)
|
|
1215
|
+
@cloud_desc_cache ||= @cloudobj.cloud_desc
|
|
1216
|
+
end
|
|
805
1217
|
end
|
|
806
|
-
if !@config.nil? and !@cloud_id.nil? and @cloud_desc_cache.nil?
|
|
1218
|
+
if !@config.nil? and !@cloud_id.nil? and (!use_cache or @cloud_desc_cache.nil?)
|
|
807
1219
|
# The find() method should be returning a Hash with the cloud_id
|
|
808
1220
|
# as a key and a cloud platform descriptor as the value.
|
|
809
1221
|
begin
|
|
1222
|
+
args = {
|
|
1223
|
+
:region => @config['region'],
|
|
1224
|
+
:cloud => @config['cloud'],
|
|
1225
|
+
:cloud_id => @cloud_id,
|
|
1226
|
+
:credentials => @credentials,
|
|
1227
|
+
:project => habitat_id, # XXX this belongs in our required_instance_methods hack
|
|
1228
|
+
:flags => @config
|
|
1229
|
+
}
|
|
1230
|
+
@cloudparentclass.required_instance_methods.each { |m|
|
|
1231
|
+
# if respond_to?(m)
|
|
1232
|
+
# args[m] = method(m).call
|
|
1233
|
+
# else
|
|
1234
|
+
args[m] = instance_variable_get(("@"+m.to_s).to_sym)
|
|
1235
|
+
# end
|
|
1236
|
+
}
|
|
810
1237
|
|
|
811
|
-
matches = self.class.find(
|
|
812
|
-
if !matches.nil? and matches.is_a?(Hash)
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
1238
|
+
matches = self.class.find(args)
|
|
1239
|
+
if !matches.nil? and matches.is_a?(Hash)
|
|
1240
|
+
# XXX or if the hash is keyed with an ::Id element, oh boy
|
|
1241
|
+
# puts matches[@cloud_id][:self_link]
|
|
1242
|
+
# puts matches[@cloud_id][:url]
|
|
1243
|
+
# if matches[@cloud_id][:self_link]
|
|
1244
|
+
# @url ||= matches[@cloud_id][:self_link]
|
|
1245
|
+
# elsif matches[@cloud_id][:url]
|
|
1246
|
+
# @url ||= matches[@cloud_id][:url]
|
|
1247
|
+
# elsif matches[@cloud_id][:arn]
|
|
1248
|
+
# @arn ||= matches[@cloud_id][:arn]
|
|
1249
|
+
# end
|
|
1250
|
+
if matches[@cloud_id]
|
|
1251
|
+
@cloud_desc_cache = matches[@cloud_id]
|
|
1252
|
+
else
|
|
1253
|
+
matches.each_pair { |k, v| # flatten out ::Id objects just in case
|
|
1254
|
+
if @cloud_id.to_s == k.to_s
|
|
1255
|
+
@cloud_desc_cache = v
|
|
1256
|
+
break
|
|
1257
|
+
end
|
|
1258
|
+
}
|
|
1259
|
+
end
|
|
1260
|
+
end
|
|
1261
|
+
|
|
1262
|
+
if !@cloud_desc_cache
|
|
1263
|
+
MU.log "cloud_desc via #{self.class.name}.find() failed to locate a live object.\nWas called by #{caller[0]}", MU::WARN, details: args
|
|
816
1264
|
end
|
|
817
1265
|
rescue Exception => e
|
|
818
1266
|
MU.log "Got #{e.inspect} trying to find cloud handle for #{self.class.shortname} #{@mu_name} (#{@cloud_id})", MU::WARN
|
|
@@ -858,19 +1306,6 @@ module MU
|
|
|
858
1306
|
end
|
|
859
1307
|
if @deploydata.has_key?('cloud_id')
|
|
860
1308
|
@cloud_id ||= @deploydata['cloud_id']
|
|
861
|
-
else
|
|
862
|
-
# XXX temp hack to catch old Amazon-style identifiers. Remove this
|
|
863
|
-
# before supporting any other cloud layers, otherwise name
|
|
864
|
-
# collision is possible.
|
|
865
|
-
["group_id", "instance_id", "awsname", "identifier", "vpc_id", "id"].each { |identifier|
|
|
866
|
-
if @deploydata.has_key?(identifier)
|
|
867
|
-
@cloud_id ||= @deploydata[identifier]
|
|
868
|
-
if @mu_name.nil? and (identifier == "awsname" or identifier == "identifier" or identifier == "group_id")
|
|
869
|
-
@mu_name = @deploydata[identifier]
|
|
870
|
-
end
|
|
871
|
-
break
|
|
872
|
-
end
|
|
873
|
-
}
|
|
874
1309
|
end
|
|
875
1310
|
end
|
|
876
1311
|
|
|
@@ -886,7 +1321,7 @@ module MU
|
|
|
886
1321
|
# resources in this deployment), as well as for certain config stanzas
|
|
887
1322
|
# which can refer to external resources (@vpc, @loadbalancers,
|
|
888
1323
|
# @add_firewall_rules)
|
|
889
|
-
def dependencies(use_cache: false)
|
|
1324
|
+
def dependencies(use_cache: false, debug: false)
|
|
890
1325
|
@dependencies = {} if @dependencies.nil?
|
|
891
1326
|
@loadbalancers = [] if @loadbalancers.nil?
|
|
892
1327
|
if @config.nil?
|
|
@@ -897,6 +1332,8 @@ module MU
|
|
|
897
1332
|
end
|
|
898
1333
|
@config['dependencies'] = [] if @config['dependencies'].nil?
|
|
899
1334
|
|
|
1335
|
+
loglevel = debug ? MU::NOTICE : MU::DEBUG
|
|
1336
|
+
|
|
900
1337
|
# First, general dependencies. These should all be fellow members of
|
|
901
1338
|
# the current deployment.
|
|
902
1339
|
@config['dependencies'].each { |dep|
|
|
@@ -904,7 +1341,7 @@ module MU
|
|
|
904
1341
|
next if @dependencies[dep['type']].has_key?(dep['name'])
|
|
905
1342
|
handle = @deploy.findLitterMate(type: dep['type'], name: dep['name']) if !@deploy.nil?
|
|
906
1343
|
if !handle.nil?
|
|
907
|
-
MU.log "Loaded dependency for #{self}: #{dep['name']} => #{handle}",
|
|
1344
|
+
MU.log "Loaded dependency for #{self}: #{dep['name']} => #{handle}", loglevel
|
|
908
1345
|
@dependencies[dep['type']][dep['name']] = handle
|
|
909
1346
|
else
|
|
910
1347
|
# XXX yell under circumstances where we should expect to have
|
|
@@ -914,62 +1351,100 @@ module MU
|
|
|
914
1351
|
|
|
915
1352
|
# Special dependencies: my containing VPC
|
|
916
1353
|
if self.class.can_live_in_vpc and !@config['vpc'].nil?
|
|
917
|
-
|
|
918
|
-
if !@config['vpc']["
|
|
919
|
-
|
|
1354
|
+
# If something hash-ified a MU::Config::Ref here, fix it
|
|
1355
|
+
if !@config['vpc']["id"].nil? and @config['vpc']["id"].is_a?(Hash)
|
|
1356
|
+
@config['vpc']["id"] = MU::Config::Ref.new(@config['vpc']["id"])
|
|
1357
|
+
end
|
|
1358
|
+
if !@config['vpc']["id"].nil? and @config['vpc']["id"].is_a?(MU::Config::Ref) and !@config['vpc']["id"].kitten.nil?
|
|
1359
|
+
@vpc = @config['vpc']["id"].kitten
|
|
1360
|
+
elsif !@config['vpc']["name"].nil? and @deploy
|
|
1361
|
+
MU.log "Attempting findLitterMate on VPC for #{self}", loglevel, details: @config['vpc']
|
|
1362
|
+
|
|
1363
|
+
sib_by_name = @deploy.findLitterMate(name: @config['vpc']['name'], type: "vpcs", return_all: true, habitat: @config['vpc']['project'], debug: debug)
|
|
920
1364
|
if sib_by_name.is_a?(Array)
|
|
921
1365
|
if sib_by_name.size == 1
|
|
922
1366
|
@vpc = matches.first
|
|
1367
|
+
MU.log "Single VPC match for #{self}", loglevel, details: @vpc.to_s
|
|
923
1368
|
else
|
|
924
1369
|
# XXX ok but this is the wrong place for this really the config parser needs to sort this out somehow
|
|
925
1370
|
# we got multiple matches, try to pick one by preferred subnet
|
|
926
1371
|
# behavior
|
|
1372
|
+
MU.log "Sorting a bunch of VPC matches for #{self}", loglevel, details: sib_by_name.map { |s| s.to_s }.join(", ")
|
|
927
1373
|
sib_by_name.each { |sibling|
|
|
928
1374
|
all_private = sibling.subnets.map { |s| s.private? }.all?(true)
|
|
929
1375
|
all_public = sibling.subnets.map { |s| s.private? }.all?(false)
|
|
1376
|
+
names = sibling.subnets.map { |s| s.name }
|
|
1377
|
+
ids = sibling.subnets.map { |s| s.cloud_id }
|
|
930
1378
|
if all_private and ["private", "all_private"].include?(@config['vpc']['subnet_pref'])
|
|
931
1379
|
@vpc = sibling
|
|
932
1380
|
break
|
|
933
1381
|
elsif all_public and ["public", "all_public"].include?(@config['vpc']['subnet_pref'])
|
|
934
1382
|
@vpc = sibling
|
|
935
1383
|
break
|
|
936
|
-
|
|
937
|
-
|
|
1384
|
+
elsif @config['vpc']['subnet_name'] and
|
|
1385
|
+
names.include?(@config['vpc']['subnet_name'])
|
|
1386
|
+
puts "CHOOSING #{@vpc.to_s} 'cause it has #{@config['vpc']['subnet_name']}"
|
|
1387
|
+
@vpc = sibling
|
|
1388
|
+
break
|
|
1389
|
+
elsif @config['vpc']['subnet_id'] and
|
|
1390
|
+
ids.include?(@config['vpc']['subnet_id'])
|
|
938
1391
|
@vpc = sibling
|
|
939
1392
|
break
|
|
940
1393
|
end
|
|
941
1394
|
}
|
|
1395
|
+
if !@vpc
|
|
1396
|
+
sibling = sib_by_name.sample
|
|
1397
|
+
MU.log "Got multiple matching VPCs for #{self.class.cfg_name} #{@mu_name}, so I'm arbitrarily choosing #{sibling.mu_name}", MU::WARN, details: @config['vpc']
|
|
1398
|
+
@vpc = sibling
|
|
1399
|
+
end
|
|
942
1400
|
end
|
|
943
1401
|
else
|
|
944
1402
|
@vpc = sib_by_name
|
|
1403
|
+
MU.log "Found exact VPC match for #{self}", loglevel, details: sib_by_name.to_s
|
|
945
1404
|
end
|
|
1405
|
+
else
|
|
1406
|
+
MU.log "No shortcuts available to fetch VPC for #{self}", loglevel, details: @config['vpc']
|
|
946
1407
|
end
|
|
947
1408
|
|
|
948
|
-
if !@vpc and !@config['vpc']["
|
|
1409
|
+
if !@vpc and !@config['vpc']["name"].nil? and
|
|
949
1410
|
@dependencies.has_key?("vpc") and
|
|
950
|
-
@dependencies["vpc"].has_key?(@config['vpc']["
|
|
951
|
-
|
|
1411
|
+
@dependencies["vpc"].has_key?(@config['vpc']["name"])
|
|
1412
|
+
MU.log "Grabbing VPC I see in @dependencies['vpc']['#{@config['vpc']["name"]}'] for #{self}", loglevel, details: @config['vpc']
|
|
1413
|
+
@vpc = @dependencies["vpc"][@config['vpc']["name"]]
|
|
952
1414
|
elsif !@vpc
|
|
953
1415
|
tag_key, tag_value = @config['vpc']['tag'].split(/=/, 2) if !@config['vpc']['tag'].nil?
|
|
954
|
-
if !@config['vpc'].has_key?("
|
|
1416
|
+
if !@config['vpc'].has_key?("id") and
|
|
955
1417
|
!@config['vpc'].has_key?("deploy_id") and !@deploy.nil?
|
|
956
1418
|
@config['vpc']["deploy_id"] = @deploy.deploy_id
|
|
957
1419
|
end
|
|
1420
|
+
MU.log "Doing findStray for VPC for #{self}", loglevel, details: @config['vpc']
|
|
958
1421
|
vpcs = MU::MommaCat.findStray(
|
|
959
1422
|
@config['cloud'],
|
|
960
1423
|
"vpc",
|
|
961
1424
|
deploy_id: @config['vpc']["deploy_id"],
|
|
962
|
-
cloud_id: @config['vpc']["
|
|
963
|
-
name: @config['vpc']["
|
|
1425
|
+
cloud_id: @config['vpc']["id"],
|
|
1426
|
+
name: @config['vpc']["name"],
|
|
964
1427
|
tag_key: tag_key,
|
|
965
1428
|
tag_value: tag_value,
|
|
1429
|
+
habitats: [@project_id],
|
|
966
1430
|
region: @config['vpc']["region"],
|
|
967
1431
|
calling_deploy: @deploy,
|
|
968
|
-
|
|
1432
|
+
credentials: @credentials,
|
|
1433
|
+
dummy_ok: true,
|
|
1434
|
+
debug: debug
|
|
969
1435
|
)
|
|
970
1436
|
@vpc = vpcs.first if !vpcs.nil? and vpcs.size > 0
|
|
971
1437
|
end
|
|
972
|
-
if
|
|
1438
|
+
if @vpc and @vpc.config and @vpc.config['bastion'] and
|
|
1439
|
+
@vpc.config['bastion'].to_h['name'] != @config['name']
|
|
1440
|
+
refhash = @vpc.config['bastion'].to_h
|
|
1441
|
+
refhash['deploy_id'] ||= @vpc.deploy.deploy_id
|
|
1442
|
+
natref = MU::Config::Ref.get(refhash)
|
|
1443
|
+
if natref and natref.kitten(@vpc.deploy)
|
|
1444
|
+
@nat = natref.kitten(@vpc.deploy)
|
|
1445
|
+
end
|
|
1446
|
+
end
|
|
1447
|
+
if @nat.nil? and !@vpc.nil? and (
|
|
973
1448
|
@config['vpc'].has_key?("nat_host_id") or
|
|
974
1449
|
@config['vpc'].has_key?("nat_host_tag") or
|
|
975
1450
|
@config['vpc'].has_key?("nat_host_ip") or
|
|
@@ -1008,6 +1483,22 @@ module MU
|
|
|
1008
1483
|
@vpc = self
|
|
1009
1484
|
end
|
|
1010
1485
|
|
|
1486
|
+
# Google accounts usually have a useful default VPC we can use
|
|
1487
|
+
if @vpc.nil? and @project_id and @cloud == "Google" and
|
|
1488
|
+
self.class.can_live_in_vpc
|
|
1489
|
+
MU.log "Seeing about default VPC for #{self.to_s}", MU::NOTICE
|
|
1490
|
+
vpcs = MU::MommaCat.findStray(
|
|
1491
|
+
"Google",
|
|
1492
|
+
"vpc",
|
|
1493
|
+
cloud_id: "default",
|
|
1494
|
+
habitats: [@project_id],
|
|
1495
|
+
credentials: @credentials,
|
|
1496
|
+
dummy_ok: true,
|
|
1497
|
+
debug: debug
|
|
1498
|
+
)
|
|
1499
|
+
@vpc = vpcs.first if !vpcs.nil? and vpcs.size > 0
|
|
1500
|
+
end
|
|
1501
|
+
|
|
1011
1502
|
# Special dependencies: LoadBalancers I've asked to attach to an
|
|
1012
1503
|
# instance.
|
|
1013
1504
|
if @config.has_key?("loadbalancers")
|
|
@@ -1046,17 +1537,46 @@ module MU
|
|
|
1046
1537
|
MU::Cloud::ALPHA
|
|
1047
1538
|
end
|
|
1048
1539
|
|
|
1540
|
+
# Return a list of "container" artifacts, by class, that apply to this
|
|
1541
|
+
# resource type in a cloud provider. This is so methods that call find
|
|
1542
|
+
# know whether to call +find+ with identifiers for parent resources.
|
|
1543
|
+
# This is similar in purpose to the +isGlobal?+ resource class method,
|
|
1544
|
+
# which tells our search functions whether or not a resource scopes to
|
|
1545
|
+
# a region. In almost all cases this is one-entry list consisting of
|
|
1546
|
+
# +:Habitat+. Notable exceptions include most implementations of
|
|
1547
|
+
# +Habitat+, which either reside inside a +:Folder+ or nothing at all;
|
|
1548
|
+
# whereas a +:Folder+ tends to not have any containing parent. Very few
|
|
1549
|
+
# resource implementations will need to override this.
|
|
1550
|
+
# A +nil+ entry in this list is interpreted as "this resource can be
|
|
1551
|
+
# global."
|
|
1552
|
+
# @return [Array<Symbol,nil>]
|
|
1553
|
+
def self.canLiveIn
|
|
1554
|
+
if self.shortname == "Folder"
|
|
1555
|
+
[nil, :Folder]
|
|
1556
|
+
elsif self.shortname == "Habitat"
|
|
1557
|
+
[:Folder]
|
|
1558
|
+
else
|
|
1559
|
+
[:Habitat]
|
|
1560
|
+
end
|
|
1561
|
+
end
|
|
1562
|
+
|
|
1049
1563
|
def self.find(*flags)
|
|
1050
1564
|
allfound = {}
|
|
1051
1565
|
|
|
1052
|
-
MU::Cloud.
|
|
1566
|
+
MU::Cloud.availableClouds.each { |cloud|
|
|
1053
1567
|
begin
|
|
1054
1568
|
args = flags.first
|
|
1569
|
+
next if args[:cloud] and args[:cloud] != cloud
|
|
1055
1570
|
# skip this cloud if we have a region argument that makes no
|
|
1056
1571
|
# sense there
|
|
1057
1572
|
cloudbase = Object.const_get("MU").const_get("Cloud").const_get(cloud)
|
|
1573
|
+
next if cloudbase.listCredentials.nil? or cloudbase.listCredentials.empty? or cloudbase.credConfig(args[:credentials]).nil?
|
|
1058
1574
|
if args[:region] and cloudbase.respond_to?(:listRegions)
|
|
1059
|
-
|
|
1575
|
+
if !cloudbase.listRegions(credentials: args[:credentials])
|
|
1576
|
+
MU.log "Failed to get region list for credentials #{args[:credentials]} in cloud #{cloud}", MU::ERR, details: caller
|
|
1577
|
+
else
|
|
1578
|
+
next if !cloudbase.listRegions(credentials: args[:credentials]).include?(args[:region])
|
|
1579
|
+
end
|
|
1060
1580
|
end
|
|
1061
1581
|
begin
|
|
1062
1582
|
cloudclass = MU::Cloud.loadCloudType(cloud, shortname)
|
|
@@ -1094,9 +1614,9 @@ module MU
|
|
|
1094
1614
|
end
|
|
1095
1615
|
end
|
|
1096
1616
|
|
|
1097
|
-
if shortname == "Server"
|
|
1617
|
+
if shortname == "Server" or shortname == "ServerPool"
|
|
1098
1618
|
def windows?
|
|
1099
|
-
return true if %w{win2k16 win2k12r2 win2k12 win2k8 win2k8r2 windows}.include?(@config['platform'])
|
|
1619
|
+
return true if %w{win2k16 win2k12r2 win2k12 win2k8 win2k8r2 win2k19 windows}.include?(@config['platform'])
|
|
1100
1620
|
begin
|
|
1101
1621
|
return true if cloud_desc.respond_to?(:platform) and cloud_desc.platform == "Windows"
|
|
1102
1622
|
# XXX ^ that's AWS-speak, doesn't cover GCP or anything else; maybe we should require cloud layers to implement this so we can just call @cloudobj.windows?
|
|
@@ -1330,7 +1850,9 @@ module MU
|
|
|
1330
1850
|
if !output.nil? and !output.empty?
|
|
1331
1851
|
raise MU::Cloud::BootstrapTempFail, "Linux package manager is still doing something, need to wait (#{output})"
|
|
1332
1852
|
end
|
|
1333
|
-
if !@config['skipinitialupdates']
|
|
1853
|
+
if !@config['skipinitialupdates'] and
|
|
1854
|
+
!@config['scrub_mu_isms'] and
|
|
1855
|
+
!@config['userdata_script']
|
|
1334
1856
|
output = ssh.exec!(lnx_updates_check)
|
|
1335
1857
|
if !output.nil? and output.match(/userdata still running/)
|
|
1336
1858
|
raise MU::Cloud::BootstrapTempFail, "Waiting for initial userdata system updates to complete"
|
|
@@ -1464,7 +1986,7 @@ module MU
|
|
|
1464
1986
|
|
|
1465
1987
|
if retries < max_retries
|
|
1466
1988
|
retries = retries + 1
|
|
1467
|
-
msg = "ssh #{ssh_user}@#{@
|
|
1989
|
+
msg = "ssh #{ssh_user}@#{@mu_name}: #{e.message}, waiting #{retry_interval}s (attempt #{retries}/#{max_retries})", MU::WARN
|
|
1468
1990
|
if retries == 1 or (retries/max_retries <= 0.5 and (retries % 3) == 0)
|
|
1469
1991
|
MU.log msg, MU::NOTICE
|
|
1470
1992
|
elsif retries/max_retries > 0.5
|
|
@@ -1473,7 +1995,7 @@ module MU
|
|
|
1473
1995
|
sleep retry_interval
|
|
1474
1996
|
retry
|
|
1475
1997
|
else
|
|
1476
|
-
raise MuError, "#{@
|
|
1998
|
+
raise MuError, "#{@mu_name}: #{e.inspect} trying to connect with SSH, max_retries exceeded", e.backtrace
|
|
1477
1999
|
end
|
|
1478
2000
|
end
|
|
1479
2001
|
return session
|
|
@@ -1501,6 +2023,17 @@ module MU
|
|
|
1501
2023
|
MU::MommaCat.unlockAll
|
|
1502
2024
|
end
|
|
1503
2025
|
|
|
2026
|
+
# A hook that is always called just before each instance method is
|
|
2027
|
+
# invoked, so that we can ensure that repetitive setup tasks (like
|
|
2028
|
+
# resolving +:resource_group+ for Azure resources) have always been
|
|
2029
|
+
# done.
|
|
2030
|
+
def resourceInitHook
|
|
2031
|
+
@cloud ||= cloud
|
|
2032
|
+
if @cloudparentclass.respond_to?(:resourceInitHook)
|
|
2033
|
+
@cloudparentclass.resourceInitHook(@cloudobj, @deploy)
|
|
2034
|
+
end
|
|
2035
|
+
end
|
|
2036
|
+
|
|
1504
2037
|
# Wrap the instance methods that this cloud resource type has to
|
|
1505
2038
|
# implement.
|
|
1506
2039
|
MU::Cloud.resource_types[name.to_sym][:instance].each { |method|
|
|
@@ -1538,15 +2071,19 @@ module MU
|
|
|
1538
2071
|
if (method == :create or method == :groom or method == :postBoot) and
|
|
1539
2072
|
(!@destroyed and !@cloudobj.destroyed)
|
|
1540
2073
|
deploydata = @cloudobj.method(:notify).call
|
|
2074
|
+
@deploydata ||= deploydata # XXX I don't remember why we're not just doing this from the get-go; maybe because we prefer some mangling occurring in @deploy.notify?
|
|
1541
2075
|
if deploydata.nil? or !deploydata.is_a?(Hash)
|
|
1542
|
-
MU.log "#{self} notify method did not return a Hash of deployment data", MU::WARN
|
|
2076
|
+
MU.log "#{self} notify method did not return a Hash of deployment data, attempting to fill in with cloud descriptor #{@cloudobj.cloud_id}", MU::WARN
|
|
1543
2077
|
deploydata = MU.structToHash(@cloudobj.cloud_desc)
|
|
2078
|
+
raise MuError, "Failed to collect metadata about #{self}" if deploydata.nil?
|
|
1544
2079
|
end
|
|
1545
|
-
deploydata['cloud_id']
|
|
2080
|
+
deploydata['cloud_id'] ||= @cloudobj.cloud_id if !@cloudobj.cloud_id.nil?
|
|
1546
2081
|
deploydata['mu_name'] = @cloudobj.mu_name if !@cloudobj.mu_name.nil?
|
|
2082
|
+
deploydata['nodename'] = @cloudobj.mu_name if !@cloudobj.mu_name.nil?
|
|
2083
|
+
deploydata.delete("#MUOBJECT")
|
|
1547
2084
|
@deploy.notify(self.class.cfg_plural, @config['name'], deploydata, triggering_node: @cloudobj, delayed_save: @delayed_save) if !@deploy.nil?
|
|
1548
2085
|
elsif method == :notify
|
|
1549
|
-
retval['cloud_id'] = @cloudobj.cloud_id if !@cloudobj.cloud_id.nil?
|
|
2086
|
+
retval['cloud_id'] = @cloudobj.cloud_id.to_s if !@cloudobj.cloud_id.nil?
|
|
1550
2087
|
retval['mu_name'] = @cloudobj.mu_name if !@cloudobj.mu_name.nil?
|
|
1551
2088
|
@deploy.notify(self.class.cfg_plural, @config['name'], retval, triggering_node: @cloudobj, delayed_save: @delayed_save) if !@deploy.nil?
|
|
1552
2089
|
end
|