cloud-mu 1.9.0.pre.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Berksfile +56 -0
- data/Berksfile.lock +250 -0
- data/Jenkinsfile +184 -0
- data/LICENSE.md +37 -0
- data/README.md +26 -0
- data/bin/mu-aws-setup +376 -0
- data/bin/mu-cleanup +68 -0
- data/bin/mu-configure +1133 -0
- data/bin/mu-deploy +166 -0
- data/bin/mu-firewall-allow-clients +30 -0
- data/bin/mu-gcp-setup +200 -0
- data/bin/mu-gen-docs +34 -0
- data/bin/mu-gen-env +42 -0
- data/bin/mu-load-config.rb +158 -0
- data/bin/mu-node-manage +683 -0
- data/bin/mu-self-update +228 -0
- data/bin/mu-ssh +23 -0
- data/bin/mu-tunnel-nagios +144 -0
- data/bin/mu-upload-chef-artifacts +757 -0
- data/bin/mu-user-manage +275 -0
- data/cookbooks/awscli/LICENSE +37 -0
- data/cookbooks/awscli/README.md +58 -0
- data/cookbooks/awscli/attributes/default.rb +1 -0
- data/cookbooks/awscli/libraries/instance_metadata.rb +21 -0
- data/cookbooks/awscli/metadata.rb +20 -0
- data/cookbooks/awscli/recipes/default.rb +56 -0
- data/cookbooks/awscli/templates/default/config.erb +18 -0
- data/cookbooks/mu-activedirectory/CHANGELOG.md +13 -0
- data/cookbooks/mu-activedirectory/LICENSE +37 -0
- data/cookbooks/mu-activedirectory/README.md +6 -0
- data/cookbooks/mu-activedirectory/attributes/default.rb +98 -0
- data/cookbooks/mu-activedirectory/files/default/password-auth +32 -0
- data/cookbooks/mu-activedirectory/files/default/sshd_pol.pp +0 -0
- data/cookbooks/mu-activedirectory/files/default/sshd_pol.te +32 -0
- data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.pp +0 -0
- data/cookbooks/mu-activedirectory/files/default/syslogd_oddjobd.te +10 -0
- data/cookbooks/mu-activedirectory/files/default/system-auth +34 -0
- data/cookbooks/mu-activedirectory/files/default/winbindpol.pp +0 -0
- data/cookbooks/mu-activedirectory/files/default/winbindpol.te +37 -0
- data/cookbooks/mu-activedirectory/libraries/config.rb +106 -0
- data/cookbooks/mu-activedirectory/libraries/helper.rb +86 -0
- data/cookbooks/mu-activedirectory/metadata.rb +17 -0
- data/cookbooks/mu-activedirectory/providers/domain.rb +152 -0
- data/cookbooks/mu-activedirectory/providers/domain_controller.rb +89 -0
- data/cookbooks/mu-activedirectory/providers/domain_node.rb +275 -0
- data/cookbooks/mu-activedirectory/recipes/default.rb +8 -0
- data/cookbooks/mu-activedirectory/recipes/domain-controller.rb +44 -0
- data/cookbooks/mu-activedirectory/recipes/domain-node.rb +50 -0
- data/cookbooks/mu-activedirectory/recipes/domain.rb +43 -0
- data/cookbooks/mu-activedirectory/recipes/sssd.rb +185 -0
- data/cookbooks/mu-activedirectory/resources/domain.rb +25 -0
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +25 -0
- data/cookbooks/mu-activedirectory/resources/domain_node.rb +20 -0
- data/cookbooks/mu-activedirectory/templates/default/dhclient-eth0.conf.erb +4 -0
- data/cookbooks/mu-activedirectory/templates/default/interface +0 -0
- data/cookbooks/mu-activedirectory/templates/default/krb5.conf.erb +23 -0
- data/cookbooks/mu-activedirectory/templates/default/ntp.conf.erb +56 -0
- data/cookbooks/mu-activedirectory/templates/default/smb.conf.erb +33 -0
- data/cookbooks/mu-activedirectory/templates/default/sssd.conf.erb +60 -0
- data/cookbooks/mu-activedirectory/templates/windows/Backup.xml.erb +20 -0
- data/cookbooks/mu-activedirectory/templates/windows/bkupInfo.xml.erb +1 -0
- data/cookbooks/mu-activedirectory/templates/windows/gpreprt.xml.erb +198 -0
- data/cookbooks/mu-activedirectory/templates/windows/gptmpl.inf.erb +12 -0
- data/cookbooks/mu-activedirectory/templates/windows/manifest.xml.erb +1 -0
- data/cookbooks/mu-firewall/CHANGELOG.md +11 -0
- data/cookbooks/mu-firewall/LICENSE +37 -0
- data/cookbooks/mu-firewall/README.md +5 -0
- data/cookbooks/mu-firewall/attributes/default.rb +3 -0
- data/cookbooks/mu-firewall/metadata.rb +16 -0
- data/cookbooks/mu-firewall/recipes/default.rb +10 -0
- data/cookbooks/mu-glusterfs/CHANGELOG.md +13 -0
- data/cookbooks/mu-glusterfs/LICENSE +37 -0
- data/cookbooks/mu-glusterfs/README.md +5 -0
- data/cookbooks/mu-glusterfs/attributes/default.rb +34 -0
- data/cookbooks/mu-glusterfs/metadata.rb +17 -0
- data/cookbooks/mu-glusterfs/recipes/client.rb +62 -0
- data/cookbooks/mu-glusterfs/recipes/default.rb +16 -0
- data/cookbooks/mu-glusterfs/recipes/samba.rb +57 -0
- data/cookbooks/mu-glusterfs/recipes/server.rb +200 -0
- data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +71 -0
- data/cookbooks/mu-glusterfs/templates/default/smb.conf.erb +14 -0
- data/cookbooks/mu-jenkins/CHANGELOG.md +13 -0
- data/cookbooks/mu-jenkins/LICENSE +37 -0
- data/cookbooks/mu-jenkins/README.md +105 -0
- data/cookbooks/mu-jenkins/attributes/default.rb +42 -0
- data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +73 -0
- data/cookbooks/mu-jenkins/files/default/deploy_config.xml +44 -0
- data/cookbooks/mu-jenkins/metadata.rb +21 -0
- data/cookbooks/mu-jenkins/recipes/default.rb +195 -0
- data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +54 -0
- data/cookbooks/mu-jenkins/recipes/public_key.rb +24 -0
- data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +24 -0
- data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +14 -0
- data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +6 -0
- data/cookbooks/mu-master/CHANGELOG.md +13 -0
- data/cookbooks/mu-master/LICENSE +37 -0
- data/cookbooks/mu-master/README.md +6 -0
- data/cookbooks/mu-master/attributes/default.rb +95 -0
- data/cookbooks/mu-master/files/default/0-mu-log-server.conf +19 -0
- data/cookbooks/mu-master/files/default/addRSA.ldif +8 -0
- data/cookbooks/mu-master/files/default/check_mem.pl +197 -0
- data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
- data/cookbooks/mu-master/files/default/dirsrv_admin.pp +0 -0
- data/cookbooks/mu-master/files/default/dirsrv_admin.te +13 -0
- data/cookbooks/mu-master/files/default/nagios_selinux.pp +0 -0
- data/cookbooks/mu-master/files/default/nagios_selinux.te +51 -0
- data/cookbooks/mu-master/files/default/nagios_selinux_7.pp +0 -0
- data/cookbooks/mu-master/files/default/nagios_selinux_7.te +17 -0
- data/cookbooks/mu-master/files/default/pam_sshd +18 -0
- data/cookbooks/mu-master/files/default/ssl_enable.ldif +18 -0
- data/cookbooks/mu-master/files/default/syslogd_oddjobd.pp +0 -0
- data/cookbooks/mu-master/files/default/syslogd_oddjobd.te +10 -0
- data/cookbooks/mu-master/files/default/vimrc +19 -0
- data/cookbooks/mu-master/libraries/mu.rb +29 -0
- data/cookbooks/mu-master/metadata.rb +30 -0
- data/cookbooks/mu-master/providers/user.rb +41 -0
- data/cookbooks/mu-master/recipes/389ds.rb +164 -0
- data/cookbooks/mu-master/recipes/basepackages.rb +58 -0
- data/cookbooks/mu-master/recipes/caching_nameserver.rb +37 -0
- data/cookbooks/mu-master/recipes/default.rb +451 -0
- data/cookbooks/mu-master/recipes/eks-kubectl.rb +41 -0
- data/cookbooks/mu-master/recipes/firewall-holes.rb +70 -0
- data/cookbooks/mu-master/recipes/init.rb +542 -0
- data/cookbooks/mu-master/recipes/ssl-certs.rb +109 -0
- data/cookbooks/mu-master/recipes/sssd.rb +89 -0
- data/cookbooks/mu-master/recipes/update_nagios_only.rb +242 -0
- data/cookbooks/mu-master/recipes/vault.rb +111 -0
- data/cookbooks/mu-master/resources/user.rb +19 -0
- data/cookbooks/mu-master/templates/default/389-directory-setup.inf.erb +28 -0
- data/cookbooks/mu-master/templates/default/chef-server.rb.erb +18 -0
- data/cookbooks/mu-master/templates/default/dhclient-eth0.conf.erb +9 -0
- data/cookbooks/mu-master/templates/default/mu-momma-cat.erb +149 -0
- data/cookbooks/mu-master/templates/default/mu.rc.erb +9 -0
- data/cookbooks/mu-master/templates/default/openssl.cnf.erb +354 -0
- data/cookbooks/mu-master/templates/default/sssd.conf.erb +44 -0
- data/cookbooks/mu-master/templates/default/web_app.conf.erb +90 -0
- data/cookbooks/mu-mongo/CHANGELOG.md +13 -0
- data/cookbooks/mu-mongo/LICENSE +37 -0
- data/cookbooks/mu-mongo/README.md +5 -0
- data/cookbooks/mu-mongo/attributes/default.rb +22 -0
- data/cookbooks/mu-mongo/files/default/keyfile +16 -0
- data/cookbooks/mu-mongo/files/default/remove_nodes.js +5 -0
- data/cookbooks/mu-mongo/metadata.rb +17 -0
- data/cookbooks/mu-mongo/recipes/default.rb +149 -0
- data/cookbooks/mu-mongo/recipes/yum-update-rule.rb +18 -0
- data/cookbooks/mu-mongo/templates/default/mongo_create_openfema_db.js.erb +2 -0
- data/cookbooks/mu-mongo/templates/default/mongo_init.js.erb +1 -0
- data/cookbooks/mu-mongo/templates/default/mongo_logrotate.erb +14 -0
- data/cookbooks/mu-mongo/templates/default/mongo_replset_addnodes.js.erb +6 -0
- data/cookbooks/mu-mongo/templates/default/replset_init.js.erb +2 -0
- data/cookbooks/mu-openvpn/CHANGELOG.md +13 -0
- data/cookbooks/mu-openvpn/LICENSE +37 -0
- data/cookbooks/mu-openvpn/README.md +6 -0
- data/cookbooks/mu-openvpn/attributes/default.rb +119 -0
- data/cookbooks/mu-openvpn/metadata.rb +18 -0
- data/cookbooks/mu-openvpn/recipes/default.rb +108 -0
- data/cookbooks/mu-openvpn/templates/default/users.json.erb +42 -0
- data/cookbooks/mu-php54/CHANGELOG.md +12 -0
- data/cookbooks/mu-php54/LICENSE +37 -0
- data/cookbooks/mu-php54/README.md +0 -0
- data/cookbooks/mu-php54/files/centos/php.ini +1802 -0
- data/cookbooks/mu-php54/files/ubuntu/php.ini +1870 -0
- data/cookbooks/mu-php54/metadata.rb +21 -0
- data/cookbooks/mu-php54/recipes/default.rb +97 -0
- data/cookbooks/mu-splunk/CHANGELOG.md +37 -0
- data/cookbooks/mu-splunk/LICENSE +37 -0
- data/cookbooks/mu-splunk/README.md +451 -0
- data/cookbooks/mu-splunk/attributes/default.rb +95 -0
- data/cookbooks/mu-splunk/attributes/upgrade.rb +49 -0
- data/cookbooks/mu-splunk/definitions/splunk_installer.rb +103 -0
- data/cookbooks/mu-splunk/files/default/splunk-nocheck +10 -0
- data/cookbooks/mu-splunk/libraries/helpers.rb +72 -0
- data/cookbooks/mu-splunk/libraries/splunk_app_provider.rb +156 -0
- data/cookbooks/mu-splunk/libraries/splunk_app_resource.rb +43 -0
- data/cookbooks/mu-splunk/metadata.json +30 -0
- data/cookbooks/mu-splunk/metadata.rb +17 -0
- data/cookbooks/mu-splunk/recipes/client.rb +143 -0
- data/cookbooks/mu-splunk/recipes/default.rb +31 -0
- data/cookbooks/mu-splunk/recipes/disabled.rb +41 -0
- data/cookbooks/mu-splunk/recipes/install_forwarder.rb +23 -0
- data/cookbooks/mu-splunk/recipes/install_server.rb +23 -0
- data/cookbooks/mu-splunk/recipes/server.rb +53 -0
- data/cookbooks/mu-splunk/recipes/service.rb +95 -0
- data/cookbooks/mu-splunk/recipes/setup_auth.rb +49 -0
- data/cookbooks/mu-splunk/recipes/setup_ssl.rb +63 -0
- data/cookbooks/mu-splunk/recipes/upgrade.rb +94 -0
- data/cookbooks/mu-splunk/recipes/user.rb +34 -0
- data/cookbooks/mu-splunk/templates/default/base_logs_unix_inputs.conf.erb +26 -0
- data/cookbooks/mu-splunk/templates/default/inputs.conf.erb +13 -0
- data/cookbooks/mu-splunk/templates/default/outputs.conf.erb +9 -0
- data/cookbooks/mu-splunk/templates/default/splunk-init.erb +74 -0
- data/cookbooks/mu-splunk/templates/default/system-web.conf.erb +7 -0
- data/cookbooks/mu-tools/CHANGELOG.md +12 -0
- data/cookbooks/mu-tools/LICENSE +37 -0
- data/cookbooks/mu-tools/README.md +188 -0
- data/cookbooks/mu-tools/attributes/default.rb +142 -0
- data/cookbooks/mu-tools/attributes/ebs_rolling_snapshots.rb +3 -0
- data/cookbooks/mu-tools/files/amazon/etc/freshclam.conf +235 -0
- data/cookbooks/mu-tools/files/centos/CentOS-Base.repo +52 -0
- data/cookbooks/mu-tools/files/centos/etc/bashrc +93 -0
- data/cookbooks/mu-tools/files/centos/etc/freshclam.conf +235 -0
- data/cookbooks/mu-tools/files/centos/etc/login.defs +72 -0
- data/cookbooks/mu-tools/files/centos/etc/profile +77 -0
- data/cookbooks/mu-tools/files/centos/etc/security/limits.conf +57 -0
- data/cookbooks/mu-tools/files/centos/etc/sysconfig/init +19 -0
- data/cookbooks/mu-tools/files/centos/etc/sysctl.conf +82 -0
- data/cookbooks/mu-tools/files/centos-6/README_MU +0 -0
- data/cookbooks/mu-tools/files/centos-6/etc/audit/stig.rules +173 -0
- data/cookbooks/mu-tools/files/centos-6/etc/bashrc +90 -0
- data/cookbooks/mu-tools/files/centos-6/etc/login.defs +70 -0
- data/cookbooks/mu-tools/files/centos-6/etc/pam.d/su +12 -0
- data/cookbooks/mu-tools/files/centos-6/etc/profile +83 -0
- data/cookbooks/mu-tools/files/centos-6/etc/securetty +12 -0
- data/cookbooks/mu-tools/files/centos-6/etc/sysconfig/init +30 -0
- data/cookbooks/mu-tools/files/centos-6/etc/sysctl.conf +40 -0
- data/cookbooks/mu-tools/files/default/Mu_CA.pem +34 -0
- data/cookbooks/mu-tools/files/default/PSWindowsUpdate.zip +0 -0
- data/cookbooks/mu-tools/files/default/ebs_snapshots.py +123 -0
- data/cookbooks/mu-tools/files/default/etc/BANNER +0 -0
- data/cookbooks/mu-tools/files/default/etc/BANNER-FEDERAL +19 -0
- data/cookbooks/mu-tools/files/default/gpo_no_uac.zip +0 -0
- data/cookbooks/mu-tools/files/default/mypol.pp +0 -0
- data/cookbooks/mu-tools/files/default/mypol.te +37 -0
- data/cookbooks/mu-tools/files/default/nrpe_c7.pp +0 -0
- data/cookbooks/mu-tools/files/default/nrpe_c7.te +31 -0
- data/cookbooks/mu-tools/files/default/nrpe_check_disk.pp +0 -0
- data/cookbooks/mu-tools/files/default/nrpe_check_disk.te +11 -0
- data/cookbooks/mu-tools/files/default/nrpe_disk.pp +0 -0
- data/cookbooks/mu-tools/files/default/nrpe_disk.te +10 -0
- data/cookbooks/mu-tools/files/default/nrpe_file.pp +0 -0
- data/cookbooks/mu-tools/files/default/nrpe_file.te +31 -0
- data/cookbooks/mu-tools/files/default/ntrights +0 -0
- data/cookbooks/mu-tools/files/default/serverclass.conf +18 -0
- data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/app.conf +1 -0
- data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_unix/local/inputs.conf +13 -0
- data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/app.conf +1 -0
- data/cookbooks/mu-tools/files/default/splunk-apps/base_logs_windows/local/inputs.conf +8 -0
- data/cookbooks/mu-tools/files/default/sshd_pol.pp +0 -0
- data/cookbooks/mu-tools/files/default/sshd_pol.te +32 -0
- data/cookbooks/mu-tools/files/redhat/etc/bashrc +93 -0
- data/cookbooks/mu-tools/files/redhat/etc/freshclam.conf +235 -0
- data/cookbooks/mu-tools/files/redhat/etc/login.defs +72 -0
- data/cookbooks/mu-tools/files/redhat/etc/profile +77 -0
- data/cookbooks/mu-tools/files/redhat/etc/security/limits.conf +57 -0
- data/cookbooks/mu-tools/files/redhat/etc/sysconfig/init +19 -0
- data/cookbooks/mu-tools/files/redhat/etc/sysctl.conf +82 -0
- data/cookbooks/mu-tools/files/redhat-6/README_MU +0 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/audit/stig.rules +173 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/bashrc +90 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/login.defs +70 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/pam.d/su +12 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/profile +83 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/securetty +12 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/sysconfig/init +30 -0
- data/cookbooks/mu-tools/files/redhat-6/etc/sysctl.conf +40 -0
- data/cookbooks/mu-tools/files/redhat-7.1/etc/freshclam.conf +235 -0
- data/cookbooks/mu-tools/files/ubuntu-12.04/etc/bash.bashrc +64 -0
- data/cookbooks/mu-tools/files/ubuntu-12.04/etc/common-session +30 -0
- data/cookbooks/mu-tools/files/ubuntu-12.04/etc/login.defs +338 -0
- data/cookbooks/mu-tools/files/ubuntu-12.04/etc/profile +30 -0
- data/cookbooks/mu-tools/files/ubuntu-12.04/etc/security/limits.conf +56 -0
- data/cookbooks/mu-tools/files/ubuntu-12.04/etc/sysctl.conf +60 -0
- data/cookbooks/mu-tools/libraries/helper.rb +292 -0
- data/cookbooks/mu-tools/metadata.rb +28 -0
- data/cookbooks/mu-tools/recipes/add_admin_ssh_keys.rb +35 -0
- data/cookbooks/mu-tools/recipes/apply_security.rb +440 -0
- data/cookbooks/mu-tools/recipes/aws_api.rb +23 -0
- data/cookbooks/mu-tools/recipes/base_repositories.rb +31 -0
- data/cookbooks/mu-tools/recipes/cisbenchmark.rb +59 -0
- data/cookbooks/mu-tools/recipes/clamav.rb +53 -0
- data/cookbooks/mu-tools/recipes/cloudinit.rb +58 -0
- data/cookbooks/mu-tools/recipes/configure_oracle_tools.rb +81 -0
- data/cookbooks/mu-tools/recipes/disable-requiretty.rb +22 -0
- data/cookbooks/mu-tools/recipes/ebs_rolling_snapshots.rb +75 -0
- data/cookbooks/mu-tools/recipes/efs.rb +70 -0
- data/cookbooks/mu-tools/recipes/eks.rb +160 -0
- data/cookbooks/mu-tools/recipes/gcloud.rb +98 -0
- data/cookbooks/mu-tools/recipes/google_api.rb +25 -0
- data/cookbooks/mu-tools/recipes/maldet.rb +67 -0
- data/cookbooks/mu-tools/recipes/nagios.rb +19 -0
- data/cookbooks/mu-tools/recipes/newclient.rb +23 -0
- data/cookbooks/mu-tools/recipes/nrpe.rb +115 -0
- data/cookbooks/mu-tools/recipes/python_pip.rb +35 -0
- data/cookbooks/mu-tools/recipes/retrieve_application.rb +51 -0
- data/cookbooks/mu-tools/recipes/rsyslog.rb +65 -0
- data/cookbooks/mu-tools/recipes/set_local_fw.rb +57 -0
- data/cookbooks/mu-tools/recipes/set_mu_hostname.rb +81 -0
- data/cookbooks/mu-tools/recipes/split_var_partitions.rb +86 -0
- data/cookbooks/mu-tools/recipes/splunk-client.rb +69 -0
- data/cookbooks/mu-tools/recipes/splunk-server.rb +104 -0
- data/cookbooks/mu-tools/recipes/store_inspec_attr.rb +8 -0
- data/cookbooks/mu-tools/recipes/updates.rb +96 -0
- data/cookbooks/mu-tools/recipes/windows-client.rb +202 -0
- data/cookbooks/mu-tools/resources/aws_windows.rb +33 -0
- data/cookbooks/mu-tools/resources/disk.rb +88 -0
- data/cookbooks/mu-tools/resources/mommacat_request.rb +11 -0
- data/cookbooks/mu-tools/resources/scheduled_tasks.rb +29 -0
- data/cookbooks/mu-tools/resources/sshd_service.rb +45 -0
- data/cookbooks/mu-tools/resources/windows_users.rb +242 -0
- data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +168 -0
- data/cookbooks/mu-tools/templates/centos-6/sshd_config.erb +212 -0
- data/cookbooks/mu-tools/templates/centos-7/sshd_config.erb +215 -0
- data/cookbooks/mu-tools/templates/default/0-mu-log-client.conf.erb +13 -0
- data/cookbooks/mu-tools/templates/default/conf.maldet.erb +137 -0
- data/cookbooks/mu-tools/templates/default/etc_hosts.erb +30 -0
- data/cookbooks/mu-tools/templates/default/etc_pamd_password-auth.erb +14 -0
- data/cookbooks/mu-tools/templates/default/etc_pamd_system-auth.erb +14 -0
- data/cookbooks/mu-tools/templates/default/etc_sysconfig_network.erb +12 -0
- data/cookbooks/mu-tools/templates/default/kubeconfig.erb +29 -0
- data/cookbooks/mu-tools/templates/default/kubelet.service.erb +35 -0
- data/cookbooks/mu-tools/templates/default/maldet_scanall.sh.erb +15 -0
- data/cookbooks/mu-tools/templates/default/nrpe.cfg.erb +233 -0
- data/cookbooks/mu-tools/templates/redhat-6/sshd_config.erb +213 -0
- data/cookbooks/mu-tools/templates/redhat-7/sshd_config.erb +215 -0
- data/cookbooks/mu-tools/templates/ubuntu-12.04/sshd_config.erb +146 -0
- data/cookbooks/mu-tools/templates/ubuntu-14.04/sshd_config.erb +145 -0
- data/cookbooks/mu-tools/templates/windows/Backup.xml.erb +20 -0
- data/cookbooks/mu-tools/templates/windows/bkupInfo.xml.erb +1 -0
- data/cookbooks/mu-tools/templates/windows/gpreprt.xml.erb +214 -0
- data/cookbooks/mu-tools/templates/windows/gptmpl.inf.erb +12 -0
- data/cookbooks/mu-tools/templates/windows/manifest.xml.erb +1 -0
- data/cookbooks/mu-tools/templates/windows/set_ad_dns_scheduled_task.ps1.erb +6 -0
- data/cookbooks/mu-tools/templates/windows/sshd_config.erb +136 -0
- data/cookbooks/mu-utility/CHANGELOG.md +12 -0
- data/cookbooks/mu-utility/LICENSE +37 -0
- data/cookbooks/mu-utility/README.md +6 -0
- data/cookbooks/mu-utility/attributes/default.rb +1 -0
- data/cookbooks/mu-utility/libraries/matchers.rb +21 -0
- data/cookbooks/mu-utility/metadata.rb +16 -0
- data/cookbooks/mu-utility/recipes/apt.rb +23 -0
- data/cookbooks/mu-utility/recipes/cleanup_image_helper.rb +118 -0
- data/cookbooks/mu-utility/recipes/iptables.rb +26 -0
- data/cookbooks/mu-utility/recipes/luks.rb +18 -0
- data/cookbooks/mu-utility/recipes/nat.rb +104 -0
- data/cookbooks/mu-utility/recipes/php.rb +33 -0
- data/cookbooks/mu-utility/recipes/rdp_gateway.rb +83 -0
- data/cookbooks/mu-utility/recipes/remi.rb +44 -0
- data/cookbooks/mu-utility/recipes/vim.rb +26 -0
- data/cookbooks/mu-utility/recipes/windows_basics.rb +37 -0
- data/cookbooks/mu-utility/recipes/zip.rb +26 -0
- data/cookbooks/mu-utility/templates/default/BundleConfig.xml.erb +34 -0
- data/cookbooks/mu-utility/templates/default/config.xml.erb +60 -0
- data/cookbooks/nagios/Berksfile +8 -0
- data/cookbooks/nagios/CHANGELOG.md +589 -0
- data/cookbooks/nagios/CONTRIBUTING.md +11 -0
- data/cookbooks/nagios/LICENSE +37 -0
- data/cookbooks/nagios/README.md +328 -0
- data/cookbooks/nagios/TESTING.md +2 -0
- data/cookbooks/nagios/attributes/config.rb +171 -0
- data/cookbooks/nagios/attributes/default.rb +228 -0
- data/cookbooks/nagios/chefignore +102 -0
- data/cookbooks/nagios/definitions/command.rb +33 -0
- data/cookbooks/nagios/definitions/contact.rb +33 -0
- data/cookbooks/nagios/definitions/contactgroup.rb +33 -0
- data/cookbooks/nagios/definitions/host.rb +33 -0
- data/cookbooks/nagios/definitions/hostdependency.rb +33 -0
- data/cookbooks/nagios/definitions/hostescalation.rb +34 -0
- data/cookbooks/nagios/definitions/hostgroup.rb +33 -0
- data/cookbooks/nagios/definitions/nagios_conf.rb +38 -0
- data/cookbooks/nagios/definitions/resource.rb +33 -0
- data/cookbooks/nagios/definitions/service.rb +33 -0
- data/cookbooks/nagios/definitions/servicedependency.rb +33 -0
- data/cookbooks/nagios/definitions/serviceescalation.rb +34 -0
- data/cookbooks/nagios/definitions/servicegroup.rb +33 -0
- data/cookbooks/nagios/definitions/timeperiod.rb +33 -0
- data/cookbooks/nagios/libraries/base.rb +314 -0
- data/cookbooks/nagios/libraries/command.rb +91 -0
- data/cookbooks/nagios/libraries/contact.rb +230 -0
- data/cookbooks/nagios/libraries/contactgroup.rb +112 -0
- data/cookbooks/nagios/libraries/custom_option.rb +36 -0
- data/cookbooks/nagios/libraries/data_bag_helper.rb +23 -0
- data/cookbooks/nagios/libraries/default.rb +90 -0
- data/cookbooks/nagios/libraries/host.rb +412 -0
- data/cookbooks/nagios/libraries/hostdependency.rb +181 -0
- data/cookbooks/nagios/libraries/hostescalation.rb +173 -0
- data/cookbooks/nagios/libraries/hostgroup.rb +119 -0
- data/cookbooks/nagios/libraries/nagios.rb +282 -0
- data/cookbooks/nagios/libraries/resource.rb +59 -0
- data/cookbooks/nagios/libraries/service.rb +455 -0
- data/cookbooks/nagios/libraries/servicedependency.rb +215 -0
- data/cookbooks/nagios/libraries/serviceescalation.rb +195 -0
- data/cookbooks/nagios/libraries/servicegroup.rb +144 -0
- data/cookbooks/nagios/libraries/timeperiod.rb +160 -0
- data/cookbooks/nagios/libraries/users_helper.rb +54 -0
- data/cookbooks/nagios/metadata.rb +25 -0
- data/cookbooks/nagios/recipes/_load_databag_config.rb +153 -0
- data/cookbooks/nagios/recipes/_load_default_config.rb +241 -0
- data/cookbooks/nagios/recipes/apache.rb +48 -0
- data/cookbooks/nagios/recipes/default.rb +204 -0
- data/cookbooks/nagios/recipes/nginx.rb +82 -0
- data/cookbooks/nagios/recipes/pagerduty.rb +143 -0
- data/cookbooks/nagios/recipes/server_package.rb +40 -0
- data/cookbooks/nagios/recipes/server_source.rb +164 -0
- data/cookbooks/nagios/templates/default/apache2.conf.erb +96 -0
- data/cookbooks/nagios/templates/default/cgi.cfg.erb +266 -0
- data/cookbooks/nagios/templates/default/commands.cfg.erb +13 -0
- data/cookbooks/nagios/templates/default/contacts.cfg.erb +37 -0
- data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +25 -0
- data/cookbooks/nagios/templates/default/hosts.cfg.erb +15 -0
- data/cookbooks/nagios/templates/default/htpasswd.users.erb +6 -0
- data/cookbooks/nagios/templates/default/nagios.cfg.erb +22 -0
- data/cookbooks/nagios/templates/default/nginx.conf.erb +62 -0
- data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +185 -0
- data/cookbooks/nagios/templates/default/resource.cfg.erb +27 -0
- data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +15 -0
- data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +14 -0
- data/cookbooks/nagios/templates/default/services.cfg.erb +14 -0
- data/cookbooks/nagios/templates/default/templates.cfg.erb +31 -0
- data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +13 -0
- data/cookbooks/s3fs/CHANGELOG.md +13 -0
- data/cookbooks/s3fs/LICENSE +37 -0
- data/cookbooks/s3fs/README.md +6 -0
- data/cookbooks/s3fs/attributes/default.rb +15 -0
- data/cookbooks/s3fs/files/default/fuse-2.9.3.zip +0 -0
- data/cookbooks/s3fs/metadata.rb +16 -0
- data/cookbooks/s3fs/recipes/default.rb +91 -0
- data/data_bags/demo/app.json +7 -0
- data/data_bags/nagios_services/chef.json +6 -0
- data/data_bags/nagios_services/linux_diskspace.json +5 -0
- data/data_bags/nagios_services/momma_cat.json +6 -0
- data/data_bags/nagios_services/mu-master-memory.json +5 -0
- data/data_bags/nagios_services/nagios_ui.json +6 -0
- data/data_bags/nagios_services/node_ssh.json +6 -0
- data/data_bags/nagios_services/ssh.json +6 -0
- data/demo/lambda_test.yaml +29 -0
- data/environments/DEV.json +8 -0
- data/environments/PROD.json +8 -0
- data/environments/dev.json +8 -0
- data/environments/development.json +8 -0
- data/environments/prod.json +8 -0
- data/extras/README.md +1 -0
- data/extras/admin-role-binding.yaml +16 -0
- data/extras/admin-user.yaml +6 -0
- data/extras/aws-auth-cm.yaml.erb +12 -0
- data/extras/clean-stock-amis +48 -0
- data/extras/git-fix-permissions-hook +12 -0
- data/extras/gitlab-eks-helper.sh.erb +20 -0
- data/extras/image-generators/README.md +2 -0
- data/extras/image-generators/aws/centos6.yaml +18 -0
- data/extras/image-generators/aws/centos7-govcloud.yaml +24 -0
- data/extras/image-generators/aws/centos7.yaml +17 -0
- data/extras/image-generators/aws/rhel7.yaml +17 -0
- data/extras/image-generators/aws/win2k12.yaml +16 -0
- data/extras/image-generators/aws/win2k16.yaml +16 -0
- data/extras/image-generators/aws/windows.yaml +18 -0
- data/extras/image-generators/gcp/centos6.yaml +17 -0
- data/extras/lambda_waf_domain_blacklist.py +103 -0
- data/extras/platform_berksfile_base +50 -0
- data/extras/ruby_rpm/build.sh +17 -0
- data/extras/ruby_rpm/muby.spec +44 -0
- data/extras/vault_tools/README.md +6 -0
- data/extras/vault_tools/export_vaults.sh +3 -0
- data/extras/vault_tools/recreate_vaults.sh +5 -0
- data/extras/vault_tools/test_vaults.sh +5 -0
- data/install/README.md +8 -0
- data/install/cfn_create_mu_master.json +1034 -0
- data/install/chef-server.rb.erb +19 -0
- data/install/deprecated-bash-library.sh +1891 -0
- data/install/images/Usage.png +0 -0
- data/install/installer +71 -0
- data/install/jenkinskeys.rb +8 -0
- data/install/user-dot-murc.erb +14 -0
- data/modules/html.erb +19 -0
- data/modules/mommacat.ru +426 -0
- data/modules/mu/cleanup.rb +339 -0
- data/modules/mu/cloud.rb +1446 -0
- data/modules/mu/clouds/README.md +201 -0
- data/modules/mu/clouds/aws/alarm.rb +319 -0
- data/modules/mu/clouds/aws/cache_cluster.rb +1010 -0
- data/modules/mu/clouds/aws/collection.rb +373 -0
- data/modules/mu/clouds/aws/container_cluster.rb +667 -0
- data/modules/mu/clouds/aws/database.rb +1836 -0
- data/modules/mu/clouds/aws/dnszone.rb +911 -0
- data/modules/mu/clouds/aws/firewall_rule.rb +641 -0
- data/modules/mu/clouds/aws/folder.rb +92 -0
- data/modules/mu/clouds/aws/function.rb +349 -0
- data/modules/mu/clouds/aws/group.rb +251 -0
- data/modules/mu/clouds/aws/loadbalancer.rb +888 -0
- data/modules/mu/clouds/aws/log.rb +363 -0
- data/modules/mu/clouds/aws/msg_queue.rb +480 -0
- data/modules/mu/clouds/aws/notification.rb +139 -0
- data/modules/mu/clouds/aws/role.rb +656 -0
- data/modules/mu/clouds/aws/search_domain.rb +646 -0
- data/modules/mu/clouds/aws/server.rb +2294 -0
- data/modules/mu/clouds/aws/server_pool.rb +1388 -0
- data/modules/mu/clouds/aws/storage_pool.rb +495 -0
- data/modules/mu/clouds/aws/user.rb +382 -0
- data/modules/mu/clouds/aws/userdata/README.md +4 -0
- data/modules/mu/clouds/aws/userdata/linux.erb +179 -0
- data/modules/mu/clouds/aws/userdata/windows.erb +278 -0
- data/modules/mu/clouds/aws/vpc.rb +1943 -0
- data/modules/mu/clouds/aws.rb +1009 -0
- data/modules/mu/clouds/cloudformation/alarm.rb +146 -0
- data/modules/mu/clouds/cloudformation/cache_cluster.rb +167 -0
- data/modules/mu/clouds/cloudformation/collection.rb +117 -0
- data/modules/mu/clouds/cloudformation/database.rb +278 -0
- data/modules/mu/clouds/cloudformation/dnszone.rb +274 -0
- data/modules/mu/clouds/cloudformation/firewall_rule.rb +308 -0
- data/modules/mu/clouds/cloudformation/loadbalancer.rb +193 -0
- data/modules/mu/clouds/cloudformation/log.rb +170 -0
- data/modules/mu/clouds/cloudformation/server.rb +370 -0
- data/modules/mu/clouds/cloudformation/server_pool.rb +279 -0
- data/modules/mu/clouds/cloudformation/vpc.rb +322 -0
- data/modules/mu/clouds/cloudformation.rb +733 -0
- data/modules/mu/clouds/docker.rb +30 -0
- data/modules/mu/clouds/google/container_cluster.rb +290 -0
- data/modules/mu/clouds/google/database.rb +152 -0
- data/modules/mu/clouds/google/firewall_rule.rb +267 -0
- data/modules/mu/clouds/google/group.rb +164 -0
- data/modules/mu/clouds/google/loadbalancer.rb +479 -0
- data/modules/mu/clouds/google/server.rb +1510 -0
- data/modules/mu/clouds/google/server_pool.rb +274 -0
- data/modules/mu/clouds/google/user.rb +266 -0
- data/modules/mu/clouds/google/userdata/README.md +4 -0
- data/modules/mu/clouds/google/userdata/linux.erb +137 -0
- data/modules/mu/clouds/google/userdata/windows.erb +275 -0
- data/modules/mu/clouds/google/vpc.rb +890 -0
- data/modules/mu/clouds/google.rb +811 -0
- data/modules/mu/config/README.md +11 -0
- data/modules/mu/config/alarm.rb +271 -0
- data/modules/mu/config/cache_cluster.rb +172 -0
- data/modules/mu/config/collection.rb +87 -0
- data/modules/mu/config/container_cluster.rb +103 -0
- data/modules/mu/config/container_cluster.yml +36 -0
- data/modules/mu/config/database.rb +458 -0
- data/modules/mu/config/database.yml +26 -0
- data/modules/mu/config/dnszone.rb +327 -0
- data/modules/mu/config/firewall_rule.rb +118 -0
- data/modules/mu/config/folder.rb +70 -0
- data/modules/mu/config/function.rb +140 -0
- data/modules/mu/config/group.rb +64 -0
- data/modules/mu/config/loadbalancer.rb +482 -0
- data/modules/mu/config/log.rb +47 -0
- data/modules/mu/config/log.yml +6 -0
- data/modules/mu/config/msg_queue.rb +47 -0
- data/modules/mu/config/msg_queue.yml +9 -0
- data/modules/mu/config/notification.rb +44 -0
- data/modules/mu/config/project.rb +71 -0
- data/modules/mu/config/role.rb +102 -0
- data/modules/mu/config/search_domain.rb +61 -0
- data/modules/mu/config/search_domain.yml +25 -0
- data/modules/mu/config/server.rb +587 -0
- data/modules/mu/config/server.yml +8 -0
- data/modules/mu/config/server_pool.rb +216 -0
- data/modules/mu/config/server_pool.yml +71 -0
- data/modules/mu/config/storage_pool.rb +145 -0
- data/modules/mu/config/user.rb +78 -0
- data/modules/mu/config/vpc.rb +743 -0
- data/modules/mu/config/vpc.yml +6 -0
- data/modules/mu/config.rb +2000 -0
- data/modules/mu/defaults/README.md +2 -0
- data/modules/mu/defaults/amazon_images.yaml +121 -0
- data/modules/mu/defaults/google_images.yaml +16 -0
- data/modules/mu/deploy.rb +686 -0
- data/modules/mu/groomer.rb +123 -0
- data/modules/mu/groomers/README.md +58 -0
- data/modules/mu/groomers/chef.rb +1024 -0
- data/modules/mu/kittens.rb +11319 -0
- data/modules/mu/logger.rb +208 -0
- data/modules/mu/master/README.md +27 -0
- data/modules/mu/master/chef.rb +471 -0
- data/modules/mu/master/ldap.rb +1005 -0
- data/modules/mu/master.rb +415 -0
- data/modules/mu/mommacat.rb +2703 -0
- data/modules/mu-load-config.rb +1 -0
- data/modules/mu.rb +724 -0
- data/modules/scratchpad.erb +1 -0
- data/modules/tests/super_complex_bok.yml +41 -0
- data/modules/tests/super_simple_bok.yml +40 -0
- data/mu.gemspec +62 -0
- data/roles/demo-dbservice-configure.json +19 -0
- data/roles/demo-portal-configure.json +19 -0
- data/roles/mu-master-jenkins.json +24 -0
- data/roles/mu-master-nagios-only.json +13 -0
- data/roles/mu-master.json +12 -0
- data/roles/mu-node.json +19 -0
- data/roles/mu-splunk-server.json +13 -0
- data/roles/mu-splunk.json +13 -0
- data/test/clean_up.py +25 -0
- data/test/demo-test-profile/README.md +3 -0
- data/test/demo-test-profile/controls/flask.rb +84 -0
- data/test/demo-test-profile/inspec.lock +7 -0
- data/test/demo-test-profile/inspec.yml +11 -0
- data/test/etco-test-profile/README.md +3 -0
- data/test/etco-test-profile/controls/all-in-one.rb +182 -0
- data/test/etco-test-profile/inspec.lock +7 -0
- data/test/etco-test-profile/inspec.yml +11 -0
- data/test/exec_inspec.py +246 -0
- data/test/exec_mu_install.py +241 -0
- data/test/exec_retry.py +44 -0
- data/test/mu-master-test/README.md +3 -0
- data/test/mu-master-test/controls/all_in_one.rb +557 -0
- data/test/mu-master-test/inspec.lock +3 -0
- data/test/mu-master-test/inspec.yml +11 -0
- data/test/mu-tools-test/README.md +3 -0
- data/test/mu-tools-test/controls/base.rb +265 -0
- data/test/mu-tools-test/inspec.lock +3 -0
- data/test/mu-tools-test/inspec.yml +8 -0
- data/test/simple-server-php-test/README.md +3 -0
- data/test/simple-server-php-test/controls/apachephp.rb +25 -0
- data/test/simple-server-php-test/controls/example.rb +19 -0
- data/test/simple-server-php-test/inspec.lock +7 -0
- data/test/simple-server-php-test/inspec.yml +12 -0
- data/test/simple-server-rails-test/README.md +3 -0
- data/test/simple-server-rails-test/controls/rails.rb +188 -0
- data/test/simple-server-rails-test/inspec.lock +7 -0
- data/test/simple-server-rails-test/inspec.yml +11 -0
- data/test/simple-windows-test/README.md +3 -0
- data/test/simple-windows-test/controls/windows.rb +20 -0
- data/test/simple-windows-test/inspec.lock +7 -0
- data/test/simple-windows-test/inspec.yml +11 -0
- data/test/smoke_test.rb +75 -0
- data/test/wordpress-test/README.md +3 -0
- data/test/wordpress-test/controls/wordpress.rb +97 -0
- data/test/wordpress-test/inspec.lock +7 -0
- data/test/wordpress-test/inspec.yml +11 -0
- metadata +979 -0
@@ -0,0 +1,2000 @@
|
|
1
|
+
# Copyright:: Copyright (c) 2014 eGlobalTech, Inc., all rights reserved
|
2
|
+
#
|
3
|
+
# Licensed under the BSD-3 license (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License in the root of the project or at
|
6
|
+
#
|
7
|
+
# http://egt-labs.com/mu/LICENSE.html
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'rubygems'
|
16
|
+
require 'json'
|
17
|
+
require 'erb'
|
18
|
+
require 'pp'
|
19
|
+
require 'json-schema'
|
20
|
+
require 'net/http'
|
21
|
+
autoload :GraphViz, 'graphviz'
|
22
|
+
autoload :ChronicDuration, 'chronic_duration'
|
23
|
+
|
24
|
+
module MU
|
25
|
+
|
26
|
+
# Methods and structures for parsing Mu's configuration files. See also {MU::Config::BasketofKittens}.
|
27
|
+
class Config
|
28
|
+
# Exception class for BoK parse or validation errors
|
29
|
+
class ValidationError < MU::MuError
|
30
|
+
end
|
31
|
+
# Exception class for deploy parameter (mu-deploy -p foo=bar) errors
|
32
|
+
class DeployParamError < MuError
|
33
|
+
end
|
34
|
+
|
35
|
+
# The default cloud provider for new resources. Must exist in MU.supportedClouds
|
36
|
+
def self.defaultCloud
|
37
|
+
begin
|
38
|
+
MU.myCloud
|
39
|
+
rescue NoMethodError
|
40
|
+
"AWS"
|
41
|
+
end
|
42
|
+
if MU::Cloud::Google.hosted
|
43
|
+
"Google"
|
44
|
+
elsif MU::Cloud::AWS.hosted
|
45
|
+
"AWS"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# The default grooming agent for new resources. Must exist in MU.supportedGroomers.
|
50
|
+
def self.defaultGroomer
|
51
|
+
"Chef"
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_accessor :nat_routes
|
55
|
+
attr_reader :skipinitialupdates
|
56
|
+
|
57
|
+
attr_reader :google_images
|
58
|
+
@@google_images = YAML.load(File.read("#{MU.myRoot}/modules/mu/defaults/google_images.yaml"))
|
59
|
+
if File.exists?("#{MU.etcDir}/google_images.yaml")
|
60
|
+
custom = YAML.load(File.read("#{MU.etcDir}/google_images.yaml"))
|
61
|
+
@@google_images.merge!(custom) { |key, oldval, newval|
|
62
|
+
if !oldval.is_a?(Hash) and !newval.nil?
|
63
|
+
if !newval.nil?
|
64
|
+
newval
|
65
|
+
else
|
66
|
+
oldval
|
67
|
+
end
|
68
|
+
else
|
69
|
+
oldval.merge(newval)
|
70
|
+
end
|
71
|
+
}
|
72
|
+
end
|
73
|
+
# The list of known Google Images which we can use for a given platform
|
74
|
+
def self.google_images
|
75
|
+
@@google_images
|
76
|
+
end
|
77
|
+
|
78
|
+
attr_reader :amazon_images
|
79
|
+
@@amazon_images = YAML.load(File.read("#{MU.myRoot}/modules/mu/defaults/amazon_images.yaml"))
|
80
|
+
if File.exists?("#{MU.etcDir}/amazon_images.yaml")
|
81
|
+
custom = YAML.load(File.read("#{MU.etcDir}/amazon_images.yaml"))
|
82
|
+
@@amazon_images.merge!(custom) { |key, oldval, newval|
|
83
|
+
if !oldval.is_a?(Hash) and !newval.nil?
|
84
|
+
if !newval.nil?
|
85
|
+
newval
|
86
|
+
else
|
87
|
+
oldval
|
88
|
+
end
|
89
|
+
else
|
90
|
+
oldval.merge(newval)
|
91
|
+
end
|
92
|
+
}
|
93
|
+
end
|
94
|
+
# The list of known Amazon AMIs, by region, which we can use for a given
|
95
|
+
# platform.
|
96
|
+
def self.amazon_images
|
97
|
+
@@amazon_images
|
98
|
+
end
|
99
|
+
|
100
|
+
@@config_path = nil
|
101
|
+
# The path to the most recently loaded configuration file
|
102
|
+
attr_reader :config_path
|
103
|
+
# The path to the most recently loaded configuration file
|
104
|
+
def self.config_path
|
105
|
+
@@config_path
|
106
|
+
end
|
107
|
+
|
108
|
+
# Accessor for our Basket of Kittens schema definition
|
109
|
+
def self.schema
|
110
|
+
@@schema
|
111
|
+
end
|
112
|
+
|
113
|
+
# Deep merge a configuration hash so we can meld different cloud providers'
|
114
|
+
# schemas together, while preserving documentation differences
|
115
|
+
def self.schemaMerge(orig, new, cloud)
|
116
|
+
if new.is_a?(Hash)
|
117
|
+
new.each_pair { |k, v|
|
118
|
+
if orig and orig.has_key?(k)
|
119
|
+
schemaMerge(orig[k], new[k], cloud)
|
120
|
+
elsif orig
|
121
|
+
orig[k] = new[k]
|
122
|
+
else
|
123
|
+
orig = new
|
124
|
+
end
|
125
|
+
}
|
126
|
+
elsif orig.is_a?(Array) and new
|
127
|
+
orig.concat(new)
|
128
|
+
orig.uniq!
|
129
|
+
elsif new.is_a?(String)
|
130
|
+
orig ||= ""
|
131
|
+
orig += "\n#{cloud.upcase}: "+new
|
132
|
+
else
|
133
|
+
# XXX I think this is a NOOP?
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Accessor for our Basket of Kittens schema definition, with various
|
138
|
+
# cloud-specific details merged so we can generate documentation for them.
|
139
|
+
def self.docSchema
|
140
|
+
docschema = Marshal.load(Marshal.dump(@@schema))
|
141
|
+
only_children = {}
|
142
|
+
MU::Cloud.resource_types.each_pair { |classname, attrs|
|
143
|
+
MU::Cloud.supportedClouds.each { |cloud|
|
144
|
+
begin
|
145
|
+
require "mu/clouds/#{cloud.downcase}/#{attrs[:cfg_name]}"
|
146
|
+
rescue LoadError => e
|
147
|
+
next
|
148
|
+
end
|
149
|
+
res_class = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(classname)
|
150
|
+
required, res_schema = res_class.schema(self)
|
151
|
+
res_schema.each { |key, cfg|
|
152
|
+
if !docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
|
153
|
+
only_children[attrs[:cfg_plural]] ||= {}
|
154
|
+
only_children[attrs[:cfg_plural]][key] ||= {}
|
155
|
+
only_children[attrs[:cfg_plural]][key][cloud] = cfg
|
156
|
+
end
|
157
|
+
}
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
# recursively chase down description fields in arrays and objects of our
|
162
|
+
# schema and prepend stuff to them for documentation
|
163
|
+
def self.prepend_descriptions(prefix, cfg)
|
164
|
+
# cfg["description"] ||= ""
|
165
|
+
# cfg["description"] = prefix+cfg["description"]
|
166
|
+
cfg["prefix"] = prefix
|
167
|
+
if cfg["type"] == "array" and cfg["items"]
|
168
|
+
cfg["items"] = prepend_descriptions(prefix, cfg["items"])
|
169
|
+
elsif cfg["type"] == "object" and cfg["properties"]
|
170
|
+
cfg["properties"].each_pair { |key, subcfg|
|
171
|
+
cfg["properties"][key] = prepend_descriptions(prefix, cfg["properties"][key])
|
172
|
+
}
|
173
|
+
end
|
174
|
+
cfg
|
175
|
+
end
|
176
|
+
|
177
|
+
MU::Cloud.resource_types.each_pair { |classname, attrs|
|
178
|
+
MU::Cloud.supportedClouds.each { |cloud|
|
179
|
+
res_class = nil
|
180
|
+
begin
|
181
|
+
res_class = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(classname)
|
182
|
+
rescue MU::Cloud::MuCloudResourceNotImplemented
|
183
|
+
next
|
184
|
+
end
|
185
|
+
required, res_schema = res_class.schema(self)
|
186
|
+
next if required.size == 0 and res_schema.size == 0
|
187
|
+
res_schema.each { |key, cfg|
|
188
|
+
cfg["description"] ||= ""
|
189
|
+
cfg["description"] = "+"+cloud.upcase+"+: "+cfg["description"]
|
190
|
+
if docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
|
191
|
+
schemaMerge(docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key], cfg, cloud)
|
192
|
+
docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["description"] ||= ""
|
193
|
+
docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["description"] += "\n"+cfg["description"]
|
194
|
+
MU.log "Munging #{cloud}-specific #{classname.to_s} schema into BasketofKittens => #{attrs[:cfg_plural]} => #{key}", MU::DEBUG, details: docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]
|
195
|
+
else
|
196
|
+
if only_children[attrs[:cfg_plural]][key]
|
197
|
+
prefix = only_children[attrs[:cfg_plural]][key].keys.map{ |x| x.upcase }.join(" & ")+" ONLY"
|
198
|
+
cfg = prepend_descriptions(prefix, cfg)
|
199
|
+
end
|
200
|
+
|
201
|
+
docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key] = cfg
|
202
|
+
end
|
203
|
+
docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["clouds"] = {}
|
204
|
+
docschema["properties"][attrs[:cfg_plural]]["items"]["properties"][key]["clouds"][cloud] = cfg
|
205
|
+
}
|
206
|
+
|
207
|
+
docschema['required'].concat(required)
|
208
|
+
docschema['required'].uniq!
|
209
|
+
}
|
210
|
+
}
|
211
|
+
|
212
|
+
docschema
|
213
|
+
end
|
214
|
+
|
215
|
+
attr_reader :config
|
216
|
+
|
217
|
+
@@parameters = {}
|
218
|
+
@@user_supplied_parameters = {}
|
219
|
+
attr_reader :parameters
|
220
|
+
# Accessor for parameters to our Basket of Kittens
|
221
|
+
def self.parameters
|
222
|
+
@@parameters
|
223
|
+
end
|
224
|
+
@@tails = {}
|
225
|
+
attr_reader :tails
|
226
|
+
# Accessor for tails in our Basket of Kittens. This should be a superset of
|
227
|
+
# user-supplied parameters. It also has machine-generated parameterized
|
228
|
+
# behaviors.
|
229
|
+
def self.tails
|
230
|
+
@@tails
|
231
|
+
end
|
232
|
+
|
233
|
+
# Run through a config hash and return a version with all
|
234
|
+
# {MU::Config::Tail} endpoints converted to plain strings. Useful for cloud
|
235
|
+
# layers that don't care about the metadata in Tails.
|
236
|
+
# @param config [Hash]: The configuration tree to convert
|
237
|
+
# @return [Hash]: The modified configuration
|
238
|
+
def self.manxify(config)
|
239
|
+
if config.is_a?(Hash)
|
240
|
+
config.each_pair { |key, val|
|
241
|
+
config[key] = self.manxify(val)
|
242
|
+
}
|
243
|
+
elsif config.is_a?(Array)
|
244
|
+
config.each { |val|
|
245
|
+
val = self.manxify(val)
|
246
|
+
}
|
247
|
+
elsif config.is_a?(MU::Config::Tail)
|
248
|
+
return config.to_s
|
249
|
+
end
|
250
|
+
return config
|
251
|
+
end
|
252
|
+
|
253
|
+
# A wrapper for config leaves that came from ERB parameters instead of raw
|
254
|
+
# YAML or JSON. Will behave like a string for things that expect that
|
255
|
+
# sort of thing. Code that needs to know that this leaf was the result of
|
256
|
+
# a parameter will be able to tell by the object class being something
|
257
|
+
# other than a plain string, array, or hash.
|
258
|
+
class Tail
|
259
|
+
@value = nil
|
260
|
+
@name = nil
|
261
|
+
@prettyname = nil
|
262
|
+
@description = nil
|
263
|
+
@prefix = ""
|
264
|
+
@suffix = ""
|
265
|
+
@is_list_element = false
|
266
|
+
@pseudo = false
|
267
|
+
@runtimecode = nil
|
268
|
+
@valid_values = []
|
269
|
+
@index = 0
|
270
|
+
attr_reader :description
|
271
|
+
attr_reader :pseudo
|
272
|
+
attr_reader :index
|
273
|
+
attr_reader :value
|
274
|
+
attr_reader :runtimecode
|
275
|
+
attr_reader :valid_values
|
276
|
+
attr_reader :is_list_element
|
277
|
+
|
278
|
+
def initialize(name, value, prettyname = nil, cloudtype = "String", valid_values = [], description = "", is_list_element = false, prefix: "", suffix: "", pseudo: false, runtimecode: nil, index: 0)
|
279
|
+
@name = name
|
280
|
+
@value = value
|
281
|
+
@valid_values = valid_values
|
282
|
+
@pseudo = pseudo
|
283
|
+
@index = index
|
284
|
+
@runtimecode = runtimecode
|
285
|
+
@cloudtype = cloudtype
|
286
|
+
@is_list_element = is_list_element
|
287
|
+
@description ||=
|
288
|
+
if !description.nil?
|
289
|
+
description
|
290
|
+
else
|
291
|
+
""
|
292
|
+
end
|
293
|
+
@prettyname ||=
|
294
|
+
if !prettyname.nil?
|
295
|
+
prettyname
|
296
|
+
else
|
297
|
+
@name.capitalize.gsub(/[^a-z0-9]/i, "")
|
298
|
+
end
|
299
|
+
@prefix = prefix if !prefix.nil?
|
300
|
+
@suffix = suffix if !suffix.nil?
|
301
|
+
end
|
302
|
+
|
303
|
+
# Return the parameter name of this Tail
|
304
|
+
def getName
|
305
|
+
@name
|
306
|
+
end
|
307
|
+
# Return the platform-specific cloud type of this Tail
|
308
|
+
def getCloudType
|
309
|
+
@cloudtype
|
310
|
+
end
|
311
|
+
# Return the human-friendly name of this Tail
|
312
|
+
def getPrettyName
|
313
|
+
@prettyname
|
314
|
+
end
|
315
|
+
# Walk like a String
|
316
|
+
def to_s
|
317
|
+
@prefix+@value+@suffix
|
318
|
+
end
|
319
|
+
# Quack like a String
|
320
|
+
def to_str
|
321
|
+
to_s
|
322
|
+
end
|
323
|
+
# Upcase like a String
|
324
|
+
def upcase
|
325
|
+
to_s.upcase
|
326
|
+
end
|
327
|
+
# Downcase like a String
|
328
|
+
def downcase
|
329
|
+
to_s.downcase
|
330
|
+
end
|
331
|
+
# Check for emptiness like a String
|
332
|
+
def empty?
|
333
|
+
to_s.empty?
|
334
|
+
end
|
335
|
+
# Match like a String
|
336
|
+
def match(*args)
|
337
|
+
to_s.match(*args)
|
338
|
+
end
|
339
|
+
# Check for equality like a String
|
340
|
+
def ==(o)
|
341
|
+
(o.class == self.class or o.class == "String") && o.to_s == to_s
|
342
|
+
end
|
343
|
+
# Perform global substitutions like a String
|
344
|
+
def gsub(*args)
|
345
|
+
to_s.gsub(*args)
|
346
|
+
end
|
347
|
+
end
|
348
|
+
|
349
|
+
# Wrapper method for creating a {MU::Config::Tail} object as a reference to
|
350
|
+
# a parameter that's valid in the loaded configuration.
|
351
|
+
# @param param [<String>]: The name of the parameter to which this should be tied.
|
352
|
+
# @param value [<String>]: The value of the parameter to return when asked
|
353
|
+
# @param prettyname [<String>]: A human-friendly parameter name to be used when generating CloudFormation templates and the like
|
354
|
+
# @param cloudtype [<String>]: A platform-specific identifier used by cloud layers to identify a parameter's type, e.g. AWS::EC2::VPC::Id
|
355
|
+
# @param valid_values [Array<String>]: A list of acceptable String values for the given parameter.
|
356
|
+
# @param description [<String>]: A long-form description of what the parameter does.
|
357
|
+
# @param list_of [<String>]: Indicates that the value should be treated as a member of a list (array) by the cloud layer.
|
358
|
+
# @param prefix [<String>]: A static String that should be prefixed to the stored value when queried
|
359
|
+
# @param suffix [<String>]: A static String that should be appended to the stored value when queried
|
360
|
+
# @param pseudo [<Boolean>]: This is a pseudo-parameter, automatically provided, and not available as user input.
|
361
|
+
# @param runtimecode [<String>]: Actual code to allow the cloud layer to interpret literally in its own idiom, e.g. '"Ref" : "AWS::StackName"' for CloudFormation
|
362
|
+
def getTail(param, value: nil, prettyname: nil, cloudtype: "String", valid_values: [], description: nil, list_of: nil, prefix: "", suffix: "", pseudo: false, runtimecode: nil)
|
363
|
+
if value.nil?
|
364
|
+
if @@parameters.nil? or !@@parameters.has_key?(param)
|
365
|
+
MU.log "Parameter '#{param}' (#{param.class.name}) referenced in config but not provided (#{caller[0]})", MU::DEBUG, details: @@parameters
|
366
|
+
return nil
|
367
|
+
# raise DeployParamError
|
368
|
+
else
|
369
|
+
value = @@parameters[param]
|
370
|
+
end
|
371
|
+
end
|
372
|
+
if !prettyname.nil?
|
373
|
+
prettyname.gsub!(/[^a-z0-9]/i, "") # comply with CloudFormation restrictions
|
374
|
+
end
|
375
|
+
if value.is_a?(MU::Config::Tail)
|
376
|
+
MU.log "Parameter #{param} is using a nested parameter as a value. This rarely works, depending on the target cloud. YMMV.", MU::WARN
|
377
|
+
tail = MU::Config::Tail.new(param, value, prettyname, cloudtype, valid_values, description, prefix: prefix, suffix: suffix, pseudo: pseudo, runtimecode: runtimecode)
|
378
|
+
elsif !list_of.nil? or (@@tails.has_key?(param) and @@tails[param].is_a?(Array))
|
379
|
+
tail = []
|
380
|
+
count = 0
|
381
|
+
value.split(/\s*,\s*/).each { |subval|
|
382
|
+
if @@tails.has_key?(param) and !@@tails[param][count].nil?
|
383
|
+
subval = @@tails[param][count].values.first.to_s if subval.nil?
|
384
|
+
list_of = @@tails[param][count].values.first.getName if list_of.nil?
|
385
|
+
prettyname = @@tails[param][count].values.first.getPrettyName if prettyname.nil?
|
386
|
+
description = @@tails[param][count].values.first.description if description.nil?
|
387
|
+
valid_values = @@tails[param][count].values.first.valid_values if valid_values.nil? or valid_values.empty?
|
388
|
+
cloudtype = @@tails[param][count].values.first.getCloudType if @@tails[param][count].values.first.getCloudType != "String"
|
389
|
+
end
|
390
|
+
prettyname = param.capitalize if prettyname.nil?
|
391
|
+
tail << { list_of => MU::Config::Tail.new(list_of, subval, prettyname, cloudtype, valid_values, description, true, pseudo: pseudo, index: count) }
|
392
|
+
count = count + 1
|
393
|
+
}
|
394
|
+
else
|
395
|
+
if @@tails.has_key?(param)
|
396
|
+
pseudo = @@tails[param].pseudo
|
397
|
+
value = @@tails[param].to_s if value.nil?
|
398
|
+
prettyname = @@tails[param].getPrettyName if prettyname.nil?
|
399
|
+
description = @@tails[param].description if description.nil?
|
400
|
+
valid_values = @@tails[param].valid_values if valid_values.nil? or valid_values.empty?
|
401
|
+
cloudtype = @@tails[param].getCloudType if @@tails[param].getCloudType != "String"
|
402
|
+
end
|
403
|
+
tail = MU::Config::Tail.new(param, value, prettyname, cloudtype, valid_values, description, prefix: prefix, suffix: suffix, pseudo: pseudo, runtimecode: runtimecode)
|
404
|
+
end
|
405
|
+
|
406
|
+
if valid_values and valid_values.size > 0 and value
|
407
|
+
if !valid_values.include?(value)
|
408
|
+
raise DeployParamError, "Invalid parameter value '#{value}' supplied for '#{param}'"
|
409
|
+
end
|
410
|
+
end
|
411
|
+
@@tails[param] = tail
|
412
|
+
|
413
|
+
tail
|
414
|
+
end
|
415
|
+
|
416
|
+
# Load up our YAML or JSON and parse it through ERB, optionally substituting
|
417
|
+
# externally-supplied parameters.
|
418
|
+
def resolveConfig(path: @@config_path, param_pass: false)
|
419
|
+
config = nil
|
420
|
+
@param_pass = param_pass
|
421
|
+
|
422
|
+
# Catch calls to missing variables in Basket of Kittens files when being
|
423
|
+
# parsed by ERB, and replace with placeholders for parameters. This
|
424
|
+
# method_missing is only defined innside {MU::Config.resolveConfig}
|
425
|
+
def method_missing(var_name)
|
426
|
+
if @param_pass
|
427
|
+
"MU::Config.getTail PLACEHOLDER #{var_name} REDLOHECALP"
|
428
|
+
else
|
429
|
+
tail = getTail(var_name.to_s)
|
430
|
+
if tail.is_a?(Array)
|
431
|
+
if @param_pass
|
432
|
+
return tail.map {|f| f.values.first.to_s }.join(",")
|
433
|
+
else
|
434
|
+
# Don't try to jam complex types into a string file format, just
|
435
|
+
# sub them back in later from a placeholder.
|
436
|
+
return "MU::Config.getTail PLACEHOLDER #{var_name} REDLOHECALP"
|
437
|
+
end
|
438
|
+
else
|
439
|
+
return "MU::Config.getTail PLACEHOLDER #{var_name} REDLOHECALP"
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
|
444
|
+
# A check for the existence of a user-supplied parameter value that can
|
445
|
+
# be easily run in an ERB block in a Basket of Kittens.
|
446
|
+
def parameter?(var_name)
|
447
|
+
@@user_supplied_parameters.has_key?(var_name)
|
448
|
+
end
|
449
|
+
|
450
|
+
# Instead of resolving a parameter, leave a placeholder for a
|
451
|
+
# cloud-specific variable that will be generated at runtime. Canonical
|
452
|
+
# use case: referring to a CloudFormation variable by reference, like
|
453
|
+
# "AWS::StackName" or "SomeChildTemplate.OutputVariableName."
|
454
|
+
# @param code [String]: A string consistent of code which will be understood by the Cloud layer, e.g. '"Ref" : "AWS::StackName"' (CloudFormation)
|
455
|
+
# @param placeholder [Object]: A placeholder value to use at the config parser stage, if the default string will not pass validation.
|
456
|
+
def cloudCode(code, placeholder = "CLOUDCODEPLACEHOLDER")
|
457
|
+
var_name = code.gsub(/[^a-z0-9]/i, "_")
|
458
|
+
placeholder = code if placeholder.nil?
|
459
|
+
getTail(var_name, value: placeholder, runtimecode: code)
|
460
|
+
"MU::Config.getTail PLACEHOLDER #{var_name} REDLOHECALP"
|
461
|
+
end
|
462
|
+
|
463
|
+
# Figure out what kind of file we're loading. We handle includes
|
464
|
+
# differently if YAML is involved. These globals get used inside
|
465
|
+
# templates. They're globals on purpose. Stop whining.
|
466
|
+
$file_format = MU::Config.guessFormat(path)
|
467
|
+
$yaml_refs = {}
|
468
|
+
erb = ERB.new(File.read(path), nil, "<>")
|
469
|
+
raw_text = erb.result(get_binding)
|
470
|
+
raw_json = nil
|
471
|
+
|
472
|
+
# If we're working in YAML, do some magic to make includes work better.
|
473
|
+
yaml_parse_error = nil
|
474
|
+
if $file_format == :yaml
|
475
|
+
begin
|
476
|
+
raw_json = JSON.generate(YAML.load(MU::Config.resolveYAMLAnchors(raw_text)))
|
477
|
+
rescue Psych::SyntaxError => e
|
478
|
+
raw_json = raw_text
|
479
|
+
yaml_parse_error = e.message
|
480
|
+
end
|
481
|
+
else
|
482
|
+
raw_json = raw_text
|
483
|
+
end
|
484
|
+
|
485
|
+
begin
|
486
|
+
config = JSON.parse(raw_json)
|
487
|
+
if param_pass
|
488
|
+
config.keys.each { |key|
|
489
|
+
if key != "parameters"
|
490
|
+
if key == "appname" and @@parameters["myAppName"].nil?
|
491
|
+
$myAppName = config["appname"].upcase.dup
|
492
|
+
$myAppName.freeze
|
493
|
+
@@parameters["myAppName"] = getTail("myAppName", value: config["appname"].upcase, pseudo: true).to_s
|
494
|
+
end
|
495
|
+
config.delete(key)
|
496
|
+
end
|
497
|
+
}
|
498
|
+
else
|
499
|
+
config.delete("parameters")
|
500
|
+
end
|
501
|
+
rescue JSON::ParserError => e
|
502
|
+
badconf = File.new("/tmp/badconf.#{$$}", File::CREAT|File::TRUNC|File::RDWR, 0400)
|
503
|
+
badconf.puts raw_text
|
504
|
+
badconf.close
|
505
|
+
if !yaml_parse_error.nil? and !path.match(/\.json/)
|
506
|
+
MU.log "YAML Error parsing #{path}! Complete file dumped to /tmp/badconf.#{$$}", MU::ERR, details: yaml_parse_error
|
507
|
+
else
|
508
|
+
MU.log "JSON Error parsing #{path}! Complete file dumped to /tmp/badconf.#{$$}", MU::ERR, details: e.message
|
509
|
+
end
|
510
|
+
raise ValidationError
|
511
|
+
end
|
512
|
+
|
513
|
+
undef :method_missing
|
514
|
+
return [MU::Config.fixDashes(config), raw_text]
|
515
|
+
end
|
516
|
+
|
517
|
+
attr_reader :kittens
|
518
|
+
attr_reader :updating
|
519
|
+
attr_reader :kittencfg_semaphore
|
520
|
+
|
521
|
+
# Load, resolve, and validate a configuration file ("Basket of Kittens").
|
522
|
+
# @param path [String]: The path to the master config file to load. Note that this can include other configuration files via ERB.
|
523
|
+
# @param skipinitialupdates [Boolean]: Whether to forcibly apply the *skipinitialupdates* flag to nodes created by this configuration.
|
524
|
+
# @param params [Hash]: Optional name-value parameter pairs, which will be passed to our configuration files as ERB variables.
|
525
|
+
# @return [Hash]: The complete validated configuration for a deployment.
|
526
|
+
def initialize(path, skipinitialupdates = false, params: params = Hash.new, updating: nil)
|
527
|
+
$myPublicIp = MU::Cloud::AWS.getAWSMetaData("public-ipv4")
|
528
|
+
$myRoot = MU.myRoot
|
529
|
+
$myRoot.freeze
|
530
|
+
|
531
|
+
$myAZ = MU.myAZ.freeze
|
532
|
+
$myAZ.freeze
|
533
|
+
$myRegion = MU.curRegion.freeze
|
534
|
+
$myRegion.freeze
|
535
|
+
|
536
|
+
$myAppName = nil
|
537
|
+
|
538
|
+
@kittens = {}
|
539
|
+
@kittencfg_semaphore = Mutex.new
|
540
|
+
@@config_path = path
|
541
|
+
@admin_firewall_rules = []
|
542
|
+
@skipinitialupdates = skipinitialupdates
|
543
|
+
@updating = updating
|
544
|
+
|
545
|
+
ok = true
|
546
|
+
params.each_pair { |name, value|
|
547
|
+
begin
|
548
|
+
raise DeployParamError, "Parameter must be formatted as name=value" if value.nil? or value.empty?
|
549
|
+
raise DeployParamError, "Parameter name must be a legal Ruby variable name" if name.match(/[^A-Za-z0-9_]/)
|
550
|
+
raise DeployParamError, "Parameter values cannot contain quotes" if value.match(/["']/)
|
551
|
+
eval("defined? $#{name} and raise DeployParamError, 'Parameter name reserved'")
|
552
|
+
@@parameters[name] = value
|
553
|
+
@@user_supplied_parameters[name] = value
|
554
|
+
eval("$#{name}='#{value}'") # support old-style $global parameter refs
|
555
|
+
MU.log "Passing variable $#{name} into #{@@config_path} with value '#{value}'"
|
556
|
+
rescue RuntimeError, SyntaxError => e
|
557
|
+
ok = false
|
558
|
+
MU.log "Error setting $#{name}='#{value}': #{e.message}", MU::ERR
|
559
|
+
end
|
560
|
+
}
|
561
|
+
raise ValidationError if !ok
|
562
|
+
|
563
|
+
# Run our input through the ERB renderer, a first pass just to extract
|
564
|
+
# the parameters section so that we can resolve all of those to variables
|
565
|
+
# for the rest of the config to reference.
|
566
|
+
# XXX Figure out how to make include() add parameters for us. Right now
|
567
|
+
# you can't specify parameters in an included file, because ERB is what's
|
568
|
+
# doing the including, and parameters need to already be resolved so that
|
569
|
+
# ERB can use them.
|
570
|
+
param_cfg, raw_erb_params_only = resolveConfig(path: @@config_path, param_pass: true)
|
571
|
+
if param_cfg.has_key?("parameters")
|
572
|
+
param_cfg["parameters"].each { |param|
|
573
|
+
if param.has_key?("default") and param["default"].nil?
|
574
|
+
param["default"] = ""
|
575
|
+
end
|
576
|
+
}
|
577
|
+
end
|
578
|
+
|
579
|
+
# Set up special Tail objects for our automatic pseudo-parameters
|
580
|
+
getTail("myPublicIp", value: $myPublicIp, pseudo: true)
|
581
|
+
getTail("myRoot", value: $myRoot, pseudo: true)
|
582
|
+
getTail("myAZ", value: $myAZ, pseudo: true)
|
583
|
+
getTail("myRegion", value: $myRegion, pseudo: true)
|
584
|
+
|
585
|
+
if param_cfg.has_key?("parameters") and !param_cfg["parameters"].nil? and param_cfg["parameters"].size > 0
|
586
|
+
param_cfg["parameters"].each { |param|
|
587
|
+
param['valid_values'] ||= []
|
588
|
+
if !@@parameters.has_key?(param['name'])
|
589
|
+
if param.has_key?("default")
|
590
|
+
@@parameters[param['name']] = param['default'].nil? ? "" : param['default']
|
591
|
+
elsif param["required"] or !param.has_key?("required")
|
592
|
+
MU.log "Required parameter '#{param['name']}' not supplied", MU::ERR
|
593
|
+
ok = false
|
594
|
+
end
|
595
|
+
if param.has_key?("cloudtype")
|
596
|
+
getTail(param['name'], value: @@parameters[param['name']], cloudtype: param["cloudtype"], valid_values: param['valid_values'], description: param['description'], prettyname: param['prettyname'], list_of: param['list_of'])
|
597
|
+
else
|
598
|
+
getTail(param['name'], value: @@parameters[param['name']], valid_values: param['valid_values'], description: param['description'], prettyname: param['prettyname'], list_of: param['list_of'])
|
599
|
+
end
|
600
|
+
end
|
601
|
+
}
|
602
|
+
end
|
603
|
+
raise ValidationError if !ok
|
604
|
+
@@parameters.each_pair { |name, val|
|
605
|
+
next if @@tails.has_key?(name) and @@tails[name].is_a?(MU::Config::Tail) and @@tails[name].pseudo
|
606
|
+
# Parameters can have limited parameterization of their own
|
607
|
+
if @@tails[name].to_s.match(/^(.*?)MU::Config.getTail PLACEHOLDER (.+?) REDLOHECALP(.*)/)
|
608
|
+
@@tails[name] = getTail(name, value: @@tails[$2])
|
609
|
+
end
|
610
|
+
|
611
|
+
if respond_to?(name.to_sym)
|
612
|
+
MU.log "Parameter name '#{name}' reserved", MU::ERR
|
613
|
+
ok = false
|
614
|
+
next
|
615
|
+
end
|
616
|
+
MU.log "Passing variable '#{name}' into #{path} with value '#{val}'"
|
617
|
+
}
|
618
|
+
raise DeployParamError, "One or more invalid parameters specified" if !ok
|
619
|
+
$parameters = @@parameters
|
620
|
+
$parameters.freeze
|
621
|
+
|
622
|
+
tmp_cfg, raw_erb = resolveConfig(path: @@config_path)
|
623
|
+
|
624
|
+
# Convert parameter entries that constitute whole config keys into
|
625
|
+
# MU::Config::Tail objects.
|
626
|
+
def resolveTails(tree, indent= "")
|
627
|
+
if tree.is_a?(Hash)
|
628
|
+
tree.each_pair { |key, val|
|
629
|
+
tree[key] = resolveTails(val, indent+" ")
|
630
|
+
}
|
631
|
+
elsif tree.is_a?(Array)
|
632
|
+
newtree = []
|
633
|
+
tree.each { |item|
|
634
|
+
newtree << resolveTails(item, indent+" ")
|
635
|
+
}
|
636
|
+
tree = newtree
|
637
|
+
elsif tree.is_a?(String) and tree.match(/^(.*?)MU::Config.getTail PLACEHOLDER (.+?) REDLOHECALP(.*)/)
|
638
|
+
tree = getTail($2, prefix: $1, suffix: $3)
|
639
|
+
if tree.nil? and @@tails.has_key?($2) # XXX why necessary?
|
640
|
+
tree = @@tails[$2]
|
641
|
+
end
|
642
|
+
end
|
643
|
+
return tree
|
644
|
+
end
|
645
|
+
@config = resolveTails(tmp_cfg)
|
646
|
+
@config.merge!(param_cfg)
|
647
|
+
|
648
|
+
if !@config.has_key?('admins') or @config['admins'].size == 0
|
649
|
+
@config['admins'] = [
|
650
|
+
{
|
651
|
+
"name" => MU.chef_user == "mu" ? "Mu Administrator" : MU.userName,
|
652
|
+
"email" => MU.userEmail
|
653
|
+
}
|
654
|
+
]
|
655
|
+
end
|
656
|
+
MU::Config.set_defaults(@config, MU::Config.schema)
|
657
|
+
validate # individual resources validate when added now, necessary because the schema can change depending on what cloud they're targeting
|
658
|
+
# XXX but now we're not validating top-level keys, argh
|
659
|
+
#pp @config
|
660
|
+
#raise "DERP"
|
661
|
+
return @config.freeze
|
662
|
+
end
|
663
|
+
|
664
|
+
# Output the dependencies of this BoK stack as a directed acyclic graph.
|
665
|
+
# Very useful for debugging.
|
666
|
+
def visualizeDependencies
|
667
|
+
# GraphViz won't like MU::Config::Tail, pare down to plain Strings
|
668
|
+
config = MU::Config.manxify(Marshal.load(Marshal.dump(@config)))
|
669
|
+
begin
|
670
|
+
g = GraphViz.new(:G, :type => :digraph)
|
671
|
+
# Generate a GraphViz node for each resource in this stack
|
672
|
+
nodes = {}
|
673
|
+
MU::Cloud.resource_types.each_pair { |classname, attrs|
|
674
|
+
nodes[attrs[:cfg_name]] = {}
|
675
|
+
if config.has_key?(attrs[:cfg_plural]) and config[attrs[:cfg_plural]]
|
676
|
+
config[attrs[:cfg_plural]].each { |resource|
|
677
|
+
nodes[attrs[:cfg_name]][resource['name']] = g.add_nodes("#{classname}: #{resource['name']}")
|
678
|
+
}
|
679
|
+
end
|
680
|
+
}
|
681
|
+
# Now add edges corresponding to the dependencies they list
|
682
|
+
MU::Cloud.resource_types.each_pair { |classname, attrs|
|
683
|
+
if config.has_key?(attrs[:cfg_plural]) and config[attrs[:cfg_plural]]
|
684
|
+
config[attrs[:cfg_plural]].each { |resource|
|
685
|
+
if resource.has_key?("dependencies")
|
686
|
+
me = nodes[attrs[:cfg_name]][resource['name']]
|
687
|
+
resource["dependencies"].each { |dep|
|
688
|
+
parent = nodes[dep['type']][dep['name']]
|
689
|
+
g.add_edges(me, parent)
|
690
|
+
}
|
691
|
+
end
|
692
|
+
}
|
693
|
+
end
|
694
|
+
}
|
695
|
+
# Spew some output?
|
696
|
+
MU.log "Emitting dependency graph as /tmp/#{config['appname']}.jpg", MU::NOTICE
|
697
|
+
g.output(:jpg => "/tmp/#{config['appname']}.jpg")
|
698
|
+
rescue Exception => e
|
699
|
+
MU.log "Failed to generate GraphViz dependency tree: #{e.inspect}. This should only matter to developers.", MU::WARN, details: e.backtrace
|
700
|
+
end
|
701
|
+
end
|
702
|
+
|
703
|
+
# Take the schema we've defined and create a dummy Ruby class tree out of
|
704
|
+
# it, basically so we can leverage Yard to document it.
|
705
|
+
def self.emitSchemaAsRuby
|
706
|
+
kittenpath = "#{MU.myRoot}/modules/mu/kittens.rb"
|
707
|
+
MU.log "Converting Basket of Kittens schema to Ruby objects in #{kittenpath}"
|
708
|
+
dummy_kitten_class = File.new(kittenpath, File::CREAT|File::TRUNC|File::RDWR, 0644)
|
709
|
+
dummy_kitten_class.puts "### THIS FILE IS AUTOMATICALLY GENERATED, DO NOT EDIT ###"
|
710
|
+
dummy_kitten_class.puts ""
|
711
|
+
dummy_kitten_class.puts "module MU"
|
712
|
+
dummy_kitten_class.puts "class Config"
|
713
|
+
dummy_kitten_class.puts "\t# The configuration file format for Mu application stacks."
|
714
|
+
self.printSchema(dummy_kitten_class, ["BasketofKittens"], MU::Config.docSchema)
|
715
|
+
dummy_kitten_class.puts "end"
|
716
|
+
dummy_kitten_class.puts "end"
|
717
|
+
dummy_kitten_class.close
|
718
|
+
|
719
|
+
end
|
720
|
+
|
721
|
+
# Take an IP block and split it into a more-or-less arbitrary number of
|
722
|
+
# subnets.
|
723
|
+
# @param ip_block [String]: CIDR of the network to subdivide
|
724
|
+
# @param subnets_desired [Integer]: Number of subnets we want back
|
725
|
+
# @param max_mask [Integer]: The highest netmask we're allowed to use for a subnet (various by cloud provider)
|
726
|
+
# @return [MU::Config::Tail]: Resulting subnet tails, or nil if an error occurred.
|
727
|
+
def divideNetwork(ip_block, subnets_desired, max_mask = 28)
|
728
|
+
cidr = NetAddr::IPv4Net.parse(ip_block.to_s)
|
729
|
+
|
730
|
+
# Ugly but reliable method of landing on the right subnet size
|
731
|
+
subnet_bits = cidr.netmask.prefix_len
|
732
|
+
begin
|
733
|
+
subnet_bits += 1
|
734
|
+
|
735
|
+
if subnet_bits > max_mask
|
736
|
+
MU.log "Can't subdivide #{cidr.to_s} into #{subnets_desired.to_s}", MU::ERR
|
737
|
+
raise MuError, "Subnets smaller than /#{max_mask} not permitted"
|
738
|
+
end
|
739
|
+
end while cidr.subnet_count(subnet_bits) < subnets_desired
|
740
|
+
|
741
|
+
if cidr.subnet_count(subnet_bits) > subnets_desired
|
742
|
+
MU.log "Requested #{subnets_desired.to_s} subnets from #{cidr.to_s}, leaving #{(cidr.subnet_count(subnet_bits)-subnets_desired).to_s} unused /#{subnet_bits.to_s}s available", MU::NOTICE
|
743
|
+
end
|
744
|
+
|
745
|
+
begin
|
746
|
+
subnets = []
|
747
|
+
(0..subnets_desired).each { |x|
|
748
|
+
subnets << cidr.nth_subnet(subnet_bits, x).to_s
|
749
|
+
}
|
750
|
+
rescue RuntimeError => e
|
751
|
+
if e.message.match(/exceeds subnets available for allocation/)
|
752
|
+
MU.log e.message, MU::ERR
|
753
|
+
MU.log "I'm attempting to create #{subnets_desired} subnets (one public and one private for each Availability Zone), of #{subnet_size} addresses each, but that's too many for a /#{cidr.netmask.prefix_len} network. Either declare a larger network, or explicitly declare a list of subnets with few enough entries to fit.", MU::ERR
|
754
|
+
return nil
|
755
|
+
else
|
756
|
+
raise e
|
757
|
+
end
|
758
|
+
end
|
759
|
+
|
760
|
+
subnets = getTail("subnetblocks", value: subnets.join(","), cloudtype: "CommaDelimitedList", description: "IP Address ranges to be used for VPC subnets", prettyname: "SubnetIpBlocks", list_of: "ip_block").map { |tail| tail["ip_block"] }
|
761
|
+
subnets
|
762
|
+
end
|
763
|
+
|
764
|
+
# See if a given resource is configured in the current stack
|
765
|
+
# @param name [String]: The name of the resource being checked
|
766
|
+
# @param type [String]: The type of resource being checked
|
767
|
+
# @return [Boolean]
|
768
|
+
def haveLitterMate?(name, type)
|
769
|
+
@kittencfg_semaphore.synchronize {
|
770
|
+
shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
|
771
|
+
@kittens[cfg_plural].each { |kitten|
|
772
|
+
return kitten if kitten['name'] == name.to_s
|
773
|
+
}
|
774
|
+
}
|
775
|
+
false
|
776
|
+
end
|
777
|
+
|
778
|
+
# Remove a resource from the current stack
|
779
|
+
# @param name [String]: The name of the resource being removed
|
780
|
+
# @param type [String]: The type of resource being removed
|
781
|
+
def removeKitten(name, type)
|
782
|
+
@kittencfg_semaphore.synchronize {
|
783
|
+
shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
|
784
|
+
deletia = nil
|
785
|
+
@kittens[cfg_plural].each { |kitten|
|
786
|
+
if kitten['name'] == name
|
787
|
+
deletia = kitten
|
788
|
+
break
|
789
|
+
end
|
790
|
+
}
|
791
|
+
@kittens[type].delete(deletia) if !deletia.nil?
|
792
|
+
}
|
793
|
+
end
|
794
|
+
|
795
|
+
# FirewallRules can reference other FirewallRules, which means we need to do
|
796
|
+
# an extra pass to make sure we get all intra-stack dependencies correct.
|
797
|
+
# @param acl [Hash]: The configuration hash for the FirewallRule to check
|
798
|
+
# @return [Hash]
|
799
|
+
def resolveIntraStackFirewallRefs(acl)
|
800
|
+
acl["rules"].each { |acl_include|
|
801
|
+
if acl_include['sgs']
|
802
|
+
acl_include['sgs'].each { |sg_ref|
|
803
|
+
if haveLitterMate?(sg_ref, "firewall_rules")
|
804
|
+
acl["dependencies"] ||= []
|
805
|
+
found = false
|
806
|
+
acl["dependencies"].each { |dep|
|
807
|
+
if dep["type"] == "firewall_rule" and dep["name"] == sg_ref
|
808
|
+
dep["no_create_wait"] = true
|
809
|
+
found = true
|
810
|
+
end
|
811
|
+
}
|
812
|
+
if !found
|
813
|
+
acl["dependencies"] << {
|
814
|
+
"type" => "firewall_rule",
|
815
|
+
"name" => sg_ref,
|
816
|
+
"no_create_wait" => true
|
817
|
+
}
|
818
|
+
end
|
819
|
+
siblingfw = haveLitterMate?(sg_ref, "firewall_rules")
|
820
|
+
if !siblingfw["#MU_VALIDATED"]
|
821
|
+
# XXX raise failure somehow
|
822
|
+
insertKitten(siblingfw, "firewall_rules")
|
823
|
+
end
|
824
|
+
end
|
825
|
+
}
|
826
|
+
end
|
827
|
+
}
|
828
|
+
acl
|
829
|
+
end
|
830
|
+
|
831
|
+
# Insert a resource into the current stack
|
832
|
+
# @param descriptor [Hash]: The configuration description, as from a Basket of Kittens
|
833
|
+
# @param type [String]: The type of resource being added
|
834
|
+
# @param delay_validation [Boolean]: Whether to hold off on calling the resource's validateConfig method
|
835
|
+
def insertKitten(descriptor, type, delay_validation = false)
|
836
|
+
append = false
|
837
|
+
|
838
|
+
@kittencfg_semaphore.synchronize {
|
839
|
+
append = !@kittens[type].include?(descriptor)
|
840
|
+
|
841
|
+
# Skip if this kitten has already been validated and appended
|
842
|
+
if !append and descriptor["#MU_VALIDATED"]
|
843
|
+
return true
|
844
|
+
end
|
845
|
+
}
|
846
|
+
ok = true
|
847
|
+
|
848
|
+
shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
|
849
|
+
descriptor["#MU_CLOUDCLASS"] = classname
|
850
|
+
inheritDefaults(descriptor, cfg_plural)
|
851
|
+
schemaclass = Object.const_get("MU").const_get("Config").const_get(shortclass)
|
852
|
+
|
853
|
+
if (descriptor["region"] and descriptor["region"].empty?) or
|
854
|
+
(descriptor['cloud'] == "Google" and ["firewall_rule", "vpc"].include?(cfg_name))
|
855
|
+
descriptor.delete("region")
|
856
|
+
end
|
857
|
+
|
858
|
+
# Make sure a sensible region has been targeted, if applicable
|
859
|
+
if descriptor["region"]
|
860
|
+
classobj = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"])
|
861
|
+
valid_regions = classobj.listRegions
|
862
|
+
if !valid_regions.include?(descriptor["region"])
|
863
|
+
MU.log "Known regions for cloud '#{descriptor['cloud']}' do not include '#{descriptor["region"]}'", MU::ERR, details: valid_regions
|
864
|
+
ok = false
|
865
|
+
end
|
866
|
+
end
|
867
|
+
|
868
|
+
# Does this resource go in a VPC?
|
869
|
+
if !descriptor["vpc"].nil? and !delay_validation
|
870
|
+
descriptor['vpc']['cloud'] = descriptor['cloud']
|
871
|
+
if descriptor['vpc']['region'].nil? and !descriptor['region'].nil? and !descriptor['region'].empty? and descriptor['vpc']['cloud'] != "Google"
|
872
|
+
descriptor['vpc']['region'] = descriptor['region']
|
873
|
+
end
|
874
|
+
|
875
|
+
# If we're using a VPC in this deploy, set it as a dependency
|
876
|
+
if !descriptor["vpc"]["vpc_name"].nil? and
|
877
|
+
haveLitterMate?(descriptor["vpc"]["vpc_name"], "vpcs") and
|
878
|
+
descriptor["vpc"]['deploy_id'].nil? and
|
879
|
+
descriptor["vpc"]['vpc_id'].nil?
|
880
|
+
descriptor["dependencies"] << {
|
881
|
+
"type" => "vpc",
|
882
|
+
"name" => descriptor["vpc"]["vpc_name"]
|
883
|
+
}
|
884
|
+
|
885
|
+
siblingvpc = haveLitterMate?(descriptor["vpc"]["vpc_name"], "vpcs")
|
886
|
+
# things that live in subnets need their VPCs to be fully
|
887
|
+
# resolved before we can proceed
|
888
|
+
if ["server", "server_pool", "loadbalancer", "database", "cache_cluster", "container_cluster", "storage_pool"].include?(cfg_name)
|
889
|
+
if !siblingvpc["#MU_VALIDATED"]
|
890
|
+
ok = false if !insertKitten(siblingvpc, "vpcs")
|
891
|
+
end
|
892
|
+
end
|
893
|
+
if !MU::Config::VPC.processReference(descriptor['vpc'],
|
894
|
+
cfg_plural,
|
895
|
+
shortclass.to_s+" '#{descriptor['name']}'",
|
896
|
+
self,
|
897
|
+
dflt_region: descriptor['region'],
|
898
|
+
is_sibling: true,
|
899
|
+
sibling_vpcs: @kittens['vpcs'])
|
900
|
+
ok = false
|
901
|
+
end
|
902
|
+
|
903
|
+
# If we're using a VPC from somewhere else, make sure the flippin'
|
904
|
+
# thing exists, and also fetch its id now so later search routines
|
905
|
+
# don't have to work so hard.
|
906
|
+
else
|
907
|
+
if !MU::Config::VPC.processReference(descriptor["vpc"], cfg_plural,
|
908
|
+
"#{shortclass} #{descriptor['name']}",
|
909
|
+
self,
|
910
|
+
dflt_region: descriptor['region'])
|
911
|
+
MU.log "insertKitten was called from #{caller[0]}", MU::ERR
|
912
|
+
ok = false
|
913
|
+
end
|
914
|
+
end
|
915
|
+
# Clean crud out of auto-created VPC declarations so they don't trip
|
916
|
+
# the schema validator when it's invoked later.
|
917
|
+
if !["server", "server_pool", "database"].include?(cfg_name)
|
918
|
+
descriptor['vpc'].delete("nat_ssh_user")
|
919
|
+
end
|
920
|
+
if descriptor['vpc']['cloud'] == "Google"
|
921
|
+
descriptor['vpc'].delete("region")
|
922
|
+
end
|
923
|
+
if ["firewall_rule", "function"].include?(cfg_name)
|
924
|
+
descriptor['vpc'].delete("subnet_pref")
|
925
|
+
end
|
926
|
+
end
|
927
|
+
|
928
|
+
# Does it have generic ingress rules?
|
929
|
+
fwname = cfg_name+descriptor['name']
|
930
|
+
|
931
|
+
if !haveLitterMate?(fwname, "firewall_rules") and
|
932
|
+
(descriptor['ingress_rules'] or
|
933
|
+
["server", "server_pool", "database"].include?(cfg_name))
|
934
|
+
descriptor['ingress_rules'] ||= []
|
935
|
+
|
936
|
+
acl = {"name" => fwname, "rules" => descriptor['ingress_rules'], "region" => descriptor['region'] }
|
937
|
+
acl["vpc"] = descriptor['vpc'].dup if descriptor['vpc']
|
938
|
+
["optional_tags", "tags", "cloud", "project"].each { |param|
|
939
|
+
acl[param] = descriptor[param] if descriptor[param]
|
940
|
+
}
|
941
|
+
descriptor["add_firewall_rules"] = [] if descriptor["add_firewall_rules"].nil?
|
942
|
+
descriptor["add_firewall_rules"] << {"rule_name" => fwname}
|
943
|
+
acl = resolveIntraStackFirewallRefs(acl)
|
944
|
+
ok = false if !insertKitten(acl, "firewall_rules")
|
945
|
+
end
|
946
|
+
|
947
|
+
# Does it declare association with any sibling LoadBalancers?
|
948
|
+
if !descriptor["loadbalancers"].nil?
|
949
|
+
descriptor["loadbalancers"].each { |lb|
|
950
|
+
if !lb["concurrent_load_balancer"].nil?
|
951
|
+
descriptor["dependencies"] << {
|
952
|
+
"type" => "loadbalancer",
|
953
|
+
"name" => lb["concurrent_load_balancer"]
|
954
|
+
}
|
955
|
+
end
|
956
|
+
}
|
957
|
+
end
|
958
|
+
|
959
|
+
# Does it want to know about Storage Pools?
|
960
|
+
if !descriptor["storage_pools"].nil?
|
961
|
+
descriptor["storage_pools"].each { |sp|
|
962
|
+
if sp["name"]
|
963
|
+
descriptor["dependencies"] << {
|
964
|
+
"type" => "storage_pool",
|
965
|
+
"name" => sp["name"]
|
966
|
+
}
|
967
|
+
end
|
968
|
+
}
|
969
|
+
end
|
970
|
+
|
971
|
+
# Does it declare association with first-class firewall_rules?
|
972
|
+
if !descriptor["add_firewall_rules"].nil?
|
973
|
+
descriptor["add_firewall_rules"].each { |acl_include|
|
974
|
+
if haveLitterMate?(acl_include["rule_name"], "firewall_rules")
|
975
|
+
descriptor["dependencies"] << {
|
976
|
+
"type" => "firewall_rule",
|
977
|
+
"name" => acl_include["rule_name"]
|
978
|
+
}
|
979
|
+
siblingfw = haveLitterMate?(acl_include["rule_name"], "firewall_rules")
|
980
|
+
if !siblingfw["#MU_VALIDATED"]
|
981
|
+
ok = false if !insertKitten(siblingfw, "firewall_rules")
|
982
|
+
end
|
983
|
+
elsif acl_include["rule_name"]
|
984
|
+
MU.log shortclass.to_s+" #{descriptor['name']} depends on FirewallRule #{acl_include["rule_name"]}, but no such rule declared.", MU::ERR
|
985
|
+
ok = false
|
986
|
+
end
|
987
|
+
}
|
988
|
+
end
|
989
|
+
|
990
|
+
# Does it declare some alarms?
|
991
|
+
if descriptor["alarms"] && !descriptor["alarms"].empty?
|
992
|
+
descriptor["alarms"].each { |alarm|
|
993
|
+
alarm["name"] = "#{cfg_name}-#{descriptor["name"]}-#{alarm["name"]}"
|
994
|
+
alarm['dimensions'] = [] if !alarm['dimensions']
|
995
|
+
alarm["#TARGETCLASS"] = cfg_name
|
996
|
+
alarm["#TARGETNAME"] = descriptor['name']
|
997
|
+
alarm['cloud'] = descriptor['cloud']
|
998
|
+
|
999
|
+
ok = false if !insertKitten(alarm, "alarms", true)
|
1000
|
+
}
|
1001
|
+
descriptor.delete("alarms")
|
1002
|
+
end
|
1003
|
+
|
1004
|
+
# Does it want to meld another deployment's resources into its metadata?
|
1005
|
+
if !descriptor["existing_deploys"].nil? and
|
1006
|
+
!descriptor["existing_deploys"].empty?
|
1007
|
+
descriptor["existing_deploys"].each { |ext_deploy|
|
1008
|
+
if ext_deploy["cloud_type"].nil?
|
1009
|
+
MU.log "You must provide a cloud_type", MU::ERR
|
1010
|
+
ok = false
|
1011
|
+
end
|
1012
|
+
|
1013
|
+
if ext_deploy["cloud_id"]
|
1014
|
+
found = MU::MommaCat.findStray(
|
1015
|
+
descriptor['cloud'],
|
1016
|
+
ext_deploy["cloud_type"],
|
1017
|
+
cloud_id: ext_deploy["cloud_id"],
|
1018
|
+
region: descriptor['region'],
|
1019
|
+
dummy_ok: false
|
1020
|
+
).first
|
1021
|
+
|
1022
|
+
if found.nil?
|
1023
|
+
MU.log "Couldn't find existing #{ext_deploy["cloud_type"]} resource #{ext_deploy["cloud_id"]}", MU::ERR
|
1024
|
+
ok = false
|
1025
|
+
end
|
1026
|
+
elsif ext_deploy["mu_name"] && ext_deploy["deploy_id"]
|
1027
|
+
found = MU::MommaCat.findStray(
|
1028
|
+
descriptor['cloud'],
|
1029
|
+
ext_deploy["cloud_type"],
|
1030
|
+
deploy_id: ext_deploy["deploy_id"],
|
1031
|
+
mu_name: ext_deploy["mu_name"],
|
1032
|
+
region: descriptor['region'],
|
1033
|
+
dummy_ok: false
|
1034
|
+
).first
|
1035
|
+
|
1036
|
+
if found.nil?
|
1037
|
+
MU.log "Couldn't find existing #{ext_deploy["cloud_type"]} resource - #{ext_deploy["mu_name"]} / #{ext_deploy["deploy_id"]}", MU::ERR
|
1038
|
+
ok = false
|
1039
|
+
end
|
1040
|
+
else
|
1041
|
+
MU.log "Trying to find existing deploy, but either the cloud_id is not valid or no mu_name and deploy_id where provided", MU::ERR
|
1042
|
+
ok = false
|
1043
|
+
end
|
1044
|
+
}
|
1045
|
+
end
|
1046
|
+
|
1047
|
+
if !delay_validation
|
1048
|
+
# Call the generic validation for the resource type, first and foremost
|
1049
|
+
# XXX this might have to be at the top of this insertKitten instead of
|
1050
|
+
# here
|
1051
|
+
ok = false if !schemaclass.validate(descriptor, self)
|
1052
|
+
|
1053
|
+
# Merge the cloud-specific JSON schema and validate against it
|
1054
|
+
myschema = Marshal.load(Marshal.dump(MU::Config.schema["properties"][cfg_plural]["items"]))
|
1055
|
+
more_required, more_schema = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get(shortclass.to_s).schema(self)
|
1056
|
+
|
1057
|
+
if more_schema
|
1058
|
+
MU::Config.schemaMerge(myschema["properties"], more_schema, descriptor["cloud"])
|
1059
|
+
MU::Config.set_defaults(descriptor, myschema)
|
1060
|
+
end
|
1061
|
+
myschema["required"] ||= []
|
1062
|
+
myschema["required"].concat(more_required)
|
1063
|
+
myschema["required"].uniq!
|
1064
|
+
MU.log "Schema check on #{descriptor['cloud']} #{cfg_name} #{descriptor['name']}", MU::DEBUG, details: myschema
|
1065
|
+
|
1066
|
+
plain_cfg = MU::Config.manxify(Marshal.load(Marshal.dump(descriptor)))
|
1067
|
+
plain_cfg.delete("#MU_CLOUDCLASS")
|
1068
|
+
plain_cfg.delete("#TARGETCLASS")
|
1069
|
+
plain_cfg.delete("#TARGETNAME")
|
1070
|
+
plain_cfg.delete("parent_block") if cfg_plural == "vpcs"
|
1071
|
+
begin
|
1072
|
+
JSON::Validator.validate!(myschema, plain_cfg)
|
1073
|
+
rescue JSON::Schema::ValidationError => e
|
1074
|
+
pp plain_cfg
|
1075
|
+
# Use fully_validate to get the complete error list, save some time
|
1076
|
+
errors = JSON::Validator.fully_validate(myschema, plain_cfg)
|
1077
|
+
realerrors = []
|
1078
|
+
errors.each { |err|
|
1079
|
+
if !err.match(/The property '.+?' of type MU::Config::Tail did not match the following type:/)
|
1080
|
+
realerrors << err
|
1081
|
+
end
|
1082
|
+
}
|
1083
|
+
if realerrors.size > 0
|
1084
|
+
MU.log "Validation error on #{descriptor['cloud']} #{cfg_name} #{descriptor['name']} (insertKitten called from #{caller[1]} with delay_validation=#{delay_validation}) #{@@config_path}!\n"+realerrors.join("\n"), MU::ERR, details: descriptor
|
1085
|
+
raise ValidationError, "Validation error on #{descriptor['cloud']} #{cfg_name} #{descriptor['name']} #{@@config_path}!\n"+realerrors.join("\n")
|
1086
|
+
end
|
1087
|
+
end
|
1088
|
+
|
1089
|
+
# Run the cloud class's deeper validation, unless we've already failed
|
1090
|
+
# on stuff that will cause spurious alarms further in
|
1091
|
+
if ok
|
1092
|
+
parser = Object.const_get("MU").const_get("Cloud").const_get(descriptor["cloud"]).const_get(shortclass.to_s)
|
1093
|
+
plain_descriptor = MU::Config.manxify(Marshal.load(Marshal.dump(descriptor)))
|
1094
|
+
return false if !parser.validateConfig(plain_descriptor, self)
|
1095
|
+
|
1096
|
+
descriptor.merge!(plain_descriptor)
|
1097
|
+
descriptor['#MU_VALIDATED'] = true
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
end
|
1101
|
+
|
1102
|
+
descriptor["dependencies"].uniq!
|
1103
|
+
|
1104
|
+
@kittencfg_semaphore.synchronize {
|
1105
|
+
@kittens[cfg_plural] << descriptor if append
|
1106
|
+
}
|
1107
|
+
ok
|
1108
|
+
end
|
1109
|
+
|
1110
|
+
allregions = []
|
1111
|
+
allregions.concat(MU::Cloud::AWS.listRegions) if MU::Cloud::AWS.myRegion
|
1112
|
+
allregions.concat(MU::Cloud::Google.listRegions) if MU::Cloud::Google.defaultProject
|
1113
|
+
|
1114
|
+
# Configuration chunk for choosing a provider region
|
1115
|
+
# @return [Hash]
|
1116
|
+
def self.region_primitive
|
1117
|
+
allregions = []
|
1118
|
+
allregions.concat(MU::Cloud::AWS.listRegions) if MU::Cloud::AWS.myRegion
|
1119
|
+
allregions.concat(MU::Cloud::Google.listRegions) if MU::Cloud::Google.defaultProject
|
1120
|
+
{
|
1121
|
+
"type" => "string",
|
1122
|
+
"enum" => allregions
|
1123
|
+
}
|
1124
|
+
end
|
1125
|
+
|
1126
|
+
# Configuration chunk for creating resource tags as an array of key/value
|
1127
|
+
# pairs.
|
1128
|
+
# @return [Hash]
|
1129
|
+
def self.optional_tags_primitive
|
1130
|
+
{
|
1131
|
+
"type" => "boolean",
|
1132
|
+
"description" => "Tag the resource with our optional tags (+MU-HANDLE+, +MU-MASTER-NAME+, +MU-OWNER+).",
|
1133
|
+
"default" => true
|
1134
|
+
}
|
1135
|
+
end
|
1136
|
+
|
1137
|
+
# Configuration chunk for creating resource tags as an array of key/value
|
1138
|
+
# pairs.
|
1139
|
+
# @return [Hash]
|
1140
|
+
def self.tags_primitive
|
1141
|
+
{
|
1142
|
+
"type" => "array",
|
1143
|
+
"minItems" => 1,
|
1144
|
+
"items" => {
|
1145
|
+
"description" => "Tags to apply to this resource. Will apply at the cloud provider level and in Chef, where applicable.",
|
1146
|
+
"type" => "object",
|
1147
|
+
"title" => "tags",
|
1148
|
+
"required" => ["key", "value"],
|
1149
|
+
"additionalProperties" => false,
|
1150
|
+
"properties" => {
|
1151
|
+
"key" => {
|
1152
|
+
"type" => "string",
|
1153
|
+
},
|
1154
|
+
"value" => {
|
1155
|
+
"type" => "string",
|
1156
|
+
}
|
1157
|
+
}
|
1158
|
+
}
|
1159
|
+
}
|
1160
|
+
end
|
1161
|
+
|
1162
|
+
# Configuration chunk for choosing a cloud provider
|
1163
|
+
# @return [Hash]
|
1164
|
+
def self.cloud_primitive
|
1165
|
+
{
|
1166
|
+
"type" => "string",
|
1167
|
+
"default" => MU::Config.defaultCloud,
|
1168
|
+
"enum" => MU::Cloud.supportedClouds
|
1169
|
+
}
|
1170
|
+
end
|
1171
|
+
|
1172
|
+
# Generate configuration for the general-pursose ADMIN firewall rulesets
|
1173
|
+
# (security groups in AWS). Note that these are unique to regions and
|
1174
|
+
# individual VPCs (as well as Classic, which is just a degenerate case of
|
1175
|
+
# a VPC for our purposes.
|
1176
|
+
# @param vpc [Hash]: A VPC reference as defined in our config schema. This originates with the calling resource, so we'll peel out just what we need (a name or cloud id of a VPC).
|
1177
|
+
# @param admin_ip [String]: Optional string of an extra IP address to allow blanket access to the calling resource.
|
1178
|
+
# @param cloud [String]: The parent resource's cloud plugin identifier
|
1179
|
+
# @param region [String]: Cloud provider region, if applicable.
|
1180
|
+
# @return [Hash<String>]: A dependency description that the calling resource can then add to itself.
|
1181
|
+
def adminFirewallRuleset(vpc: nil, admin_ip: nil, region: nil, cloud: nil)
|
1182
|
+
if !cloud or (cloud == "AWS" and !region)
|
1183
|
+
raise MuError, "Cannot call adminFirewallRuleset without specifying the parent's region and cloud provider"
|
1184
|
+
end
|
1185
|
+
hosts = Array.new
|
1186
|
+
hosts << "#{MU.my_public_ip}/32" if MU.my_public_ip
|
1187
|
+
hosts << "#{MU.my_private_ip}/32" if MU.my_private_ip
|
1188
|
+
hosts << "#{MU.mu_public_ip}/32" if MU.mu_public_ip
|
1189
|
+
hosts << "#{admin_ip}/32" if admin_ip
|
1190
|
+
hosts.uniq!
|
1191
|
+
name = "admin"
|
1192
|
+
realvpc = nil
|
1193
|
+
|
1194
|
+
if vpc
|
1195
|
+
realvpc = {}
|
1196
|
+
realvpc['vpc_id'] = vpc['vpc_id'] if !vpc['vpc_id'].nil?
|
1197
|
+
realvpc['vpc_name'] = vpc['vpc_name'] if !vpc['vpc_name'].nil?
|
1198
|
+
realvpc['deploy_id'] = vpc['deploy_id'] if !vpc['deploy_id'].nil?
|
1199
|
+
if !realvpc['vpc_id'].nil? and !realvpc['vpc_id'].empty?
|
1200
|
+
# Stupid kludge for Google cloud_ids which are sometimes URLs and
|
1201
|
+
# sometimes not. Requirements are inconsistent from scenario to
|
1202
|
+
# scenario.
|
1203
|
+
name = name + "-" + realvpc['vpc_id'].gsub(/.*\//, "")
|
1204
|
+
realvpc['vpc_id'] = getTail("vpc_id", value: realvpc['vpc_id'], prettyname: "Admin Firewall Ruleset #{name} Target VPC", cloudtype: "AWS::EC2::VPC::Id") if realvpc["vpc_id"].is_a?(String)
|
1205
|
+
elsif !realvpc['vpc_name'].nil?
|
1206
|
+
name = name + "-" + realvpc['vpc_name']
|
1207
|
+
end
|
1208
|
+
end
|
1209
|
+
|
1210
|
+
hosts.uniq!
|
1211
|
+
|
1212
|
+
rules = []
|
1213
|
+
if cloud == "Google"
|
1214
|
+
rules = [
|
1215
|
+
{ "ingress" => true, "proto" => "all", "hosts" => hosts },
|
1216
|
+
{ "egress" => true, "proto" => "all", "hosts" => hosts }
|
1217
|
+
]
|
1218
|
+
else
|
1219
|
+
rules = [
|
1220
|
+
{ "proto" => "tcp", "port_range" => "0-65535", "hosts" => hosts },
|
1221
|
+
{ "proto" => "udp", "port_range" => "0-65535", "hosts" => hosts },
|
1222
|
+
{ "proto" => "icmp", "port_range" => "-1", "hosts" => hosts }
|
1223
|
+
]
|
1224
|
+
end
|
1225
|
+
|
1226
|
+
acl = {"name" => name, "rules" => rules, "vpc" => realvpc, "cloud" => cloud, "admin" => true}
|
1227
|
+
acl.delete("vpc") if !acl["vpc"]
|
1228
|
+
acl["region"] == region if !region.nil? and !region.empty?
|
1229
|
+
@admin_firewall_rules << acl if !@admin_firewall_rules.include?(acl)
|
1230
|
+
return {"type" => "firewall_rule", "name" => name}
|
1231
|
+
end
|
1232
|
+
|
1233
|
+
private
|
1234
|
+
|
1235
|
+
def self.resolveYAMLAnchors(lines)
|
1236
|
+
new_text = ""
|
1237
|
+
lines.each_line { |line|
|
1238
|
+
if line.match(/# MU::Config\.include PLACEHOLDER /)
|
1239
|
+
$yaml_refs.each_pair { |anchor, data|
|
1240
|
+
if line.sub!(/^(\s+).*?# MU::Config\.include PLACEHOLDER #{Regexp.quote(anchor)} REDLOHECALP/, "")
|
1241
|
+
indent = $1
|
1242
|
+
MU::Config.resolveYAMLAnchors(data).each_line { |addline|
|
1243
|
+
line = line + indent + addline
|
1244
|
+
}
|
1245
|
+
break
|
1246
|
+
end
|
1247
|
+
}
|
1248
|
+
end
|
1249
|
+
new_text = new_text + line
|
1250
|
+
}
|
1251
|
+
return new_text
|
1252
|
+
end
|
1253
|
+
|
1254
|
+
|
1255
|
+
# Given a path to a config file, try to guess whether it's YAML or JSON.
|
1256
|
+
# @param path [String]: The path to the file to check.
|
1257
|
+
def self.guessFormat(path)
|
1258
|
+
raw = File.read(path)
|
1259
|
+
# Rip out ERB references that will bollocks parser syntax, first.
|
1260
|
+
stripped = raw.gsub(/<%.*?%>,?/, "").gsub(/,[\n\s]*([\]\}])/, '\1')
|
1261
|
+
begin
|
1262
|
+
JSON.parse(stripped)
|
1263
|
+
rescue JSON::ParserError => e
|
1264
|
+
begin
|
1265
|
+
YAML.load(raw.gsub(/<%.*?%>/, ""))
|
1266
|
+
rescue Psych::SyntaxError => e
|
1267
|
+
# Ok, well neither of those worked, let's assume that filenames are
|
1268
|
+
# meaningful.
|
1269
|
+
if path.match(/\.(yaml|yml)$/i)
|
1270
|
+
MU.log "Guessing that #{path} is YAML based on filename", MU::NOTICE
|
1271
|
+
return :yaml
|
1272
|
+
elsif path.match(/\.(json|jsn|js)$/i)
|
1273
|
+
MU.log "Guessing that #{path} is JSON based on filename", MU::NOTICE
|
1274
|
+
return :json
|
1275
|
+
else
|
1276
|
+
# For real? Ok, let's try the dumbest possible method.
|
1277
|
+
dashes = raw.match(/\-/)
|
1278
|
+
braces = raw.match(/[{}]/)
|
1279
|
+
if dashes.size > braces.size
|
1280
|
+
MU.log "Guessing that #{path} is YAML by... counting dashes.", MU::WARN
|
1281
|
+
return :yaml
|
1282
|
+
elsif braces.size > dashes.size
|
1283
|
+
MU.log "Guessing that #{path} is JSON by... counting braces.", MU::WARN
|
1284
|
+
return :json
|
1285
|
+
else
|
1286
|
+
raise "Unable to guess composition of #{path} by any means"
|
1287
|
+
end
|
1288
|
+
end
|
1289
|
+
end
|
1290
|
+
MU.log "Guessing that #{path} is YAML based on parser", MU::NOTICE
|
1291
|
+
return :yaml
|
1292
|
+
end
|
1293
|
+
MU.log "Guessing that #{path} is JSON based on parser", MU::NOTICE
|
1294
|
+
return :json
|
1295
|
+
end
|
1296
|
+
|
1297
|
+
# We used to be inconsistent about config keys using dashes versus
|
1298
|
+
# underscores. Now we've standardized on the latter. Be polite and
|
1299
|
+
# translate for older configs, since we're not fussed about name collisions.
|
1300
|
+
def self.fixDashes(conf)
|
1301
|
+
if conf.is_a?(Hash)
|
1302
|
+
newhash = Hash.new
|
1303
|
+
conf.each_pair { |key, val|
|
1304
|
+
if val.is_a?(Hash) or val.is_a?(Array)
|
1305
|
+
val = self.fixDashes(val)
|
1306
|
+
end
|
1307
|
+
if key.match(/-/)
|
1308
|
+
MU.log "Replacing #{key} with #{key.gsub(/-/, "_")}", MU::DEBUG
|
1309
|
+
newhash[key.gsub(/-/, "_")] = val
|
1310
|
+
else
|
1311
|
+
newhash[key] = val
|
1312
|
+
end
|
1313
|
+
}
|
1314
|
+
return newhash
|
1315
|
+
elsif conf.is_a?(Array)
|
1316
|
+
conf.map! { |val|
|
1317
|
+
if val.is_a?(Hash) or val.is_a?(Array)
|
1318
|
+
self.fixDashes(val)
|
1319
|
+
else
|
1320
|
+
val
|
1321
|
+
end
|
1322
|
+
}
|
1323
|
+
end
|
1324
|
+
|
1325
|
+
return conf
|
1326
|
+
end
|
1327
|
+
|
1328
|
+
@skipinitialupdates = false
|
1329
|
+
|
1330
|
+
# This can be called with ERB from within a stack config file, like so:
|
1331
|
+
# <%= Config.include("drupal.json") %>
|
1332
|
+
# It will first try the literal path you pass it, and if it fails to find
|
1333
|
+
# that it will look in the directory containing the main (top-level) config.
|
1334
|
+
def self.include(file, binding = nil, param_pass = false)
|
1335
|
+
loglevel = param_pass ? MU::NOTICE : MU::DEBUG
|
1336
|
+
retries = 0
|
1337
|
+
orig_filename = file
|
1338
|
+
assume_type = nil
|
1339
|
+
if file.match(/(js|json|jsn)$/i)
|
1340
|
+
assume_type = :json
|
1341
|
+
elsif file.match(/(yaml|yml)$/i)
|
1342
|
+
assume_type = :yaml
|
1343
|
+
end
|
1344
|
+
begin
|
1345
|
+
erb = ERB.new(File.read(file), nil, "<>")
|
1346
|
+
rescue Errno::ENOENT => e
|
1347
|
+
retries = retries + 1
|
1348
|
+
if retries == 1
|
1349
|
+
file = File.dirname(MU::Config.config_path)+"/"+orig_filename
|
1350
|
+
retry
|
1351
|
+
elsif retries == 2
|
1352
|
+
file = File.dirname(MU.myRoot)+"/lib/demo/"+orig_filename
|
1353
|
+
retry
|
1354
|
+
else
|
1355
|
+
raise ValidationError, "Couldn't read #{file} included from #{MU::Config.config_path}"
|
1356
|
+
end
|
1357
|
+
end
|
1358
|
+
begin
|
1359
|
+
# Include as just a drop-in block of text if the filename doesn't imply
|
1360
|
+
# a particular format, or if we're melding JSON into JSON.
|
1361
|
+
if ($file_format == :json and assume_type == :json) or assume_type.nil?
|
1362
|
+
MU.log "Including #{file} as uninterpreted text", loglevel
|
1363
|
+
return erb.result(binding)
|
1364
|
+
end
|
1365
|
+
# ...otherwise, try to parse into something useful so we can meld
|
1366
|
+
# differing file formats, or work around YAML's annoying dependence
|
1367
|
+
# on indentation.
|
1368
|
+
parsed_cfg = nil
|
1369
|
+
begin
|
1370
|
+
parsed_cfg = JSON.parse(erb.result(binding))
|
1371
|
+
parsed_as = :json
|
1372
|
+
rescue JSON::ParserError => e
|
1373
|
+
MU.log e.inspect, MU::DEBUG
|
1374
|
+
begin
|
1375
|
+
parsed_cfg = YAML.load(MU::Config.resolveYAMLAnchors(erb.result(binding)))
|
1376
|
+
parsed_as = :yaml
|
1377
|
+
rescue Psych::SyntaxError => e
|
1378
|
+
MU.log e.inspect, MU::DEBUG
|
1379
|
+
MU.log "#{file} parsed neither as JSON nor as YAML, including as raw text", MU::WARN if @param_pass
|
1380
|
+
return erb.result(binding)
|
1381
|
+
end
|
1382
|
+
end
|
1383
|
+
if $file_format == :json
|
1384
|
+
MU.log "Including #{file} as interpreted JSON", loglevel
|
1385
|
+
return JSON.generate(parsed_cfg)
|
1386
|
+
else
|
1387
|
+
MU.log "Including #{file} as interpreted YAML", loglevel
|
1388
|
+
$yaml_refs[file] = ""+YAML.dump(parsed_cfg).sub(/^---\n/, "")
|
1389
|
+
return "# MU::Config.include PLACEHOLDER #{file} REDLOHECALP"
|
1390
|
+
end
|
1391
|
+
rescue SyntaxError => e
|
1392
|
+
raise ValidationError, "ERB in #{file} threw a syntax error"
|
1393
|
+
end
|
1394
|
+
end
|
1395
|
+
|
1396
|
+
# (see #include)
|
1397
|
+
def include(file)
|
1398
|
+
MU::Config.include(file, get_binding, param_pass = @param_pass)
|
1399
|
+
end
|
1400
|
+
|
1401
|
+
# Namespace magic to pass to ERB's result method.
|
1402
|
+
def get_binding
|
1403
|
+
binding
|
1404
|
+
end
|
1405
|
+
|
1406
|
+
def self.set_defaults(conf_chunk = config, schema_chunk = schema, depth = 0, siblings = nil)
|
1407
|
+
return if schema_chunk.nil?
|
1408
|
+
|
1409
|
+
if conf_chunk != nil and schema_chunk["properties"].kind_of?(Hash) and conf_chunk.is_a?(Hash)
|
1410
|
+
if schema_chunk["properties"]["creation_style"].nil? or
|
1411
|
+
schema_chunk["properties"]["creation_style"] != "existing"
|
1412
|
+
schema_chunk["properties"].each_pair { |key, subschema|
|
1413
|
+
new_val = self.set_defaults(conf_chunk[key], subschema, depth+1, conf_chunk)
|
1414
|
+
conf_chunk[key] = new_val if new_val != nil
|
1415
|
+
}
|
1416
|
+
end
|
1417
|
+
elsif schema_chunk["type"] == "array" and conf_chunk.kind_of?(Array)
|
1418
|
+
conf_chunk.map! { |item|
|
1419
|
+
self.set_defaults(item, schema_chunk["items"], depth+1, conf_chunk)
|
1420
|
+
}
|
1421
|
+
else
|
1422
|
+
if conf_chunk.nil? and !schema_chunk["default_if"].nil? and !siblings.nil?
|
1423
|
+
schema_chunk["default_if"].each { |cond|
|
1424
|
+
if siblings[cond["key_is"]] == cond["value_is"]
|
1425
|
+
return cond["set"]
|
1426
|
+
end
|
1427
|
+
}
|
1428
|
+
end
|
1429
|
+
if conf_chunk.nil? and schema_chunk["default"] != nil
|
1430
|
+
return schema_chunk["default"]
|
1431
|
+
end
|
1432
|
+
end
|
1433
|
+
return conf_chunk
|
1434
|
+
end
|
1435
|
+
|
1436
|
+
# For our resources which specify intra-stack dependencies, make sure those
|
1437
|
+
# dependencies are actually declared.
|
1438
|
+
# TODO check for loops
|
1439
|
+
def self.check_dependencies(config)
|
1440
|
+
ok = true
|
1441
|
+
config.each { |type|
|
1442
|
+
if type.instance_of?(Array)
|
1443
|
+
type.each { |container|
|
1444
|
+
if container.instance_of?(Array)
|
1445
|
+
container.each { |resource|
|
1446
|
+
if resource.kind_of?(Hash) and resource["dependencies"] != nil
|
1447
|
+
resource["dependencies"].each { |dependency|
|
1448
|
+
collection = dependency["type"]+"s"
|
1449
|
+
found = false
|
1450
|
+
names_seen = []
|
1451
|
+
if config[collection] != nil
|
1452
|
+
config[collection].each { |service|
|
1453
|
+
names_seen << service["name"].to_s
|
1454
|
+
found = true if service["name"].to_s == dependency["name"].to_s
|
1455
|
+
}
|
1456
|
+
end
|
1457
|
+
if !found
|
1458
|
+
MU.log "Missing dependency: #{type[0]}{#{resource['name']}} needs #{collection}{#{dependency['name']}}", MU::ERR, details: names_seen
|
1459
|
+
ok = false
|
1460
|
+
end
|
1461
|
+
}
|
1462
|
+
end
|
1463
|
+
}
|
1464
|
+
end
|
1465
|
+
}
|
1466
|
+
end
|
1467
|
+
}
|
1468
|
+
return ok
|
1469
|
+
end
|
1470
|
+
|
1471
|
+
|
1472
|
+
# Verify that a server or server_pool has a valid AD config referencing
|
1473
|
+
# valid Vaults for credentials.
|
1474
|
+
def self.check_vault_refs(server)
|
1475
|
+
ok = true
|
1476
|
+
server['vault_access'] = [] if server['vault_access'].nil?
|
1477
|
+
server['groomer'] ||= "Chef"
|
1478
|
+
groomclass = MU::Groomer.loadGroomer(server['groomer'])
|
1479
|
+
|
1480
|
+
begin
|
1481
|
+
if !server['active_directory'].nil?
|
1482
|
+
["domain_admin_vault", "domain_join_vault"].each { |vault_class|
|
1483
|
+
server['vault_access'] << {
|
1484
|
+
"vault" => server['active_directory'][vault_class]['vault'],
|
1485
|
+
"item" => server['active_directory'][vault_class]['item']
|
1486
|
+
}
|
1487
|
+
item = groomclass.getSecret(
|
1488
|
+
vault: server['active_directory'][vault_class]['vault'],
|
1489
|
+
item: server['active_directory'][vault_class]['item'],
|
1490
|
+
)
|
1491
|
+
["username_field", "password_field"].each { |field|
|
1492
|
+
if !item.has_key?(server['active_directory'][vault_class][field])
|
1493
|
+
ok = false
|
1494
|
+
MU.log "I don't see a value named #{field} in Chef Vault #{server['active_directory'][vault_class]['vault']}:#{server['active_directory'][vault_class]['item']}", MU::ERR
|
1495
|
+
end
|
1496
|
+
}
|
1497
|
+
}
|
1498
|
+
end
|
1499
|
+
|
1500
|
+
if !server['windows_auth_vault'].nil?
|
1501
|
+
server['use_cloud_provider_windows_password'] = false
|
1502
|
+
|
1503
|
+
server['vault_access'] << {
|
1504
|
+
"vault" => server['windows_auth_vault']['vault'],
|
1505
|
+
"item" => server['windows_auth_vault']['item']
|
1506
|
+
}
|
1507
|
+
item = groomclass.getSecret(
|
1508
|
+
vault: server['windows_auth_vault']['vault'],
|
1509
|
+
item: server['windows_auth_vault']['item']
|
1510
|
+
)
|
1511
|
+
["password_field", "ec2config_password_field", "sshd_password_field"].each { |field|
|
1512
|
+
if !item.has_key?(server['windows_auth_vault'][field])
|
1513
|
+
MU.log "No value named #{field} in Chef Vault #{server['windows_auth_vault']['vault']}:#{server['windows_auth_vault']['item']}, will use a generated password.", MU::NOTICE
|
1514
|
+
server['windows_auth_vault'].delete(field)
|
1515
|
+
end
|
1516
|
+
}
|
1517
|
+
end
|
1518
|
+
# Check all of the non-special ones while we're at it
|
1519
|
+
server['vault_access'].each { |v|
|
1520
|
+
next if v['vault'] == "splunk" and v['item'] == "admin_user"
|
1521
|
+
item = groomclass.getSecret(vault: v['vault'], item: v['item'])
|
1522
|
+
}
|
1523
|
+
rescue MuError
|
1524
|
+
MU.log "Can't load a Chef Vault I was configured to use. Does it exist?", MU::ERR
|
1525
|
+
ok = false
|
1526
|
+
end
|
1527
|
+
return ok
|
1528
|
+
end
|
1529
|
+
|
1530
|
+
|
1531
|
+
# Given a bare hash describing a resource, insert default values which can
|
1532
|
+
# be inherited from the current live parent configuration.
|
1533
|
+
# @param kitten [Hash]: A resource descriptor
|
1534
|
+
# @param type [String]: The type of resource this is ("servers" etc)
|
1535
|
+
def inheritDefaults(kitten, type)
|
1536
|
+
kitten['cloud'] ||= MU::Config.defaultCloud
|
1537
|
+
|
1538
|
+
schema_fields = ["region", "us_only", "scrub_mu_isms"]
|
1539
|
+
if kitten['cloud'] == "Google"
|
1540
|
+
kitten["project"] ||= MU::Cloud::Google.defaultProject
|
1541
|
+
schema_fields << "project"
|
1542
|
+
if kitten['region'].nil? and !kitten['#MU_CLOUDCLASS'].nil? and
|
1543
|
+
![MU::Cloud::VPC, MU::Cloud::FirewallRule].include?(kitten['#MU_CLOUDCLASS'])
|
1544
|
+
if !$MU_CFG['google'] or !$MU_CFG['google']['region']
|
1545
|
+
raise ValidationError, "Google resource declared without a region, but no default Google region declared in mu.yaml"
|
1546
|
+
end
|
1547
|
+
kitten['region'] ||= $MU_CFG['google']['region']
|
1548
|
+
end
|
1549
|
+
else
|
1550
|
+
if !$MU_CFG['aws'] or !$MU_CFG['aws']['region']
|
1551
|
+
raise ValidationError, "AWS resource declared without a region, but no default AWS region declared in mu.yaml"
|
1552
|
+
end
|
1553
|
+
kitten['region'] ||= $MU_CFG['aws']['region']
|
1554
|
+
end
|
1555
|
+
|
1556
|
+
kitten['us_only'] ||= @config['us_only']
|
1557
|
+
kitten['us_only'] ||= false
|
1558
|
+
|
1559
|
+
kitten['scrub_mu_isms'] ||= @config['scrub_mu_isms']
|
1560
|
+
kitten['scrub_mu_isms'] ||= false
|
1561
|
+
|
1562
|
+
kitten["dependencies"] ||= []
|
1563
|
+
|
1564
|
+
# Make sure the schema knows about these "new" fields, so that validation
|
1565
|
+
# doesn't trip over them.
|
1566
|
+
schema_fields.each { |field|
|
1567
|
+
if @@schema["properties"][field]
|
1568
|
+
MU.log "Adding #{field} to schema for #{type} #{kitten['cloud']}", MU::DEBUG
|
1569
|
+
@@schema["properties"][type]["items"]["properties"][field] ||= @@schema["properties"][field]
|
1570
|
+
end
|
1571
|
+
}
|
1572
|
+
end
|
1573
|
+
|
1574
|
+
def validate(config = @config)
|
1575
|
+
ok = true
|
1576
|
+
plain_cfg = MU::Config.manxify(Marshal.load(Marshal.dump(config)))
|
1577
|
+
|
1578
|
+
count = 0
|
1579
|
+
@kittens ||= {}
|
1580
|
+
types = MU::Cloud.resource_types.values.map { |v| v[:cfg_plural] }
|
1581
|
+
|
1582
|
+
types.each { |type|
|
1583
|
+
@kittens[type] = config[type]
|
1584
|
+
@kittens[type] ||= []
|
1585
|
+
@kittens[type].each { |k|
|
1586
|
+
inheritDefaults(k, type)
|
1587
|
+
}
|
1588
|
+
count = count + @kittens[type].size
|
1589
|
+
}
|
1590
|
+
|
1591
|
+
if count == 0
|
1592
|
+
MU.log "You must declare at least one resource to create", MU::ERR
|
1593
|
+
ok = false
|
1594
|
+
end
|
1595
|
+
|
1596
|
+
@nat_routes ||= {}
|
1597
|
+
types.each { |type|
|
1598
|
+
@kittens[type].each { |descriptor|
|
1599
|
+
ok = false if !insertKitten(descriptor, type)
|
1600
|
+
}
|
1601
|
+
}
|
1602
|
+
|
1603
|
+
@kittens["firewall_rules"].each { |acl|
|
1604
|
+
acl = resolveIntraStackFirewallRefs(acl)
|
1605
|
+
}
|
1606
|
+
|
1607
|
+
# Make sure validation has been called for all on-the-fly generated
|
1608
|
+
# resources.
|
1609
|
+
types.each { |type|
|
1610
|
+
@kittens[type].each { |descriptor|
|
1611
|
+
if !descriptor["#MU_VALIDATED"]
|
1612
|
+
ok = false if !insertKitten(descriptor, type)
|
1613
|
+
end
|
1614
|
+
}
|
1615
|
+
}
|
1616
|
+
|
1617
|
+
# add some default holes to allow dependent instances into databases
|
1618
|
+
@kittens["databases"].each { |db|
|
1619
|
+
if db['port'].nil?
|
1620
|
+
db['port'] = 3306 if ["mysql", "aurora"].include?(db['engine'])
|
1621
|
+
db['port'] = 5432 if ["postgres"].include?(db['engine'])
|
1622
|
+
db['port'] = 1433 if db['engine'].match(/^sqlserver\-/)
|
1623
|
+
db['port'] = 1521 if db['engine'].match(/^oracle\-/)
|
1624
|
+
end
|
1625
|
+
|
1626
|
+
ruleset = haveLitterMate?("database"+db['name'], "firewall_rules")
|
1627
|
+
if ruleset
|
1628
|
+
["server_pools", "servers"].each { |type|
|
1629
|
+
shortclass, cfg_name, cfg_plural, classname = MU::Cloud.getResourceNames(type)
|
1630
|
+
@kittens[cfg_plural].each { |server|
|
1631
|
+
server["dependencies"].each { |dep|
|
1632
|
+
if dep["type"] == "database" and dep["name"] == db["name"]
|
1633
|
+
# XXX this is AWS-specific, I think. We need to use source_tags to make this happen in Google. This logic probably needs to be dumped into the database layer.
|
1634
|
+
ruleset["rules"] << {
|
1635
|
+
"proto" => "tcp",
|
1636
|
+
"port" => db["port"],
|
1637
|
+
"sgs" => [cfg_name+server['name']]
|
1638
|
+
}
|
1639
|
+
|
1640
|
+
ruleset["dependencies"] << {
|
1641
|
+
"name" => cfg_name+server['name'],
|
1642
|
+
"type" => "firewall_rule",
|
1643
|
+
"no_create_wait" => true
|
1644
|
+
}
|
1645
|
+
end
|
1646
|
+
}
|
1647
|
+
}
|
1648
|
+
}
|
1649
|
+
end
|
1650
|
+
}
|
1651
|
+
|
1652
|
+
seen = []
|
1653
|
+
# XXX seem to be not detecting duplicate admin firewall_rules in adminFirewallRuleset
|
1654
|
+
@admin_firewall_rules.each { |acl|
|
1655
|
+
next if seen.include?(acl['name'])
|
1656
|
+
ok = false if !insertKitten(acl, "firewall_rules")
|
1657
|
+
seen << acl['name']
|
1658
|
+
}
|
1659
|
+
types.each { |type|
|
1660
|
+
config[type] = @kittens[type] if @kittens[type].size > 0
|
1661
|
+
}
|
1662
|
+
ok = false if !MU::Config.check_dependencies(config)
|
1663
|
+
|
1664
|
+
# TODO enforce uniqueness of resource names
|
1665
|
+
raise ValidationError if !ok
|
1666
|
+
|
1667
|
+
# XXX Does commenting this out make sense? Do we want to apply it to top-level
|
1668
|
+
# keys and ignore resources, which validate when insertKitten is called now?
|
1669
|
+
# begin
|
1670
|
+
# JSON::Validator.validate!(MU::Config.schema, plain_cfg)
|
1671
|
+
# rescue JSON::Schema::ValidationError => e
|
1672
|
+
# # Use fully_validate to get the complete error list, save some time
|
1673
|
+
# errors = JSON::Validator.fully_validate(MU::Config.schema, plain_cfg)
|
1674
|
+
# realerrors = []
|
1675
|
+
# errors.each { |err|
|
1676
|
+
# if !err.match(/The property '.+?' of type MU::Config::Tail did not match the following type:/)
|
1677
|
+
# realerrors << err
|
1678
|
+
# end
|
1679
|
+
# }
|
1680
|
+
# if realerrors.size > 0
|
1681
|
+
# raise ValidationError, "Validation error in #{@@config_path}!\n"+realerrors.join("\n")
|
1682
|
+
# end
|
1683
|
+
# end
|
1684
|
+
end
|
1685
|
+
|
1686
|
+
|
1687
|
+
# Emit our Basket of Kittesn schema in a format that YARD can comprehend
|
1688
|
+
# and turn into documentation.
|
1689
|
+
def self.printSchema(dummy_kitten_class, class_hierarchy, schema, in_array = false, required = false, prefix: nil)
|
1690
|
+
return if schema.nil?
|
1691
|
+
if schema["type"] == "object"
|
1692
|
+
printme = Array.new
|
1693
|
+
if !schema["properties"].nil?
|
1694
|
+
# order sub-elements by whether they're required, so we can use YARD's
|
1695
|
+
# grouping tags on them
|
1696
|
+
if !schema["required"].nil? and schema["required"].size > 0
|
1697
|
+
prop_list = schema["properties"].keys.sort_by { |name|
|
1698
|
+
schema["required"].include?(name) ? 0 : 1
|
1699
|
+
}
|
1700
|
+
else
|
1701
|
+
prop_list = schema["properties"].keys
|
1702
|
+
end
|
1703
|
+
req = false
|
1704
|
+
printme << "# @!group Optional parameters" if schema["required"].nil? or schema["required"].size == 0
|
1705
|
+
prop_list.each { |name|
|
1706
|
+
prop = schema["properties"][name]
|
1707
|
+
if !schema["required"].nil? and schema["required"].include?(name)
|
1708
|
+
printme << "# @!group Required parameters" if !req
|
1709
|
+
req = true
|
1710
|
+
else
|
1711
|
+
if req
|
1712
|
+
printme << "# @!endgroup"
|
1713
|
+
printme << "# @!group Optional parameters"
|
1714
|
+
end
|
1715
|
+
req = false
|
1716
|
+
end
|
1717
|
+
|
1718
|
+
printme << self.printSchema(dummy_kitten_class, class_hierarchy+ [name], prop, false, req, prefix: schema["prefix"])
|
1719
|
+
}
|
1720
|
+
printme << "# @!endgroup"
|
1721
|
+
end
|
1722
|
+
|
1723
|
+
tabs = 1
|
1724
|
+
class_hierarchy.each { |classname|
|
1725
|
+
if classname == class_hierarchy.last and !schema['description'].nil?
|
1726
|
+
dummy_kitten_class.puts ["\t"].cycle(tabs).to_a.join('') + "# #{schema['description']}\n"
|
1727
|
+
end
|
1728
|
+
dummy_kitten_class.puts ["\t"].cycle(tabs).to_a.join('') + "class #{classname}"
|
1729
|
+
tabs = tabs + 1
|
1730
|
+
}
|
1731
|
+
printme.each { |lines|
|
1732
|
+
if !lines.nil? and lines.is_a?(String)
|
1733
|
+
lines.lines.each { |line|
|
1734
|
+
dummy_kitten_class.puts ["\t"].cycle(tabs).to_a.join('') + line
|
1735
|
+
}
|
1736
|
+
end
|
1737
|
+
}
|
1738
|
+
|
1739
|
+
class_hierarchy.each { |classname|
|
1740
|
+
tabs = tabs - 1
|
1741
|
+
dummy_kitten_class.puts ["\t"].cycle(tabs).to_a.join('') + "end"
|
1742
|
+
}
|
1743
|
+
|
1744
|
+
# And now that we've dealt with our children, pass our own rendered
|
1745
|
+
# commentary back up to our caller.
|
1746
|
+
name = class_hierarchy.last
|
1747
|
+
if in_array
|
1748
|
+
type = "Array<#{class_hierarchy.join("::")}>"
|
1749
|
+
else
|
1750
|
+
type = class_hierarchy.join("::")
|
1751
|
+
end
|
1752
|
+
|
1753
|
+
docstring = "\n"
|
1754
|
+
docstring = docstring + "# **REQUIRED**\n" if required
|
1755
|
+
docstring = docstring + "# **"+schema["prefix"]+"**\n" if schema["prefix"]
|
1756
|
+
docstring = docstring + "# #{schema['description'].gsub(/\n/, "\n#")}\n" if !schema['description'].nil?
|
1757
|
+
docstring = docstring + "#\n"
|
1758
|
+
docstring = docstring + "# @return [#{type}]\n"
|
1759
|
+
docstring = docstring + "# @see #{class_hierarchy.join("::")}\n"
|
1760
|
+
docstring = docstring + "attr_accessor :#{name}"
|
1761
|
+
return docstring
|
1762
|
+
|
1763
|
+
elsif schema["type"] == "array"
|
1764
|
+
return self.printSchema(dummy_kitten_class, class_hierarchy, schema['items'], true, required, prefix: prefix)
|
1765
|
+
else
|
1766
|
+
name = class_hierarchy.last
|
1767
|
+
if schema['type'].nil?
|
1768
|
+
MU.log "Couldn't determine schema type in #{class_hierarchy.join(" => ")}", MU::WARN, details: schema
|
1769
|
+
return nil
|
1770
|
+
end
|
1771
|
+
if in_array
|
1772
|
+
type = "Array<#{schema['type'].capitalize}>"
|
1773
|
+
else
|
1774
|
+
type = schema['type'].capitalize
|
1775
|
+
end
|
1776
|
+
docstring = "\n"
|
1777
|
+
|
1778
|
+
prefixes = []
|
1779
|
+
prefixes << "# **REQUIRED**" if required and schema['default'].nil?
|
1780
|
+
prefixes << "# **"+schema["prefix"]+"**" if schema["prefix"]
|
1781
|
+
prefixes << "# **Default: `#{schema['default']}`**" if !schema['default'].nil?
|
1782
|
+
if !schema['enum'].nil?
|
1783
|
+
prefixes << "# **Must be one of: `#{schema['enum'].join(', ')}`**"
|
1784
|
+
elsif !schema['pattern'].nil?
|
1785
|
+
# XXX unquoted regex chars confuse the hell out of YARD. How do we
|
1786
|
+
# quote {}[] etc in YARD-speak?
|
1787
|
+
prefixes << "# **Must match pattern `#{schema['pattern'].gsub(/\n/, "\n#")}`**"
|
1788
|
+
end
|
1789
|
+
|
1790
|
+
if prefixes.size > 0
|
1791
|
+
docstring += prefixes.join(",\n")
|
1792
|
+
if schema['description'] and schema['description'].size > 1
|
1793
|
+
docstring += " - "
|
1794
|
+
end
|
1795
|
+
docstring += "\n"
|
1796
|
+
end
|
1797
|
+
|
1798
|
+
docstring = docstring + "# #{schema['description'].gsub(/\n/, "\n#")}\n" if !schema['description'].nil?
|
1799
|
+
docstring = docstring + "#\n"
|
1800
|
+
docstring = docstring + "# @return [#{type}]\n"
|
1801
|
+
docstring = docstring + "attr_accessor :#{name}"
|
1802
|
+
|
1803
|
+
return docstring
|
1804
|
+
end
|
1805
|
+
|
1806
|
+
return nil
|
1807
|
+
end
|
1808
|
+
|
1809
|
+
def self.dependencies_primitive
|
1810
|
+
{
|
1811
|
+
"type" => "array",
|
1812
|
+
"items" => {
|
1813
|
+
"type" => "object",
|
1814
|
+
"description" => "Declare other objects which this resource requires. This resource will wait until the others are available to create itself.",
|
1815
|
+
"required" => ["name", "type"],
|
1816
|
+
"additionalProperties" => false,
|
1817
|
+
"properties" => {
|
1818
|
+
"name" => {"type" => "string"},
|
1819
|
+
"type" => {
|
1820
|
+
"type" => "string",
|
1821
|
+
"enum" => MU::Cloud.resource_types.values.map { |v| v[:cfg_name] }
|
1822
|
+
},
|
1823
|
+
"phase" => {
|
1824
|
+
"type" => "string",
|
1825
|
+
"description" => "Which part of the creation process of the resource we depend on should we wait for before starting our own creation? Defaults are usually sensible, but sometimes you want, say, a Server to wait on another Server to be completely ready (through its groom phase) before starting up.",
|
1826
|
+
"enum" => ["create", "groom"]
|
1827
|
+
},
|
1828
|
+
"no_create_wait" => {
|
1829
|
+
"type" => "boolean",
|
1830
|
+
"default" => false,
|
1831
|
+
"description" => "By default, it's assumed that we want to wait on our parents' creation phase, in addition to whatever is declared in this stanza. Setting this flag will bypass waiting on our parent resource's creation, so that our create or groom phase can instead depend only on the parent's groom phase. "
|
1832
|
+
}
|
1833
|
+
}
|
1834
|
+
}
|
1835
|
+
}
|
1836
|
+
end
|
1837
|
+
|
1838
|
+
CIDR_PATTERN = "^\\d+\\.\\d+\\.\\d+\\.\\d+\/[0-9]{1,2}$"
|
1839
|
+
CIDR_DESCRIPTION = "CIDR-formatted IP block, e.g. 1.2.3.4/32"
|
1840
|
+
CIDR_PRIMITIVE = {
|
1841
|
+
"type" => "string",
|
1842
|
+
"pattern" => CIDR_PATTERN,
|
1843
|
+
"description" => CIDR_DESCRIPTION
|
1844
|
+
}
|
1845
|
+
|
1846
|
+
# Have a default value available for config schema elements that take an
|
1847
|
+
# email address.
|
1848
|
+
# @return [String]
|
1849
|
+
def self.notification_email
|
1850
|
+
if MU.chef_user == "mu"
|
1851
|
+
ENV['MU_ADMIN_EMAIL']
|
1852
|
+
else
|
1853
|
+
MU.userEmail
|
1854
|
+
end
|
1855
|
+
end
|
1856
|
+
|
1857
|
+
@@schema = {
|
1858
|
+
"$schema" => "http://json-schema.org/draft-04/schema#",
|
1859
|
+
"title" => "MU Application",
|
1860
|
+
"type" => "object",
|
1861
|
+
"description" => "A MU application stack, consisting of at least one resource.",
|
1862
|
+
"required" => ["admins", "appname"],
|
1863
|
+
"properties" => {
|
1864
|
+
"appname" => {
|
1865
|
+
"type" => "string",
|
1866
|
+
"description" => "A name for your application stack. Should be short, but easy to differentiate from other applications.",
|
1867
|
+
},
|
1868
|
+
"scrub_mu_isms" => {
|
1869
|
+
"type" => "boolean",
|
1870
|
+
"description" => "When 'cloud' is set to 'CloudFormation,' use this flag to strip out Mu-specific artifacts (tags, standard userdata, naming conventions, etc) to yield a clean, source-agnostic template. Setting this flag here will override declarations in individual resources."
|
1871
|
+
},
|
1872
|
+
"project" => {
|
1873
|
+
"type" => "string",
|
1874
|
+
"description" => "GOOGLE: The project into which to deploy resources",
|
1875
|
+
"default" => MU::Cloud::Google.defaultProject
|
1876
|
+
},
|
1877
|
+
"region" => MU::Config.region_primitive,
|
1878
|
+
"us_only" => {
|
1879
|
+
"type" => "boolean",
|
1880
|
+
"description" => "For resources which span regions, restrict to regions inside the United States",
|
1881
|
+
"default" => false
|
1882
|
+
},
|
1883
|
+
"conditions" => {
|
1884
|
+
"type" => "array",
|
1885
|
+
"items" => {
|
1886
|
+
"type" => "object",
|
1887
|
+
"required" => ["name", "cloudcode"],
|
1888
|
+
"description" => "CloudFormation-specific. Define Conditions as in http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/conditions-section-structure.html. Arguments must use the cloudCode() macro.",
|
1889
|
+
"properties" => {
|
1890
|
+
"name" => { "required" => true, "type" => "string" },
|
1891
|
+
"cloudcode" => { "required" => true, "type" => "string" },
|
1892
|
+
}
|
1893
|
+
}
|
1894
|
+
},
|
1895
|
+
"parameters" => {
|
1896
|
+
"type" => "array",
|
1897
|
+
"items" => {
|
1898
|
+
"type" => "object",
|
1899
|
+
"title" => "parameter",
|
1900
|
+
"description" => "Parameters to be substituted elsewhere in this Basket of Kittens as ERB variables (<%= varname %>)",
|
1901
|
+
"additionalProperties" => false,
|
1902
|
+
"properties" => {
|
1903
|
+
"name" => { "required" => true, "type" => "string" },
|
1904
|
+
"default" => { "type" => "string" },
|
1905
|
+
"list_of" => {
|
1906
|
+
"type" => "string",
|
1907
|
+
"description" => "Treat the value as a comma-separated list of values with this key name, equivalent to CloudFormation's various List<> types. For example, set to 'subnet_id' to pass values as an array of subnet identifiers as the 'subnets' argument of a VPC stanza."
|
1908
|
+
},
|
1909
|
+
"prettyname" => {
|
1910
|
+
"type" => "string",
|
1911
|
+
"description" => "An alternative name to use when generating parameter fields in, for example, CloudFormation templates"
|
1912
|
+
},
|
1913
|
+
"description" => {"type" => "string"},
|
1914
|
+
"cloudtype" => {
|
1915
|
+
"type" => "string",
|
1916
|
+
"description" => "A platform-specific string describing the type of validation to use for this parameter. E.g. when generating a CloudFormation template, set to AWS::EC2::Image::Id to validate input as an AMI identifier."
|
1917
|
+
},
|
1918
|
+
"required" => {
|
1919
|
+
"type" => "boolean",
|
1920
|
+
"default" => true
|
1921
|
+
},
|
1922
|
+
"valid_values" => {
|
1923
|
+
"type" => "array",
|
1924
|
+
"description" => "List of valid values for this parameter. Can only be a list of static strings, for now.",
|
1925
|
+
"items" => {
|
1926
|
+
"type" => "string"
|
1927
|
+
}
|
1928
|
+
}
|
1929
|
+
}
|
1930
|
+
}
|
1931
|
+
},
|
1932
|
+
# TODO availability zones (or an array thereof)
|
1933
|
+
|
1934
|
+
"admins" => {
|
1935
|
+
"type" => "array",
|
1936
|
+
"items" => {
|
1937
|
+
"type" => "object",
|
1938
|
+
"title" => "admin",
|
1939
|
+
"description" => "Administrative contacts for this application stack. Will be automatically set to invoking Mu user, if not specified.",
|
1940
|
+
"required" => ["name", "email"],
|
1941
|
+
"additionalProperties" => false,
|
1942
|
+
"properties" => {
|
1943
|
+
"name" => {"type" => "string"},
|
1944
|
+
"email" => {"type" => "string"},
|
1945
|
+
"public_key" => {
|
1946
|
+
"type" => "string",
|
1947
|
+
"description" => "An OpenSSH-style public key string. This will be installed on all instances created in this deployment."
|
1948
|
+
}
|
1949
|
+
}
|
1950
|
+
},
|
1951
|
+
"minItems" => 1,
|
1952
|
+
"uniqueItems" => true
|
1953
|
+
}
|
1954
|
+
},
|
1955
|
+
"additionalProperties" => false
|
1956
|
+
}
|
1957
|
+
|
1958
|
+
failed = []
|
1959
|
+
|
1960
|
+
# Load all of the config stub files at the Ruby level
|
1961
|
+
MU::Cloud.resource_types.each_pair { |type, cfg|
|
1962
|
+
begin
|
1963
|
+
require "mu/config/#{cfg[:cfg_name]}"
|
1964
|
+
rescue LoadError => e
|
1965
|
+
# raise MuError, "MU::Config implemention of #{type} missing from modules/mu/config/#{cfg[:cfg_name]}.rb"
|
1966
|
+
MU.log "MU::Config::#{type} stub class is missing", MU::ERR
|
1967
|
+
failed << type
|
1968
|
+
next
|
1969
|
+
end
|
1970
|
+
}
|
1971
|
+
|
1972
|
+
MU::Cloud.resource_types.each_pair { |type, cfg|
|
1973
|
+
begin
|
1974
|
+
schemaclass = Object.const_get("MU").const_get("Config").const_get(type)
|
1975
|
+
[:schema, :validate].each { |method|
|
1976
|
+
if !schemaclass.respond_to?(method)
|
1977
|
+
MU.log "MU::Config::#{type}.#{method.to_s} doesn't seem to be implemented", MU::ERR
|
1978
|
+
failed << type
|
1979
|
+
end
|
1980
|
+
}
|
1981
|
+
next if failed.include?(type)
|
1982
|
+
@@schema["properties"][cfg[:cfg_plural]] = {
|
1983
|
+
"type" => "array",
|
1984
|
+
"items" => schemaclass.schema
|
1985
|
+
}
|
1986
|
+
@@schema["properties"][cfg[:cfg_plural]]["items"]["properties"]["dependencies"] = MU::Config.dependencies_primitive
|
1987
|
+
@@schema["properties"][cfg[:cfg_plural]]["items"]["properties"]["cloud"] = MU::Config.cloud_primitive
|
1988
|
+
@@schema["properties"][cfg[:cfg_plural]]["items"]["title"] = type.to_s
|
1989
|
+
rescue NameError => e
|
1990
|
+
failed << type
|
1991
|
+
MU.log "Error loading #{type} schema from mu/config/#{cfg[:cfg_name]}", MU::ERR, details: "\t"+e.inspect+"\n\t"+e.backtrace[0]
|
1992
|
+
end
|
1993
|
+
}
|
1994
|
+
failed.uniq!
|
1995
|
+
if failed.size > 0
|
1996
|
+
raise MuError, "Resource type config loaders failed checks, aborting"
|
1997
|
+
end
|
1998
|
+
|
1999
|
+
end #class
|
2000
|
+
end #module
|