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
data/modules/mu/groomer.rb
CHANGED
|
@@ -30,6 +30,21 @@ module MU
|
|
|
30
30
|
["Chef", "Ansible"]
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
# List of known/supported groomers which are installed and appear to be working
|
|
34
|
+
# @return [Array<String>]
|
|
35
|
+
def self.availableGroomers
|
|
36
|
+
available = []
|
|
37
|
+
MU::Groomer.supportedGroomers.each { |groomer|
|
|
38
|
+
begin
|
|
39
|
+
groomerbase = loadGroomer(groomer)
|
|
40
|
+
available << groomer if groomerbase.available?
|
|
41
|
+
rescue LoadError
|
|
42
|
+
end
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
available
|
|
46
|
+
end
|
|
47
|
+
|
|
33
48
|
# Instance methods that any Groomer plugin must implement
|
|
34
49
|
def self.requiredMethods
|
|
35
50
|
[:preClean, :bootstrap, :haveBootstrapped?, :run, :saveDeployData, :getSecret, :saveSecret, :deleteSecret, :reinstall]
|
|
@@ -37,7 +52,7 @@ module MU
|
|
|
37
52
|
|
|
38
53
|
# Class methods that any Groomer plugin must implement
|
|
39
54
|
def self.requiredClassMethods
|
|
40
|
-
[:getSecret, :cleanup, :saveSecret, :deleteSecret]
|
|
55
|
+
[:getSecret, :cleanup, :saveSecret, :deleteSecret, :available?]
|
|
41
56
|
end
|
|
42
57
|
|
|
43
58
|
class Ansible;
|
|
@@ -121,7 +136,7 @@ module MU
|
|
|
121
136
|
else
|
|
122
137
|
retval = @groomer_obj.method(method).call
|
|
123
138
|
end
|
|
124
|
-
rescue
|
|
139
|
+
rescue StandardError => e
|
|
125
140
|
pp e.backtrace
|
|
126
141
|
raise MU::Groomer::RunError, e.message, e.backtrace
|
|
127
142
|
end
|
|
@@ -24,6 +24,10 @@ module MU
|
|
|
24
24
|
class NoAnsibleExecError < MuError;
|
|
25
25
|
end
|
|
26
26
|
|
|
27
|
+
# One or more Python dependencies missing
|
|
28
|
+
class AnsibleLibrariesError < MuError;
|
|
29
|
+
end
|
|
30
|
+
|
|
27
31
|
# Location in which we'll find our Ansible executables. This only applies
|
|
28
32
|
# to full-grown Mu masters; minimalist gem installs will have to make do
|
|
29
33
|
# with whatever Ansible executables they can find in $PATH.
|
|
@@ -40,6 +44,10 @@ module MU
|
|
|
40
44
|
@ansible_path = node.deploy.deploy_dir+"/ansible"
|
|
41
45
|
@ansible_execs = MU::Groomer::Ansible.ansibleExecDir
|
|
42
46
|
|
|
47
|
+
if !MU::Groomer::Ansible.checkPythonDependencies(@server.windows?)
|
|
48
|
+
raise AnsibleLibrariesError, "One or more python dependencies not available"
|
|
49
|
+
end
|
|
50
|
+
|
|
43
51
|
if !@ansible_execs or @ansible_execs.empty?
|
|
44
52
|
raise NoAnsibleExecError, "No Ansible executables found in visible paths"
|
|
45
53
|
end
|
|
@@ -54,6 +62,10 @@ module MU
|
|
|
54
62
|
installRoles
|
|
55
63
|
end
|
|
56
64
|
|
|
65
|
+
# Are Ansible executables and key libraries present and accounted for?
|
|
66
|
+
def self.available?(windows = false)
|
|
67
|
+
MU::Groomer::Ansible.checkPythonDependencies(windows)
|
|
68
|
+
end
|
|
57
69
|
|
|
58
70
|
# Indicate whether our server has been bootstrapped with Ansible
|
|
59
71
|
def haveBootstrapped?
|
|
@@ -66,6 +78,7 @@ module MU
|
|
|
66
78
|
# @param permissions [Boolean]: If true, save the secret under the current active deploy (if any), rather than in the global location for this user
|
|
67
79
|
# @param deploy_dir [String]: If permissions is +true+, save the secret here
|
|
68
80
|
def self.saveSecret(vault: nil, item: nil, data: nil, permissions: false, deploy_dir: nil)
|
|
81
|
+
|
|
69
82
|
if vault.nil? or vault.empty? or item.nil? or item.empty?
|
|
70
83
|
raise MuError, "Must call saveSecret with vault and item names"
|
|
71
84
|
end
|
|
@@ -73,7 +86,6 @@ module MU
|
|
|
73
86
|
raise MuError, "Ansible vault/item names cannot include forward slashes"
|
|
74
87
|
end
|
|
75
88
|
pwfile = vaultPasswordFile
|
|
76
|
-
|
|
77
89
|
|
|
78
90
|
dir = if permissions
|
|
79
91
|
if deploy_dir
|
|
@@ -95,8 +107,9 @@ module MU
|
|
|
95
107
|
if File.exist?(path)
|
|
96
108
|
MU.log "Overwriting existing vault #{vault} item #{item}"
|
|
97
109
|
end
|
|
110
|
+
|
|
98
111
|
File.open(path, File::CREAT|File::RDWR|File::TRUNC, 0600) { |f|
|
|
99
|
-
f.write data
|
|
112
|
+
f.write data.to_yaml
|
|
100
113
|
}
|
|
101
114
|
|
|
102
115
|
cmd = %Q{#{ansibleExecDir}/ansible-vault encrypt #{path} --vault-password-file #{pwfile}}
|
|
@@ -115,14 +128,23 @@ module MU
|
|
|
115
128
|
# @param item [String]: The item within the repository to retrieve
|
|
116
129
|
# @param field [String]: OPTIONAL - A specific field within the item to return.
|
|
117
130
|
# @return [Hash]
|
|
118
|
-
def self.getSecret(vault: nil, item: nil, field: nil)
|
|
131
|
+
def self.getSecret(vault: nil, item: nil, field: nil, deploy_dir: nil)
|
|
119
132
|
if vault.nil? or vault.empty?
|
|
120
133
|
raise MuError, "Must call getSecret with at least a vault name"
|
|
121
134
|
end
|
|
122
|
-
|
|
123
135
|
pwfile = vaultPasswordFile
|
|
124
|
-
|
|
125
|
-
|
|
136
|
+
|
|
137
|
+
dir = nil
|
|
138
|
+
try = [secret_dir+"/"+vault]
|
|
139
|
+
try << deploy_dir+"/ansible/vaults/"+vault if deploy_dir
|
|
140
|
+
try << MU.mommacat.deploy_dir+"/ansible/vaults/"+vault if MU.mommacat.deploy_dir
|
|
141
|
+
try.each { |maybe_dir|
|
|
142
|
+
if Dir.exist?(maybe_dir) and (item.nil? or File.exist?(maybe_dir+"/"+item))
|
|
143
|
+
dir = maybe_dir
|
|
144
|
+
break
|
|
145
|
+
end
|
|
146
|
+
}
|
|
147
|
+
if dir.nil?
|
|
126
148
|
raise MuNoSuchSecret, "No such vault #{vault}"
|
|
127
149
|
end
|
|
128
150
|
|
|
@@ -135,17 +157,23 @@ module MU
|
|
|
135
157
|
cmd = %Q{#{ansibleExecDir}/ansible-vault view #{itempath} --vault-password-file #{pwfile}}
|
|
136
158
|
MU.log cmd
|
|
137
159
|
a = `#{cmd}`
|
|
138
|
-
# If we happen to have stored recognizeable JSON, return it
|
|
139
|
-
# which is a behavior we're used to from Chef vault.
|
|
140
|
-
# a String.
|
|
160
|
+
# If we happen to have stored recognizeable JSON or YAML, return it
|
|
161
|
+
# as parsed, which is a behavior we're used to from Chef vault.
|
|
162
|
+
# Otherwise, return a String.
|
|
141
163
|
begin
|
|
142
164
|
data = JSON.parse(a)
|
|
143
|
-
if field and data[field]
|
|
144
|
-
data = data[field]
|
|
145
|
-
end
|
|
146
165
|
rescue JSON::ParserError
|
|
147
|
-
|
|
166
|
+
begin
|
|
167
|
+
data = YAML.load(a)
|
|
168
|
+
rescue Psych::SyntaxError => e
|
|
169
|
+
data = a
|
|
170
|
+
end
|
|
148
171
|
end
|
|
172
|
+
[vault, item, field].each { |tier|
|
|
173
|
+
if data and data.is_a?(Hash) and tier and data[tier]
|
|
174
|
+
data = data[tier]
|
|
175
|
+
end
|
|
176
|
+
}
|
|
149
177
|
else
|
|
150
178
|
data = []
|
|
151
179
|
Dir.foreach(dir) { |entry|
|
|
@@ -160,7 +188,7 @@ module MU
|
|
|
160
188
|
|
|
161
189
|
# see {MU::Groomer::Ansible.getSecret}
|
|
162
190
|
def getSecret(vault: nil, item: nil, field: nil)
|
|
163
|
-
self.class.getSecret(vault: vault, item: item, field: field)
|
|
191
|
+
self.class.getSecret(vault: vault, item: item, field: field, deploy_dir: @server.deploy.deploy_dir)
|
|
164
192
|
end
|
|
165
193
|
|
|
166
194
|
# Delete a Ansible data bag / Vault
|
|
@@ -174,7 +202,6 @@ module MU
|
|
|
174
202
|
raise MuNoSuchSecret, "No such vault #{vault}"
|
|
175
203
|
end
|
|
176
204
|
|
|
177
|
-
data = nil
|
|
178
205
|
if item
|
|
179
206
|
itempath = dir+"/"+item
|
|
180
207
|
if !File.exist?(itempath)
|
|
@@ -191,7 +218,7 @@ module MU
|
|
|
191
218
|
|
|
192
219
|
# see {MU::Groomer::Ansible.deleteSecret}
|
|
193
220
|
def deleteSecret(vault: nil, item: nil)
|
|
194
|
-
self.class.deleteSecret(vault: vault, item:
|
|
221
|
+
self.class.deleteSecret(vault: vault, item: item)
|
|
195
222
|
end
|
|
196
223
|
|
|
197
224
|
# Invoke the Ansible client on the node at the other end of a provided SSH
|
|
@@ -207,22 +234,63 @@ module MU
|
|
|
207
234
|
|
|
208
235
|
ssh_user = @server.config['ssh_user'] || "root"
|
|
209
236
|
|
|
210
|
-
|
|
237
|
+
if update_runlist
|
|
238
|
+
bootstrap
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
tmpfile = nil
|
|
242
|
+
playbook = if override_runlist and !override_runlist.empty?
|
|
243
|
+
play = {
|
|
244
|
+
"hosts" => @server.config['name']
|
|
245
|
+
}
|
|
246
|
+
if !@server.windows? and @server.config['ssh_user'] != "root"
|
|
247
|
+
play["become"] = "yes"
|
|
248
|
+
end
|
|
249
|
+
play["roles"] = override_runlist if @server.config['run_list'] and !@server.config['run_list'].empty?
|
|
250
|
+
play["vars"] = @server.config['ansible_vars'] if @server.config['ansible_vars']
|
|
251
|
+
|
|
252
|
+
tmpfile = Tempfile.new("#{@server.config['name']}-override-runlist.yml")
|
|
253
|
+
tmpfile.puts [play].to_yaml
|
|
254
|
+
tmpfile.close
|
|
255
|
+
tmpfile.path
|
|
256
|
+
else
|
|
257
|
+
"#{@server.config['name']}.yml"
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
cmd = %Q{cd #{@ansible_path} && echo "#{purpose}" && #{@ansible_execs}/ansible-playbook -i hosts #{playbook} --limit=#{@server.windows? ? @server.canonicalIP : @server.mu_name} --vault-password-file #{pwfile} --timeout=30 --vault-password-file #{@ansible_path}/.vault_pw -u #{ssh_user}}
|
|
211
261
|
|
|
212
262
|
retries = 0
|
|
213
263
|
begin
|
|
214
264
|
MU.log cmd
|
|
215
|
-
|
|
216
|
-
|
|
265
|
+
Timeout::timeout(timeout) {
|
|
266
|
+
if output
|
|
267
|
+
system("#{cmd}")
|
|
268
|
+
else
|
|
269
|
+
%x{#{cmd} 2>&1}
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
if $?.exitstatus != 0
|
|
273
|
+
raise MU::Groomer::RunError, "Failed Ansible command: #{cmd}"
|
|
274
|
+
end
|
|
275
|
+
}
|
|
276
|
+
rescue Timeout::Error, MU::Groomer::RunError => e
|
|
217
277
|
if retries < max_retries
|
|
278
|
+
if reboot_first_fail and e.class.name == "MU::Groomer::RunError"
|
|
279
|
+
@server.reboot
|
|
280
|
+
reboot_first_fail = false
|
|
281
|
+
end
|
|
218
282
|
sleep 30
|
|
219
283
|
retries += 1
|
|
220
284
|
MU.log "Failed Ansible run, will retry (#{retries.to_s}/#{max_retries.to_s})", MU::NOTICE, details: cmd
|
|
285
|
+
|
|
221
286
|
retry
|
|
222
287
|
else
|
|
288
|
+
tmpfile.unlink if tmpfile
|
|
223
289
|
raise MuError, "Failed Ansible command: #{cmd}"
|
|
224
290
|
end
|
|
225
291
|
end
|
|
292
|
+
|
|
293
|
+
tmpfile.unlink if tmpfile
|
|
226
294
|
end
|
|
227
295
|
|
|
228
296
|
# This is a stub; since Ansible is effectively agentless, this operation
|
|
@@ -238,12 +306,12 @@ module MU
|
|
|
238
306
|
# Bootstrap our server with Ansible- basically, just make sure this node
|
|
239
307
|
# is listed in our deployment's Ansible inventory.
|
|
240
308
|
def bootstrap
|
|
241
|
-
@inventory.add(@server.config['name'], @server.mu_name)
|
|
309
|
+
@inventory.add(@server.config['name'], @server.windows? ? @server.canonicalIP : @server.mu_name)
|
|
242
310
|
play = {
|
|
243
311
|
"hosts" => @server.config['name']
|
|
244
312
|
}
|
|
245
313
|
|
|
246
|
-
if @server.config['ssh_user'] != "root"
|
|
314
|
+
if !@server.windows? and @server.config['ssh_user'] != "root"
|
|
247
315
|
play["become"] = "yes"
|
|
248
316
|
end
|
|
249
317
|
|
|
@@ -255,9 +323,26 @@ module MU
|
|
|
255
323
|
play["vars"] = @server.config['ansible_vars']
|
|
256
324
|
end
|
|
257
325
|
|
|
326
|
+
if @server.windows?
|
|
327
|
+
play["vars"] ||= {}
|
|
328
|
+
play["vars"]["ansible_connection"] = "winrm"
|
|
329
|
+
play["vars"]["ansible_winrm_scheme"] = "https"
|
|
330
|
+
play["vars"]["ansible_winrm_transport"] = "ntlm"
|
|
331
|
+
play["vars"]["ansible_winrm_server_cert_validation"] = "ignore" # XXX this sucks; use Mu_CA.pem if we can get it to work
|
|
332
|
+
# play["vars"]["ansible_winrm_ca_trust_path"] = "#{MU.mySSLDir}/Mu_CA.pem"
|
|
333
|
+
play["vars"]["ansible_user"] = @server.config['windows_admin_username']
|
|
334
|
+
win_pw = @server.getWindowsAdminPassword
|
|
335
|
+
|
|
336
|
+
pwfile = MU::Groomer::Ansible.vaultPasswordFile
|
|
337
|
+
cmd = %Q{#{MU::Groomer::Ansible.ansibleExecDir}/ansible-vault}
|
|
338
|
+
output = %x{#{cmd} encrypt_string '#{win_pw.gsub(/'/, "\\\\'")}' --vault-password-file #{pwfile}}
|
|
339
|
+
|
|
340
|
+
play["vars"]["ansible_password"] = output
|
|
341
|
+
end
|
|
342
|
+
|
|
258
343
|
File.open(@ansible_path+"/"+@server.config['name']+".yml", File::CREAT|File::RDWR|File::TRUNC, 0600) { |f|
|
|
259
344
|
f.flock(File::LOCK_EX)
|
|
260
|
-
f.puts [play].to_yaml
|
|
345
|
+
f.puts [play].to_yaml.sub(/ansible_password: \|-?[\n\s]+/, 'ansible_password: ') # Ansible doesn't like this (legal) YAML
|
|
261
346
|
f.flock(File::LOCK_UN)
|
|
262
347
|
}
|
|
263
348
|
end
|
|
@@ -265,7 +350,7 @@ module MU
|
|
|
265
350
|
# Synchronize the deployment structure managed by {MU::MommaCat} into some Ansible variables, so that nodes can access this metadata.
|
|
266
351
|
# @return [Hash]: The data synchronized.
|
|
267
352
|
def saveDeployData
|
|
268
|
-
@server.describe
|
|
353
|
+
@server.describe
|
|
269
354
|
|
|
270
355
|
allvars = {
|
|
271
356
|
"mu_deployment" => MU::Config.stripConfig(@server.deploy.deployment),
|
|
@@ -314,16 +399,24 @@ module MU
|
|
|
314
399
|
allvars['deployment']
|
|
315
400
|
end
|
|
316
401
|
|
|
402
|
+
# Nuke everything associated with a deploy. Since we're just some files
|
|
403
|
+
# in the deploy directory, this doesn't have to do anything.
|
|
404
|
+
def self.cleanup(deploy_id, noop = false)
|
|
405
|
+
# deploy = MU::MommaCat.new(MU.deploy_id)
|
|
406
|
+
# inventory = Inventory.new(deploy)
|
|
407
|
+
end
|
|
408
|
+
|
|
317
409
|
# Expunge Ansible resources associated with a node.
|
|
318
410
|
# @param node [String]: The Mu name of the node in question.
|
|
319
|
-
# @param
|
|
411
|
+
# @param _vaults_to_clean [Array<Hash>]: Dummy argument, part of this method's interface but not used by the Ansible layer
|
|
320
412
|
# @param noop [Boolean]: Skip actual deletion, just state what we'd do
|
|
321
|
-
|
|
322
|
-
def self.cleanup(node, vaults_to_clean = [], noop = false, nodeonly: false)
|
|
413
|
+
def self.purge(node, _vaults_to_clean = [], noop = false)
|
|
323
414
|
deploy = MU::MommaCat.new(MU.deploy_id)
|
|
324
415
|
inventory = Inventory.new(deploy)
|
|
325
|
-
ansible_path = deploy.deploy_dir+"/ansible"
|
|
326
|
-
|
|
416
|
+
# ansible_path = deploy.deploy_dir+"/ansible"
|
|
417
|
+
if !noop
|
|
418
|
+
inventory.remove(node)
|
|
419
|
+
end
|
|
327
420
|
end
|
|
328
421
|
|
|
329
422
|
# List the Ansible vaults, if any, owned by the specified Mu user
|
|
@@ -344,23 +437,69 @@ module MU
|
|
|
344
437
|
# the results to +STDOUT+.
|
|
345
438
|
# @param name [String]: The variable name to use for the string's YAML key
|
|
346
439
|
# @param string [String]: The string to encrypt
|
|
347
|
-
|
|
348
|
-
def self.encryptString(name, string, for_user = nil)
|
|
440
|
+
def self.encryptString(name, string)
|
|
349
441
|
pwfile = vaultPasswordFile
|
|
350
442
|
cmd = %Q{#{ansibleExecDir}/ansible-vault}
|
|
351
443
|
if !system(cmd, "encrypt_string", string, "--name", name, "--vault-password-file", pwfile)
|
|
352
444
|
raise MuError, "Failed Ansible command: #{cmd} encrypt_string <redacted> --name #{name} --vault-password-file"
|
|
353
445
|
end
|
|
446
|
+
output
|
|
354
447
|
end
|
|
355
448
|
|
|
356
|
-
|
|
449
|
+
# Hunt down and return a path for a Python executable
|
|
450
|
+
# @return [String]
|
|
451
|
+
def self.pythonExecDir
|
|
452
|
+
path = nil
|
|
453
|
+
|
|
454
|
+
if File.exist?(BINDIR+"/python")
|
|
455
|
+
path = BINDIR
|
|
456
|
+
else
|
|
457
|
+
paths = [ansibleExecDir]
|
|
458
|
+
paths.concat(ENV['PATH'].split(/:/))
|
|
459
|
+
paths << "/usr/bin" # not always in path, esp in pared-down Docker images
|
|
460
|
+
paths.reject! { |p| p.nil? }
|
|
461
|
+
paths.uniq.each { |bindir|
|
|
462
|
+
if File.exist?(bindir+"/python")
|
|
463
|
+
path = bindir
|
|
464
|
+
break
|
|
465
|
+
end
|
|
466
|
+
}
|
|
467
|
+
end
|
|
468
|
+
path
|
|
469
|
+
end
|
|
357
470
|
|
|
471
|
+
# Make sure what's in our Python requirements.txt is reflected in the
|
|
472
|
+
# Python we're about to run for Ansible
|
|
473
|
+
def self.checkPythonDependencies(windows = false)
|
|
474
|
+
return nil if !ansibleExecDir
|
|
475
|
+
|
|
476
|
+
execline = File.readlines(ansibleExecDir+"/ansible-playbook").first.chomp.sub(/^#!/, '')
|
|
477
|
+
if !execline
|
|
478
|
+
MU.log "Unable to extract a Python executable from #{ansibleExecDir}/ansible-playbook", MU::ERR
|
|
479
|
+
return false
|
|
480
|
+
end
|
|
481
|
+
|
|
482
|
+
require 'tempfile'
|
|
483
|
+
f = Tempfile.new("pythoncheck")
|
|
484
|
+
f.puts "import ansible"
|
|
485
|
+
f.puts "import winrm" if windows
|
|
486
|
+
f.close
|
|
487
|
+
|
|
488
|
+
system(%Q{#{execline} #{f.path}})
|
|
489
|
+
f.unlink
|
|
490
|
+
$?.exitstatus == 0 ? true : false
|
|
491
|
+
end
|
|
492
|
+
|
|
493
|
+
# Hunt down and return a path for Ansible executables
|
|
494
|
+
# @return [String]
|
|
358
495
|
def self.ansibleExecDir
|
|
359
496
|
path = nil
|
|
360
497
|
if File.exist?(BINDIR+"/ansible-playbook")
|
|
361
498
|
path = BINDIR
|
|
362
499
|
else
|
|
363
|
-
ENV['PATH'].split(/:/)
|
|
500
|
+
paths = ENV['PATH'].split(/:/)
|
|
501
|
+
paths << "/usr/bin"
|
|
502
|
+
paths.uniq.each { |bindir|
|
|
364
503
|
if File.exist?(bindir+"/ansible-playbook")
|
|
365
504
|
path = bindir
|
|
366
505
|
if !File.exist?(bindir+"/ansible-vault")
|
|
@@ -376,8 +515,12 @@ module MU
|
|
|
376
515
|
path
|
|
377
516
|
end
|
|
378
517
|
|
|
379
|
-
# Get the +.vault_pw+ file for the appropriate user. If it
|
|
380
|
-
# generate
|
|
518
|
+
# Get path to the +.vault_pw+ file for the appropriate user. If it
|
|
519
|
+
# doesn't exist, generate it.
|
|
520
|
+
#
|
|
521
|
+
# @param for_user [String]:
|
|
522
|
+
# @param pwfile [String]
|
|
523
|
+
# @return [String]
|
|
381
524
|
def self.vaultPasswordFile(for_user = nil, pwfile: nil)
|
|
382
525
|
pwfile ||= secret_dir(for_user)+"/.vault_pw"
|
|
383
526
|
@@pwfile_semaphore.synchronize {
|
|
@@ -392,11 +535,8 @@ module MU
|
|
|
392
535
|
end
|
|
393
536
|
|
|
394
537
|
# Figure out where our main stash of secrets is, and make sure it exists
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
# Figure out where our main stash of secrets is, and make sure it exists
|
|
538
|
+
# @param user [String]:
|
|
539
|
+
# @return [String]
|
|
400
540
|
def self.secret_dir(user = MU.mu_user)
|
|
401
541
|
path = MU.dataDir(user) + "/ansible-secrets"
|
|
402
542
|
Dir.mkdir(path, 0755) if !Dir.exist?(path)
|
|
@@ -404,6 +544,13 @@ module MU
|
|
|
404
544
|
path
|
|
405
545
|
end
|
|
406
546
|
|
|
547
|
+
private
|
|
548
|
+
|
|
549
|
+
# Figure out where our main stash of secrets is, and make sure it exists
|
|
550
|
+
def secret_dir
|
|
551
|
+
MU::Groomer::Ansible.secret_dir(@mu_user)
|
|
552
|
+
end
|
|
553
|
+
|
|
407
554
|
# Make an effort to distinguish an Ansible role from other sorts of
|
|
408
555
|
# artifacts, since 'roles' is an awfully generic name for a directory.
|
|
409
556
|
# Short of a full, slow syntax check, this is the best we're liable to do.
|
|
@@ -543,7 +690,7 @@ module MU
|
|
|
543
690
|
def haveNode?(name)
|
|
544
691
|
lock
|
|
545
692
|
read
|
|
546
|
-
@inv.
|
|
693
|
+
@inv.values.each { |nodes|
|
|
547
694
|
if nodes.include?(name)
|
|
548
695
|
unlock
|
|
549
696
|
return true
|
|
@@ -574,7 +721,7 @@ module MU
|
|
|
574
721
|
def remove(name)
|
|
575
722
|
lock
|
|
576
723
|
read
|
|
577
|
-
@inv.each_pair { |
|
|
724
|
+
@inv.each_pair { |_group, nodes|
|
|
578
725
|
nodes.delete(name)
|
|
579
726
|
}
|
|
580
727
|
save!
|