cloud-mu 3.1.6 → 3.4.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.
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]