cloud-mu 2.1.0beta → 3.0.0beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/Berksfile +4 -5
- data/Berksfile.lock +179 -0
- data/README.md +1 -6
- data/ansible/roles/geerlingguy.firewall/templates/firewall.bash.j2 +0 -0
- data/ansible/roles/mu-installer/README.md +33 -0
- data/ansible/roles/mu-installer/defaults/main.yml +2 -0
- data/ansible/roles/mu-installer/handlers/main.yml +2 -0
- data/ansible/roles/mu-installer/meta/main.yml +60 -0
- data/ansible/roles/mu-installer/tasks/main.yml +13 -0
- data/ansible/roles/mu-installer/tests/inventory +2 -0
- data/ansible/roles/mu-installer/tests/test.yml +5 -0
- data/ansible/roles/mu-installer/vars/main.yml +2 -0
- data/bin/mu-adopt +125 -0
- data/bin/mu-aws-setup +4 -4
- data/bin/mu-azure-setup +265 -0
- data/bin/mu-azure-tests +43 -0
- data/bin/mu-cleanup +20 -8
- data/bin/mu-configure +224 -98
- data/bin/mu-deploy +8 -3
- data/bin/mu-gcp-setup +16 -8
- data/bin/mu-gen-docs +92 -8
- data/bin/mu-load-config.rb +52 -12
- data/bin/mu-momma-cat +36 -0
- data/bin/mu-node-manage +34 -27
- data/bin/mu-self-update +2 -2
- data/bin/mu-ssh +12 -8
- data/bin/mu-upload-chef-artifacts +11 -4
- data/bin/mu-user-manage +3 -0
- data/cloud-mu.gemspec +8 -11
- data/cookbooks/firewall/libraries/helpers_iptables.rb +2 -2
- data/cookbooks/firewall/metadata.json +1 -1
- data/cookbooks/firewall/recipes/default.rb +5 -9
- data/cookbooks/mu-firewall/attributes/default.rb +2 -0
- data/cookbooks/mu-firewall/metadata.rb +1 -1
- data/cookbooks/mu-glusterfs/templates/default/mu-gluster-client.erb +0 -0
- data/cookbooks/mu-master/Berksfile +2 -2
- data/cookbooks/mu-master/files/default/check_mem.pl +0 -0
- data/cookbooks/mu-master/files/default/cloudamatic.png +0 -0
- data/cookbooks/mu-master/metadata.rb +5 -4
- data/cookbooks/mu-master/recipes/389ds.rb +1 -1
- data/cookbooks/mu-master/recipes/basepackages.rb +30 -10
- data/cookbooks/mu-master/recipes/default.rb +59 -7
- data/cookbooks/mu-master/recipes/firewall-holes.rb +1 -1
- data/cookbooks/mu-master/recipes/init.rb +65 -47
- data/cookbooks/mu-master/recipes/{eks-kubectl.rb → kubectl.rb} +4 -10
- data/cookbooks/mu-master/recipes/sssd.rb +2 -1
- data/cookbooks/mu-master/recipes/update_nagios_only.rb +6 -6
- data/cookbooks/mu-master/templates/default/web_app.conf.erb +2 -2
- data/cookbooks/mu-master/templates/mods/ldap.conf.erb +4 -0
- data/cookbooks/mu-php54/Berksfile +1 -2
- data/cookbooks/mu-php54/metadata.rb +4 -5
- data/cookbooks/mu-php54/recipes/default.rb +1 -1
- data/cookbooks/mu-splunk/templates/default/splunk-init.erb +0 -0
- data/cookbooks/mu-tools/Berksfile +3 -2
- data/cookbooks/mu-tools/files/default/Mu_CA.pem +33 -0
- data/cookbooks/mu-tools/libraries/helper.rb +20 -8
- data/cookbooks/mu-tools/metadata.rb +5 -2
- data/cookbooks/mu-tools/recipes/apply_security.rb +2 -3
- data/cookbooks/mu-tools/recipes/eks.rb +1 -1
- data/cookbooks/mu-tools/recipes/gcloud.rb +5 -30
- data/cookbooks/mu-tools/recipes/nagios.rb +1 -1
- data/cookbooks/mu-tools/recipes/rsyslog.rb +1 -0
- data/cookbooks/mu-tools/recipes/selinux.rb +19 -0
- data/cookbooks/mu-tools/recipes/split_var_partitions.rb +0 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +256 -122
- data/cookbooks/mu-tools/resources/disk.rb +3 -1
- data/cookbooks/mu-tools/templates/amazon/sshd_config.erb +1 -1
- data/cookbooks/mu-tools/templates/default/etc_hosts.erb +1 -1
- data/cookbooks/mu-tools/templates/default/{kubeconfig.erb → kubeconfig-eks.erb} +0 -0
- data/cookbooks/mu-tools/templates/default/kubeconfig-gke.erb +27 -0
- data/cookbooks/mu-tools/templates/windows-10/sshd_config.erb +137 -0
- data/cookbooks/mu-utility/recipes/nat.rb +4 -0
- data/extras/alpha.png +0 -0
- data/extras/beta.png +0 -0
- data/extras/clean-stock-amis +2 -2
- data/extras/generate-stock-images +131 -0
- data/extras/git-fix-permissions-hook +0 -0
- data/extras/image-generators/AWS/centos6.yaml +17 -0
- data/extras/image-generators/{aws → AWS}/centos7-govcloud.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/centos7.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/rhel7.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/win2k12.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/win2k16.yaml +0 -0
- data/extras/image-generators/{aws → AWS}/windows.yaml +0 -0
- data/extras/image-generators/{gcp → Google}/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +18 -0
- data/extras/python_rpm/build.sh +0 -0
- data/extras/release.png +0 -0
- data/extras/ruby_rpm/build.sh +0 -0
- data/extras/ruby_rpm/muby.spec +1 -1
- data/install/README.md +43 -5
- data/install/deprecated-bash-library.sh +0 -0
- data/install/installer +1 -1
- data/install/jenkinskeys.rb +0 -0
- data/install/mu-master.yaml +55 -0
- data/modules/mommacat.ru +41 -7
- data/modules/mu.rb +444 -149
- data/modules/mu/adoption.rb +500 -0
- data/modules/mu/cleanup.rb +235 -158
- data/modules/mu/cloud.rb +675 -138
- data/modules/mu/clouds/aws.rb +156 -24
- data/modules/mu/clouds/aws/alarm.rb +4 -14
- data/modules/mu/clouds/aws/bucket.rb +60 -18
- data/modules/mu/clouds/aws/cache_cluster.rb +8 -20
- data/modules/mu/clouds/aws/collection.rb +12 -22
- data/modules/mu/clouds/aws/container_cluster.rb +209 -118
- data/modules/mu/clouds/aws/database.rb +120 -45
- data/modules/mu/clouds/aws/dnszone.rb +7 -18
- data/modules/mu/clouds/aws/endpoint.rb +5 -15
- data/modules/mu/clouds/aws/firewall_rule.rb +144 -72
- data/modules/mu/clouds/aws/folder.rb +4 -11
- data/modules/mu/clouds/aws/function.rb +6 -16
- data/modules/mu/clouds/aws/group.rb +4 -12
- data/modules/mu/clouds/aws/habitat.rb +11 -13
- data/modules/mu/clouds/aws/loadbalancer.rb +40 -28
- data/modules/mu/clouds/aws/log.rb +5 -13
- data/modules/mu/clouds/aws/msg_queue.rb +9 -24
- data/modules/mu/clouds/aws/nosqldb.rb +4 -12
- data/modules/mu/clouds/aws/notifier.rb +6 -13
- data/modules/mu/clouds/aws/role.rb +69 -40
- data/modules/mu/clouds/aws/search_domain.rb +17 -20
- data/modules/mu/clouds/aws/server.rb +184 -94
- data/modules/mu/clouds/aws/server_pool.rb +33 -38
- data/modules/mu/clouds/aws/storage_pool.rb +5 -12
- data/modules/mu/clouds/aws/user.rb +59 -33
- data/modules/mu/clouds/aws/userdata/linux.erb +18 -30
- data/modules/mu/clouds/aws/userdata/windows.erb +9 -9
- data/modules/mu/clouds/aws/vpc.rb +214 -145
- data/modules/mu/clouds/azure.rb +978 -44
- data/modules/mu/clouds/azure/container_cluster.rb +413 -0
- data/modules/mu/clouds/azure/firewall_rule.rb +500 -0
- data/modules/mu/clouds/azure/habitat.rb +167 -0
- data/modules/mu/clouds/azure/loadbalancer.rb +205 -0
- data/modules/mu/clouds/azure/role.rb +211 -0
- data/modules/mu/clouds/azure/server.rb +810 -0
- data/modules/mu/clouds/azure/user.rb +257 -0
- data/modules/mu/clouds/azure/userdata/README.md +4 -0
- data/modules/mu/clouds/azure/userdata/linux.erb +137 -0
- data/modules/mu/clouds/azure/userdata/windows.erb +275 -0
- data/modules/mu/clouds/azure/vpc.rb +782 -0
- data/modules/mu/clouds/cloudformation.rb +12 -9
- data/modules/mu/clouds/cloudformation/firewall_rule.rb +5 -13
- data/modules/mu/clouds/cloudformation/server.rb +10 -1
- data/modules/mu/clouds/cloudformation/server_pool.rb +1 -0
- data/modules/mu/clouds/cloudformation/vpc.rb +0 -2
- data/modules/mu/clouds/google.rb +554 -117
- data/modules/mu/clouds/google/bucket.rb +173 -32
- data/modules/mu/clouds/google/container_cluster.rb +1112 -157
- data/modules/mu/clouds/google/database.rb +24 -47
- data/modules/mu/clouds/google/firewall_rule.rb +344 -89
- data/modules/mu/clouds/google/folder.rb +156 -79
- data/modules/mu/clouds/google/group.rb +272 -82
- data/modules/mu/clouds/google/habitat.rb +177 -52
- data/modules/mu/clouds/google/loadbalancer.rb +9 -34
- data/modules/mu/clouds/google/role.rb +1211 -0
- data/modules/mu/clouds/google/server.rb +491 -227
- data/modules/mu/clouds/google/server_pool.rb +233 -48
- data/modules/mu/clouds/google/user.rb +479 -125
- data/modules/mu/clouds/google/userdata/linux.erb +3 -3
- data/modules/mu/clouds/google/userdata/windows.erb +9 -9
- data/modules/mu/clouds/google/vpc.rb +381 -223
- data/modules/mu/config.rb +689 -214
- data/modules/mu/config/bucket.rb +1 -1
- data/modules/mu/config/cache_cluster.rb +1 -1
- data/modules/mu/config/cache_cluster.yml +0 -4
- data/modules/mu/config/container_cluster.rb +18 -9
- data/modules/mu/config/database.rb +6 -23
- data/modules/mu/config/firewall_rule.rb +9 -15
- data/modules/mu/config/folder.rb +22 -21
- data/modules/mu/config/habitat.rb +22 -21
- data/modules/mu/config/loadbalancer.rb +2 -2
- data/modules/mu/config/role.rb +9 -40
- data/modules/mu/config/server.rb +26 -5
- data/modules/mu/config/server_pool.rb +1 -1
- data/modules/mu/config/storage_pool.rb +2 -2
- data/modules/mu/config/user.rb +4 -0
- data/modules/mu/config/vpc.rb +350 -110
- data/modules/mu/defaults/{amazon_images.yaml → AWS.yaml} +37 -39
- data/modules/mu/defaults/Azure.yaml +17 -0
- data/modules/mu/defaults/Google.yaml +24 -0
- data/modules/mu/defaults/README.md +1 -1
- data/modules/mu/deploy.rb +168 -125
- data/modules/mu/groomer.rb +2 -1
- data/modules/mu/groomers/ansible.rb +104 -32
- data/modules/mu/groomers/chef.rb +96 -44
- data/modules/mu/kittens.rb +20602 -0
- data/modules/mu/logger.rb +38 -11
- data/modules/mu/master.rb +90 -8
- data/modules/mu/master/chef.rb +2 -3
- data/modules/mu/master/ldap.rb +0 -1
- data/modules/mu/master/ssl.rb +250 -0
- data/modules/mu/mommacat.rb +917 -513
- data/modules/scratchpad.erb +1 -1
- data/modules/tests/super_complex_bok.yml +0 -0
- data/modules/tests/super_simple_bok.yml +0 -0
- data/roles/mu-master.json +2 -1
- data/spec/azure_creds +5 -0
- data/spec/mu.yaml +56 -0
- data/spec/mu/clouds/azure_spec.rb +164 -27
- data/spec/spec_helper.rb +5 -0
- data/test/clean_up.py +0 -0
- data/test/exec_inspec.py +0 -0
- data/test/exec_mu_install.py +0 -0
- data/test/exec_retry.py +0 -0
- data/test/smoke_test.rb +0 -0
- metadata +90 -118
- data/cookbooks/mu-jenkins/Berksfile +0 -14
- data/cookbooks/mu-jenkins/CHANGELOG.md +0 -13
- data/cookbooks/mu-jenkins/LICENSE +0 -37
- data/cookbooks/mu-jenkins/README.md +0 -105
- data/cookbooks/mu-jenkins/attributes/default.rb +0 -42
- data/cookbooks/mu-jenkins/files/default/cleanup_deploy_config.xml +0 -73
- data/cookbooks/mu-jenkins/files/default/deploy_config.xml +0 -44
- data/cookbooks/mu-jenkins/metadata.rb +0 -21
- data/cookbooks/mu-jenkins/recipes/default.rb +0 -195
- data/cookbooks/mu-jenkins/recipes/node-ssh-config.rb +0 -54
- data/cookbooks/mu-jenkins/recipes/public_key.rb +0 -24
- data/cookbooks/mu-jenkins/templates/default/example_job.config.xml.erb +0 -24
- data/cookbooks/mu-jenkins/templates/default/org.jvnet.hudson.plugins.SSHBuildWrapper.xml.erb +0 -14
- data/cookbooks/mu-jenkins/templates/default/ssh_config.erb +0 -6
- data/cookbooks/nagios/Berksfile +0 -11
- data/cookbooks/nagios/CHANGELOG.md +0 -589
- data/cookbooks/nagios/CONTRIBUTING.md +0 -11
- data/cookbooks/nagios/LICENSE +0 -37
- data/cookbooks/nagios/README.md +0 -328
- data/cookbooks/nagios/TESTING.md +0 -2
- data/cookbooks/nagios/attributes/config.rb +0 -171
- data/cookbooks/nagios/attributes/default.rb +0 -228
- data/cookbooks/nagios/chefignore +0 -102
- data/cookbooks/nagios/definitions/command.rb +0 -33
- data/cookbooks/nagios/definitions/contact.rb +0 -33
- data/cookbooks/nagios/definitions/contactgroup.rb +0 -33
- data/cookbooks/nagios/definitions/host.rb +0 -33
- data/cookbooks/nagios/definitions/hostdependency.rb +0 -33
- data/cookbooks/nagios/definitions/hostescalation.rb +0 -34
- data/cookbooks/nagios/definitions/hostgroup.rb +0 -33
- data/cookbooks/nagios/definitions/nagios_conf.rb +0 -38
- data/cookbooks/nagios/definitions/resource.rb +0 -33
- data/cookbooks/nagios/definitions/service.rb +0 -33
- data/cookbooks/nagios/definitions/servicedependency.rb +0 -33
- data/cookbooks/nagios/definitions/serviceescalation.rb +0 -34
- data/cookbooks/nagios/definitions/servicegroup.rb +0 -33
- data/cookbooks/nagios/definitions/timeperiod.rb +0 -33
- data/cookbooks/nagios/libraries/base.rb +0 -314
- data/cookbooks/nagios/libraries/command.rb +0 -91
- data/cookbooks/nagios/libraries/contact.rb +0 -230
- data/cookbooks/nagios/libraries/contactgroup.rb +0 -112
- data/cookbooks/nagios/libraries/custom_option.rb +0 -36
- data/cookbooks/nagios/libraries/data_bag_helper.rb +0 -23
- data/cookbooks/nagios/libraries/default.rb +0 -90
- data/cookbooks/nagios/libraries/host.rb +0 -412
- data/cookbooks/nagios/libraries/hostdependency.rb +0 -181
- data/cookbooks/nagios/libraries/hostescalation.rb +0 -173
- data/cookbooks/nagios/libraries/hostgroup.rb +0 -119
- data/cookbooks/nagios/libraries/nagios.rb +0 -282
- data/cookbooks/nagios/libraries/resource.rb +0 -59
- data/cookbooks/nagios/libraries/service.rb +0 -455
- data/cookbooks/nagios/libraries/servicedependency.rb +0 -215
- data/cookbooks/nagios/libraries/serviceescalation.rb +0 -195
- data/cookbooks/nagios/libraries/servicegroup.rb +0 -144
- data/cookbooks/nagios/libraries/timeperiod.rb +0 -160
- data/cookbooks/nagios/libraries/users_helper.rb +0 -54
- data/cookbooks/nagios/metadata.rb +0 -25
- data/cookbooks/nagios/recipes/_load_databag_config.rb +0 -153
- data/cookbooks/nagios/recipes/_load_default_config.rb +0 -241
- data/cookbooks/nagios/recipes/apache.rb +0 -48
- data/cookbooks/nagios/recipes/default.rb +0 -204
- data/cookbooks/nagios/recipes/nginx.rb +0 -82
- data/cookbooks/nagios/recipes/pagerduty.rb +0 -143
- data/cookbooks/nagios/recipes/server_package.rb +0 -40
- data/cookbooks/nagios/recipes/server_source.rb +0 -164
- data/cookbooks/nagios/templates/default/apache2.conf.erb +0 -96
- data/cookbooks/nagios/templates/default/cgi.cfg.erb +0 -266
- data/cookbooks/nagios/templates/default/commands.cfg.erb +0 -13
- data/cookbooks/nagios/templates/default/contacts.cfg.erb +0 -37
- data/cookbooks/nagios/templates/default/hostgroups.cfg.erb +0 -25
- data/cookbooks/nagios/templates/default/hosts.cfg.erb +0 -15
- data/cookbooks/nagios/templates/default/htpasswd.users.erb +0 -6
- data/cookbooks/nagios/templates/default/nagios.cfg.erb +0 -22
- data/cookbooks/nagios/templates/default/nginx.conf.erb +0 -62
- data/cookbooks/nagios/templates/default/pagerduty.cgi.erb +0 -185
- data/cookbooks/nagios/templates/default/resource.cfg.erb +0 -27
- data/cookbooks/nagios/templates/default/servicedependencies.cfg.erb +0 -15
- data/cookbooks/nagios/templates/default/servicegroups.cfg.erb +0 -14
- data/cookbooks/nagios/templates/default/services.cfg.erb +0 -14
- data/cookbooks/nagios/templates/default/templates.cfg.erb +0 -31
- data/cookbooks/nagios/templates/default/timeperiods.cfg.erb +0 -13
- data/extras/image-generators/aws/centos6.yaml +0 -18
- data/modules/mu/defaults/google_images.yaml +0 -16
- data/roles/mu-master-jenkins.json +0 -24
|
File without changes
|
data/install/installer
CHANGED
data/install/jenkinskeys.rb
CHANGED
|
File without changes
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
---
|
|
2
|
+
appname: mu
|
|
3
|
+
parameters:
|
|
4
|
+
- name: cloud
|
|
5
|
+
default: <%= MU.myCloud || "AWS" %>
|
|
6
|
+
valid_values:
|
|
7
|
+
<% MU::Cloud.availableClouds.each { |c| %>
|
|
8
|
+
- <%= c %>
|
|
9
|
+
<% } %>
|
|
10
|
+
- name: public
|
|
11
|
+
default: true
|
|
12
|
+
- name: name
|
|
13
|
+
default: mu-master
|
|
14
|
+
scrub_mu_isms: true
|
|
15
|
+
servers:
|
|
16
|
+
- name: <%= name %>
|
|
17
|
+
groomer: Ansible
|
|
18
|
+
run_list:
|
|
19
|
+
- mu-installer
|
|
20
|
+
platform: centos7
|
|
21
|
+
cloud: <%= cloud %>
|
|
22
|
+
<% if cloud == "AWS" %>
|
|
23
|
+
size: t2.medium
|
|
24
|
+
<% elsif cloud == "Azure" %>
|
|
25
|
+
size: Standard_DS1_v2
|
|
26
|
+
<% elsif cloud == "Google" %>
|
|
27
|
+
size: n1-standard-1
|
|
28
|
+
<% end %>
|
|
29
|
+
vpc:
|
|
30
|
+
name: <%= name %>-vpc
|
|
31
|
+
<% if public == "true" %>
|
|
32
|
+
subnet_pref: public
|
|
33
|
+
static_ip:
|
|
34
|
+
assign_ip: true
|
|
35
|
+
associate_public_ip: true
|
|
36
|
+
<% else %>
|
|
37
|
+
subnet_pref: private
|
|
38
|
+
<% end %>
|
|
39
|
+
<% if cloud == "AWS" %>
|
|
40
|
+
canned_iam_policies:
|
|
41
|
+
- AdministratorAccess
|
|
42
|
+
<% elsif cloud == "Azure" %>
|
|
43
|
+
roles:
|
|
44
|
+
- Owner
|
|
45
|
+
<% elsif cloud == "Google" %>
|
|
46
|
+
roles:
|
|
47
|
+
- role:
|
|
48
|
+
id: roles/owner
|
|
49
|
+
<% end %>
|
|
50
|
+
vpcs:
|
|
51
|
+
- name: <%= name %>-vpc
|
|
52
|
+
cloud: <%= cloud %>
|
|
53
|
+
<% if public %>
|
|
54
|
+
create_bastion: false
|
|
55
|
+
<% end %>
|
data/modules/mommacat.ru
CHANGED
|
@@ -32,7 +32,12 @@ $LOAD_PATH << "#{$MUDIR}/modules"
|
|
|
32
32
|
require File.realpath(File.expand_path(File.dirname(__FILE__)+"/mu-load-config.rb"))
|
|
33
33
|
require 'mu'
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
begin
|
|
36
|
+
MU::Groomer::Chef.loadChefLib # pre-cache this so we don't take a hit on a user-interactive need
|
|
37
|
+
$ENABLE_SCRATCHPAD = true
|
|
38
|
+
rescue LoadError
|
|
39
|
+
MU.log "Chef libraries not available, disabling Scratchpad", MU::WARN
|
|
40
|
+
end
|
|
36
41
|
#MU.setLogging($opts[:verbose], $opts[:web])
|
|
37
42
|
if MU.myCloud == "AWS"
|
|
38
43
|
MU::Cloud::AWS.openFirewallForClients # XXX add the other clouds, or abstract
|
|
@@ -57,7 +62,7 @@ Thread.new {
|
|
|
57
62
|
MU.dupGlobals(parent_thread_id)
|
|
58
63
|
begin
|
|
59
64
|
MU::MommaCat.cleanTerminatedInstances
|
|
60
|
-
MU::Master.cleanExpiredScratchpads
|
|
65
|
+
MU::Master.cleanExpiredScratchpads if $ENABLE_SCRATCHPAD
|
|
61
66
|
sleep 60
|
|
62
67
|
rescue Exception => e
|
|
63
68
|
MU.log "Error in cleanTerminatedInstances thread: #{e.inspect}", MU::ERR, details: e.backtrace
|
|
@@ -199,6 +204,17 @@ app = proc do |env|
|
|
|
199
204
|
]
|
|
200
205
|
begin
|
|
201
206
|
if !env.nil? and !env['REQUEST_PATH'].nil? and env['REQUEST_PATH'].match(/^\/scratchpad/)
|
|
207
|
+
if !$ENABLE_SCRATCHPAD
|
|
208
|
+
msg = "Scratchpad disabled in non-Chef Mu installations"
|
|
209
|
+
return [
|
|
210
|
+
504,
|
|
211
|
+
{
|
|
212
|
+
'Content-Type' => 'text/html',
|
|
213
|
+
'Content-Length' => msg.length.to_s
|
|
214
|
+
},
|
|
215
|
+
[msg]
|
|
216
|
+
]
|
|
217
|
+
end
|
|
202
218
|
itemname = env['REQUEST_PATH'].sub(/^\/scratchpad\//, "")
|
|
203
219
|
begin
|
|
204
220
|
if itemname.sub!(/\/secret$/, "")
|
|
@@ -257,7 +273,7 @@ app = proc do |env|
|
|
|
257
273
|
[page]
|
|
258
274
|
]
|
|
259
275
|
end
|
|
260
|
-
rescue MU::Groomer::
|
|
276
|
+
rescue MU::Groomer::MuNoSuchSecret
|
|
261
277
|
page = nil
|
|
262
278
|
if $MU_CFG.has_key?('scratchpad') and
|
|
263
279
|
$MU_CFG['scratchpad'].has_key?("template_path") and
|
|
@@ -287,6 +303,7 @@ app = proc do |env|
|
|
|
287
303
|
]
|
|
288
304
|
end
|
|
289
305
|
elsif !env.nil? and !env['REQUEST_PATH'].nil? and env['REQUEST_PATH'].match(/^\/rest\//)
|
|
306
|
+
|
|
290
307
|
action, filter, path = env['REQUEST_PATH'].sub(/^\/rest\/?/, "").split(/\//, 3)
|
|
291
308
|
# Don't give away the store. This can't be public until we can
|
|
292
309
|
# authenticate and access-control properly.
|
|
@@ -295,7 +312,23 @@ app = proc do |env|
|
|
|
295
312
|
next
|
|
296
313
|
end
|
|
297
314
|
|
|
298
|
-
if action == "
|
|
315
|
+
if action == "hosts_add"
|
|
316
|
+
if Process.uid != 0
|
|
317
|
+
returnval = throw500 "Service not available"
|
|
318
|
+
elsif !filter or !path
|
|
319
|
+
returnval = throw404 env['REQUEST_PATH']
|
|
320
|
+
else
|
|
321
|
+
MU::MommaCat.addInstanceToEtcHosts(path, filter)
|
|
322
|
+
returnval = [
|
|
323
|
+
200,
|
|
324
|
+
{
|
|
325
|
+
'Content-Type' => 'text/plain',
|
|
326
|
+
'Content-Length' => 2
|
|
327
|
+
},
|
|
328
|
+
["ok"]
|
|
329
|
+
]
|
|
330
|
+
end
|
|
331
|
+
elsif action == "deploy"
|
|
299
332
|
returnval = throw404 env['REQUEST_PATH'] if !filter
|
|
300
333
|
MU.log "Loading deploy data for #{filter} #{path}"
|
|
301
334
|
kittenpile = MU::MommaCat.getLitter(filter)
|
|
@@ -313,9 +346,9 @@ app = proc do |env|
|
|
|
313
346
|
200,
|
|
314
347
|
{
|
|
315
348
|
'Content-Type' => 'text/plain',
|
|
316
|
-
'Content-Length' => MU.adminBucketName.length.to_s
|
|
349
|
+
'Content-Length' => MU.adminBucketName(filter, credentials: path).length.to_s
|
|
317
350
|
},
|
|
318
|
-
[MU.adminBucketName]
|
|
351
|
+
[MU.adminBucketName(filter, credentials: path)]
|
|
319
352
|
]
|
|
320
353
|
else
|
|
321
354
|
returnval = throw404 env['REQUEST_PATH']
|
|
@@ -389,7 +422,8 @@ app = proc do |env|
|
|
|
389
422
|
if instance.respond_to?(:addVolume)
|
|
390
423
|
# XXX make sure we handle mangled input safely
|
|
391
424
|
params = JSON.parse(Base64.decode64(req["add_volume"]))
|
|
392
|
-
|
|
425
|
+
MU.log "ADDVOLUME REQUEST", MU::WARN, details: params
|
|
426
|
+
instance.addVolume(params["dev"], params["size"], delete_on_termination: params["delete_on_termination"])
|
|
393
427
|
else
|
|
394
428
|
returnval = throw500 "I don't know how to add a volume for #{instance}"
|
|
395
429
|
ok = false
|
data/modules/mu.rb
CHANGED
|
@@ -36,20 +36,252 @@ class Object
|
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
|
|
39
|
+
# Mu extensions to Ruby's {Hash} type for internal Mu use
|
|
40
|
+
class Hash
|
|
41
|
+
|
|
42
|
+
# Strip extraneous fields out of a {MU::Config} hash to make it suitable for
|
|
43
|
+
# shorthand printing, such as with <tt>mu-adopt --diff</tt>
|
|
44
|
+
def self.bok_minimize(o)
|
|
45
|
+
if o.is_a?(Hash)
|
|
46
|
+
newhash = o.reject { |k, v|
|
|
47
|
+
!v.is_a?(Array) and !v.is_a?(Hash) and !["name", "id", "cloud_id"].include?(k)
|
|
48
|
+
}
|
|
49
|
+
# newhash.delete("cloud_id") if newhash["name"] or newhash["id"]
|
|
50
|
+
newhash.each_pair { |k, v|
|
|
51
|
+
newhash[k] = bok_minimize(v)
|
|
52
|
+
}
|
|
53
|
+
newhash.reject! { |_k, v| v.nil? or v.empty? }
|
|
54
|
+
newhash = newhash.values.first if newhash.size == 1
|
|
55
|
+
return newhash
|
|
56
|
+
elsif o.is_a?(Array)
|
|
57
|
+
newarray = []
|
|
58
|
+
o.each { |v|
|
|
59
|
+
newvalue = bok_minimize(v)
|
|
60
|
+
newarray << newvalue if !newvalue.nil? and !newvalue.empty?
|
|
61
|
+
}
|
|
62
|
+
newarray = newarray.first if newarray.size == 1
|
|
63
|
+
return newarray
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
o
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# A comparison function for sorting arrays of hashes
|
|
70
|
+
def <=>(other)
|
|
71
|
+
return 1 if other.nil? or self.size > other.size
|
|
72
|
+
return -1 if other.size > self.size
|
|
73
|
+
# Sort any array children we have
|
|
74
|
+
self.each_pair { |k, v|
|
|
75
|
+
self[k] = v.sort if v.is_a?(Array)
|
|
76
|
+
}
|
|
77
|
+
other.each_pair { |k, v|
|
|
78
|
+
other[k] = v.sort if v.is_a?(Array)
|
|
79
|
+
}
|
|
80
|
+
return 0 if self == other # that was easy!
|
|
81
|
+
# compare elements and decide who's "bigger" based on their totals?
|
|
82
|
+
0
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Recursively compare two hashes
|
|
86
|
+
def diff(with, on = self, level: 0, parents: [])
|
|
87
|
+
return if with.nil? and on.nil?
|
|
88
|
+
if with.nil? or on.nil? or with.class != on.class
|
|
89
|
+
return # XXX ...however we're flagging differences
|
|
90
|
+
end
|
|
91
|
+
return if on == with
|
|
92
|
+
|
|
93
|
+
tree = ""
|
|
94
|
+
indentsize = 0
|
|
95
|
+
parents.each { |p|
|
|
96
|
+
tree += (" " * indentsize) + p + " => \n"
|
|
97
|
+
indentsize += 2
|
|
98
|
+
}
|
|
99
|
+
indent = (" " * indentsize)
|
|
100
|
+
|
|
101
|
+
changes = []
|
|
102
|
+
if on.is_a?(Hash)
|
|
103
|
+
on_unique = (on.keys - with.keys)
|
|
104
|
+
with_unique = (with.keys - on.keys)
|
|
105
|
+
shared = (with.keys & on.keys)
|
|
106
|
+
shared.each { |k|
|
|
107
|
+
diff(with[k], on[k], level: level+1, parents: parents + [k])
|
|
108
|
+
}
|
|
109
|
+
on_unique.each { |k|
|
|
110
|
+
changes << "- ".red+PP.pp({k => on[k] }, '')
|
|
111
|
+
}
|
|
112
|
+
with_unique.each { |k|
|
|
113
|
+
changes << "+ ".green+PP.pp({k => with[k]}, '')
|
|
114
|
+
}
|
|
115
|
+
elsif on.is_a?(Array)
|
|
116
|
+
return if with == on
|
|
117
|
+
# special case- Basket of Kittens lists of declared resources of a type;
|
|
118
|
+
# we use this to decide if we can compare two array elements as if they
|
|
119
|
+
# should be equivalent
|
|
120
|
+
# We also implement comparison operators for {Hash} and our various
|
|
121
|
+
# custom objects which we might find in here so that we can get away with
|
|
122
|
+
# sorting arrays full of weird, non-primitive types.
|
|
123
|
+
done = []
|
|
124
|
+
# before_a = on.dup
|
|
125
|
+
# after_a = on.dup.sort
|
|
126
|
+
# before_b = with.dup
|
|
127
|
+
# after_b = with.dup.sort
|
|
128
|
+
on.sort.each { |elt|
|
|
129
|
+
if elt.is_a?(Hash) and elt['name'] or elt['entity']# or elt['cloud_id']
|
|
130
|
+
with.sort.each { |other_elt|
|
|
131
|
+
if (elt['name'] and other_elt['name'] == elt['name']) or
|
|
132
|
+
(elt['name'].nil? and !elt["id"].nil? and elt["id"] == other_elt["id"]) or
|
|
133
|
+
(elt['name'].nil? and elt["id"].nil? and
|
|
134
|
+
!elt["entity"].nil? and !other_elt["entity"].nil? and
|
|
135
|
+
(
|
|
136
|
+
(elt["entity"]["id"] and elt["entity"]["id"] == other_elt["entity"]["id"]) or
|
|
137
|
+
(elt["entity"]["name"] and elt["entity"]["name"] == other_elt["entity"]["name"])
|
|
138
|
+
)
|
|
139
|
+
)
|
|
140
|
+
break if elt == other_elt
|
|
141
|
+
done << elt
|
|
142
|
+
done << other_elt
|
|
143
|
+
namestr = if elt['type']
|
|
144
|
+
"#{elt['type']}[#{elt['name']}]"
|
|
145
|
+
elsif elt['name']
|
|
146
|
+
elt['name']
|
|
147
|
+
elsif elt['entity'] and elt["entity"]["id"]
|
|
148
|
+
elt['entity']['id']
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
diff(other_elt, elt, level: level+1, parents: parents + [namestr])
|
|
152
|
+
break
|
|
153
|
+
end
|
|
154
|
+
}
|
|
155
|
+
end
|
|
156
|
+
}
|
|
157
|
+
on_unique = (on - with) - done
|
|
158
|
+
with_unique = (with - on) - done
|
|
159
|
+
# if on_unique.size > 0 or with_unique.size > 0
|
|
160
|
+
# if before_a != after_a
|
|
161
|
+
# MU.log "A BEFORE", MU::NOTICE, details: before_a
|
|
162
|
+
# MU.log "A AFTER", MU::NOTICE, details: after_a
|
|
163
|
+
# end
|
|
164
|
+
# if before_b != after_b
|
|
165
|
+
# MU.log "B BEFORE", MU::NOTICE, details: before_b
|
|
166
|
+
# MU.log "B AFTER", MU::NOTICE, details: after_b
|
|
167
|
+
# end
|
|
168
|
+
# end
|
|
169
|
+
on_unique.each { |e|
|
|
170
|
+
changes << if e.is_a?(Hash)
|
|
171
|
+
"- ".red+PP.pp(Hash.bok_minimize(e), '').gsub(/\n/, "\n "+(indent))
|
|
172
|
+
else
|
|
173
|
+
"- ".red+e.to_s
|
|
174
|
+
end
|
|
175
|
+
}
|
|
176
|
+
with_unique.each { |e|
|
|
177
|
+
changes << if e.is_a?(Hash)
|
|
178
|
+
"+ ".green+PP.pp(Hash.bok_minimize(e), '').gsub(/\n/, "\n "+(indent))
|
|
179
|
+
else
|
|
180
|
+
"+ ".green+e.to_s
|
|
181
|
+
end
|
|
182
|
+
}
|
|
183
|
+
else
|
|
184
|
+
if on != with
|
|
185
|
+
changes << "-".red+" #{on.to_s}"
|
|
186
|
+
changes << "+".green+" #{with.to_s}"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
if changes.size > 0
|
|
191
|
+
puts tree
|
|
192
|
+
changes.each { |c|
|
|
193
|
+
puts indent+c
|
|
194
|
+
}
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Implement a merge! that just updates each hash leaf as needed, not
|
|
199
|
+
# trashing the branch on the way there.
|
|
200
|
+
def deep_merge!(with, on = self)
|
|
201
|
+
|
|
202
|
+
if on and with and with.is_a?(Hash)
|
|
203
|
+
with.each_pair { |k, v|
|
|
204
|
+
if !on[k] or !on[k].is_a?(Hash)
|
|
205
|
+
on[k] = v
|
|
206
|
+
else
|
|
207
|
+
deep_merge!(with[k], on[k])
|
|
208
|
+
end
|
|
209
|
+
}
|
|
210
|
+
elsif with
|
|
211
|
+
on = with
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
on
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
39
218
|
ENV['HOME'] = Etc.getpwuid(Process.uid).dir
|
|
40
219
|
|
|
41
220
|
require 'mu/logger'
|
|
42
221
|
module MU
|
|
43
222
|
|
|
223
|
+
# Subclass core thread so we can gracefully handle it when we hit system
|
|
224
|
+
# thread limits. Back off and wait makes sense for us, since most of our
|
|
225
|
+
# threads are terminal (in the dependency sense) and this is unlikely to get
|
|
226
|
+
# us deadlocks.
|
|
227
|
+
class Thread < ::Thread
|
|
228
|
+
@@mu_global_threads = []
|
|
229
|
+
@@mu_global_thread_semaphore = Mutex.new
|
|
230
|
+
|
|
231
|
+
def initialize(*args, &block)
|
|
232
|
+
@@mu_global_thread_semaphore.synchronize {
|
|
233
|
+
@@mu_global_threads.reject! { |t| t.nil? or !t.status }
|
|
234
|
+
}
|
|
235
|
+
newguy = nil
|
|
236
|
+
start = Time.now
|
|
237
|
+
begin
|
|
238
|
+
newguy = super(*args, &block)
|
|
239
|
+
if newguy.nil?
|
|
240
|
+
MU.log "I somehow got a nil trying to create a thread", MU::WARN, details: caller
|
|
241
|
+
sleep 1
|
|
242
|
+
end
|
|
243
|
+
rescue ::ThreadError => e
|
|
244
|
+
if e.message.match(/Resource temporarily unavailable/)
|
|
245
|
+
toomany = @@mu_global_threads.size
|
|
246
|
+
MU.log "Hit the wall at #{toomany.to_s} threads, waiting until there are fewer", MU::WARN
|
|
247
|
+
if @@mu_global_threads.size >= toomany
|
|
248
|
+
sleep 1
|
|
249
|
+
begin
|
|
250
|
+
@@mu_global_thread_semaphore.synchronize {
|
|
251
|
+
@@mu_global_threads.each { |t|
|
|
252
|
+
next if t == ::Thread.current
|
|
253
|
+
t.join(0.1)
|
|
254
|
+
}
|
|
255
|
+
@@mu_global_threads.reject! { |t| t.nil? or !t.status }
|
|
256
|
+
}
|
|
257
|
+
if (Time.now - start) > 150
|
|
258
|
+
MU.log "Failed to get a free thread slot after 150 seconds- are we in a deadlock situation?", MU::ERR, details: caller
|
|
259
|
+
raise e
|
|
260
|
+
end
|
|
261
|
+
end while @@mu_global_threads.size >= toomany
|
|
262
|
+
end
|
|
263
|
+
retry
|
|
264
|
+
else
|
|
265
|
+
raise e
|
|
266
|
+
end
|
|
267
|
+
end while newguy.nil?
|
|
268
|
+
|
|
269
|
+
@@mu_global_thread_semaphore.synchronize {
|
|
270
|
+
@@mu_global_threads << newguy
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
|
|
44
276
|
# Wrapper class for fatal Exceptions. Gives our internals something to
|
|
45
277
|
# inherit that will log an error message appropriately before bubbling up.
|
|
46
278
|
class MuError < StandardError
|
|
47
279
|
def initialize(message = nil)
|
|
48
|
-
MU.log message, MU::ERR if !message.nil?
|
|
280
|
+
MU.log message, MU::ERR, details: caller[2] if !message.nil?
|
|
49
281
|
if MU.verbosity == MU::Logger::SILENT
|
|
50
|
-
super
|
|
51
|
-
else
|
|
52
282
|
super ""
|
|
283
|
+
else
|
|
284
|
+
super message
|
|
53
285
|
end
|
|
54
286
|
end
|
|
55
287
|
end
|
|
@@ -60,9 +292,9 @@ module MU
|
|
|
60
292
|
def initialize(message = nil)
|
|
61
293
|
MU.log message, MU::NOTICE if !message.nil?
|
|
62
294
|
if MU.verbosity == MU::Logger::SILENT
|
|
63
|
-
super
|
|
64
|
-
else
|
|
65
295
|
super ""
|
|
296
|
+
else
|
|
297
|
+
super message
|
|
66
298
|
end
|
|
67
299
|
end
|
|
68
300
|
end
|
|
@@ -80,11 +312,37 @@ module MU
|
|
|
80
312
|
@@myRoot
|
|
81
313
|
end
|
|
82
314
|
|
|
315
|
+
# utility routine for sorting semantic versioning strings
|
|
316
|
+
def self.version_sort(a, b)
|
|
317
|
+
a_parts = a.split(/[^a-z0-9]/)
|
|
318
|
+
b_parts = b.split(/[^a-z0-9]/)
|
|
319
|
+
for i in 0..a_parts.size
|
|
320
|
+
matchval = if a_parts[i] and b_parts[i] and
|
|
321
|
+
a_parts[i].match(/^\d+/) and b_parts[i].match(/^\d+/)
|
|
322
|
+
a_parts[i].to_i <=> b_parts[i].to_i
|
|
323
|
+
elsif a_parts[i] and !b_parts[i]
|
|
324
|
+
1
|
|
325
|
+
elsif !a_parts[i] and b_parts[i]
|
|
326
|
+
-1
|
|
327
|
+
else
|
|
328
|
+
a_parts[i] <=> b_parts[i]
|
|
329
|
+
end
|
|
330
|
+
return matchval if matchval != 0
|
|
331
|
+
end
|
|
332
|
+
0
|
|
333
|
+
end
|
|
334
|
+
|
|
83
335
|
# Front our global $MU_CFG hash with a read-only copy
|
|
84
336
|
def self.muCfg
|
|
85
337
|
Marshal.load(Marshal.dump($MU_CFG)).freeze
|
|
86
338
|
end
|
|
87
339
|
|
|
340
|
+
# Returns true if we're running without a full systemwide Mu Master install,
|
|
341
|
+
# typically as a gem.
|
|
342
|
+
def self.localOnly
|
|
343
|
+
((Gem.paths and Gem.paths.home and File.realpath(File.expand_path(File.dirname(__FILE__))).match(/^#{Gem.paths.home}/)) or !Dir.exist?("/opt/mu"))
|
|
344
|
+
end
|
|
345
|
+
|
|
88
346
|
# The main (root) Mu user's data directory.
|
|
89
347
|
@@mainDataDir = File.expand_path(@@myRoot+"/../var")
|
|
90
348
|
# The main (root) Mu user's data directory.
|
|
@@ -133,6 +391,7 @@ module MU
|
|
|
133
391
|
# Copy the set of global variables in use by another thread, typically our
|
|
134
392
|
# parent thread.
|
|
135
393
|
def self.dupGlobals(parent_thread_id)
|
|
394
|
+
@@globals[parent_thread_id] ||= {}
|
|
136
395
|
@@globals[parent_thread_id].each_pair { |name, value|
|
|
137
396
|
setVar(name, value)
|
|
138
397
|
}
|
|
@@ -215,13 +474,15 @@ module MU
|
|
|
215
474
|
@myDataDir = @@mainDataDir if @myDataDir.nil?
|
|
216
475
|
# Mu's deployment metadata directory.
|
|
217
476
|
def self.dataDir(for_user = MU.mu_user)
|
|
218
|
-
if
|
|
477
|
+
if !localOnly and
|
|
478
|
+
((Process.uid == 0 and (for_user.nil? or for_user.empty?)) or
|
|
479
|
+
for_user == "mu" or for_user == "root")
|
|
219
480
|
return @myDataDir
|
|
220
481
|
else
|
|
221
482
|
for_user ||= MU.mu_user
|
|
222
483
|
basepath = Etc.getpwnam(for_user).dir+"/.mu"
|
|
223
|
-
Dir.mkdir(basepath, 0755) if !Dir.
|
|
224
|
-
Dir.mkdir(basepath+"/var", 0755) if !Dir.
|
|
484
|
+
Dir.mkdir(basepath, 0755) if !Dir.exist?(basepath)
|
|
485
|
+
Dir.mkdir(basepath+"/var", 0755) if !Dir.exist?(basepath+"/var")
|
|
225
486
|
return basepath+"/var"
|
|
226
487
|
end
|
|
227
488
|
end
|
|
@@ -233,14 +494,24 @@ module MU
|
|
|
233
494
|
end
|
|
234
495
|
@@globals[Thread.current.object_id]['verbosity']
|
|
235
496
|
end
|
|
497
|
+
|
|
498
|
+
# The color logging flag merits a default value.
|
|
499
|
+
def self.color
|
|
500
|
+
if @@globals[Thread.current.object_id].nil? or @@globals[Thread.current.object_id]['color'].nil?
|
|
501
|
+
MU.setVar("color", true)
|
|
502
|
+
end
|
|
503
|
+
@@globals[Thread.current.object_id]['color']
|
|
504
|
+
end
|
|
236
505
|
|
|
237
506
|
# Set parameters parameters for calls to {MU#log}
|
|
238
|
-
def self.setLogging(verbosity, webify_logs = false, handle = STDOUT)
|
|
507
|
+
def self.setLogging(verbosity, webify_logs = false, handle = STDOUT, color = true)
|
|
239
508
|
MU.setVar("verbosity", verbosity)
|
|
240
|
-
|
|
509
|
+
MU.setVar("color", color)
|
|
510
|
+
@@logger ||= MU::Logger.new(verbosity, webify_logs, handle, color)
|
|
241
511
|
@@logger.html = webify_logs
|
|
242
512
|
@@logger.verbosity = verbosity
|
|
243
513
|
@@logger.handle = handle
|
|
514
|
+
@@logger.color = color
|
|
244
515
|
end
|
|
245
516
|
|
|
246
517
|
setLogging(MU::Logger::NORMAL, false)
|
|
@@ -252,7 +523,7 @@ module MU
|
|
|
252
523
|
end
|
|
253
524
|
|
|
254
525
|
# Shortcut to invoke {MU::Logger#log}
|
|
255
|
-
def self.log(msg, level = MU::INFO, details: nil, html:
|
|
526
|
+
def self.log(msg, level = MU::INFO, details: nil, html: false, verbosity: MU.verbosity, color: true)
|
|
256
527
|
return if (level == MU::DEBUG and verbosity <= MU::Logger::LOUD)
|
|
257
528
|
return if verbosity == MU::Logger::SILENT
|
|
258
529
|
|
|
@@ -275,9 +546,9 @@ module MU
|
|
|
275
546
|
extra = Hash.new if extra.nil?
|
|
276
547
|
extra[:details] = details
|
|
277
548
|
end
|
|
278
|
-
@@logger.log(msg, level, details: extra, verbosity: MU::Logger::LOUD, html: html)
|
|
549
|
+
@@logger.log(msg, level, details: extra, verbosity: MU::Logger::LOUD, html: html, color: color)
|
|
279
550
|
else
|
|
280
|
-
@@logger.log(msg, level, html: html, verbosity: verbosity)
|
|
551
|
+
@@logger.log(msg, level, html: html, verbosity: verbosity, color: color)
|
|
281
552
|
end
|
|
282
553
|
end
|
|
283
554
|
|
|
@@ -308,49 +579,66 @@ module MU
|
|
|
308
579
|
require 'mu/groomer'
|
|
309
580
|
|
|
310
581
|
# Little hack to initialize library-only environments' config files
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if cfg_blob
|
|
324
|
-
new_cfg[cloud.downcase] = cfg_blob
|
|
325
|
-
MU.log "Adding #{cloud} stanza to #{cfgPath}", MU::NOTICE
|
|
326
|
-
end
|
|
327
|
-
elsif !$MU_CFG[cloud.downcase] and !cloudclass.config_example.nil?
|
|
328
|
-
examples[cloud.downcase] = cloudclass.config_example
|
|
582
|
+
def self.detectCloudProviders
|
|
583
|
+
MU.log "Auto-detecting cloud providers"
|
|
584
|
+
new_cfg = $MU_CFG.dup
|
|
585
|
+
examples = {}
|
|
586
|
+
MU::Cloud.supportedClouds.each { |cloud|
|
|
587
|
+
cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
|
|
588
|
+
begin
|
|
589
|
+
if cloudclass.hosted? and !$MU_CFG[cloud.downcase]
|
|
590
|
+
cfg_blob = cloudclass.hosted_config
|
|
591
|
+
if cfg_blob
|
|
592
|
+
new_cfg[cloud.downcase] = cfg_blob
|
|
593
|
+
MU.log "Adding auto-detected #{cloud} stanza", MU::NOTICE
|
|
329
594
|
end
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
MU.log e.message, MU::WARN
|
|
595
|
+
elsif !$MU_CFG[cloud.downcase] and !cloudclass.config_example.nil?
|
|
596
|
+
examples[cloud.downcase] = cloudclass.config_example
|
|
333
597
|
end
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
MU.log "Generating #{cfgPath}"
|
|
338
|
-
saveMuConfig(new_cfg, examples) # XXX and reload it
|
|
598
|
+
rescue NoMethodError => e
|
|
599
|
+
# missing .hosted? is normal for dummy layers like CloudFormation
|
|
600
|
+
MU.log e.message, MU::WARN
|
|
339
601
|
end
|
|
602
|
+
}
|
|
603
|
+
new_cfg['auto_detection_done'] = true
|
|
604
|
+
if new_cfg != $MU_CFG or !cfgExists?
|
|
605
|
+
MU.log "Generating #{cfgPath}"
|
|
606
|
+
saveMuConfig(new_cfg, examples) # XXX and reload it
|
|
340
607
|
end
|
|
608
|
+
new_cfg
|
|
609
|
+
end
|
|
610
|
+
|
|
611
|
+
if !$MU_CFG
|
|
612
|
+
require "#{@@myRoot}/bin/mu-load-config.rb"
|
|
613
|
+
if !$MU_CFG['auto_detection_done'] and (!$MU_CFG['multiuser'] or !cfgExists?)
|
|
614
|
+
detectCloudProviders
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
|
|
618
|
+
@@mommacat_port = 2260
|
|
619
|
+
if !$MU_CFG.nil? and !$MU_CFG['mommacat_port'].nil? and
|
|
620
|
+
!$MU_CFG['mommacat_port'] != "" and $MU_CFG['mommacat_port'].to_i > 0 and
|
|
621
|
+
$MU_CFG['mommacat_port'].to_i < 65536
|
|
622
|
+
@@mommacat_port = $MU_CFG['mommacat_port'].to_i
|
|
623
|
+
end
|
|
624
|
+
# The port on which the Momma Cat daemon should listen for requests
|
|
625
|
+
# @return [Integer]
|
|
626
|
+
def self.mommaCatPort
|
|
627
|
+
@@mommacat_port
|
|
341
628
|
end
|
|
342
629
|
|
|
343
630
|
@@my_private_ip = nil
|
|
344
631
|
@@my_public_ip = nil
|
|
345
632
|
@@mu_public_addr = nil
|
|
346
633
|
@@mu_public_ip = nil
|
|
347
|
-
if
|
|
634
|
+
if MU::Cloud::AWS.hosted?
|
|
348
635
|
@@my_private_ip = MU::Cloud::AWS.getAWSMetaData("local-ipv4")
|
|
349
636
|
@@my_public_ip = MU::Cloud::AWS.getAWSMetaData("public-ipv4")
|
|
350
637
|
@@mu_public_addr = @@my_public_ip
|
|
351
638
|
@@mu_public_ip = @@my_public_ip
|
|
352
639
|
end
|
|
353
|
-
if !$MU_CFG.nil? and !$MU_CFG['public_address'].nil? and
|
|
640
|
+
if !$MU_CFG.nil? and !$MU_CFG['public_address'].nil? and
|
|
641
|
+
!$MU_CFG['public_address'].empty? and @@my_public_ip != $MU_CFG['public_address']
|
|
354
642
|
@@mu_public_addr = $MU_CFG['public_address']
|
|
355
643
|
if !@@mu_public_addr.match(/^\d+\.\d+\.\d+\.\d+$/)
|
|
356
644
|
resolver = Resolv::DNS.new
|
|
@@ -400,7 +688,9 @@ module MU
|
|
|
400
688
|
def self.userEmail(user = MU.mu_user)
|
|
401
689
|
@userlist ||= MU::Master.listUsers
|
|
402
690
|
user = "mu" if user == "root"
|
|
403
|
-
if Dir.
|
|
691
|
+
if Dir.exist?("#{MU.mainDataDir}/users/#{user}") and
|
|
692
|
+
File.readable?("#{MU.mainDataDir}/users/#{user}/email") and
|
|
693
|
+
File.size?("#{MU.mainDataDir}/users/#{user}/email")
|
|
404
694
|
return File.read("#{MU.mainDataDir}/users/#{user}/email").chomp
|
|
405
695
|
elsif @userlist.has_key?(user)
|
|
406
696
|
return @userlist[user]['email']
|
|
@@ -413,7 +703,9 @@ module MU
|
|
|
413
703
|
# Fetch the real-world name of a given Mu user
|
|
414
704
|
def self.userName(user = MU.mu_user)
|
|
415
705
|
@userlist ||= MU::Master.listUsers
|
|
416
|
-
if Dir.
|
|
706
|
+
if Dir.exist?("#{MU.mainDataDir}/users/#{user}") and
|
|
707
|
+
File.readable?("#{MU.mainDataDir}/users/#{user}/realname") and
|
|
708
|
+
File.size?("#{MU.mainDataDir}/users/#{user}/realname")
|
|
417
709
|
return File.read("#{MU.mainDataDir}/users/#{user}/realname").chomp
|
|
418
710
|
elsif @userlist.has_key?(user)
|
|
419
711
|
return @userlist[user]['email']
|
|
@@ -430,6 +722,14 @@ module MU
|
|
|
430
722
|
["Chef", "Ansible"]
|
|
431
723
|
end
|
|
432
724
|
|
|
725
|
+
# The version of Chef we will install on nodes.
|
|
726
|
+
@@chefVersion = "14.0.190"
|
|
727
|
+
# The version of Chef we will install on nodes.
|
|
728
|
+
# @return [String]
|
|
729
|
+
def self.chefVersion
|
|
730
|
+
@@chefVersion
|
|
731
|
+
end
|
|
732
|
+
|
|
433
733
|
MU.supportedGroomers.each { |groomer|
|
|
434
734
|
require "mu/groomers/#{groomer.downcase}"
|
|
435
735
|
}
|
|
@@ -451,6 +751,8 @@ module MU
|
|
|
451
751
|
@@myRegion_var = zone.gsub(/^.*?\/|\-\d+$/, "")
|
|
452
752
|
elsif MU::Cloud::AWS.hosted?
|
|
453
753
|
@@myRegion_var ||= MU::Cloud::AWS.myRegion
|
|
754
|
+
elsif MU::Cloud::Azure.hosted?
|
|
755
|
+
@@myRegion_var ||= MU::Cloud::Azure.myRegion
|
|
454
756
|
else
|
|
455
757
|
@@myRegion_var = nil
|
|
456
758
|
end
|
|
@@ -458,6 +760,7 @@ module MU
|
|
|
458
760
|
end
|
|
459
761
|
|
|
460
762
|
require 'mu/config'
|
|
763
|
+
require 'mu/adoption'
|
|
461
764
|
|
|
462
765
|
# Figure out what cloud provider we're in, if any.
|
|
463
766
|
# @return [String]: Google, AWS, etc. Returns nil if we don't seem to be in a cloud.
|
|
@@ -468,6 +771,10 @@ module MU
|
|
|
468
771
|
elsif MU::Cloud::AWS.hosted?
|
|
469
772
|
@@myInstanceId = MU::Cloud::AWS.getAWSMetaData("instance-id")
|
|
470
773
|
return "AWS"
|
|
774
|
+
elsif MU::Cloud::Azure.hosted?
|
|
775
|
+
metadata = MU::Cloud::Azure.get_metadata()["compute"]
|
|
776
|
+
@@myInstanceId = MU::Cloud::Azure::Id.new("/subscriptions/"+metadata["subscriptionId"]+"/resourceGroups/"+metadata["resourceGroupName"]+"/providers/Microsoft.Compute/virtualMachines/"+metadata["name"])
|
|
777
|
+
return "Azure"
|
|
471
778
|
end
|
|
472
779
|
nil
|
|
473
780
|
end
|
|
@@ -513,63 +820,105 @@ module MU
|
|
|
513
820
|
@@myAZ_var
|
|
514
821
|
end
|
|
515
822
|
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
)
|
|
523
|
-
elsif MU::Cloud::AWS.hosted?
|
|
823
|
+
# Recursively turn a Ruby OpenStruct into a Hash
|
|
824
|
+
# @param struct [OpenStruct]
|
|
825
|
+
# @param stringify_keys [Boolean]
|
|
826
|
+
# @return [Hash]
|
|
827
|
+
def self.structToHash(struct, stringify_keys: false)
|
|
828
|
+
google_struct = false
|
|
524
829
|
begin
|
|
525
|
-
|
|
526
|
-
rescue
|
|
527
|
-
rescue Aws::Errors::MissingCredentialsError => e
|
|
528
|
-
MU.log "I'm hosted in AWS, but I can't make API calls. Does this instance have an appropriate IAM profile?", MU::WARN
|
|
830
|
+
google_struct = struct.class.ancestors.include?(::Google::Apis::Core::Hashable)
|
|
831
|
+
rescue NameError
|
|
529
832
|
end
|
|
530
|
-
end
|
|
531
833
|
|
|
834
|
+
aws_struct = false
|
|
835
|
+
begin
|
|
836
|
+
aws_struct = struct.class.ancestors.include?(::Seahorse::Client::Response)
|
|
837
|
+
rescue NameError
|
|
838
|
+
end
|
|
532
839
|
|
|
533
|
-
|
|
534
|
-
# The VPC/Network in which this Mu master resides
|
|
535
|
-
# XXX account for Google and non-cloud situations
|
|
536
|
-
def self.myVPC
|
|
537
|
-
return nil if MU.myCloudDescriptor.nil?
|
|
840
|
+
azure_struct = false
|
|
538
841
|
begin
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
842
|
+
azure_struct = struct.class.ancestors.include?(::MsRestAzure) or struct.class.name.match(/Azure::.*?::Mgmt::.*?::Models::/)
|
|
843
|
+
rescue NameError
|
|
844
|
+
end
|
|
845
|
+
|
|
846
|
+
if struct.is_a?(Struct) or struct.class.ancestors.include?(Struct) or
|
|
847
|
+
google_struct or aws_struct or azure_struct
|
|
848
|
+
|
|
849
|
+
hash = if azure_struct
|
|
850
|
+
MU::Cloud::Azure.respToHash(struct)
|
|
543
851
|
else
|
|
544
|
-
|
|
852
|
+
struct.to_h
|
|
853
|
+
end
|
|
854
|
+
|
|
855
|
+
if stringify_keys
|
|
856
|
+
newhash = {}
|
|
857
|
+
hash.each_pair { |k, v|
|
|
858
|
+
newhash[k.to_s] = v
|
|
859
|
+
}
|
|
860
|
+
hash = newhash
|
|
545
861
|
end
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
862
|
+
|
|
863
|
+
hash.each_pair { |key, value|
|
|
864
|
+
hash[key] = self.structToHash(value, stringify_keys: stringify_keys)
|
|
865
|
+
}
|
|
866
|
+
return hash
|
|
867
|
+
elsif struct.is_a?(MU::Config::Ref)
|
|
868
|
+
struct = struct.to_h
|
|
869
|
+
elsif struct.is_a?(MU::Cloud::Azure::Id)
|
|
870
|
+
struct = struct.to_s
|
|
871
|
+
elsif struct.is_a?(Hash)
|
|
872
|
+
if stringify_keys
|
|
873
|
+
newhash = {}
|
|
874
|
+
struct.each_pair { |k, v|
|
|
875
|
+
newhash[k.to_s] = v
|
|
876
|
+
}
|
|
877
|
+
struct = newhash
|
|
878
|
+
end
|
|
879
|
+
struct.each_pair { |key, value|
|
|
880
|
+
struct[key] = self.structToHash(value, stringify_keys: stringify_keys)
|
|
881
|
+
}
|
|
882
|
+
return struct
|
|
883
|
+
elsif struct.is_a?(Array)
|
|
884
|
+
struct.map! { |elt|
|
|
885
|
+
self.structToHash(elt, stringify_keys: stringify_keys)
|
|
886
|
+
}
|
|
887
|
+
elsif struct.is_a?(String)
|
|
888
|
+
# Cleanse weird encoding problems
|
|
889
|
+
return struct.dup.to_s.force_encoding("ASCII-8BIT").encode('UTF-8', invalid: :replace, undef: :replace, replace: '?')
|
|
890
|
+
else
|
|
891
|
+
return struct
|
|
549
892
|
end
|
|
550
|
-
@@myVPC_var
|
|
551
893
|
end
|
|
552
894
|
|
|
553
|
-
@@
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
{
|
|
560
|
-
name: "vpc-id",
|
|
561
|
-
values: [MU.myVPC]
|
|
562
|
-
}
|
|
563
|
-
]
|
|
564
|
-
).subnets
|
|
895
|
+
@@myCloudDescriptor = nil
|
|
896
|
+
if MU.myCloud
|
|
897
|
+
found = MU::MommaCat.findStray(MU.myCloud, "server", cloud_id: @@myInstanceId, dummy_ok: true, region: MU.myRegion)
|
|
898
|
+
if !found.nil? and found.size == 1
|
|
899
|
+
@@myCloudDescriptor = found.first.cloud_desc
|
|
900
|
+
end
|
|
565
901
|
end
|
|
566
902
|
|
|
567
|
-
|
|
568
|
-
@@
|
|
569
|
-
# The
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
@@
|
|
903
|
+
|
|
904
|
+
@@myVPCObj_var = nil
|
|
905
|
+
# The VPC/Network in which this Mu master resides
|
|
906
|
+
def self.myVPCObj
|
|
907
|
+
return nil if MU.myCloud.nil?
|
|
908
|
+
return @@myVPCObj_var if @@myVPCObj_var
|
|
909
|
+
cloudclass = const_get("MU").const_get("Cloud").const_get(MU.myCloud)
|
|
910
|
+
@@myVPCObj_var ||= cloudclass.myVPCObj
|
|
911
|
+
@@myVPCObj_var
|
|
912
|
+
end
|
|
913
|
+
|
|
914
|
+
@@myVPC_var = nil
|
|
915
|
+
# The VPC/Network in which this Mu master resides
|
|
916
|
+
def self.myVPC
|
|
917
|
+
return nil if MU.myCloud.nil?
|
|
918
|
+
return @@myVPC_var if @@myVPC_var
|
|
919
|
+
my_vpc_desc = MU.myVPCObj
|
|
920
|
+
@@myVPC_var ||= my_vpc_desc.cloud_id if my_vpc_desc
|
|
921
|
+
@@myVPC_var
|
|
573
922
|
end
|
|
574
923
|
|
|
575
924
|
# Mu's SSL certificate directory
|
|
@@ -590,20 +939,20 @@ module MU
|
|
|
590
939
|
# @return [Boolean]
|
|
591
940
|
def self.hashCmp(hash1, hash2, missing_is_default: false)
|
|
592
941
|
return false if hash1.nil?
|
|
593
|
-
hash2.
|
|
942
|
+
hash2.keys.each { |k|
|
|
594
943
|
if hash1[k].nil?
|
|
595
944
|
return false
|
|
596
945
|
end
|
|
597
946
|
}
|
|
598
947
|
if !missing_is_default
|
|
599
|
-
hash1.
|
|
948
|
+
hash1.keys.each { |k|
|
|
600
949
|
if hash2[k].nil?
|
|
601
950
|
return false
|
|
602
951
|
end
|
|
603
952
|
}
|
|
604
953
|
end
|
|
605
954
|
|
|
606
|
-
hash1.
|
|
955
|
+
hash1.keys.each { |k|
|
|
607
956
|
if hash1[k].is_a?(Array)
|
|
608
957
|
return false if !missing_is_default and hash2[k].nil?
|
|
609
958
|
if !hash2[k].nil?
|
|
@@ -654,75 +1003,21 @@ module MU
|
|
|
654
1003
|
end
|
|
655
1004
|
|
|
656
1005
|
|
|
657
|
-
# Recursively turn a Ruby OpenStruct into a Hash
|
|
658
|
-
# @param struct [OpenStruct]
|
|
659
|
-
# @param stringify_keys [Boolean]
|
|
660
|
-
# @return [Hash]
|
|
661
|
-
def self.structToHash(struct, stringify_keys: false)
|
|
662
|
-
google_struct = false
|
|
663
|
-
begin
|
|
664
|
-
google_struct = struct.class.ancestors.include?(::Google::Apis::Core::Hashable)
|
|
665
|
-
rescue NameError
|
|
666
|
-
end
|
|
667
|
-
|
|
668
|
-
aws_struct = false
|
|
669
|
-
begin
|
|
670
|
-
aws_struct = struct.class.ancestors.include?(::Seahorse::Client::Response)
|
|
671
|
-
rescue NameError
|
|
672
|
-
end
|
|
673
|
-
|
|
674
|
-
if struct.is_a?(Struct) or struct.class.ancestors.include?(Struct) or
|
|
675
|
-
google_struct or aws_struct
|
|
676
|
-
|
|
677
|
-
hash = struct.to_h
|
|
678
|
-
if stringify_keys
|
|
679
|
-
newhash = {}
|
|
680
|
-
hash.each_pair { |k, v|
|
|
681
|
-
newhash[k.to_s] = v
|
|
682
|
-
}
|
|
683
|
-
hash = newhash
|
|
684
|
-
end
|
|
685
|
-
|
|
686
|
-
hash.each_pair { |key, value|
|
|
687
|
-
hash[key] = self.structToHash(value, stringify_keys: stringify_keys)
|
|
688
|
-
}
|
|
689
|
-
return hash
|
|
690
|
-
elsif struct.is_a?(Hash)
|
|
691
|
-
if stringify_keys
|
|
692
|
-
newhash = {}
|
|
693
|
-
struct.each_pair { |k, v|
|
|
694
|
-
newhash[k.to_s] = v
|
|
695
|
-
}
|
|
696
|
-
struct = newhash
|
|
697
|
-
end
|
|
698
|
-
struct.each_pair { |key, value|
|
|
699
|
-
struct[key] = self.structToHash(value, stringify_keys: stringify_keys)
|
|
700
|
-
}
|
|
701
|
-
return struct
|
|
702
|
-
elsif struct.is_a?(Array)
|
|
703
|
-
struct.map! { |elt|
|
|
704
|
-
self.structToHash(elt, stringify_keys: stringify_keys)
|
|
705
|
-
}
|
|
706
|
-
else
|
|
707
|
-
return struct
|
|
708
|
-
end
|
|
709
|
-
end
|
|
710
|
-
|
|
711
1006
|
# Generate a random password which will satisfy the complexity requirements of stock Amazon Windows AMIs.
|
|
712
1007
|
# return [String]: A password string.
|
|
713
|
-
def self.generateWindowsPassword
|
|
1008
|
+
def self.generateWindowsPassword(safe_pattern: '~!@#%^&*_-+=`|(){}[]:;<>,.?', retries: 25)
|
|
714
1009
|
# We have dopey complexity requirements, be stringent here.
|
|
715
1010
|
# I'll be nice and not condense this into one elegant-but-unreadable regular expression
|
|
716
1011
|
attempts = 0
|
|
717
|
-
safe_metachars = Regexp.escape(
|
|
1012
|
+
safe_metachars = Regexp.escape(safe_pattern)
|
|
718
1013
|
begin
|
|
719
|
-
if attempts >
|
|
1014
|
+
if attempts > retries
|
|
720
1015
|
MU.log "Failed to generate an adequate Windows password after #{attempts}", MU::ERR
|
|
721
1016
|
raise MuError, "Failed to generate an adequate Windows password after #{attempts}"
|
|
722
1017
|
end
|
|
723
1018
|
winpass = Password.random(14..16)
|
|
724
1019
|
attempts += 1
|
|
725
|
-
end while winpass.nil? or !winpass.match(/[A-Z]/) or !winpass.match(/[a-z]/) or !winpass.match(/\d/) or !winpass.match(/[#{safe_metachars}]/) or winpass.match(/[^\w\d#{safe_metachars}]/)
|
|
1020
|
+
end while winpass.nil? or !winpass.match(/^[a-z]/i) or !winpass.match(/[A-Z]/) or !winpass.match(/[a-z]/) or !winpass.match(/\d/) or !winpass.match(/[#{safe_metachars}]/) or winpass.match(/[^\w\d#{safe_metachars}]/)
|
|
726
1021
|
|
|
727
1022
|
MU.log "Generated Windows password after #{attempts} attempts", MU::DEBUG
|
|
728
1023
|
return winpass
|