cloud-mu 3.1.6 → 3.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (181) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +1 -1
  3. data/bin/mu-adopt +15 -12
  4. data/bin/mu-azure-tests +57 -0
  5. data/bin/mu-cleanup +2 -4
  6. data/bin/mu-configure +37 -1
  7. data/bin/mu-deploy +3 -3
  8. data/bin/mu-findstray-tests +25 -0
  9. data/bin/mu-gen-docs +2 -4
  10. data/bin/mu-load-config.rb +2 -1
  11. data/bin/mu-run-tests +37 -12
  12. data/cloud-mu.gemspec +4 -4
  13. data/cookbooks/mu-tools/attributes/default.rb +7 -0
  14. data/cookbooks/mu-tools/libraries/helper.rb +87 -3
  15. data/cookbooks/mu-tools/recipes/apply_security.rb +39 -23
  16. data/cookbooks/mu-tools/recipes/aws_api.rb +13 -0
  17. data/cookbooks/mu-tools/recipes/google_api.rb +4 -0
  18. data/cookbooks/mu-tools/recipes/rsyslog.rb +8 -1
  19. data/cookbooks/mu-tools/resources/disk.rb +33 -12
  20. data/cookbooks/mu-tools/resources/mommacat_request.rb +1 -2
  21. data/cookbooks/mu-tools/templates/centos-8/sshd_config.erb +215 -0
  22. data/extras/clean-stock-amis +10 -2
  23. data/extras/generate-stock-images +7 -3
  24. data/extras/image-generators/AWS/centos7.yaml +19 -16
  25. data/extras/image-generators/AWS/{rhel7.yaml → rhel71.yaml} +0 -0
  26. data/extras/image-generators/AWS/{win2k12.yaml → win2k12r2.yaml} +0 -0
  27. data/modules/mommacat.ru +2 -2
  28. data/modules/mu.rb +84 -97
  29. data/modules/mu/adoption.rb +359 -59
  30. data/modules/mu/cleanup.rb +67 -44
  31. data/modules/mu/cloud.rb +108 -1754
  32. data/modules/mu/cloud/database.rb +49 -0
  33. data/modules/mu/cloud/dnszone.rb +44 -0
  34. data/modules/mu/cloud/machine_images.rb +212 -0
  35. data/modules/mu/cloud/providers.rb +81 -0
  36. data/modules/mu/cloud/resource_base.rb +929 -0
  37. data/modules/mu/cloud/server.rb +40 -0
  38. data/modules/mu/cloud/server_pool.rb +1 -0
  39. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  40. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  41. data/modules/mu/cloud/wrappers.rb +178 -0
  42. data/modules/mu/config.rb +122 -80
  43. data/modules/mu/config/alarm.rb +2 -6
  44. data/modules/mu/config/bucket.rb +32 -3
  45. data/modules/mu/config/cache_cluster.rb +2 -2
  46. data/modules/mu/config/cdn.rb +100 -0
  47. data/modules/mu/config/collection.rb +1 -1
  48. data/modules/mu/config/container_cluster.rb +2 -2
  49. data/modules/mu/config/database.rb +84 -105
  50. data/modules/mu/config/database.yml +1 -2
  51. data/modules/mu/config/dnszone.rb +5 -4
  52. data/modules/mu/config/doc_helpers.rb +4 -5
  53. data/modules/mu/config/endpoint.rb +2 -1
  54. data/modules/mu/config/firewall_rule.rb +3 -19
  55. data/modules/mu/config/folder.rb +1 -1
  56. data/modules/mu/config/function.rb +17 -8
  57. data/modules/mu/config/group.rb +1 -1
  58. data/modules/mu/config/habitat.rb +1 -1
  59. data/modules/mu/config/job.rb +89 -0
  60. data/modules/mu/config/loadbalancer.rb +57 -11
  61. data/modules/mu/config/log.rb +1 -1
  62. data/modules/mu/config/msg_queue.rb +1 -1
  63. data/modules/mu/config/nosqldb.rb +1 -1
  64. data/modules/mu/config/notifier.rb +8 -19
  65. data/modules/mu/config/ref.rb +81 -9
  66. data/modules/mu/config/role.rb +1 -1
  67. data/modules/mu/config/schema_helpers.rb +30 -34
  68. data/modules/mu/config/search_domain.rb +1 -1
  69. data/modules/mu/config/server.rb +5 -13
  70. data/modules/mu/config/server_pool.rb +3 -7
  71. data/modules/mu/config/storage_pool.rb +1 -1
  72. data/modules/mu/config/tail.rb +10 -0
  73. data/modules/mu/config/user.rb +1 -1
  74. data/modules/mu/config/vpc.rb +13 -17
  75. data/modules/mu/defaults/AWS.yaml +106 -106
  76. data/modules/mu/defaults/Azure.yaml +1 -0
  77. data/modules/mu/defaults/Google.yaml +1 -0
  78. data/modules/mu/deploy.rb +33 -19
  79. data/modules/mu/groomer.rb +15 -0
  80. data/modules/mu/groomers/chef.rb +3 -0
  81. data/modules/mu/logger.rb +120 -144
  82. data/modules/mu/master.rb +22 -1
  83. data/modules/mu/mommacat.rb +71 -26
  84. data/modules/mu/mommacat/daemon.rb +23 -14
  85. data/modules/mu/mommacat/naming.rb +82 -3
  86. data/modules/mu/mommacat/search.rb +59 -16
  87. data/modules/mu/mommacat/storage.rb +119 -48
  88. data/modules/mu/{clouds → providers}/README.md +1 -1
  89. data/modules/mu/{clouds → providers}/aws.rb +248 -62
  90. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  91. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  92. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  93. data/modules/mu/providers/aws/cdn.rb +782 -0
  94. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  95. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +65 -63
  96. data/modules/mu/providers/aws/database.rb +1747 -0
  97. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  98. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  99. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  100. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  101. data/modules/mu/{clouds → providers}/aws/function.rb +291 -133
  102. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  103. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  104. data/modules/mu/providers/aws/job.rb +469 -0
  105. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  106. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  107. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  108. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  109. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  110. data/modules/mu/{clouds → providers}/aws/role.rb +112 -78
  111. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  112. data/modules/mu/{clouds → providers}/aws/server.rb +120 -145
  113. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  114. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  115. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  116. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  117. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  118. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  119. data/modules/mu/{clouds → providers}/aws/vpc.rb +141 -73
  120. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  121. data/modules/mu/{clouds → providers}/azure.rb +4 -1
  122. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  123. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  124. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  126. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  127. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  128. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  129. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  130. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  132. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  133. data/modules/mu/{clouds → providers}/cloudformation.rb +1 -1
  134. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  135. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  136. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  137. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  138. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  142. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  143. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  144. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  145. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  146. data/modules/mu/{clouds → providers}/google.rb +15 -6
  147. data/modules/mu/{clouds → providers}/google/bucket.rb +2 -2
  148. data/modules/mu/{clouds → providers}/google/container_cluster.rb +29 -14
  149. data/modules/mu/{clouds → providers}/google/database.rb +2 -9
  150. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +3 -3
  151. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  152. data/modules/mu/{clouds → providers}/google/function.rb +4 -4
  153. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  154. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  155. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +2 -2
  156. data/modules/mu/{clouds → providers}/google/role.rb +46 -35
  157. data/modules/mu/{clouds → providers}/google/server.rb +26 -11
  158. data/modules/mu/{clouds → providers}/google/server_pool.rb +11 -11
  159. data/modules/mu/{clouds → providers}/google/user.rb +32 -22
  160. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  161. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  162. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  163. data/modules/mu/{clouds → providers}/google/vpc.rb +38 -3
  164. data/modules/tests/aws-jobs-functions.yaml +46 -0
  165. data/modules/tests/centos6.yaml +15 -0
  166. data/modules/tests/centos7.yaml +15 -0
  167. data/modules/tests/centos8.yaml +12 -0
  168. data/modules/tests/ecs.yaml +2 -2
  169. data/modules/tests/eks.yaml +1 -1
  170. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  171. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  172. data/modules/tests/microservice_app.yaml +288 -0
  173. data/modules/tests/rds.yaml +108 -0
  174. data/modules/tests/regrooms/rds.yaml +123 -0
  175. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  176. data/modules/tests/super_complex_bok.yml +2 -2
  177. data/modules/tests/super_simple_bok.yml +2 -2
  178. data/spec/mu/clouds/azure_spec.rb +2 -2
  179. metadata +126 -98
  180. data/modules/mu/clouds/aws/database.rb +0 -1974
  181. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -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]
