cloud-mu 3.1.3 → 3.3.0
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 +4 -4
- data/Dockerfile +15 -3
- data/ansible/roles/mu-windows/README.md +33 -0
- data/ansible/roles/mu-windows/defaults/main.yml +2 -0
- data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
- data/ansible/roles/mu-windows/files/config.xml +76 -0
- data/ansible/roles/mu-windows/handlers/main.yml +2 -0
- data/ansible/roles/mu-windows/meta/main.yml +53 -0
- data/ansible/roles/mu-windows/tasks/main.yml +36 -0
- data/ansible/roles/mu-windows/tests/inventory +2 -0
- data/ansible/roles/mu-windows/tests/test.yml +5 -0
- data/ansible/roles/mu-windows/vars/main.yml +2 -0
- data/bin/mu-adopt +21 -13
- data/bin/mu-azure-tests +57 -0
- data/bin/mu-cleanup +2 -4
- data/bin/mu-configure +52 -0
- data/bin/mu-deploy +3 -3
- data/bin/mu-findstray-tests +25 -0
- data/bin/mu-gen-docs +2 -4
- data/bin/mu-load-config.rb +4 -4
- data/bin/mu-node-manage +15 -16
- data/bin/mu-run-tests +147 -37
- data/cloud-mu.gemspec +22 -20
- data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
- data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
- data/cookbooks/mu-tools/libraries/helper.rb +3 -2
- data/cookbooks/mu-tools/libraries/monkey.rb +35 -0
- data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
- data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
- data/cookbooks/mu-tools/recipes/eks.rb +2 -2
- data/cookbooks/mu-tools/recipes/google_api.rb +2 -2
- data/cookbooks/mu-tools/recipes/selinux.rb +2 -1
- data/cookbooks/mu-tools/recipes/windows-client.rb +163 -164
- data/cookbooks/mu-tools/resources/disk.rb +1 -1
- data/cookbooks/mu-tools/resources/windows_users.rb +44 -43
- data/extras/clean-stock-amis +25 -19
- data/extras/generate-stock-images +1 -0
- data/extras/image-generators/AWS/win2k12.yaml +18 -13
- data/extras/image-generators/AWS/win2k16.yaml +18 -13
- data/extras/image-generators/AWS/win2k19.yaml +21 -0
- data/extras/image-generators/Google/centos6.yaml +1 -0
- data/extras/image-generators/Google/centos7.yaml +1 -1
- data/modules/mommacat.ru +6 -16
- data/modules/mu.rb +158 -111
- data/modules/mu/adoption.rb +404 -71
- data/modules/mu/cleanup.rb +221 -306
- data/modules/mu/cloud.rb +129 -1633
- data/modules/mu/cloud/database.rb +49 -0
- data/modules/mu/cloud/dnszone.rb +44 -0
- data/modules/mu/cloud/machine_images.rb +212 -0
- data/modules/mu/cloud/providers.rb +81 -0
- data/modules/mu/cloud/resource_base.rb +926 -0
- data/modules/mu/cloud/server.rb +40 -0
- data/modules/mu/cloud/server_pool.rb +1 -0
- data/modules/mu/cloud/ssh_sessions.rb +228 -0
- data/modules/mu/cloud/winrm_sessions.rb +237 -0
- data/modules/mu/cloud/wrappers.rb +169 -0
- data/modules/mu/config.rb +171 -1767
- data/modules/mu/config/alarm.rb +2 -6
- data/modules/mu/config/bucket.rb +32 -3
- data/modules/mu/config/cache_cluster.rb +2 -2
- data/modules/mu/config/cdn.rb +100 -0
- data/modules/mu/config/collection.rb +4 -4
- data/modules/mu/config/container_cluster.rb +9 -4
- data/modules/mu/config/database.rb +84 -105
- data/modules/mu/config/database.yml +1 -2
- data/modules/mu/config/dnszone.rb +10 -9
- data/modules/mu/config/doc_helpers.rb +516 -0
- data/modules/mu/config/endpoint.rb +5 -4
- data/modules/mu/config/firewall_rule.rb +103 -4
- data/modules/mu/config/folder.rb +4 -4
- data/modules/mu/config/function.rb +19 -10
- data/modules/mu/config/group.rb +4 -4
- data/modules/mu/config/habitat.rb +4 -4
- data/modules/mu/config/job.rb +89 -0
- data/modules/mu/config/loadbalancer.rb +60 -14
- data/modules/mu/config/log.rb +4 -4
- data/modules/mu/config/msg_queue.rb +4 -4
- data/modules/mu/config/nosqldb.rb +4 -4
- data/modules/mu/config/notifier.rb +10 -21
- data/modules/mu/config/ref.rb +411 -0
- data/modules/mu/config/role.rb +4 -4
- data/modules/mu/config/schema_helpers.rb +509 -0
- data/modules/mu/config/search_domain.rb +4 -4
- data/modules/mu/config/server.rb +98 -71
- data/modules/mu/config/server.yml +1 -0
- data/modules/mu/config/server_pool.rb +5 -9
- data/modules/mu/config/storage_pool.rb +1 -1
- data/modules/mu/config/tail.rb +200 -0
- data/modules/mu/config/user.rb +4 -4
- data/modules/mu/config/vpc.rb +71 -27
- data/modules/mu/config/vpc.yml +0 -1
- data/modules/mu/defaults/AWS.yaml +91 -68
- data/modules/mu/defaults/Azure.yaml +1 -0
- data/modules/mu/defaults/Google.yaml +3 -2
- data/modules/mu/deploy.rb +43 -26
- data/modules/mu/groomer.rb +17 -2
- data/modules/mu/groomers/ansible.rb +188 -41
- data/modules/mu/groomers/chef.rb +116 -55
- data/modules/mu/logger.rb +127 -148
- data/modules/mu/master.rb +410 -2
- data/modules/mu/master/chef.rb +3 -4
- data/modules/mu/master/ldap.rb +3 -3
- data/modules/mu/master/ssl.rb +12 -3
- data/modules/mu/mommacat.rb +218 -2612
- data/modules/mu/mommacat/daemon.rb +403 -0
- data/modules/mu/mommacat/naming.rb +473 -0
- data/modules/mu/mommacat/search.rb +495 -0
- data/modules/mu/mommacat/storage.rb +722 -0
- data/modules/mu/{clouds → providers}/README.md +1 -1
- data/modules/mu/{clouds → providers}/aws.rb +380 -122
- data/modules/mu/{clouds → providers}/aws/alarm.rb +7 -5
- data/modules/mu/{clouds → providers}/aws/bucket.rb +297 -59
- data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +37 -71
- data/modules/mu/providers/aws/cdn.rb +782 -0
- data/modules/mu/{clouds → providers}/aws/collection.rb +26 -25
- data/modules/mu/{clouds → providers}/aws/container_cluster.rb +724 -744
- data/modules/mu/providers/aws/database.rb +1744 -0
- data/modules/mu/{clouds → providers}/aws/dnszone.rb +88 -70
- data/modules/mu/providers/aws/endpoint.rb +1072 -0
- data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +220 -247
- data/modules/mu/{clouds → providers}/aws/folder.rb +8 -8
- data/modules/mu/{clouds → providers}/aws/function.rb +300 -142
- data/modules/mu/{clouds → providers}/aws/group.rb +31 -29
- data/modules/mu/{clouds → providers}/aws/habitat.rb +18 -15
- data/modules/mu/providers/aws/job.rb +466 -0
- data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +66 -56
- data/modules/mu/{clouds → providers}/aws/log.rb +17 -14
- data/modules/mu/{clouds → providers}/aws/msg_queue.rb +29 -19
- data/modules/mu/{clouds → providers}/aws/nosqldb.rb +114 -16
- data/modules/mu/{clouds → providers}/aws/notifier.rb +142 -65
- data/modules/mu/{clouds → providers}/aws/role.rb +158 -118
- data/modules/mu/{clouds → providers}/aws/search_domain.rb +201 -59
- data/modules/mu/{clouds → providers}/aws/server.rb +844 -1139
- data/modules/mu/{clouds → providers}/aws/server_pool.rb +74 -65
- data/modules/mu/{clouds → providers}/aws/storage_pool.rb +26 -44
- data/modules/mu/{clouds → providers}/aws/user.rb +24 -25
- data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
- data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +2 -1
- data/modules/mu/{clouds → providers}/aws/vpc.rb +525 -931
- data/modules/mu/providers/aws/vpc_subnet.rb +286 -0
- data/modules/mu/{clouds → providers}/azure.rb +29 -9
- data/modules/mu/{clouds → providers}/azure/container_cluster.rb +3 -8
- data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +18 -11
- data/modules/mu/{clouds → providers}/azure/habitat.rb +8 -6
- data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +5 -5
- data/modules/mu/{clouds → providers}/azure/role.rb +8 -10
- data/modules/mu/{clouds → providers}/azure/server.rb +97 -49
- data/modules/mu/{clouds → providers}/azure/user.rb +6 -8
- data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/azure/vpc.rb +16 -21
- data/modules/mu/{clouds → providers}/cloudformation.rb +18 -7
- data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
- data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
- data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
- data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
- data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +5 -7
- data/modules/mu/{clouds → providers}/docker.rb +0 -0
- data/modules/mu/{clouds → providers}/google.rb +68 -30
- data/modules/mu/{clouds → providers}/google/bucket.rb +13 -15
- data/modules/mu/{clouds → providers}/google/container_cluster.rb +85 -78
- data/modules/mu/{clouds → providers}/google/database.rb +11 -21
- data/modules/mu/{clouds → providers}/google/firewall_rule.rb +15 -14
- data/modules/mu/{clouds → providers}/google/folder.rb +20 -17
- data/modules/mu/{clouds → providers}/google/function.rb +140 -168
- data/modules/mu/{clouds → providers}/google/group.rb +29 -34
- data/modules/mu/{clouds → providers}/google/habitat.rb +21 -22
- data/modules/mu/{clouds → providers}/google/loadbalancer.rb +19 -21
- data/modules/mu/{clouds → providers}/google/role.rb +94 -58
- data/modules/mu/{clouds → providers}/google/server.rb +243 -156
- data/modules/mu/{clouds → providers}/google/server_pool.rb +26 -45
- data/modules/mu/{clouds → providers}/google/user.rb +95 -31
- data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
- data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
- data/modules/mu/{clouds → providers}/google/vpc.rb +103 -79
- data/modules/tests/aws-jobs-functions.yaml +46 -0
- data/modules/tests/bucket.yml +4 -0
- data/modules/tests/centos6.yaml +15 -0
- data/modules/tests/centos7.yaml +15 -0
- data/modules/tests/centos8.yaml +12 -0
- data/modules/tests/ecs.yaml +23 -0
- data/modules/tests/eks.yaml +1 -1
- data/modules/tests/functions/node-function/lambda_function.js +10 -0
- data/modules/tests/functions/python-function/lambda_function.py +12 -0
- data/modules/tests/includes-and-params.yaml +2 -1
- data/modules/tests/microservice_app.yaml +288 -0
- data/modules/tests/rds.yaml +108 -0
- data/modules/tests/regrooms/aws-iam.yaml +201 -0
- data/modules/tests/regrooms/bucket.yml +19 -0
- data/modules/tests/regrooms/rds.yaml +123 -0
- data/modules/tests/server-with-scrub-muisms.yaml +2 -1
- data/modules/tests/super_complex_bok.yml +2 -2
- data/modules/tests/super_simple_bok.yml +3 -5
- data/modules/tests/win2k12.yaml +17 -5
- data/modules/tests/win2k16.yaml +25 -0
- data/modules/tests/win2k19.yaml +25 -0
- data/requirements.txt +1 -0
- data/spec/mu/clouds/azure_spec.rb +2 -2
- metadata +240 -154
- data/extras/image-generators/AWS/windows.yaml +0 -18
- data/modules/mu/clouds/aws/database.rb +0 -1985
- data/modules/mu/clouds/aws/endpoint.rb +0 -592
|
@@ -0,0 +1,722 @@
|
|
|
1
|
+
# Copyright:: Copyright (c) 2020 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
|
+
module MU
|
|
16
|
+
|
|
17
|
+
# MommaCat is in charge of managing metadata about resources we've created,
|
|
18
|
+
# as well as orchestrating amongst them and bootstrapping nodes outside of
|
|
19
|
+
# the normal synchronous deploy sequence invoked by *mu-deploy*.
|
|
20
|
+
class MommaCat
|
|
21
|
+
@myhome = Etc.getpwuid(Process.uid).dir
|
|
22
|
+
@nagios_home = "/opt/mu/var/nagios_user_home"
|
|
23
|
+
@locks = Hash.new
|
|
24
|
+
@deploy_cache = Hash.new
|
|
25
|
+
|
|
26
|
+
# Return a {MU::MommaCat} instance for an existing deploy. Use this instead
|
|
27
|
+
# of using #initialize directly to avoid loading deploys multiple times or
|
|
28
|
+
# stepping on the global context for the deployment you're really working
|
|
29
|
+
# on..
|
|
30
|
+
# @param deploy_id [String]: The deploy ID of the deploy to load.
|
|
31
|
+
# @param set_context_to_me [Boolean]: Whether new MommaCat objects should overwrite any existing per-thread global deploy variables.
|
|
32
|
+
# @param use_cache [Boolean]: If we have an existing object for this deploy, use that
|
|
33
|
+
# @return [MU::MommaCat]
|
|
34
|
+
def self.getLitter(deploy_id, set_context_to_me: false, use_cache: true)
|
|
35
|
+
if deploy_id.nil? or deploy_id.empty?
|
|
36
|
+
raise MuError, "Cannot fetch a deployment without a deploy_id"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# XXX this caching may be harmful, causing stale resource objects to stick
|
|
40
|
+
# around. Have we fixed this? Sort of. Bad entries seem to have no kittens,
|
|
41
|
+
# so force a reload if we see that. That's probably not the root problem.
|
|
42
|
+
littercache = nil
|
|
43
|
+
begin
|
|
44
|
+
@@litter_semaphore.synchronize {
|
|
45
|
+
littercache = @@litters.dup
|
|
46
|
+
}
|
|
47
|
+
if littercache[deploy_id] and @@litters_loadtime[deploy_id]
|
|
48
|
+
deploy_root = File.expand_path(MU.dataDir+"/deployments")
|
|
49
|
+
this_deploy_dir = deploy_root+"/"+deploy_id
|
|
50
|
+
if File.exist?("#{this_deploy_dir}/deployment.json")
|
|
51
|
+
lastmod = File.mtime("#{this_deploy_dir}/deployment.json")
|
|
52
|
+
if lastmod > @@litters_loadtime[deploy_id]
|
|
53
|
+
MU.log "Deployment metadata for #{deploy_id} was modified on disk, reload", MU::NOTICE
|
|
54
|
+
use_cache = false
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
rescue ThreadError => e
|
|
59
|
+
# already locked by a parent caller and this is a read op, so this is ok
|
|
60
|
+
raise e if !e.message.match(/recursive locking/)
|
|
61
|
+
littercache = @@litters.dup
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
if !use_cache or littercache[deploy_id].nil?
|
|
65
|
+
need_gc = !littercache[deploy_id].nil?
|
|
66
|
+
newlitter = MU::MommaCat.new(deploy_id, set_context_to_me: set_context_to_me)
|
|
67
|
+
# This, we have to synchronize, as it's a write
|
|
68
|
+
@@litter_semaphore.synchronize {
|
|
69
|
+
@@litters[deploy_id] = newlitter
|
|
70
|
+
@@litters_loadtime[deploy_id] = Time.now
|
|
71
|
+
}
|
|
72
|
+
GC.start if need_gc
|
|
73
|
+
elsif set_context_to_me
|
|
74
|
+
MU::MommaCat.setThreadContext(@@litters[deploy_id])
|
|
75
|
+
end
|
|
76
|
+
return @@litters[deploy_id]
|
|
77
|
+
# MU::MommaCat.new(deploy_id, set_context_to_me: set_context_to_me)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# List the currently held flock() locks.
|
|
81
|
+
def self.trapSafeLocks;
|
|
82
|
+
@locks
|
|
83
|
+
end
|
|
84
|
+
# List the currently held flock() locks.
|
|
85
|
+
def self.locks;
|
|
86
|
+
@lock_semaphore.synchronize {
|
|
87
|
+
@locks
|
|
88
|
+
}
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
# Overwrite this deployment's configuration with a new version. Save the
|
|
93
|
+
# previous version as well.
|
|
94
|
+
# @param new_conf [Hash]: A new configuration, fully resolved by {MU::Config}
|
|
95
|
+
def updateBasketofKittens(new_conf, skip_validation: false, new_metadata: nil, save_now: false)
|
|
96
|
+
loadDeploy
|
|
97
|
+
if new_conf == @original_config
|
|
98
|
+
return
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
scrub_with = nil
|
|
102
|
+
|
|
103
|
+
# Make sure the new config that we were just handed resolves and makes
|
|
104
|
+
# sense
|
|
105
|
+
if !skip_validation
|
|
106
|
+
f = Tempfile.new(@deploy_id)
|
|
107
|
+
f.write JSON.parse(JSON.generate(new_conf)).to_yaml
|
|
108
|
+
conf_engine = MU::Config.new(f.path) # will throw an exception if it's bad, adoption should catch this and cope reasonably
|
|
109
|
+
scrub_with = conf_engine.config
|
|
110
|
+
f.close
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
backup = "#{deploy_dir}/basket_of_kittens.json.#{Time.now.to_i.to_s}"
|
|
114
|
+
MU.log "Saving previous config of #{@deploy_id} to #{backup}"
|
|
115
|
+
config = File.new(backup, File::CREAT|File::TRUNC|File::RDWR, 0600)
|
|
116
|
+
config.flock(File::LOCK_EX)
|
|
117
|
+
config.puts JSON.pretty_generate(@original_config)
|
|
118
|
+
config.flock(File::LOCK_UN)
|
|
119
|
+
config.close
|
|
120
|
+
|
|
121
|
+
@original_config = new_conf.clone
|
|
122
|
+
|
|
123
|
+
MU::Cloud.resource_types.each_pair { |res_type, attrs|
|
|
124
|
+
next if !@deployment.has_key?(attrs[:cfg_plural])
|
|
125
|
+
deletia = []
|
|
126
|
+
@deployment[attrs[:cfg_plural]].each_pair { |res_name, data|
|
|
127
|
+
orig_cfg = findResourceConfig(attrs[:cfg_plural], res_name, (scrub_with || @original_config))
|
|
128
|
+
|
|
129
|
+
if orig_cfg.nil?
|
|
130
|
+
MU.log "#{res_type} #{res_name} no longer configured, will remove deployment metadata", MU::NOTICE
|
|
131
|
+
deletia << res_name
|
|
132
|
+
end
|
|
133
|
+
}
|
|
134
|
+
@deployment[attrs[:cfg_plural]].reject! { |k, v| deletia.include?(k) }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if save_now
|
|
138
|
+
save!
|
|
139
|
+
MU.log "New config saved to #{deploy_dir}/basket_of_kittens.json"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
@lock_semaphore = Mutex.new
|
|
144
|
+
# Release all flock() locks held by the current thread.
|
|
145
|
+
def self.unlockAll
|
|
146
|
+
if !@locks.nil? and !@locks[Thread.current.object_id].nil?
|
|
147
|
+
# Work from a copy so we can iterate without worrying about contention
|
|
148
|
+
# in lock() or unlock(). We can't just wrap our iterator block in a
|
|
149
|
+
# semaphore here, because we're calling another method that uses the
|
|
150
|
+
# same semaphore.
|
|
151
|
+
@lock_semaphore.synchronize {
|
|
152
|
+
delete_list = []
|
|
153
|
+
@locks[Thread.current.object_id].keys.each { |id|
|
|
154
|
+
MU.log "Releasing lock on #{deploy_dir(MU.deploy_id)}/locks/#{id}.lock (thread #{Thread.current.object_id})", MU::DEBUG
|
|
155
|
+
begin
|
|
156
|
+
@locks[Thread.current.object_id][id].flock(File::LOCK_UN)
|
|
157
|
+
@locks[Thread.current.object_id][id].close
|
|
158
|
+
rescue IOError => e
|
|
159
|
+
MU.log "Got #{e.inspect} unlocking #{id} on #{Thread.current.object_id}", MU::WARN
|
|
160
|
+
end
|
|
161
|
+
delete_list << id
|
|
162
|
+
}
|
|
163
|
+
# We do this here because we can't mangle a Hash while we're iterating
|
|
164
|
+
# over it.
|
|
165
|
+
delete_list.each { |id|
|
|
166
|
+
@locks[Thread.current.object_id].delete(id)
|
|
167
|
+
}
|
|
168
|
+
if @locks[Thread.current.object_id].size == 0
|
|
169
|
+
@locks.delete(Thread.current.object_id)
|
|
170
|
+
end
|
|
171
|
+
}
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# Create/hold a flock() lock.
|
|
176
|
+
# @param id [String]: The lock identifier to release.
|
|
177
|
+
# @param nonblock [Boolean]: Whether to block while waiting for the lock. In non-blocking mode, we simply return false if the lock is not available.
|
|
178
|
+
# return [false, nil]
|
|
179
|
+
def self.lock(id, nonblock = false, global = false, deploy_id: MU.deploy_id)
|
|
180
|
+
raise MuError, "Can't pass a nil id to MU::MommaCat.lock" if id.nil?
|
|
181
|
+
|
|
182
|
+
if !global
|
|
183
|
+
lockdir = "#{deploy_dir(deploy_id)}/locks"
|
|
184
|
+
else
|
|
185
|
+
lockdir = File.expand_path(MU.dataDir+"/locks")
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
if !Dir.exist?(lockdir)
|
|
189
|
+
MU.log "Creating #{lockdir}", MU::DEBUG
|
|
190
|
+
Dir.mkdir(lockdir, 0700)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
@lock_semaphore.synchronize {
|
|
194
|
+
if @locks[Thread.current.object_id].nil?
|
|
195
|
+
@locks[Thread.current.object_id] = Hash.new
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
@locks[Thread.current.object_id][id] = File.open("#{lockdir}/#{id}.lock", File::CREAT|File::RDWR, 0600)
|
|
199
|
+
}
|
|
200
|
+
MU.log "Getting a lock on #{lockdir}/#{id}.lock (thread #{Thread.current.object_id})...", MU::DEBUG
|
|
201
|
+
begin
|
|
202
|
+
if nonblock
|
|
203
|
+
if !@locks[Thread.current.object_id][id].flock(File::LOCK_EX|File::LOCK_NB)
|
|
204
|
+
return false
|
|
205
|
+
end
|
|
206
|
+
else
|
|
207
|
+
@locks[Thread.current.object_id][id].flock(File::LOCK_EX)
|
|
208
|
+
end
|
|
209
|
+
rescue IOError
|
|
210
|
+
raise MU::BootstrapTempFail, "Interrupted waiting for lock on thread #{Thread.current.object_id}, probably just a node rebooting as part of a synchronous install"
|
|
211
|
+
end
|
|
212
|
+
MU.log "Lock on #{lockdir}/#{id}.lock on thread #{Thread.current.object_id} acquired", MU::DEBUG
|
|
213
|
+
return true
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
# Release a flock() lock.
|
|
217
|
+
# @param id [String]: The lock identifier to release.
|
|
218
|
+
def self.unlock(id, global = false, deploy_id: MU.deploy_id)
|
|
219
|
+
raise MuError, "Can't pass a nil id to MU::MommaCat.unlock" if id.nil?
|
|
220
|
+
lockdir = nil
|
|
221
|
+
if !global
|
|
222
|
+
lockdir = "#{deploy_dir(deploy_id)}/locks"
|
|
223
|
+
else
|
|
224
|
+
lockdir = File.expand_path(MU.dataDir+"/locks")
|
|
225
|
+
end
|
|
226
|
+
@lock_semaphore.synchronize {
|
|
227
|
+
return if @locks.nil? or @locks[Thread.current.object_id].nil? or @locks[Thread.current.object_id][id].nil?
|
|
228
|
+
}
|
|
229
|
+
MU.log "Releasing lock on #{lockdir}/#{id}.lock (thread #{Thread.current.object_id})", MU::DEBUG
|
|
230
|
+
begin
|
|
231
|
+
@locks[Thread.current.object_id][id].flock(File::LOCK_UN)
|
|
232
|
+
@locks[Thread.current.object_id][id].close
|
|
233
|
+
if !@locks[Thread.current.object_id].nil?
|
|
234
|
+
@locks[Thread.current.object_id].delete(id)
|
|
235
|
+
end
|
|
236
|
+
if @locks[Thread.current.object_id].size == 0
|
|
237
|
+
@locks.delete(Thread.current.object_id)
|
|
238
|
+
end
|
|
239
|
+
rescue IOError => e
|
|
240
|
+
MU.log "Got #{e.inspect} unlocking #{id} on #{Thread.current.object_id}", MU::WARN
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Remove a deployment's metadata.
|
|
245
|
+
# @param deploy_id [String]: The deployment identifier to remove.
|
|
246
|
+
def self.purge(deploy_id)
|
|
247
|
+
if deploy_id.nil? or deploy_id.empty?
|
|
248
|
+
raise MuError, "Got nil deploy_id in MU::MommaCat.purge"
|
|
249
|
+
end
|
|
250
|
+
# XXX archiving is better than annihilating
|
|
251
|
+
path = File.expand_path(MU.dataDir+"/deployments")
|
|
252
|
+
if Dir.exist?(path+"/"+deploy_id)
|
|
253
|
+
unlockAll
|
|
254
|
+
MU.log "Purging #{path}/#{deploy_id}" if File.exist?(path+"/"+deploy_id+"/deployment.json")
|
|
255
|
+
|
|
256
|
+
FileUtils.rm_rf(path+"/"+deploy_id, :secure => true)
|
|
257
|
+
end
|
|
258
|
+
if File.exist?(path+"/unique_ids")
|
|
259
|
+
File.open(path+"/unique_ids", File::CREAT|File::RDWR, 0600) { |f|
|
|
260
|
+
newlines = []
|
|
261
|
+
f.flock(File::LOCK_EX)
|
|
262
|
+
f.readlines.each { |line|
|
|
263
|
+
newlines << line if !line.match(/:#{deploy_id}$/)
|
|
264
|
+
}
|
|
265
|
+
f.rewind
|
|
266
|
+
f.truncate(0)
|
|
267
|
+
f.puts(newlines)
|
|
268
|
+
f.flush
|
|
269
|
+
f.flock(File::LOCK_UN)
|
|
270
|
+
}
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Remove the metadata of the currently loaded deployment.
|
|
275
|
+
def purge!
|
|
276
|
+
MU::MommaCat.purge(MU.deploy_id)
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Return a list of all currently active deploy identifiers.
|
|
280
|
+
# @return [Array<String>]
|
|
281
|
+
def self.listDeploys
|
|
282
|
+
return [] if !Dir.exist?("#{MU.dataDir}/deployments")
|
|
283
|
+
deploys = []
|
|
284
|
+
Dir.entries("#{MU.dataDir}/deployments").reverse_each { |muid|
|
|
285
|
+
next if !Dir.exist?("#{MU.dataDir}/deployments/#{muid}") or muid == "." or muid == ".."
|
|
286
|
+
deploys << muid
|
|
287
|
+
}
|
|
288
|
+
return deploys
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# Return a list of all nodes in all deployments. Does so without loading
|
|
292
|
+
# deployments fully.
|
|
293
|
+
# @return [Hash]
|
|
294
|
+
def self.listAllNodes
|
|
295
|
+
nodes = Hash.new
|
|
296
|
+
MU::MommaCat.deploy_struct_semaphore.synchronize {
|
|
297
|
+
MU::MommaCat.listDeploys.each { |deploy|
|
|
298
|
+
if !Dir.exist?(MU::MommaCat.deploy_dir(deploy)) or
|
|
299
|
+
!File.size?("#{MU::MommaCat.deploy_dir(deploy)}/deployment.json")
|
|
300
|
+
MU.log "Didn't see deployment metadata for '#{deploy}'", MU::WARN
|
|
301
|
+
next
|
|
302
|
+
end
|
|
303
|
+
data = File.open("#{MU::MommaCat.deploy_dir(deploy)}/deployment.json", File::RDONLY)
|
|
304
|
+
MU.log "Getting lock to read #{MU::MommaCat.deploy_dir(deploy)}/deployment.json", MU::DEBUG
|
|
305
|
+
data.flock(File::LOCK_EX)
|
|
306
|
+
begin
|
|
307
|
+
deployment = JSON.parse(File.read("#{MU::MommaCat.deploy_dir(deploy)}/deployment.json"))
|
|
308
|
+
deployment["deploy_id"] = deploy
|
|
309
|
+
if deployment.has_key?("servers")
|
|
310
|
+
deployment["servers"].each_key { |nodeclass|
|
|
311
|
+
deployment["servers"][nodeclass].each_pair { |mu_name, metadata|
|
|
312
|
+
nodes[mu_name] = metadata
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
end
|
|
316
|
+
rescue JSON::ParserError => e
|
|
317
|
+
MU.log "JSON parse failed on #{MU::MommaCat.deploy_dir(deploy)}/deployment.json", MU::ERR, details: e.message
|
|
318
|
+
end
|
|
319
|
+
data.flock(File::LOCK_UN)
|
|
320
|
+
data.close
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return nodes
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# @return [String]: The Mu Master filesystem directory holding metadata for the current deployment
|
|
327
|
+
def deploy_dir
|
|
328
|
+
MU::MommaCat.deploy_dir(@deploy_id)
|
|
329
|
+
end
|
|
330
|
+
|
|
331
|
+
# Locate and return the deploy, if any, which matches the provided origin
|
|
332
|
+
# description
|
|
333
|
+
# @param origin [Hash]
|
|
334
|
+
def self.findMatchingDeploy(origin)
|
|
335
|
+
MU::MommaCat.listDeploys.each { |deploy_id|
|
|
336
|
+
o_path = deploy_dir(deploy_id)+"/origin.json"
|
|
337
|
+
next if !File.exist?(o_path)
|
|
338
|
+
this_origin = JSON.parse(File.read(o_path))
|
|
339
|
+
if origin == this_origin
|
|
340
|
+
MU.log "Deploy #{deploy_id} matches origin hash, loading", details: origin
|
|
341
|
+
return MU::MommaCat.new(deploy_id)
|
|
342
|
+
end
|
|
343
|
+
}
|
|
344
|
+
nil
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# Synchronize all in-memory information related to this to deployment to
|
|
348
|
+
# disk.
|
|
349
|
+
# @param triggering_node [MU::Cloud::Server]: If we're being triggered by the addition/removal/update of a node, this allows us to notify any sibling or dependent nodes of changes
|
|
350
|
+
# @param force [Boolean]: Save even if +no_artifacts+ is set
|
|
351
|
+
# @param origin [Hash]: Optional blob of data indicating how this deploy was created
|
|
352
|
+
def save!(triggering_node = nil, force: false, origin: nil)
|
|
353
|
+
|
|
354
|
+
return if @no_artifacts and !force
|
|
355
|
+
|
|
356
|
+
MU::MommaCat.deploy_struct_semaphore.synchronize {
|
|
357
|
+
MU.log "Saving deployment #{MU.deploy_id}", MU::DEBUG
|
|
358
|
+
|
|
359
|
+
if !Dir.exist?(deploy_dir)
|
|
360
|
+
MU.log "Creating #{deploy_dir}", MU::DEBUG
|
|
361
|
+
Dir.mkdir(deploy_dir, 0700)
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
writeFile("origin.json", JSON.pretty_generate(origin)) if !origin.nil?
|
|
365
|
+
writeFile("private_key", @private_key) if !@private_key.nil?
|
|
366
|
+
writeFile("public_key", @public_key) if !@public_key.nil?
|
|
367
|
+
|
|
368
|
+
if !@deployment.nil? and @deployment.size > 0
|
|
369
|
+
@deployment['handle'] = MU.handle if @deployment['handle'].nil? and !MU.handle.nil?
|
|
370
|
+
[:public_key, :timestamp, :seed, :appname, :handle, :ssh_public_key].each { |var|
|
|
371
|
+
value = instance_variable_get(("@"+var.to_s).to_sym)
|
|
372
|
+
@deployment[var.to_s] = value if value
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
begin
|
|
376
|
+
# XXX doing this to trigger JSON errors before stomping the stored
|
|
377
|
+
# file...
|
|
378
|
+
JSON.pretty_generate(@deployment, max_nesting: false)
|
|
379
|
+
deploy = File.new("#{deploy_dir}/deployment.json", File::CREAT|File::TRUNC|File::RDWR, 0600)
|
|
380
|
+
MU.log "Getting lock to write #{deploy_dir}/deployment.json", MU::DEBUG
|
|
381
|
+
deploy.flock(File::LOCK_EX)
|
|
382
|
+
deploy.puts JSON.pretty_generate(@deployment, max_nesting: false)
|
|
383
|
+
rescue JSON::NestingError => e
|
|
384
|
+
MU.log e.inspect, MU::ERR, details: @deployment
|
|
385
|
+
raise MuError, "Got #{e.message} trying to save deployment"
|
|
386
|
+
rescue Encoding::UndefinedConversionError => e
|
|
387
|
+
MU.log e.inspect, MU::ERR, details: @deployment
|
|
388
|
+
raise MuError, "Got #{e.message} at #{e.error_char.dump} (#{e.source_encoding_name} => #{e.destination_encoding_name}) trying to save deployment"
|
|
389
|
+
end
|
|
390
|
+
deploy.flock(File::LOCK_UN)
|
|
391
|
+
deploy.close
|
|
392
|
+
@need_deploy_flush = false
|
|
393
|
+
MU::MommaCat.updateLitter(@deploy_id, self)
|
|
394
|
+
end
|
|
395
|
+
|
|
396
|
+
if !@original_config.nil? and @original_config.is_a?(Hash)
|
|
397
|
+
writeFile("basket_of_kittens.json", JSON.pretty_generate(MU::Config.manxify(@original_config)))
|
|
398
|
+
end
|
|
399
|
+
|
|
400
|
+
writeFile("node_ssh.key", @ssh_private_key) if !@ssh_private_key.nil?
|
|
401
|
+
writeFile("node_ssh.pub", @ssh_public_key) if !@ssh_public_key.nil?
|
|
402
|
+
writeFile("ssh_key_name", @ssh_key_name) if !@ssh_key_name.nil?
|
|
403
|
+
writeFile("environment_name", @environment) if !@environment.nil?
|
|
404
|
+
writeFile("deploy_secret", @deploy_secret) if !@deploy_secret.nil?
|
|
405
|
+
|
|
406
|
+
if !@secrets.nil?
|
|
407
|
+
secretdir = "#{deploy_dir}/secrets"
|
|
408
|
+
if !Dir.exist?(secretdir)
|
|
409
|
+
MU.log "Creating #{secretdir}", MU::DEBUG
|
|
410
|
+
Dir.mkdir(secretdir, 0700)
|
|
411
|
+
end
|
|
412
|
+
@secrets.each_pair { |type, servers|
|
|
413
|
+
servers.each_pair { |server, svr_secret|
|
|
414
|
+
writeFile("secrets/#{type}.#{server}", svr_secret)
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
end
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
# Update groomer copies of this metadata
|
|
421
|
+
syncLitter(@deployment['servers'].keys, triggering_node: triggering_node, save_only: true) if @deployment.has_key?("servers")
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
# Read all of our +deployment.json+ files in and stick them in a hash. Used
|
|
425
|
+
# by search routines that just need to skim this data without loading
|
|
426
|
+
# entire {MU::MommaCat} objects.
|
|
427
|
+
def self.cacheDeployMetadata(deploy_id = nil, use_cache: false)
|
|
428
|
+
deploy_root = File.expand_path(MU.dataDir+"/deployments")
|
|
429
|
+
MU::MommaCat.deploy_struct_semaphore.synchronize {
|
|
430
|
+
@@deploy_cache ||= {}
|
|
431
|
+
return if !Dir.exist?(deploy_root)
|
|
432
|
+
|
|
433
|
+
Dir.entries(deploy_root).each { |deploy|
|
|
434
|
+
this_deploy_dir = deploy_root+"/"+deploy
|
|
435
|
+
this_deploy_file = this_deploy_dir+"/deployment.json"
|
|
436
|
+
|
|
437
|
+
if deploy == "." or deploy == ".." or !Dir.exist?(this_deploy_dir) or
|
|
438
|
+
(deploy_id and deploy_id != deploy) or
|
|
439
|
+
!File.size?(this_deploy_file) or
|
|
440
|
+
(use_cache and @@deploy_cache[deploy] and @@deploy_cache[deploy]['mtime'] == File.mtime(this_deploy_file))
|
|
441
|
+
next
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
@@deploy_cache[deploy] ||= {}
|
|
445
|
+
|
|
446
|
+
MU.log "Caching deploy #{deploy}", MU::DEBUG
|
|
447
|
+
lock = File.open(this_deploy_file, File::RDONLY)
|
|
448
|
+
lock.flock(File::LOCK_EX)
|
|
449
|
+
@@deploy_cache[deploy]['mtime'] = File.mtime(this_deploy_file)
|
|
450
|
+
|
|
451
|
+
begin
|
|
452
|
+
@@deploy_cache[deploy]['data'] = JSON.parse(File.read(this_deploy_file))
|
|
453
|
+
next if @@deploy_cache[deploy]['data'].nil?
|
|
454
|
+
# Populate some generable entries that should be in the deploy
|
|
455
|
+
# data. Also, bounce out if we realize we've found exactly what
|
|
456
|
+
# we needed already.
|
|
457
|
+
MU::Cloud.resource_types.values.each { |attrs|
|
|
458
|
+
|
|
459
|
+
next if @@deploy_cache[deploy]['data'][attrs[:cfg_plural]].nil?
|
|
460
|
+
if attrs[:has_multiples]
|
|
461
|
+
@@deploy_cache[deploy]['data'][attrs[:cfg_plural]].each_pair { |node_class, nodes|
|
|
462
|
+
next if nodes.nil? or !nodes.is_a?(Hash)
|
|
463
|
+
nodes.each_pair { |nodename, data|
|
|
464
|
+
next if !data.is_a?(Hash)
|
|
465
|
+
data['#MU_NODE_CLASS'] ||= node_class
|
|
466
|
+
data['#MU_NAME'] ||= nodename
|
|
467
|
+
data["cloud"] ||= MU::Config.defaultCloud
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
end
|
|
471
|
+
}
|
|
472
|
+
rescue JSON::ParserError
|
|
473
|
+
raise MuError, "JSON parse failed on #{this_deploy_file}\n\n"+File.read(this_deploy_file)
|
|
474
|
+
ensure
|
|
475
|
+
lock.flock(File::LOCK_UN)
|
|
476
|
+
lock.close
|
|
477
|
+
end
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
@@deploy_cache
|
|
482
|
+
end
|
|
483
|
+
|
|
484
|
+
# Get the deploy directory
|
|
485
|
+
# @param deploy_id [String]
|
|
486
|
+
# @return [String]
|
|
487
|
+
def self.deploy_dir(deploy_id)
|
|
488
|
+
raise MuError, "deploy_dir must get a deploy_id if called as class method (from #{caller[0]}; #{caller[1]})" if deploy_id.nil?
|
|
489
|
+
# XXX this will blow up if someone sticks MU in /
|
|
490
|
+
path = File.expand_path(MU.dataDir+"/deployments")
|
|
491
|
+
if !Dir.exist?(path)
|
|
492
|
+
MU.log "Creating #{path}", MU::DEBUG
|
|
493
|
+
Dir.mkdir(path, 0700)
|
|
494
|
+
end
|
|
495
|
+
path = path+"/"+deploy_id
|
|
496
|
+
return path
|
|
497
|
+
end
|
|
498
|
+
|
|
499
|
+
# Does the deploy with the given id exist?
|
|
500
|
+
# @param deploy_id [String]
|
|
501
|
+
# @return [String]
|
|
502
|
+
def self.deploy_exists?(deploy_id)
|
|
503
|
+
if deploy_id.nil? or deploy_id.empty?
|
|
504
|
+
MU.log "Got nil deploy_id in MU::MommaCat.deploy_exists?", MU::WARN
|
|
505
|
+
return
|
|
506
|
+
end
|
|
507
|
+
path = File.expand_path(MU.dataDir+"/deployments")
|
|
508
|
+
if !Dir.exist?(path)
|
|
509
|
+
Dir.mkdir(path, 0700)
|
|
510
|
+
end
|
|
511
|
+
deploy_path = File.expand_path(path+"/"+deploy_id)
|
|
512
|
+
return Dir.exist?(deploy_path)
|
|
513
|
+
end
|
|
514
|
+
|
|
515
|
+
private
|
|
516
|
+
|
|
517
|
+
def writeFile(filename, contents)
|
|
518
|
+
file = File.new("#{deploy_dir}/#{filename}", File::CREAT|File::TRUNC|File::RDWR, 0600)
|
|
519
|
+
file.puts contents
|
|
520
|
+
file.close
|
|
521
|
+
end
|
|
522
|
+
|
|
523
|
+
# Helper for +initialize+
|
|
524
|
+
def setDeploySecret
|
|
525
|
+
credsets = {}
|
|
526
|
+
MU::Cloud.resource_types.values.each { |attrs|
|
|
527
|
+
if !@original_config[attrs[:cfg_plural]].nil? and @original_config[attrs[:cfg_plural]].size > 0
|
|
528
|
+
@original_config[attrs[:cfg_plural]].each { |resource|
|
|
529
|
+
|
|
530
|
+
credsets[resource['cloud']] ||= []
|
|
531
|
+
credsets[resource['cloud']] << resource['credentials']
|
|
532
|
+
@clouds[resource['cloud']] = 0 if !@clouds.has_key?(resource['cloud'])
|
|
533
|
+
@clouds[resource['cloud']] = @clouds[resource['cloud']] + 1
|
|
534
|
+
|
|
535
|
+
}
|
|
536
|
+
end
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
MU.log "Creating deploy secret for #{MU.deploy_id}"
|
|
540
|
+
@deploy_secret = Password.random(256)
|
|
541
|
+
if !@original_config['scrub_mu_isms'] and !@no_artifacts
|
|
542
|
+
credsets.each_pair { |cloud, creds|
|
|
543
|
+
creds.uniq!
|
|
544
|
+
creds.each { |credentials|
|
|
545
|
+
MU::Cloud.cloudClass(cloud).writeDeploySecret(@deploy_id, @deploy_secret, credentials: credentials)
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
end
|
|
549
|
+
end
|
|
550
|
+
|
|
551
|
+
def loadObjects(delay_descriptor_load)
|
|
552
|
+
# Load up MU::Cloud objects for all our kittens in this deploy
|
|
553
|
+
|
|
554
|
+
MU::Cloud.resource_types.each_pair { |res_type, attrs|
|
|
555
|
+
type = attrs[:cfg_plural]
|
|
556
|
+
next if !@deployment.has_key?(type)
|
|
557
|
+
|
|
558
|
+
deletia = {}
|
|
559
|
+
@deployment[type].each_pair { |res_name, data|
|
|
560
|
+
orig_cfg = findResourceConfig(type, res_name)
|
|
561
|
+
|
|
562
|
+
if orig_cfg.nil?
|
|
563
|
+
MU.log "Failed to locate original config for #{attrs[:cfg_name]} #{res_name} in #{@deploy_id}", MU::WARN if !["firewall_rules", "databases", "storage_pools", "cache_clusters", "alarms"].include?(type) # XXX shaddap
|
|
564
|
+
next
|
|
565
|
+
end
|
|
566
|
+
|
|
567
|
+
if orig_cfg['vpc']
|
|
568
|
+
ref = if orig_cfg['vpc']['id'] and orig_cfg['vpc']['id'].is_a?(Hash)
|
|
569
|
+
orig_cfg['vpc']['id']['mommacat'] = self
|
|
570
|
+
MU::Config::Ref.get(orig_cfg['vpc']['id'])
|
|
571
|
+
else
|
|
572
|
+
orig_cfg['vpc']['mommacat'] = self
|
|
573
|
+
MU::Config::Ref.get(orig_cfg['vpc'])
|
|
574
|
+
end
|
|
575
|
+
orig_cfg['vpc'].delete('mommacat')
|
|
576
|
+
orig_cfg['vpc'] = ref if ref.kitten(shallow: true)
|
|
577
|
+
end
|
|
578
|
+
|
|
579
|
+
begin
|
|
580
|
+
if attrs[:has_multiples]
|
|
581
|
+
data.keys.each { |mu_name|
|
|
582
|
+
addKitten(type, res_name, attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: mu_name, delay_descriptor_load: delay_descriptor_load))
|
|
583
|
+
}
|
|
584
|
+
else
|
|
585
|
+
addKitten(type, res_name, attrs[:interface].new(mommacat: self, kitten_cfg: orig_cfg, mu_name: data['mu_name'], cloud_id: data['cloud_id']))
|
|
586
|
+
end
|
|
587
|
+
rescue StandardError => e
|
|
588
|
+
if e.class != MU::Cloud::MuCloudResourceNotImplemented
|
|
589
|
+
MU.log "Failed to load an existing resource of type '#{type}' in #{@deploy_id}: #{e.inspect}", MU::WARN, details: e.backtrace
|
|
590
|
+
end
|
|
591
|
+
end
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
}
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# Helper for +initialize+
|
|
598
|
+
def initDeployDirectory
|
|
599
|
+
if !Dir.exist?(MU.dataDir+"/deployments")
|
|
600
|
+
MU.log "Creating #{MU.dataDir}/deployments", MU::DEBUG
|
|
601
|
+
Dir.mkdir(MU.dataDir+"/deployments", 0700)
|
|
602
|
+
end
|
|
603
|
+
path = File.expand_path(MU.dataDir+"/deployments")+"/"+@deploy_id
|
|
604
|
+
if !Dir.exist?(path)
|
|
605
|
+
MU.log "Creating #{path}", MU::DEBUG
|
|
606
|
+
Dir.mkdir(path, 0700)
|
|
607
|
+
end
|
|
608
|
+
|
|
609
|
+
@ssh_key_name, @ssh_private_key, @ssh_public_key = self.SSHKey
|
|
610
|
+
if !File.exist?(deploy_dir+"/private_key")
|
|
611
|
+
@private_key, @public_key = createDeployKey
|
|
612
|
+
end
|
|
613
|
+
|
|
614
|
+
end
|
|
615
|
+
|
|
616
|
+
###########################################################################
|
|
617
|
+
###########################################################################
|
|
618
|
+
def loadDeployFromCache(set_context_to_me = true)
|
|
619
|
+
return false if !File.size?(deploy_dir+"/deployment.json")
|
|
620
|
+
|
|
621
|
+
deploy = File.open("#{deploy_dir}/deployment.json", File::RDONLY)
|
|
622
|
+
MU.log "Getting lock to read #{deploy_dir}/deployment.json", MU::DEBUG
|
|
623
|
+
# deploy.flock(File::LOCK_EX)
|
|
624
|
+
begin
|
|
625
|
+
Timeout::timeout(90) {deploy.flock(File::LOCK_EX)}
|
|
626
|
+
rescue Timeout::Error
|
|
627
|
+
raise MuError, "Timed out trying to get an exclusive lock on #{deploy_dir}/deployment.json"
|
|
628
|
+
end
|
|
629
|
+
|
|
630
|
+
begin
|
|
631
|
+
@deployment = JSON.parse(File.read("#{deploy_dir}/deployment.json"))
|
|
632
|
+
rescue JSON::ParserError => e
|
|
633
|
+
MU.log "JSON parse failed on #{deploy_dir}/deployment.json", MU::ERR, details: e.message
|
|
634
|
+
end
|
|
635
|
+
|
|
636
|
+
deploy.flock(File::LOCK_UN)
|
|
637
|
+
deploy.close
|
|
638
|
+
|
|
639
|
+
setThreadContextToMe if set_context_to_me
|
|
640
|
+
|
|
641
|
+
@timestamp = @deployment['timestamp']
|
|
642
|
+
@seed = @deployment['seed']
|
|
643
|
+
@appname = @deployment['appname']
|
|
644
|
+
@handle = @deployment['handle']
|
|
645
|
+
|
|
646
|
+
true
|
|
647
|
+
end
|
|
648
|
+
|
|
649
|
+
###########################################################################
|
|
650
|
+
###########################################################################
|
|
651
|
+
def loadDeploy(deployment_json_only = false, set_context_to_me: true)
|
|
652
|
+
MU::MommaCat.deploy_struct_semaphore.synchronize {
|
|
653
|
+
success = loadDeployFromCache(set_context_to_me)
|
|
654
|
+
|
|
655
|
+
return if deployment_json_only and success
|
|
656
|
+
|
|
657
|
+
if File.exist?(deploy_dir+"/private_key")
|
|
658
|
+
@private_key = File.read("#{deploy_dir}/private_key")
|
|
659
|
+
@public_key = File.read("#{deploy_dir}/public_key")
|
|
660
|
+
end
|
|
661
|
+
|
|
662
|
+
if File.exist?(deploy_dir+"/basket_of_kittens.json")
|
|
663
|
+
begin
|
|
664
|
+
@original_config = JSON.parse(File.read("#{deploy_dir}/basket_of_kittens.json"))
|
|
665
|
+
rescue JSON::ParserError => e
|
|
666
|
+
MU.log "JSON parse failed on #{deploy_dir}/basket_of_kittens.json", MU::ERR, details: e.message
|
|
667
|
+
end
|
|
668
|
+
end
|
|
669
|
+
if File.exist?(deploy_dir+"/ssh_key_name")
|
|
670
|
+
@ssh_key_name = File.read("#{deploy_dir}/ssh_key_name").chomp!
|
|
671
|
+
end
|
|
672
|
+
if File.exist?(deploy_dir+"/node_ssh.key")
|
|
673
|
+
@ssh_private_key = File.read("#{deploy_dir}/node_ssh.key")
|
|
674
|
+
end
|
|
675
|
+
if File.exist?(deploy_dir+"/node_ssh.pub")
|
|
676
|
+
@ssh_public_key = File.read("#{deploy_dir}/node_ssh.pub")
|
|
677
|
+
end
|
|
678
|
+
if File.exist?(deploy_dir+"/environment_name")
|
|
679
|
+
@environment = File.read("#{deploy_dir}/environment_name").chomp!
|
|
680
|
+
end
|
|
681
|
+
if File.exist?(deploy_dir+"/deploy_secret")
|
|
682
|
+
@deploy_secret = File.read("#{deploy_dir}/deploy_secret")
|
|
683
|
+
end
|
|
684
|
+
if Dir.exist?("#{deploy_dir}/secrets")
|
|
685
|
+
@secrets.each_key { |type|
|
|
686
|
+
Dir.glob("#{deploy_dir}/secrets/#{type}.*") { |filename|
|
|
687
|
+
server = File.basename(filename).split(/\./)[1]
|
|
688
|
+
|
|
689
|
+
@secrets[type][server] = File.read(filename).chomp!
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
end
|
|
693
|
+
}
|
|
694
|
+
end
|
|
695
|
+
|
|
696
|
+
def findResourceConfig(type, name, config = @original_config)
|
|
697
|
+
orig_cfg = nil
|
|
698
|
+
if config.has_key?(type)
|
|
699
|
+
config[type].each { |resource|
|
|
700
|
+
if resource["name"] == name
|
|
701
|
+
orig_cfg = resource
|
|
702
|
+
break
|
|
703
|
+
end
|
|
704
|
+
}
|
|
705
|
+
end
|
|
706
|
+
|
|
707
|
+
# Some Server objects originated from ServerPools, get their
|
|
708
|
+
# configs from there
|
|
709
|
+
if type == "servers" and orig_cfg.nil? and config.has_key?("server_pools")
|
|
710
|
+
config["server_pools"].each { |resource|
|
|
711
|
+
if resource["name"] == name
|
|
712
|
+
orig_cfg = resource
|
|
713
|
+
break
|
|
714
|
+
end
|
|
715
|
+
}
|
|
716
|
+
end
|
|
717
|
+
|
|
718
|
+
orig_cfg
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
end #class
|
|
722
|
+
end #module
|