@@ -805,6 +805,9 @@ retry
805
805
  end
806
806
  end
807
807
 
808
+ # Purge Chef resources matching a particular deploy
809
+ # @param deploy_id [String]
810
+ # @param noop [Boolean]
808
811
  def self.cleanup(deploy_id, noop = false)
809
812
  return nil if deploy_id.nil? or deploy_id.empty?
810
813
  begin
@@ -33,6 +33,33 @@ module MU
33
33
  # Show DEBUG log entries and extra call stack and threading info
34
34
  LOUD = 2.freeze
35
35
 
36
+ # stash a hash map for color outputs
37
+ COLORMAP = {
38
+ MU::DEBUG => { :html => "orange", :ansi => :yellow },
39
+ MU::INFO => { :html => "green", :ansi => :green },
40
+ MU::NOTICE => { :html => "yellow", :ansi => :yellow },
41
+ MU::WARN => { :html => "orange", :ansi => :light_red },
42
+ MU::ERR => { :html => "red", :ansi => :red }
43
+ }.freeze
44
+
45
+ # minimum log verbosity at which we'll print various types of messages
46
+ PRINT_MSG_IF = {
47
+ MU::DEBUG => { :msg => LOUD, :details => LOUD },
48
+ MU::INFO => { :msg => NORMAL, :details => LOUD },
49
+ MU::NOTICE => { :msg => nil, :details => QUIET },
50
+ MU::WARN => { :msg => nil, :details => SILENT },
51
+ MU::ERR => { :msg => nil, :details => nil }
52
+ }.freeze
53
+
54
+ # Syslog equivalents of our log levels
55
+ SYSLOG_MAP = {
56
+ MU::DEBUG => Syslog::LOG_DEBUG,
57
+ MU::INFO => Syslog::LOG_NOTICE,
58
+ MU::NOTICE => Syslog::LOG_NOTICE,
59
+ MU::WARN => Syslog::LOG_WARNING,
60
+ MU::ERR => Syslog::LOG_ERR
61
+ }.freeze
62
+
36
63
  attr_accessor :verbosity
37
64
  @verbosity = MU::Logger::NORMAL
38
65
  @quiet = false
@@ -76,59 +103,28 @@ module MU
76
103
  html ||= @html
77
104
  handle ||= @handle
78
105
  color ||= @color
79
- return if verbosity == MU::Logger::SILENT
80
- return if verbosity < MU::Logger::LOUD and level == DEBUG
81
- return if verbosity < MU::Logger::NORMAL and level == INFO
82
106
 
83
- # By which we mean, "get the filename (with the .rb stripped off) which
84
- # originated the call to this method. Which, for our purposes, is the
85
- # MU subclass that called us. Useful information. And it looks like Perl.
86
- mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
87
- bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
88
- caller_name = caller[1]
107
+ if verbosity == MU::Logger::SILENT or (verbosity < MU::Logger::LOUD and level == DEBUG) or (verbosity < MU::Logger::NORMAL and level == INFO)
108
+ return
109
+ end
89
110
 
90
- caller_name.sub!(/:.*/, "")
91
- caller_name.sub!(/^\.\//, "")
92
- caller_name.sub!(/^#{mod_root}/, "")
93
- caller_name.sub!(/^#{bin_root}/, "")
94
- caller_name.sub!(/\.r[ub]$/, "")
95
- caller_name.sub!(/#{Regexp.quote(MU.myRoot)}\//, "")
96
- caller_name.sub!(/^modules\//, "")
111
+ if level == SUMMARY
112
+ @summary << msg
113
+ return
114
+ end
115
+
116
+ caller_name = extract_caller_name(caller[1])
97
117
 
98
118
  time = Time.now.strftime("%b %d %H:%M:%S").to_s
99
119
 
100
120
  Syslog.open("Mu/"+caller_name, Syslog::LOG_PID, Syslog::LOG_DAEMON | Syslog::LOG_LOCAL3) if !Syslog.opened?
101
- if !details.nil?
102
- if details.is_a?(Hash) and details.has_key?(:details)
103
- details = details[:details]
104
- end
105
- details = PP.pp(details, '') if !details.is_a?(String)
106
- end
107
- details = "<pre>"+details+"</pre>" if html
108
- # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
109
- # ugly line feeds too.
110
- if !details.nil?
111
- details = details.dup # in case it's frozen or something
112
- details.gsub!(/\\n/, "\n")
113
- details.gsub!(/(\\r|\r)/, "")
114
- end
121
+
122
+ details = format_details(details, html)
115
123
 
116
124
  msg = msg.first if msg.is_a?(Array)
117
125
  msg = "" if msg == nil
118
126
  msg = msg.to_s if !msg.is_a?(String) and msg.respond_to?(:to_s)
119
127
 
120
- # wrapper for writing a log entry to multiple filehandles
121
- # @param handles [Array<IO>]
122
- # @param msgs [Array<String>]
123
- def write(handles = [], msgs = [])
124
- return if handles.nil? or msgs.nil?
125
- handles.each { |h|
126
- msgs.each { |m|
127
- h.puts m
128
- }
129
- }
130
- end
131
-
132
128
  @@log_semaphere.synchronize {
133
129
  handles = [handle]
134
130
  extra_logfile = if deploy and deploy.deploy_dir and Dir.exist?(deploy.deploy_dir)
@@ -137,110 +133,41 @@ module MU
137
133
  handles << extra_logfile if extra_logfile
138
134
  msgs = []
139
135
 
140
- case level
141
- when SUMMARY
142
- @summary << msg
143
- when DEBUG
144
- if verbosity >= MU::Logger::LOUD
145
- if html
146
- html_out "#{time} - #{caller_name} - #{msg}", "orange"
147
- html_out "&nbsp;#{details}" if details
148
- elsif color
149
- msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
150
- msgs << "#{details}".white.on_black if details
151
- else
152
- msgs << "#{time} - #{caller_name} - #{msg}"
153
- msgs << "#{details}" if details
154
- end
155
- Syslog.log(Syslog::LOG_DEBUG, msg.gsub(/%/, ''))
156
- Syslog.log(Syslog::LOG_DEBUG, details.gsub(/%/, '')) if details
157
- end
158
- when INFO
159
- if verbosity >= MU::Logger::NORMAL
160
- if html
161
- html_out "#{time} - #{caller_name} - #{msg}", "green"
162
- elsif color
163
- msgs << "#{time} - #{caller_name} - #{msg}".green.on_black
164
- else
165
- msgs << "#{time} - #{caller_name} - #{msg}"
166
- end
167
- if verbosity >= MU::Logger::LOUD
168
- if html
169
- html_out "&nbsp;#{details}"
170
- elsif color
171
- msgs << "#{details}".white.on_black if details
172
- else
173
- msgs << "#{details}" if details
174
- end
175
- end
176
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
177
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
178
- end
179
- when NOTICE
180
- if html
181
- html_out "#{time} - #{caller_name} - #{msg}", "yellow"
182
- elsif color
183
- msgs << "#{time} - #{caller_name} - #{msg}".yellow.on_black
184
- else
185
- msgs << "#{time} - #{caller_name} - #{msg}"
186
- end
187
- if verbosity >= MU::Logger::QUIET
188
- if html
189
- html_out "#{caller_name} - #{msg}"
190
- elsif color
191
- msgs << "#{details}".white.on_black if details
192
- else
193
- msgs << "#{details}" if details
194
- end
195
- end
196
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
197
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
198
- when WARN
199
- if html
200
- html_out "#{time} - #{caller_name} - #{msg}", "orange"
201
- elsif color
202
- msgs << "#{time} - #{caller_name} - #{msg}".light_red.on_black
203
- else
204
- msgs << "#{time} - #{caller_name} - #{msg}"
205
- end
206
- if verbosity >= MU::Logger::SILENT
207
- if html
208
- html_out "#{caller_name} - #{msg}"
209
- elsif color
210
- msgs << "#{details}".white.on_black if details
211
- else
212
- msgs << "#{details}" if details
213
- end
214
- end
215
- Syslog.log(Syslog::LOG_WARNING, msg.gsub(/%/, ''))
216
- Syslog.log(Syslog::LOG_WARNING, details.gsub(/%/, '')) if details
217
- when ERR
218
- if html
219
- html_out "#{time} - #{caller_name} - #{msg}", "red"
220
- html_out "&nbsp;#{details}" if details
221
- elsif color
222
- msgs << "#{time} - #{caller_name} - #{msg}".red.on_black
223
- msgs << "#{details}".white.on_black if details
224
- else
225
- msgs << "#{time} - #{caller_name} - #{msg}"
226
- msgs << "#{details}" if details
227
- end
228
- Syslog.log(Syslog::LOG_ERR, msg.gsub(/%/, ''))
229
- Syslog.log(Syslog::LOG_ERR, details.gsub(/%/, '')) if details
136
+ if !PRINT_MSG_IF[level][:msg] or level >= PRINT_MSG_IF[level][:msg]
137
+ if html
138
+ html_out "#{time} - #{caller_name} - #{msg}", COLORMAP[level][:html]
139
+ else
140
+ str = "#{time} - #{caller_name} - #{msg}"
141
+ str = str.send(COLORMAP[level][:ansi]).on_black if color
142
+ msgs << str
143
+ end
144
+ Syslog.log(SYSLOG_MAP[level], msg.gsub(/%/, ''))
145
+ end
146
+
147
+ if details and (!PRINT_MSG_IF[level][:details] or level >= PRINT_MSG_IF[level][:details])
148
+ if html
149
+ html_out "&nbsp;#{details}"
230
150
  else
231
- if html
232
- html_out "#{time} - #{caller_name} - #{msg}"
233
- html_out "&nbsp;#{details}" if details
234
- elsif color
235
- msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
236
- msgs << "#{details}".white.on_black if details
237
- else
238
- msgs << "#{time} - #{caller_name} - #{msg}"
239
- msgs << "#{details}" if details
240
- end
241
- Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
242
- Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
151
+ details = details.white.on_black if color
152
+ msgs << details
153
+ end
154
+ Syslog.log(SYSLOG_MAP[level], details.gsub(/%/, ''))
243
155
  end
156
+
157
+ # else
158
+ # if html
159
+ # html_out "#{time} - #{caller_name} - #{msg}"
160
+ # html_out "&nbsp;#{details}" if details
161
+ # elsif color
162
+ # msgs << "#{time} - #{caller_name} - #{msg}".white.on_black
163
+ # msgs << "#{details}".white.on_black if details
164
+ # else
165
+ # msgs << "#{time} - #{caller_name} - #{msg}"
166
+ # msgs << "#{details}" if details
167
+ # end
168
+ # Syslog.log(Syslog::LOG_NOTICE, msg.gsub(/%/, ''))
169
+ # Syslog.log(Syslog::LOG_NOTICE, details.gsub(/%/, '')) if details
170
+
244
171
  write(handles, msgs)
245
172
 
246
173
  extra_logfile.close if extra_logfile
@@ -250,6 +177,43 @@ module MU
250
177
 
251
178
  private
252
179
 
180
+ def format_details(details, html = false)
181
+ return if details.nil?
182
+
183
+ if details.is_a?(Hash) and details.has_key?(:details)
184
+ details = details[:details]
185
+ end
186
+ details = PP.pp(details, '') if !details.is_a?(String)
187
+
188
+ details = "<pre>"+details+"</pre>" if html
189
+ # We get passed literal quoted newlines sometimes, fix 'em. Get Windows'
190
+ # ugly line feeds too.
191
+
192
+ details = details.dup # in case it's frozen or something
193
+ details.gsub!(/\\n/, "\n")
194
+ details.gsub!(/(\\r|\r)/, "")
195
+
196
+ details
197
+ end
198
+
199
+ # By which we mean, "get the filename (with the .rb stripped off) which
200
+ # originated the call to this method. Which, for our purposes, is the
201
+ # MU subclass that called us. Useful information. And it looks like Perl.
202
+ def extract_caller_name(caller_name)
203
+ return nil if !caller_name or !caller_name.is_a?(String)
204
+ mod_root = Regexp.quote("#{ENV['MU_LIBDIR']}/modules/mu/")
205
+ bin_root = Regexp.quote("#{ENV['MU_INSTALLDIR']}/bin/")
206
+
207
+ caller_name.sub!(/:.*/, "")
208
+ caller_name.sub!(/^\.\//, "")
209
+ caller_name.sub!(/^#{mod_root}/, "")
210
+ caller_name.sub!(/^#{bin_root}/, "")
211
+ caller_name.sub!(/\.r[ub]$/, "")
212
+ caller_name.sub!(/#{Regexp.quote(MU.myRoot)}\//, "")
213
+ caller_name.sub!(/^modules\//, "")
214
+ caller_name
215
+ end
216
+
253
217
  # Output a log message as HTML.
254
218
  #
255
219
  # @param msg [String]: The log message to print
@@ -259,5 +223,17 @@ module MU
259
223
  @handle.puts "<span style='color:#{rgb.css_rgb};'>#{msg}</span>"
260
224
  end
261
225
 
226
+ # wrapper for writing a log entry to multiple filehandles
227
+ # @param handles [Array<IO>]
228
+ # @param msgs [Array<String>]
229
+ def write(handles = [], msgs = [])
230
+ return if handles.nil? or msgs.nil?
231
+ handles.each { |h|
232
+ msgs.each { |m|
233
+ h.puts m
234
+ }
235
+ }
236
+ end
237
+
262
238
  end #class
263
239
  end #module
@@ -602,7 +602,7 @@ module MU
602
602
  return
603
603
  end
604
604
  if ssh_key_name.nil? or ssh_key_name.empty?
605
- MU.log "Failed to extract ssh_key_name for #{ssh_key_name.mu_name} in addHostToSSHConfig", MU::ERR
605
+ MU.log "Failed to extract ssh_key_name for #{server.mu_name} in addHostToSSHConfig", MU::ERR
606
606
  return
607
607
  end
608
608
 
@@ -880,5 +880,26 @@ module MU
880
880
  end
881
881
  end
882
882
 
883
+ # Recursively zip a directory
884
+ # @param srcdir [String]
885
+ # @param outfile [String]
886
+ def self.zipDir(srcdir, outfile)
887
+ require 'zip'
888
+ ::Zip::File.open(outfile, ::Zip::File::CREATE) { |zipfile|
889
+ addpath = Proc.new { |zip_path, parent_path|
890
+ Dir.entries(parent_path).reject{ |d| [".", ".."].include?(d) }.each { |entry|
891
+ src = File.join(parent_path, entry)
892
+ dst = File.join(zip_path, entry).sub(/^\//, '')
893
+ if File.directory?(src)
894
+ addpath.call(dst, src)
895
+ else
896
+ zipfile.add(dst, src)
897
+ end
898
+ }
899
+ }
900
+ addpath.call("", srcdir)
901
+ }
902
+ end
903
+
883
904
  end
884
905
  end
@@ -167,6 +167,7 @@ module MU
167
167
  @need_deploy_flush = false
168
168
  @node_cert_semaphore = Mutex.new
169
169
  @deployment = deployment_data
170
+
170
171
  @deployment['mu_public_ip'] = MU.mu_public_ip
171
172
  @private_key = nil
172
173
  @public_key = nil
@@ -182,6 +183,7 @@ module MU
182
183
  @appname ||= @original_config['name'] if @original_config
183
184
  @timestamp = timestamp
184
185
  @environment = environment
186
+ @original_config['environment'] ||= @environment if @original_config
185
187
 
186
188
  if set_context_to_me
187
189
  MU::MommaCat.setThreadContext(self)
@@ -253,8 +255,7 @@ module MU
253
255
  seen << resource['credentials']
254
256
  else
255
257
  cloudconst = @original_config['cloud'] ? @original_config['cloud'] : MU::Config.defaultCloud
256
- Object.const_get("MU").const_get("Cloud").const_get(cloudconst)
257
- seen << cloudclass.credConfig(name_only: true)
258
+ seen << MU::Cloud.cloudClass(cloudconst).credConfig(name_only: true)
258
259
  end
259
260
  }
260
261
  end
@@ -289,11 +290,10 @@ module MU
289
290
  habitats << hab_ref.id
290
291
  end
291
292
  elsif resource['cloud']
292
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud'])
293
293
  # XXX this should be a general method implemented by each cloud
294
294
  # provider
295
295
  if resource['cloud'] == "Google"
296
- habitats << cloudclass.defaultProject(resource['credentials'])
296
+ habitats << MU::Cloud.cloudClass(resource['cloud']).defaultProject(resource['credentials'])
297
297
  end
298
298
  end
299
299
  }
@@ -317,13 +317,11 @@ module MU
317
317
  if @original_config[type]
318
318
  @original_config[type].each { |resource|
319
319
  if resource['cloud']
320
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud'])
321
- resclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud']).const_get(res_type.to_s)
322
- if resclass.isGlobal?
320
+ if MU::Cloud.resourceClass(resource['cloud'], res_type).isGlobal?
323
321
  # XXX why was I doing this, urgh
324
322
  next
325
323
  elsif !resource['region']
326
- regions << cloudclass.myRegion
324
+ regions << MU::Cloud.cloudClass(resource['cloud']).myRegion(resource['credentials'])
327
325
  end
328
326
  end
329
327
  if resource['region']
@@ -401,7 +399,7 @@ module MU
401
399
  # @param type [String]:
402
400
  # @param name [String]:
403
401
  # @param object [MU::Cloud]:
404
- def addKitten(type, name, object)
402
+ def addKitten(type, name, object, do_notify: false)
405
403
  if !type or !name or !object or !object.mu_name
406
404
  raise MuError, "Nil arguments to addKitten are not allowed (got type: #{type}, name: #{name}, and '#{object}' to add)"
407
405
  end
@@ -409,7 +407,7 @@ module MU
409
407
  _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type)
410
408
  object.intoDeploy(self)
411
409
 
412
- @kitten_semaphore.synchronize {
410
+ add_block = Proc.new {
413
411
  @kittens[type] ||= {}
414
412
  @kittens[type][object.habitat] ||= {}
415
413
  if attrs[:has_multiples]
@@ -418,7 +416,20 @@ module MU
418
416
  else
419
417
  @kittens[type][object.habitat][name] = object
420
418
  end
419
+ if do_notify
420
+ notify(type, name, object.notify, triggering_node: object, delayed_save: true)
421
+ end
421
422
  }
423
+
424
+ begin
425
+ @kitten_semaphore.synchronize {
426
+ add_block.call()
427
+ }
428
+ rescue ThreadError => e
429
+ # already locked by a parent call to this method, so this should be safe
430
+ raise e if !e.message.match(/recursive locking/)
431
+ add_block.call()
432
+ end
422
433
  end
423
434
 
424
435
  # Encrypt a string with the deployment's public key.
@@ -536,16 +547,26 @@ module MU
536
547
  # @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it.
537
548
  # @return [void]
538
549
  def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false)
539
- return if @no_artifacts
550
+ no_write = (@no_artifacts or !caller.grep(/\/mommacat\.rb:\d+:in `notify'/).empty?)
540
551
 
541
552
  begin
542
- MU::MommaCat.lock("deployment-notification")
553
+ if !no_write
554
+ if !MU::MommaCat.lock("deployment-notification", deploy_id: @deploy_id, retries: 10)
555
+ raise MuError, "Failed to get deployment-notifcation lock for #{@deploy_id}"
556
+ end
557
+ end
543
558
 
544
559
  if !@need_deploy_flush or @deployment.nil? or @deployment.empty?
545
560
  loadDeploy(true) # make sure we're saving the latest and greatest
546
561
  end
547
562
 
548
- _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type, false)
563
+ @timestamp ||= @deployment['timestamp']
564
+ @seed ||= @deployment['seed']
565
+ @appname ||= @deployment['appname']
566
+ @handle ||= @deployment['handle']
567
+
568
+ _shortclass, _cfg_name, mu_type, _classname, attrs = MU::Cloud.getResourceNames(type, false)
569
+ type = mu_type if mu_type
549
570
  has_multiples = attrs[:has_multiples] ? true : false
550
571
 
551
572
  mu_name ||= if !data.nil? and !data["mu_name"].nil?
@@ -559,6 +580,7 @@ module MU
559
580
  end
560
581
 
561
582
  @need_deploy_flush = true
583
+ @last_modified = Time.now
562
584
 
563
585
  if !remove
564
586
  if data.nil?
@@ -578,7 +600,9 @@ module MU
578
600
  @deployment[type][key] = data
579
601
  MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data
580
602
  end
581
- save!(key) if !delayed_save
603
+ if !delayed_save and !no_write
604
+ save!(key)
605
+ end
582
606
  else
583
607
  have_deploy = true
584
608
  if @deployment[type].nil? or @deployment[type][key].nil?
@@ -603,10 +627,10 @@ module MU
603
627
  end
604
628
  }
605
629
  end
606
- save! if !delayed_save
630
+ save! if !delayed_save and !no_write
607
631
  end
608
632
  ensure
609
- MU::MommaCat.unlock("deployment-notification")
633
+ MU::MommaCat.unlock("deployment-notification", deploy_id: @deploy_id) if !no_write
610
634
  end
611
635
  end
612
636
 
@@ -614,18 +638,37 @@ module MU
614
638
  # @param subject [String]: The subject line of the message.
615
639
  # @param msg [String]: The message body.
616
640
  # @return [void]
617
- def sendAdminSlack(subject, msg: "")
618
- if $MU_CFG['slack'] and $MU_CFG['slack']['webhook'] and
619
- (!$MU_CFG['slack']['skip_environments'] or !$MU_CFG['slack']['skip_environments'].any?{ |s| s.casecmp(MU.environment)==0 })
641
+ def sendAdminSlack(subject, msg: "", scrub_mu_isms: true, snippets: [], noop: false)
642
+ if MU.muCfg['slack'] and MU.muCfg['slack']['webhook'] and
643
+ (!MU.muCfg['slack']['skip_environments'] or !MU.muCfg['slack']['skip_environments'].any?{ |s| s.casecmp(MU.environment)==0 })
620
644
  require 'slack-notifier'
621
- slack = Slack::Notifier.new $MU_CFG['slack']['webhook']
645
+ slackargs = nil
646
+ keyword_args = { channel: MU.muCfg['slack']['channel'] }
647
+ begin
648
+ slack = Slack::Notifier.new MU.muCfg['slack']['webhook']
649
+ prefix = scrub_mu_isms ? subject : "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}"
650
+
651
+ text = if msg and !msg.empty?
652
+ "#{prefix}:\n\n```#{msg}```"
653
+ else
654
+ prefix
655
+ end
622
656
 
623
- if msg and !msg.empty?
624
- slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}:\n\n```#{msg}\n```", channel: $MU_CFG['slack']['channel']
625
- else
626
- slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}", channel: $MU_CFG['slack']['channel']
657
+ if snippets and snippets.size > 0
658
+ keyword_args[:attachments] = snippets
659
+ end
660
+
661
+ if !noop
662
+ slack.ping(text, **keyword_args)
663
+ else
664
+ MU.log "Would send to #{MU.muCfg['slack']['channel']}", MU::NOTICE, details: [ text, keyword_args ]
665
+ end
666
+ rescue Slack::Notifier::APIError => e
667
+ MU.log "Failed to send message to slack: #{e.message}", MU::ERR, details: keyword_args
668
+ return false
627
669
  end
628
670
  end
671
+ true
629
672
  end
630
673
 
631
674
  # Send an email notification to a deployment's administrators.
@@ -754,7 +797,7 @@ MAIL_HEAD_END
754
797
  end
755
798
 
756
799
  siblings = findLitterMate(type: "server", return_all: true)
757
- return if siblings.nil? or siblings.empty?
800
+ return if siblings.nil? or (siblings.respond_to?(:empty?) and siblings.empty?)
758
801
 
759
802
  update_servers = []
760
803
  siblings.each_pair { |mu_name, node|
@@ -782,6 +825,7 @@ MAIL_HEAD_END
782
825
 
783
826
  threads = []
784
827
  update_servers.each { |sibling|
828
+ next if sibling.config.has_key?("groom") and !sibling.config["groom"]
785
829
  threads << Thread.new {
786
830
  Thread.abort_on_exception = true
787
831
  Thread.current.thread_variable_set("name", "sync-"+sibling.mu_name.downcase)
@@ -838,7 +882,7 @@ MAIL_HEAD_END
838
882
  end
839
883
 
840
884
  if resource and resource.config and resource.config['cloud']
841
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource.config['cloud'])
885
+ cloudclass = MU::Cloud.cloudClass(resource.config['cloud'])
842
886
 
843
887
  cloudclass.writeDeploySecret(@deploy_id, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials'])
844
888
  cloudclass.writeDeploySecret(@deploy_id, key.to_pem, cert_cn+".key", credentials: resource.config['credentials'])
@@ -866,6 +910,7 @@ MAIL_HEAD_END
866
910
  ###########################################################################
867
911
  ###########################################################################
868
912
  def setThreadContextToMe
913
+
869
914
  ["appname", "environment", "timestamp", "seed", "handle"].each { |var|
870
915
  @deployment[var] ||= instance_variable_get("@#{var}".to_sym)
871
916
  if @deployment[var]