cloud-mu 3.1.5 → 3.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +4 -4
  2. data/Dockerfile +5 -1
  3. data/ansible/roles/mu-windows/files/LaunchConfig.json +9 -0
  4. data/ansible/roles/mu-windows/files/config.xml +76 -0
  5. data/ansible/roles/mu-windows/tasks/main.yml +16 -0
  6. data/bin/mu-adopt +16 -12
  7. data/bin/mu-azure-tests +57 -0
  8. data/bin/mu-cleanup +2 -4
  9. data/bin/mu-configure +52 -0
  10. data/bin/mu-deploy +3 -3
  11. data/bin/mu-findstray-tests +25 -0
  12. data/bin/mu-gen-docs +2 -4
  13. data/bin/mu-load-config.rb +2 -1
  14. data/bin/mu-node-manage +15 -16
  15. data/bin/mu-run-tests +37 -12
  16. data/cloud-mu.gemspec +3 -3
  17. data/cookbooks/mu-activedirectory/resources/domain.rb +4 -4
  18. data/cookbooks/mu-activedirectory/resources/domain_controller.rb +4 -4
  19. data/cookbooks/mu-tools/libraries/helper.rb +1 -1
  20. data/cookbooks/mu-tools/recipes/apply_security.rb +14 -14
  21. data/cookbooks/mu-tools/recipes/aws_api.rb +9 -0
  22. data/cookbooks/mu-tools/recipes/eks.rb +2 -2
  23. data/cookbooks/mu-tools/recipes/windows-client.rb +25 -22
  24. data/extras/clean-stock-amis +25 -19
  25. data/extras/generate-stock-images +1 -0
  26. data/extras/image-generators/AWS/win2k12.yaml +2 -0
  27. data/extras/image-generators/AWS/win2k16.yaml +2 -0
  28. data/extras/image-generators/AWS/win2k19.yaml +2 -0
  29. data/modules/mommacat.ru +1 -1
  30. data/modules/mu.rb +86 -98
  31. data/modules/mu/adoption.rb +373 -58
  32. data/modules/mu/cleanup.rb +214 -303
  33. data/modules/mu/cloud.rb +128 -1733
  34. data/modules/mu/cloud/database.rb +49 -0
  35. data/modules/mu/cloud/dnszone.rb +44 -0
  36. data/modules/mu/cloud/machine_images.rb +212 -0
  37. data/modules/mu/cloud/providers.rb +81 -0
  38. data/modules/mu/cloud/resource_base.rb +929 -0
  39. data/modules/mu/cloud/server.rb +40 -0
  40. data/modules/mu/cloud/server_pool.rb +1 -0
  41. data/modules/mu/cloud/ssh_sessions.rb +228 -0
  42. data/modules/mu/cloud/winrm_sessions.rb +237 -0
  43. data/modules/mu/cloud/wrappers.rb +169 -0
  44. data/modules/mu/config.rb +123 -81
  45. data/modules/mu/config/alarm.rb +2 -6
  46. data/modules/mu/config/bucket.rb +32 -3
  47. data/modules/mu/config/cache_cluster.rb +2 -2
  48. data/modules/mu/config/cdn.rb +100 -0
  49. data/modules/mu/config/collection.rb +1 -1
  50. data/modules/mu/config/container_cluster.rb +7 -2
  51. data/modules/mu/config/database.rb +84 -105
  52. data/modules/mu/config/database.yml +1 -2
  53. data/modules/mu/config/dnszone.rb +5 -4
  54. data/modules/mu/config/doc_helpers.rb +5 -6
  55. data/modules/mu/config/endpoint.rb +2 -1
  56. data/modules/mu/config/firewall_rule.rb +3 -19
  57. data/modules/mu/config/folder.rb +1 -1
  58. data/modules/mu/config/function.rb +17 -8
  59. data/modules/mu/config/group.rb +1 -1
  60. data/modules/mu/config/habitat.rb +1 -1
  61. data/modules/mu/config/job.rb +89 -0
  62. data/modules/mu/config/loadbalancer.rb +57 -11
  63. data/modules/mu/config/log.rb +1 -1
  64. data/modules/mu/config/msg_queue.rb +1 -1
  65. data/modules/mu/config/nosqldb.rb +1 -1
  66. data/modules/mu/config/notifier.rb +8 -19
  67. data/modules/mu/config/ref.rb +92 -14
  68. data/modules/mu/config/role.rb +1 -1
  69. data/modules/mu/config/schema_helpers.rb +38 -37
  70. data/modules/mu/config/search_domain.rb +1 -1
  71. data/modules/mu/config/server.rb +12 -13
  72. data/modules/mu/config/server_pool.rb +3 -7
  73. data/modules/mu/config/storage_pool.rb +1 -1
  74. data/modules/mu/config/tail.rb +11 -0
  75. data/modules/mu/config/user.rb +1 -1
  76. data/modules/mu/config/vpc.rb +27 -23
  77. data/modules/mu/config/vpc.yml +0 -1
  78. data/modules/mu/defaults/AWS.yaml +90 -90
  79. data/modules/mu/defaults/Azure.yaml +1 -0
  80. data/modules/mu/defaults/Google.yaml +1 -0
  81. data/modules/mu/deploy.rb +34 -20
  82. data/modules/mu/groomer.rb +16 -1
  83. data/modules/mu/groomers/ansible.rb +69 -4
  84. data/modules/mu/groomers/chef.rb +51 -4
  85. data/modules/mu/logger.rb +120 -144
  86. data/modules/mu/master.rb +97 -4
  87. data/modules/mu/mommacat.rb +160 -874
  88. data/modules/mu/mommacat/daemon.rb +23 -14
  89. data/modules/mu/mommacat/naming.rb +110 -3
  90. data/modules/mu/mommacat/search.rb +497 -0
  91. data/modules/mu/mommacat/storage.rb +252 -194
  92. data/modules/mu/{clouds → providers}/README.md +1 -1
  93. data/modules/mu/{clouds → providers}/aws.rb +258 -57
  94. data/modules/mu/{clouds → providers}/aws/alarm.rb +3 -3
  95. data/modules/mu/{clouds → providers}/aws/bucket.rb +275 -41
  96. data/modules/mu/{clouds → providers}/aws/cache_cluster.rb +14 -50
  97. data/modules/mu/providers/aws/cdn.rb +782 -0
  98. data/modules/mu/{clouds → providers}/aws/collection.rb +5 -5
  99. data/modules/mu/{clouds → providers}/aws/container_cluster.rb +95 -84
  100. data/modules/mu/providers/aws/database.rb +1744 -0
  101. data/modules/mu/{clouds → providers}/aws/dnszone.rb +26 -12
  102. data/modules/mu/providers/aws/endpoint.rb +1072 -0
  103. data/modules/mu/{clouds → providers}/aws/firewall_rule.rb +39 -32
  104. data/modules/mu/{clouds → providers}/aws/folder.rb +1 -1
  105. data/modules/mu/{clouds → providers}/aws/function.rb +289 -134
  106. data/modules/mu/{clouds → providers}/aws/group.rb +18 -20
  107. data/modules/mu/{clouds → providers}/aws/habitat.rb +3 -3
  108. data/modules/mu/providers/aws/job.rb +466 -0
  109. data/modules/mu/{clouds → providers}/aws/loadbalancer.rb +77 -47
  110. data/modules/mu/{clouds → providers}/aws/log.rb +5 -5
  111. data/modules/mu/{clouds → providers}/aws/msg_queue.rb +14 -11
  112. data/modules/mu/{clouds → providers}/aws/nosqldb.rb +96 -5
  113. data/modules/mu/{clouds → providers}/aws/notifier.rb +135 -63
  114. data/modules/mu/{clouds → providers}/aws/role.rb +76 -48
  115. data/modules/mu/{clouds → providers}/aws/search_domain.rb +172 -41
  116. data/modules/mu/{clouds → providers}/aws/server.rb +66 -98
  117. data/modules/mu/{clouds → providers}/aws/server_pool.rb +42 -60
  118. data/modules/mu/{clouds → providers}/aws/storage_pool.rb +21 -38
  119. data/modules/mu/{clouds → providers}/aws/user.rb +12 -16
  120. data/modules/mu/{clouds → providers}/aws/userdata/README.md +0 -0
  121. data/modules/mu/{clouds → providers}/aws/userdata/linux.erb +5 -4
  122. data/modules/mu/{clouds → providers}/aws/userdata/windows.erb +0 -0
  123. data/modules/mu/{clouds → providers}/aws/vpc.rb +143 -74
  124. data/modules/mu/{clouds → providers}/aws/vpc_subnet.rb +0 -0
  125. data/modules/mu/{clouds → providers}/azure.rb +13 -0
  126. data/modules/mu/{clouds → providers}/azure/container_cluster.rb +1 -5
  127. data/modules/mu/{clouds → providers}/azure/firewall_rule.rb +8 -1
  128. data/modules/mu/{clouds → providers}/azure/habitat.rb +0 -0
  129. data/modules/mu/{clouds → providers}/azure/loadbalancer.rb +0 -0
  130. data/modules/mu/{clouds → providers}/azure/role.rb +0 -0
  131. data/modules/mu/{clouds → providers}/azure/server.rb +32 -24
  132. data/modules/mu/{clouds → providers}/azure/user.rb +1 -1
  133. data/modules/mu/{clouds → providers}/azure/userdata/README.md +0 -0
  134. data/modules/mu/{clouds → providers}/azure/userdata/linux.erb +0 -0
  135. data/modules/mu/{clouds → providers}/azure/userdata/windows.erb +0 -0
  136. data/modules/mu/{clouds → providers}/azure/vpc.rb +4 -6
  137. data/modules/mu/{clouds → providers}/cloudformation.rb +10 -0
  138. data/modules/mu/{clouds → providers}/cloudformation/alarm.rb +3 -3
  139. data/modules/mu/{clouds → providers}/cloudformation/cache_cluster.rb +3 -3
  140. data/modules/mu/{clouds → providers}/cloudformation/collection.rb +3 -3
  141. data/modules/mu/{clouds → providers}/cloudformation/database.rb +6 -17
  142. data/modules/mu/{clouds → providers}/cloudformation/dnszone.rb +3 -3
  143. data/modules/mu/{clouds → providers}/cloudformation/firewall_rule.rb +3 -3
  144. data/modules/mu/{clouds → providers}/cloudformation/loadbalancer.rb +3 -3
  145. data/modules/mu/{clouds → providers}/cloudformation/log.rb +3 -3
  146. data/modules/mu/{clouds → providers}/cloudformation/server.rb +7 -7
  147. data/modules/mu/{clouds → providers}/cloudformation/server_pool.rb +5 -5
  148. data/modules/mu/{clouds → providers}/cloudformation/vpc.rb +3 -3
  149. data/modules/mu/{clouds → providers}/docker.rb +0 -0
  150. data/modules/mu/{clouds → providers}/google.rb +29 -6
  151. data/modules/mu/{clouds → providers}/google/bucket.rb +4 -4
  152. data/modules/mu/{clouds → providers}/google/container_cluster.rb +38 -20
  153. data/modules/mu/{clouds → providers}/google/database.rb +5 -12
  154. data/modules/mu/{clouds → providers}/google/firewall_rule.rb +5 -5
  155. data/modules/mu/{clouds → providers}/google/folder.rb +5 -9
  156. data/modules/mu/{clouds → providers}/google/function.rb +6 -6
  157. data/modules/mu/{clouds → providers}/google/group.rb +9 -17
  158. data/modules/mu/{clouds → providers}/google/habitat.rb +4 -8
  159. data/modules/mu/{clouds → providers}/google/loadbalancer.rb +5 -5
  160. data/modules/mu/{clouds → providers}/google/role.rb +50 -31
  161. data/modules/mu/{clouds → providers}/google/server.rb +41 -24
  162. data/modules/mu/{clouds → providers}/google/server_pool.rb +14 -14
  163. data/modules/mu/{clouds → providers}/google/user.rb +34 -24
  164. data/modules/mu/{clouds → providers}/google/userdata/README.md +0 -0
  165. data/modules/mu/{clouds → providers}/google/userdata/linux.erb +0 -0
  166. data/modules/mu/{clouds → providers}/google/userdata/windows.erb +0 -0
  167. data/modules/mu/{clouds → providers}/google/vpc.rb +45 -14
  168. data/modules/tests/aws-jobs-functions.yaml +46 -0
  169. data/modules/tests/centos6.yaml +15 -0
  170. data/modules/tests/centos7.yaml +15 -0
  171. data/modules/tests/centos8.yaml +12 -0
  172. data/modules/tests/ecs.yaml +2 -2
  173. data/modules/tests/eks.yaml +1 -1
  174. data/modules/tests/functions/node-function/lambda_function.js +10 -0
  175. data/modules/tests/functions/python-function/lambda_function.py +12 -0
  176. data/modules/tests/microservice_app.yaml +288 -0
  177. data/modules/tests/rds.yaml +108 -0
  178. data/modules/tests/regrooms/rds.yaml +123 -0
  179. data/modules/tests/server-with-scrub-muisms.yaml +1 -1
  180. data/modules/tests/super_complex_bok.yml +2 -2
  181. data/modules/tests/super_simple_bok.yml +3 -5
  182. data/spec/mu/clouds/azure_spec.rb +2 -2
  183. metadata +122 -92
  184. data/modules/mu/clouds/aws/database.rb +0 -1974
  185. data/modules/mu/clouds/aws/endpoint.rb +0 -596
@@ -91,6 +91,7 @@ $opts[:clouds].each { |cloud|
91
91
  end
92
92
  next if !needed
93
93
  end
94
+ MU.log "Loading "+bok_dir+"/"+cloud+"/"+platform+".yaml"
94
95
  conf_engine = MU::Config.new(
95
96
  bok_dir+"/"+cloud+"/"+platform+".yaml",
96
97
  default_credentials: $opts[(cloud.downcase+"_creds").to_sym]
@@ -12,6 +12,8 @@
12
12
  groomer: Ansible
13
13
  run_list:
14
14
  - mu-windows
15
+ ansible_vars:
16
+ mu_build_image: true
15
17
  create_image:
16
18
  image_then_destroy: true
17
19
  public: true
@@ -12,6 +12,8 @@
12
12
  groomer: Ansible
13
13
  run_list:
14
14
  - mu-windows
15
+ ansible_vars:
16
+ mu_build_image: true
15
17
  create_image:
16
18
  image_then_destroy: true
17
19
  public: true
@@ -12,6 +12,8 @@
12
12
  groomer: Ansible
13
13
  run_list:
14
14
  - mu-windows
15
+ ansible_vars:
16
+ mu_build_image: true
15
17
  create_image:
16
18
  image_then_destroy: true
17
19
  public: true
@@ -51,7 +51,7 @@ Signal.trap("URG") do
51
51
  end
52
52
 
53
53
  begin
54
- MU::MommaCat.syncMonitoringConfig(false)
54
+ MU::Master.syncMonitoringConfig(false)
55
55
  rescue StandardError => e
56
56
  MU.log e.inspect, MU::ERR, details: e.backtrace
57
57
  # ...but don't die!
@@ -79,38 +79,40 @@ class Hash
79
79
  }
80
80
  return 0 if self == other # that was easy!
81
81
  # compare elements and decide who's "bigger" based on their totals?
82
- 0
82
+
83
+ # fine, try some brute force and just hope everything implements to_s
84
+ self.flatten.map { |e| e.to_s }.join() <=> other.flatten.map { |e| e.to_s }.join()
83
85
  end
84
86
 
85
- # Recursively compare two hashes
86
- def diff(with, on = self, level: 0, parents: [])
87
+ # Recursively compare two Mu Basket of Kittens hashes and report the differences
88
+ def diff(with, on = self, level: 0, parents: [], report: {}, habitat: nil)
87
89
  return if with.nil? and on.nil?
88
90
  if with.nil? or on.nil? or with.class != on.class
89
91
  return # XXX ...however we're flagging differences
90
92
  end
91
93
  return if on == with
92
94
 
93
- tree = ""
94
- indentsize = 0
95
- parents.each { |p|
96
- tree += (" " * indentsize) + p + " => \n"
97
- indentsize += 2
98
- }
99
- indent = (" " * indentsize)
100
-
101
95
  changes = []
96
+ report ||= {}
102
97
  if on.is_a?(Hash)
103
98
  on_unique = (on.keys - with.keys)
104
99
  with_unique = (with.keys - on.keys)
105
100
  shared = (with.keys & on.keys)
106
101
  shared.each { |k|
107
- diff(with[k], on[k], level: level+1, parents: parents + [k])
102
+
103
+ report_data = diff(with[k], on[k], level: level+1, parents: parents + [k], report: report[k], habitat: habitat)
104
+ if report_data and !report_data.empty?
105
+ report ||= {}
106
+ report[k] = report_data
107
+ end
108
108
  }
109
109
  on_unique.each { |k|
110
- changes << "- ".red+PP.pp({k => on[k] }, '')
110
+ report[k] = { :action => :removed, :parents => parents, :value => on[k].clone }
111
+ report[k][:habitat] = habitat if habitat
111
112
  }
112
113
  with_unique.each { |k|
113
- changes << "+ ".green+PP.pp({k => with[k]}, '')
114
+ report[k] = { :action => :added, :parents => parents, :value => with[k].clone }
115
+ report[k][:habitat] = habitat if habitat
114
116
  }
115
117
  elsif on.is_a?(Array)
116
118
  return if with == on
@@ -122,29 +124,27 @@ class Hash
122
124
  # sorting arrays full of weird, non-primitive types.
123
125
  done = []
124
126
  on.sort.each { |elt|
125
- if elt.is_a?(Hash) and elt['name'] or elt['entity']# or elt['cloud_id']
126
- with.sort.each { |other_elt|
127
- # Figure out what convention this thing is using for resource identification
128
- compare_a, compare_b = if elt['name'].nil? and elt["id"].nil? and !elt["entity"].nil? and !other_elt["entity"].nil?
129
- [elt["entity"], other_elt["entity"]]
130
- else
131
- [elt, other_elt]
132
- end
127
+ if elt.is_a?(Hash) and !MU::MommaCat.getChunkName(elt).first.nil?
128
+ elt_namestr, elt_location, elt_location_list = MU::MommaCat.getChunkName(elt)
133
129
 
134
- if (compare_a['name'] and compare_b['name'] == compare_a['name']) or
135
- (compare_a['name'].nil? and !compare_a["id"].nil? and compare_a["id"] == compare_b["id"])
136
- break if elt == other_elt
130
+ with.sort.each { |other_elt|
131
+ other_elt_namestr, other_elt_location, other_elt_location_list = MU::MommaCat.getChunkName(other_elt)
132
+
133
+ # Case 1: The array element exists in both version of this array
134
+ if elt_namestr and other_elt_namestr and
135
+ elt_namestr == other_elt_namestr and
136
+ (elt_location.nil? or other_elt_location.nil? or
137
+ elt_location == other_elt_location or
138
+ !(elt_location_list & other_elt_location_list).empty?
139
+ )
137
140
  done << elt
138
141
  done << other_elt
139
- namestr = if elt['type']
140
- "#{elt['type']}[#{elt['name']}]"
141
- elsif elt['name']
142
- elt['name']
143
- elsif elt['entity'] and elt["entity"]["id"]
144
- elt['entity']['id']
142
+ break if elt == other_elt # if they're identical, we're done
143
+ report_data = diff(other_elt, elt, level: level+1, parents: parents + [elt_namestr], habitat: (elt_location || habitat))
144
+ if report_data and !report_data.empty?
145
+ report ||= {}
146
+ report[elt_namestr] = report_data
145
147
  end
146
-
147
- diff(other_elt, elt, level: level+1, parents: parents + [namestr])
148
148
  break
149
149
  end
150
150
  }
@@ -152,43 +152,34 @@ class Hash
152
152
  }
153
153
  on_unique = (on - with) - done
154
154
  with_unique = (with - on) - done
155
- # if on_unique.size > 0 or with_unique.size > 0
156
- # if before_a != after_a
157
- # MU.log "A BEFORE", MU::NOTICE, details: before_a
158
- # MU.log "A AFTER", MU::NOTICE, details: after_a
159
- # end
160
- # if before_b != after_b
161
- # MU.log "B BEFORE", MU::NOTICE, details: before_b
162
- # MU.log "B AFTER", MU::NOTICE, details: after_b
163
- # end
164
- # end
155
+
156
+ # Case 2: This array entry exists in the old version, but not the new one
165
157
  on_unique.each { |e|
166
- changes << if e.is_a?(Hash)
167
- "- ".red+PP.pp(Hash.bok_minimize(e), '').gsub(/\n/, "\n "+(indent))
168
- else
169
- "- ".red+e.to_s
170
- end
158
+ namestr, loc = MU::MommaCat.getChunkName(e)
159
+
160
+ report ||= {}
161
+ report[namestr] = { :action => :removed, :parents => parents, :value => e.clone }
162
+ report[namestr][:habitat] = loc if loc
171
163
  }
164
+
165
+ # Case 3: This array entry exists in the new version, but not the old one
172
166
  with_unique.each { |e|
173
- changes << if e.is_a?(Hash)
174
- "+ ".green+PP.pp(Hash.bok_minimize(e), '').gsub(/\n/, "\n "+(indent))
175
- else
176
- "+ ".green+e.to_s
177
- end
167
+ namestr, loc = MU::MommaCat.getChunkName(e)
168
+
169
+ report ||= {}
170
+ report[namestr] = { :action => :added, :parents => parents, :value => e.clone }
171
+ report[namestr][:habitat] = loc if loc
178
172
  }
173
+
174
+ # A plain old leaf node of data
179
175
  else
180
176
  if on != with
181
- changes << "-".red+" #{on.to_s}"
182
- changes << "+".green+" #{with.to_s}"
177
+ report = { :action => :changed, :parents => parents, :oldvalue => on, :value => with.clone }
178
+ report[:habitat] = habitat if habitat
183
179
  end
184
180
  end
185
181
 
186
- if changes.size > 0
187
- puts tree
188
- changes.each { |c|
189
- puts indent+c
190
- }
191
- end
182
+ report.freeze
192
183
  end
193
184
 
194
185
  # Implement a merge! that just updates each hash leaf as needed, not
@@ -212,8 +203,29 @@ class Hash
212
203
  end
213
204
 
214
205
  ENV['HOME'] = Etc.getpwuid(Process.uid).dir
206
+ module MU
207
+
208
+ # For log entries that should only be logged when we're in verbose mode
209
+ DEBUG = 0.freeze
210
+ # For ordinary log entries
211
+ INFO = 1.freeze
212
+ # For more interesting log entries which are not errors
213
+ NOTICE = 2.freeze
214
+ # Log entries for non-fatal errors
215
+ WARN = 3.freeze
216
+ # Log entries for non-fatal errors
217
+ WARNING = 3.freeze
218
+ # Log entries for fatal errors
219
+ ERR = 4.freeze
220
+ # Log entries for fatal errors
221
+ ERROR = 4.freeze
222
+ # Log entries that will be held and displayed/emailed at the end of deploy,
223
+ # cleanup, etc.
224
+ SUMMARY = 5.freeze
225
+ end
215
226
 
216
227
  require 'mu/logger'
228
+
217
229
  module MU
218
230
 
219
231
  # Subclass core thread so we can gracefully handle it when we hit system
@@ -273,8 +285,9 @@ module MU
273
285
  # Wrapper class for fatal Exceptions. Gives our internals something to
274
286
  # inherit that will log an error message appropriately before bubbling up.
275
287
  class MuError < StandardError
276
- def initialize(message = nil)
277
- MU.log message, MU::ERR, details: caller[2] if !message.nil?
288
+ def initialize(message = nil, silent: false, details: nil)
289
+ details ||= caller[2]
290
+ MU.log message, MU::ERR, details: details if !message.nil? and !silent
278
291
  if MU.verbosity == MU::Logger::SILENT
279
292
  super ""
280
293
  else
@@ -286,8 +299,8 @@ module MU
286
299
  # Wrapper class for temporary Exceptions. Gives our internals something to
287
300
  # inherit that will log a notice message appropriately before bubbling up.
288
301
  class MuNonFatal < StandardError
289
- def initialize(message = nil)
290
- MU.log message, MU::NOTICE if !message.nil?
302
+ def initialize(message = nil, silent: false, details: nil)
303
+ MU.log message, MU::NOTICE, details: details if !message.nil? and !silent
291
304
  if MU.verbosity == MU::Logger::SILENT
292
305
  super ""
293
306
  else
@@ -598,9 +611,10 @@ module MU
598
611
  end
599
612
 
600
613
  # Shortcut to invoke {MU::Logger#log}
601
- def self.log(msg, level = MU::INFO, details: nil, html: false, verbosity: nil, color: true)
614
+ def self.log(msg, level = MU::INFO, shorthand_details = nil, details: nil, html: false, verbosity: nil, color: true)
602
615
  return if (level == MU::DEBUG and verbosity and verbosity <= MU::Logger::LOUD)
603
616
  return if verbosity and verbosity == MU::Logger::SILENT
617
+ details ||= shorthand_details
604
618
 
605
619
  if (level == MU::ERR or
606
620
  level == MU::WARN or
@@ -619,25 +633,6 @@ module MU
619
633
  @@logger.log(msg, level, details: details, html: html, verbosity: verbosity, color: color)
620
634
  end
621
635
 
622
- # For log entries that should only be logged when we're in verbose mode
623
- DEBUG = 0.freeze
624
- # For ordinary log entries
625
- INFO = 1.freeze
626
- # For more interesting log entries which are not errors
627
- NOTICE = 2.freeze
628
- # Log entries for non-fatal errors
629
- WARN = 3.freeze
630
- # Log entries for non-fatal errors
631
- WARNING = 3.freeze
632
- # Log entries for fatal errors
633
- ERR = 4.freeze
634
- # Log entries for fatal errors
635
- ERROR = 4.freeze
636
- # Log entries that will be held and displayed/emailed at the end of deploy,
637
- # cleanup, etc.
638
- SUMMARY = 5.freeze
639
-
640
-
641
636
  autoload :Cleanup, 'mu/cleanup'
642
637
  autoload :Deploy, 'mu/deploy'
643
638
  autoload :MommaCat, 'mu/mommacat'
@@ -651,7 +646,7 @@ module MU
651
646
  new_cfg = $MU_CFG.dup
652
647
  examples = {}
653
648
  MU::Cloud.supportedClouds.each { |cloud|
654
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
649
+ cloudclass = MU::Cloud.cloudClass(cloud)
655
650
  begin
656
651
  if cloudclass.hosted? and !$MU_CFG[cloud.downcase]
657
652
  cfg_blob = cloudclass.hosted_config
@@ -807,11 +802,7 @@ module MU
807
802
  # @param groomer [String]: The grooming agent to load.
808
803
  # @return [Class]: The class object implementing this groomer agent
809
804
  def self.loadGroomer(groomer)
810
- if !File.size?(MU.myRoot+"/modules/mu/groomers/#{groomer.downcase}.rb")
811
- raise MuError, "Requested to use unsupported grooming agent #{groomer}"
812
- end
813
- require "mu/groomers/#{groomer.downcase}"
814
- return Object.const_get("MU").const_get("Groomer").const_get(groomer)
805
+ MU::Groomer.loadGroomer(groomer)
815
806
  end
816
807
 
817
808
  @@myRegion_var = nil
@@ -965,8 +956,7 @@ module MU
965
956
 
966
957
  @@myCloudDescriptor = nil
967
958
  if MU.myCloud
968
- svrclass = const_get("MU").const_get("Cloud").const_get(MU.myCloud).const_get("Server")
969
- found = svrclass.find(cloud_id: @@myInstanceId, region: MU.myRegion) # XXX need habitat arg for google et al
959
+ found = MU::Cloud.resourceClass(MU.myCloud, "Server").find(cloud_id: @@myInstanceId, region: MU.myRegion) # XXX need habitat arg for google et al
970
960
  # found = MU::MommaCat.findStray(MU.myCloud, "server", cloud_id: @@myInstanceId, dummy_ok: true, region: MU.myRegion)
971
961
  if !found.nil? and found.size == 1
972
962
  @@myCloudDescriptor = found.values.first
@@ -979,8 +969,7 @@ module MU
979
969
  def self.myVPCObj
980
970
  return nil if MU.myCloud.nil?
981
971
  return @@myVPCObj_var if @@myVPCObj_var
982
- cloudclass = const_get("MU").const_get("Cloud").const_get(MU.myCloud)
983
- @@myVPCObj_var ||= cloudclass.myVPCObj
972
+ @@myVPCObj_var ||= MU::Cloud.cloudClass(MU.myCloud).myVPCObj
984
973
  @@myVPCObj_var
985
974
  end
986
975
 
@@ -1105,10 +1094,9 @@ module MU
1105
1094
 
1106
1095
  clouds = platform.nil? ? MU::Cloud.supportedClouds : [platform]
1107
1096
  clouds.each { |cloud|
1108
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
1109
- bucketname = cloudclass.adminBucketName(credentials)
1097
+ bucketname = MU::Cloud.cloudClass(cloud).adminBucketName(credentials)
1110
1098
  begin
1111
- if platform or (cloudclass.hosted? and platform.nil?) or cloud == MU::Config.defaultCloud
1099
+ if platform or (MU::Cloud.cloudClass(cloud).hosted? and platform.nil?) or cloud == MU::Config.defaultCloud
1112
1100
  return bucketname
1113
1101
  end
1114
1102
  end
@@ -30,7 +30,7 @@ module MU
30
30
  :omnibus => "Jam everything into one monolothic configuration"
31
31
  }
32
32
 
33
- def initialize(clouds: MU::Cloud.supportedClouds, types: MU::Cloud.resource_types.keys, parent: nil, billing: nil, sources: nil, credentials: nil, group_by: :logical, savedeploys: false, diff: false, habitats: [])
33
+ def initialize(clouds: MU::Cloud.supportedClouds, types: MU::Cloud.resource_types.keys, parent: nil, billing: nil, sources: nil, credentials: nil, group_by: :logical, savedeploys: false, diff: false, habitats: [], scrub_mu_isms: false, regions: [], merge: false, pattern: nil)
34
34
  @scraped = {}
35
35
  @clouds = clouds
36
36
  @types = types
@@ -44,7 +44,11 @@ module MU
44
44
  @savedeploys = savedeploys
45
45
  @diff = diff
46
46
  @habitats = habitats
47
+ @regions = regions
47
48
  @habitats ||= []
49
+ @scrub_mu_isms = scrub_mu_isms
50
+ @merge = merge
51
+ @pattern = pattern
48
52
  end
49
53
 
50
54
  # Walk cloud providers with available credentials to discover resources
@@ -52,7 +56,7 @@ module MU
52
56
  @default_parent = nil
53
57
 
54
58
  @clouds.each { |cloud|
55
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
59
+ cloudclass = MU::Cloud.cloudClass(cloud)
56
60
  next if cloudclass.listCredentials.nil?
57
61
 
58
62
  if cloud == "Google" and !@parent and @target_creds
@@ -64,6 +68,10 @@ module MU
64
68
 
65
69
  cloudclass.listCredentials.each { |credset|
66
70
  next if @sources and !@sources.include?(credset)
71
+ cfg = cloudclass.credConfig(credset)
72
+ if cfg and cfg['restrict_to_habitats']
73
+ cfg['restrict_to_habitats'] << cfg['project'] if cfg['project']
74
+ end
67
75
 
68
76
  if @parent
69
77
  # TODO handle different inputs (cloud_id, etc)
@@ -84,7 +92,7 @@ module MU
84
92
 
85
93
  @types.each { |type|
86
94
  begin
87
- resclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(type)
95
+ resclass = MU::Cloud.resourceClass(cloud, type)
88
96
  rescue ::MU::Cloud::MuCloudResourceNotImplemented
89
97
  next
90
98
  end
@@ -100,17 +108,31 @@ module MU
100
108
  credentials: credset,
101
109
  allow_multi: true,
102
110
  habitats: @habitats.dup,
111
+ region: @regions,
103
112
  dummy_ok: true,
104
- debug: false,
105
- flags: { "skip_provider_owned" => true }
113
+ skip_provider_owned: true,
114
+ # debug: false#,
106
115
  )
107
116
 
108
117
 
109
118
  if found and found.size > 0
119
+ if resclass.cfg_plural == "habitats"
120
+ found.reject! { |h|
121
+ !cloudclass.listHabitats(credset).include?(h.cloud_id)
122
+ }
123
+ end
110
124
  MU.log "Found #{found.size.to_s} raw #{resclass.cfg_plural} in #{cloud}"
111
125
  @scraped[type] ||= {}
112
126
  found.each { |obj|
127
+ if obj.habitat and !cloudclass.listHabitats(credset).include?(obj.habitat)
128
+ next
129
+ end
130
+
113
131
  # XXX apply any filters (e.g. MU-ID tags)
132
+ if obj.cloud_id.nil?
133
+ MU.log "This damn thing gave me no cloud id, what do I even do with that", MU::ERR, details: obj
134
+ exit
135
+ end
114
136
  @scraped[type][obj.cloud_id] = obj
115
137
  }
116
138
  end
@@ -188,33 +210,62 @@ module MU
188
210
  prefix = "mu" if prefix.empty? # so that appnames aren't ever empty
189
211
  end
190
212
 
213
+ # Find any previous deploys with this particular profile, which we'll use
214
+ # later for --diff.
215
+ @existing_deploys = {}
216
+ @existing_deploys_by_id = {}
217
+ @origins = {}
218
+ @types_found_in = {}
191
219
  groupings.each_pair { |appname, types|
192
- bok = { "appname" => prefix+appname }
193
- if @target_creds
194
- bok["credentials"] = @target_creds
195
- end
196
-
197
- count = 0
198
220
  allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
199
221
  next if (types & allowed_types).size == 0
200
222
  origin = {
201
- "appname" => bok['appname'],
223
+ "appname" => prefix+appname,
202
224
  "types" => (types & allowed_types).sort,
203
225
  "habitats" => @habitats.sort,
204
226
  "group_by" => @group_by.to_s
205
227
  }
206
228
 
207
- deploy = MU::MommaCat.findMatchingDeploy(origin)
208
- if @diff and !deploy
209
- MU.log "--diff was set but I failed to find a deploy like me to compare to", MU::ERR, details: origin
210
- exit 1
229
+ @existing_deploys[appname] = MU::MommaCat.findMatchingDeploy(origin)
230
+ if @existing_deploys[appname]
231
+ @existing_deploys_by_id[@existing_deploys[appname].deploy_id] = @existing_deploys[appname]
232
+ @origins[appname] = origin
233
+ origin['types'].each { |t|
234
+ @types_found_in[t] = @existing_deploys[appname]
235
+ }
236
+ end
237
+ }
238
+
239
+ groupings.each_pair { |appname, types|
240
+ allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
241
+ next if (types & allowed_types).size == 0
242
+
243
+ bok = { "appname" => prefix+appname }
244
+ if @scrub_mu_isms
245
+ bok["scrub_mu_isms"] = true
246
+ end
247
+ if @target_creds
248
+ bok["credentials"] = @target_creds
249
+ end
250
+
251
+ count = 0
252
+ if @diff
253
+ if !@existing_deploys[appname]
254
+ MU.log "--diff was set but I failed to find a deploy like '#{appname}' to compare to (have #{@existing_deploys.keys.join(", ")})", MU::ERR, details: @origins[appname]
255
+ exit 1
256
+ else
257
+ MU.log "Will diff current live resources against #{@existing_deploys[appname].deploy_id}", MU::NOTICE, details: @origins[appname]
258
+ end
211
259
  end
212
260
 
213
261
  threads = []
262
+ timers = {}
263
+ walltimers = {}
214
264
  @clouds.each { |cloud|
215
265
  @scraped.each_pair { |type, resources|
266
+ typestart = Time.now
216
267
  res_class = begin
217
- MU::Cloud.loadCloudType(cloud, type)
268
+ MU::Cloud.resourceClass(cloud, type)
218
269
  rescue MU::Cloud::MuCloudResourceNotImplemented
219
270
  # XXX I don't think this can actually happen
220
271
  next
@@ -222,6 +273,7 @@ module MU
222
273
  next if !types.include?(res_class.cfg_plural)
223
274
 
224
275
  bok[res_class.cfg_plural] ||= []
276
+ timers[type] ||= {}
225
277
 
226
278
  class_semaphore = Mutex.new
227
279
 
@@ -238,13 +290,18 @@ module MU
238
290
  end
239
291
  end
240
292
  threads << Thread.new(obj_thr) { |obj|
293
+ start = Time.now
241
294
 
242
- kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats)
243
- if kitten_cfg
295
+ kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats, types: @types)
296
+ if kitten_cfg and (!@pattern or @pattern.match(kitten_cfg['name']))
244
297
  print "."
245
298
  kitten_cfg.delete("credentials") if @target_creds
246
299
  class_semaphore.synchronize {
247
300
  bok[res_class.cfg_plural] << kitten_cfg
301
+ if !kitten_cfg['cloud_id']
302
+ MU.log "No cloud id in this #{res_class.cfg_name} kitten!", MU::ERR, details: kitten_cfg
303
+ end
304
+ timers[type][kitten_cfg['cloud_id']] = (Time.now - start)
248
305
  }
249
306
  count += 1
250
307
  end
@@ -255,6 +312,7 @@ module MU
255
312
  threads.each { |t|
256
313
  t.join
257
314
  }
315
+
258
316
  puts ""
259
317
  bok[res_class.cfg_plural].sort! { |a, b|
260
318
  strs = [a, b].map { |x|
@@ -276,24 +334,30 @@ module MU
276
334
  bok[res_class.cfg_plural].each { |sibling|
277
335
  next if kitten_cfg == sibling
278
336
  if sibling['name'] == kitten_cfg['name']
279
- MU.log "#{res_class.cfg_name} name #{sibling['name']} unavailable, will attempt to rename duplicate object", MU::DEBUG, details: kitten_cfg
280
- if kitten_cfg['parent'] and kitten_cfg['parent'].respond_to?(:id) and kitten_cfg['parent'].id
281
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['parent'].id
282
- elsif kitten_cfg['project']
283
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['project']
284
- elsif kitten_cfg['region']
285
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['region']
286
- elsif kitten_cfg['cloud_id']
287
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['cloud_id'].gsub(/[^a-z0-9]/i, "-")
288
- else
289
- raise MU::Config::DuplicateNameError, "Saw duplicate #{res_class.cfg_name} name #{sibling['name']} and couldn't come up with a good way to differentiate them"
290
- end
337
+ MU::Adoption.deDuplicateName(kitten_cfg, res_class)
291
338
  MU.log "De-duplication: Renamed #{res_class.cfg_name} name '#{sibling['name']}' => '#{kitten_cfg['name']}'", MU::NOTICE
292
339
  break
293
340
  end
294
341
  }
295
342
  }
343
+ walltimers[type] ||= 0
344
+ walltimers[type] += (Time.now - typestart)
345
+ }
346
+ }
347
+
348
+ timers.each_pair { |type, resources|
349
+ next if resources.empty?
350
+ total = resources.values.sum
351
+ top_5 = resources.keys.sort { |a, b|
352
+ resources[b] <=> resources[a]
353
+ }.slice(0, 5).map { |k|
354
+ k.to_s+": "+sprintf("%.2fs", resources[k])
296
355
  }
356
+ if walltimers[type] < 45
357
+ MU.log "Kittened #{resources.size.to_s} eligible #{type}s in #{sprintf("%.2fs", walltimers[type])}"
358
+ else
359
+ MU.log "Kittened #{resources.size.to_s} eligible #{type}s in #{sprintf("%.2fs", walltimers[type])} (CPU time #{sprintf("%.2fs", total)}, avg #{sprintf("%.2fs", total/resources.size)}). Top 5:", MU::NOTICE, details: top_5
360
+ end
297
361
  }
298
362
 
299
363
  # No matching resources isn't necessarily an error
@@ -302,23 +366,36 @@ module MU
302
366
  # Now walk through all of the Refs in these objects, resolve them, and minimize
303
367
  # their config footprint
304
368
  MU.log "Minimizing footprint of #{count.to_s} found resources", MU::DEBUG
305
- @boks[bok['appname']] = vacuum(bok, origin: origin, save: @savedeploys)
306
369
 
307
- if @diff and !deploy
370
+ generated_deploy = generateStubDeploy(bok)
371
+ @boks[bok['appname']] = vacuum(bok, origin: @origins[appname], deploy: generated_deploy, save: @savedeploys)
372
+
373
+ if @diff and !@existing_deploys[appname]
308
374
  MU.log "diff flag set, but no comparable deploy provided for #{bok['appname']}", MU::ERR
309
375
  exit 1
310
376
  end
311
377
 
312
- if deploy and @diff
313
- prevcfg = MU::Config.manxify(vacuum(deploy.original_config, deploy: deploy))
378
+ if @diff
379
+ prev_vacuumed = vacuum(@existing_deploys[appname].original_config, deploy: @existing_deploys[appname], keep_missing: true, copy_from: generated_deploy)
380
+ prevcfg = MU::Config.manxify(prev_vacuumed)
314
381
  if !prevcfg
315
- MU.log "#{deploy.deploy_id} didn't have a working original config for me to compare", MU::ERR
382
+ MU.log "#{@existing_deploys[appname].deploy_id} didn't have a working original config for me to compare", MU::ERR
316
383
  exit 1
317
384
  end
318
385
  newcfg = MU::Config.manxify(@boks[bok['appname']])
386
+ report = prevcfg.diff(newcfg)
387
+
388
+ if report
389
+
390
+ if MU.muCfg['adopt_change_notify']
391
+ notifyChanges(@existing_deploys[appname], report.freeze)
392
+ end
393
+ if @merge
394
+ MU.log "Saving changes to #{@existing_deploys[appname].deploy_id}"
395
+ @existing_deploys[appname].updateBasketofKittens(newcfg, save_now: true)
396
+ end
397
+ end
319
398
 
320
- prevcfg.diff(newcfg)
321
- exit
322
399
  end
323
400
  }
324
401
  @boks
@@ -326,6 +403,183 @@ module MU
326
403
 
327
404
  private
328
405
 
406
+ # @param tier [Hash]
407
+ # @param parent_key [String]
408
+ def crawlChangeReport(tier, parent_key = nil, indent: "")
409
+ report = []
410
+ if tier.is_a?(Array)
411
+ tier.each { |a|
412
+ sub_report = crawlChangeReport(a, parent_key)
413
+ report.concat(sub_report) if sub_report and !sub_report.empty?
414
+ }
415
+ elsif tier.is_a?(Hash)
416
+ if tier[:action]
417
+ preposition = if tier[:action] == :added
418
+ "to"
419
+ elsif tier[:action] == :removed
420
+ "from"
421
+ else
422
+ "in"
423
+ end
424
+
425
+ name = ""
426
+ type_of = parent_key.sub(/s$|\[.*/, '') if parent_key
427
+ loc = tier[:habitat]
428
+
429
+ if tier[:value] and tier[:value].is_a?(Hash)
430
+ name, loc = MU::MommaCat.getChunkName(tier[:value], type_of)
431
+ elsif parent_key
432
+ name = parent_key
433
+ end
434
+
435
+ path_str = []
436
+ slack_path_str = ""
437
+ if tier[:parents] and tier[:parents].size > 2
438
+ path = tier[:parents].clone
439
+ slack_path_str += "#{preposition} \*"+path.join(" ⇨ ")+"\*" if path.size > 0
440
+ path.shift
441
+ path.shift
442
+ path.pop if path.last == name
443
+ for c in (0..(path.size-1)) do
444
+ path_str << (" " * (c+2)) + (path[c] || "<nil>")
445
+ end
446
+ end
447
+ path_str << "" if !path_str.empty?
448
+
449
+ plain = (name ? name : type_of) if name or type_of
450
+ plain ||= "" # XXX but this is a problem
451
+ slack = "`"+plain+"`"
452
+
453
+ plain += " ("+loc+")" if loc and !loc.empty?
454
+ color = plain
455
+
456
+ if tier[:action] == :added
457
+ color = "+ ".green + plain
458
+ plain = "+ " + plain
459
+ slack += " added"
460
+ elsif tier[:action] == :removed
461
+ color = "- ".red + plain
462
+ plain = "- " + plain
463
+ slack += " removed"
464
+ end
465
+
466
+ slack += " #{tier[:action]} #{preposition} \*#{loc}\*" if loc and !loc.empty? and [Array, Hash].include?(tier[:value].class)
467
+
468
+ plain = path_str.join(" => \n") + indent + plain
469
+ color = path_str.join(" => \n") + indent + color
470
+
471
+ slack += " "+slack_path_str if !slack_path_str.empty?
472
+ myreport = {
473
+ "slack" => slack,
474
+ "plain" => plain,
475
+ "color" => color
476
+ }
477
+
478
+ append = ""
479
+ if tier[:value] and (tier[:value].is_a?(Array) or tier[:value].is_a?(Hash))
480
+ if tier[:value].is_a?(Hash)
481
+ if name
482
+ tier[:value].delete("entity")
483
+ tier[:value].delete(name.sub(/\[.*/, '')) if name
484
+ end
485
+ if (tier[:value].keys - ["id", "name", "type"]).size > 0
486
+ myreport["details"] = tier[:value].clone
487
+ append = PP.pp(tier[:value], '').gsub(/(^|\n)/, '\1'+indent)
488
+ end
489
+ else
490
+ append = indent+"["+tier[:value].map { |v| MU::MommaCat.getChunkName(v, type_of).reverse.join("/") || v.to_s.light_blue }.join(", ")+"]"
491
+ slack += " #{tier[:action].to_s}: "+tier[:value].map { |v| MU::MommaCat.getChunkName(v, type_of).reverse.join("/") || v.to_s }.join(", ")
492
+ end
493
+ else
494
+ tier[:value] ||= "<nil>"
495
+ if ![:removed].include?(tier[:action])
496
+ myreport["slack"] += ". New #{tier[:field] ? "`"+tier[:field]+"`" : :value}: \*#{tier[:value]}\*"
497
+ else
498
+ myreport["slack"] += " (was \*#{tier[:value]}\*)"
499
+ end
500
+ append = tier[:value].to_s.bold
501
+ end
502
+
503
+ if append and !append.empty?
504
+ myreport["plain"] += " =>\n "+indent+append
505
+ myreport["color"] += " =>\n "+indent+append
506
+ end
507
+
508
+ report << myreport if tier[:action]
509
+ end
510
+
511
+ # Just because we've got changes at this level doesn't mean there aren't
512
+ # more further down.
513
+ tier.each_pair { |k, v|
514
+ next if !(v.is_a?(Hash) or v.is_a?(Array))
515
+ sub_report = crawlChangeReport(v, k, indent: indent+" ")
516
+ report.concat(sub_report) if sub_report and !sub_report.empty?
517
+ }
518
+ end
519
+
520
+ report
521
+ end
522
+
523
+
524
+ def notifyChanges(deploy, report)
525
+ snippet_threshold = (MU.muCfg['adopt_change_notify'] && MU.muCfg['adopt_change_notify']['slack_snippet_threshold']) || 5
526
+
527
+ report.each_pair { |res_type, resources|
528
+ shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(res_type, false)
529
+ next if !shortclass # we don't really care about Mu metadata changes
530
+ resources.each_pair { |name, data|
531
+ if MU::MommaCat.getChunkName(data[:value], res_type).first.nil?
532
+ symbol = if data[:action] == :added
533
+ "+".green
534
+ elsif data[:action] == :removed
535
+ "-".red
536
+ else
537
+ "~".yellow
538
+ end
539
+ puts (symbol+" "+res_type+"["+name+"]")
540
+ end
541
+
542
+ noun = shortclass ? shortclass.to_s : res_type.capitalize
543
+ verb = if data[:action]
544
+ data[:action].to_s
545
+ else
546
+ "modified"
547
+ end
548
+
549
+ changes = crawlChangeReport(data.freeze, res_type)
550
+
551
+ slacktext = "#{noun} \*#{name}\* was #{verb}"
552
+ if data[:habitat]
553
+ slacktext += " in \*#{data[:habitat]}\*"
554
+ end
555
+ snippets = []
556
+
557
+ if [:added, :removed].include?(data[:action]) and data[:value]
558
+ snippets << { text: "```"+JSON.pretty_generate(data[:value])+"```" }
559
+ else
560
+ changes.each { |c|
561
+ slacktext += "\n • "+c["slack"]
562
+ if c["details"]
563
+ details = JSON.pretty_generate(c["details"])
564
+ snippets << { text: "```"+JSON.pretty_generate(c["details"])+"```" }
565
+ end
566
+ }
567
+ end
568
+
569
+ changes.each { |c|
570
+ puts c["color"]
571
+ }
572
+ puts ""
573
+
574
+ if MU.muCfg['adopt_change_notify'] and MU.muCfg['adopt_change_notify']['slack']
575
+ deploy.sendAdminSlack(slacktext, scrub_mu_isms: MU.muCfg['adopt_scrub_mu_isms'], snippets: snippets, noop: false)
576
+ end
577
+
578
+ }
579
+ }
580
+
581
+ end
582
+
329
583
  def scrubSchemaDefaults(conf_chunk, schema_chunk, depth = 0, type: nil)
330
584
  return if schema_chunk.nil?
331
585
 
@@ -333,7 +587,7 @@ module MU
333
587
  deletia = []
334
588
  schema_chunk["properties"].each_pair { |key, subschema|
335
589
  next if !conf_chunk[key]
336
- shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(key)
590
+ shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(key, false)
337
591
 
338
592
  if subschema["default_if"]
339
593
  subschema["default_if"].each { |cond|
@@ -357,8 +611,7 @@ module MU
357
611
  # theory
358
612
  realschema = if type and schema_chunk["items"] and schema_chunk["items"]["properties"] and item["cloud"] and MU::Cloud.supportedClouds.include?(item['cloud'])
359
613
 
360
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(item["cloud"]).const_get(type)
361
- _toplevel_required, cloudschema = cloudclass.schema(self)
614
+ _toplevel_required, cloudschema = MU::Cloud.resourceClass(item['cloud'], type).schema(self)
362
615
 
363
616
  newschema = schema_chunk["items"].dup
364
617
  newschema["properties"].merge!(cloudschema)
@@ -382,8 +635,7 @@ module MU
382
635
  # Do the same for our main objects: if they all use the same credentials,
383
636
  # for example, remove the explicit +credentials+ attributes and set that
384
637
  # value globally, once.
385
- def vacuum(bok, origin: nil, save: false, deploy: nil)
386
- deploy ||= generateStubDeploy(bok)
638
+ def vacuum(bok, origin: nil, save: false, deploy: nil, copy_from: nil, keep_missing: false)
387
639
 
388
640
  globals = {
389
641
  'cloud' => {},
@@ -403,11 +655,24 @@ module MU
403
655
  end
404
656
  }
405
657
  obj = deploy.findLitterMate(type: attrs[:cfg_plural], name: resource['name'])
658
+ inject_metadata = save
659
+ if obj.nil? and copy_from
660
+ obj = copy_from.findLitterMate(type: attrs[:cfg_plural], name: resource['name'])
661
+ if obj
662
+ inject_metadata = true
663
+ obj.intoDeploy(deploy, force: true)
664
+ end
665
+ end
666
+
406
667
  begin
407
668
  raise Incomplete if obj.nil?
669
+ if inject_metadata
670
+ deploydata = obj.notify
671
+ deploy.notify(attrs[:cfg_plural], resource['name'], deploydata, triggering_node: obj)
672
+ end
408
673
  new_cfg = resolveReferences(resource, deploy, obj)
409
674
  new_cfg.delete("cloud_id")
410
- cred_cfg = MU::Cloud.const_get(obj.cloud).credConfig(obj.credentials)
675
+ cred_cfg = MU::Cloud.cloudClass(obj.cloud).credConfig(obj.credentials)
411
676
  if cred_cfg['region'] == new_cfg['region']
412
677
  new_cfg.delete('region')
413
678
  end
@@ -417,6 +682,11 @@ module MU
417
682
  end
418
683
  processed << new_cfg
419
684
  rescue Incomplete
685
+ if keep_missing
686
+ processed << resource
687
+ else
688
+ MU.log "#{attrs[:cfg_name]} #{resource['name']} didn't show up from findLitterMate", MU::WARN, details: deploy.original_config[attrs[:cfg_plural]].reject { |r| r['name'] != "" }
689
+ end
420
690
  end
421
691
  }
422
692
 
@@ -427,24 +697,23 @@ module MU
427
697
 
428
698
  # Pare out global values like +cloud+ or +region+ that appear to be
429
699
  # universal in the deploy we're creating.
430
- def scrub_globals(h, field)
700
+ scrub_globals = Proc.new { |h, field|
431
701
  if h.is_a?(Hash)
432
702
  newhash = {}
433
703
  h.each_pair { |k, v|
434
704
  next if k == field
435
- newhash[k] = scrub_globals(v, field)
705
+ newhash[k] = scrub_globals.call(v, field)
436
706
  }
437
707
  h = newhash
438
708
  elsif h.is_a?(Array)
439
709
  newarr = []
440
710
  h.each { |v|
441
- newarr << scrub_globals(v, field)
711
+ newarr << scrub_globals.call(v, field)
442
712
  }
443
- h = newarr
713
+ h = newarr.uniq
444
714
  end
445
-
446
715
  h
447
- end
716
+ }
448
717
 
449
718
  globals.each_pair { |field, counts|
450
719
  next if counts.size != 1
@@ -454,7 +723,7 @@ module MU
454
723
  if bok[attrs[:cfg_plural]]
455
724
  new_resources = []
456
725
  bok[attrs[:cfg_plural]].each { |resource|
457
- new_resources << scrub_globals(resource, field)
726
+ new_resources << scrub_globals.call(resource, field)
458
727
  }
459
728
  bok[attrs[:cfg_plural]] = new_resources
460
729
  end
@@ -472,11 +741,33 @@ module MU
472
741
  end
473
742
 
474
743
  def resolveReferences(cfg, deploy, parent)
744
+ mask_deploy_id = false
745
+
746
+ check_deploy_id = Proc.new { |cfgblob|
747
+ (deploy and
748
+ (cfgblob.is_a?(MU::Config::Ref) or cfgblob.is_a?(Hash)) and
749
+ cfgblob['deploy_id'] and
750
+ cfgblob['deploy_id'] != deploy.deploy_id and
751
+ @diff and
752
+ @types_found_in[cfgblob['type']] and
753
+ @types_found_in[cfgblob['type']].deploy_id == cfgblob['deploy_id']
754
+ )
755
+ }
756
+
757
+ mask_deploy_id = check_deploy_id.call(cfg)
758
+
475
759
  if cfg.is_a?(MU::Config::Ref)
476
- cfg.kitten(deploy) || cfg.kitten
760
+ if mask_deploy_id
761
+ cfg.delete("deploy_id")
762
+ cfg.delete("mommacat")
763
+ cfg.kitten(deploy)
764
+ else
765
+ cfg.kitten(deploy) || cfg.kitten
766
+ end
767
+
477
768
  hashcfg = cfg.to_h
478
769
 
479
- if cfg.kitten(deploy)
770
+ if cfg.kitten
480
771
  littermate = deploy.findLitterMate(type: cfg.type, name: cfg.name, cloud_id: cfg.id, habitat: cfg.habitat)
481
772
 
482
773
  if littermate and littermate.config['name']
@@ -501,13 +792,12 @@ module MU
501
792
  elsif hashcfg["id"] and !hashcfg["name"]
502
793
  hashcfg.delete("deploy_id")
503
794
  else
504
- pp parent.cloud_desc
505
- raise Incomplete, "Failed to resolve reference on behalf of #{parent}"
795
+ raise Incomplete.new "Failed to resolve reference on behalf of #{parent}", details: hashcfg
506
796
  end
507
797
  hashcfg.delete("deploy_id") if hashcfg['deploy_id'] == deploy.deploy_id
508
798
 
509
799
  if parent and parent.config
510
- cred_cfg = MU::Cloud.const_get(parent.cloud).credConfig(parent.credentials)
800
+ cred_cfg = MU::Cloud.cloudClass(parent.cloud).credConfig(parent.credentials)
511
801
 
512
802
  if parent.config['region'] == hashcfg['region'] or
513
803
  cred_cfg['region'] == hashcfg['region']
@@ -566,7 +856,12 @@ module MU
566
856
  MU.log "Dropping unresolved value", MU::WARN, details: value
567
857
  end
568
858
  }
569
- cfg = new_array
859
+ cfg = new_array.uniq
860
+ end
861
+
862
+ if mask_deploy_id or check_deploy_id.call(cfg)
863
+ cfg.delete("deploy_id")
864
+ MU.log "#{parent} in #{deploy.deploy_id} references something in #{@types_found_in[cfg['type']].deploy_id}, ditching extraneous deploy_id", MU::DEBUG, details: cfg.to_h
570
865
  end
571
866
 
572
867
  cfg
@@ -616,6 +911,10 @@ module MU
616
911
 
617
912
  if !@scraped[typename][kitten['cloud_id']]
618
913
  MU.log "No object in scraped tree for #{attrs[:cfg_name]} #{kitten['cloud_id']} (#{kitten['name']})", MU::ERR, details: kitten
914
+ if kitten['cloud_id'].nil?
915
+ pp caller
916
+ exit
917
+ end
619
918
  next
620
919
  end
621
920
 
@@ -626,7 +925,8 @@ module MU
626
925
  deploy.addKitten(
627
926
  attrs[:cfg_plural],
628
927
  kitten['name'],
629
- @scraped[typename][kitten['cloud_id']]
928
+ @scraped[typename][kitten['cloud_id']],
929
+ do_notify: true
630
930
  )
631
931
  }
632
932
  end
@@ -635,6 +935,21 @@ module MU
635
935
  deploy
636
936
  end
637
937
 
938
+ def self.deDuplicateName(kitten_cfg, res_class)
939
+ orig_name = kitten_cfg['name'].dup
940
+ if kitten_cfg['parent'] and kitten_cfg['parent'].respond_to?(:id) and kitten_cfg['parent'].id
941
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['parent'].id
942
+ elsif kitten_cfg['project']
943
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['project']
944
+ elsif kitten_cfg['region']
945
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['region']
946
+ elsif kitten_cfg['cloud_id']
947
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['cloud_id'].gsub(/[^a-z0-9]/i, "-")
948
+ else
949
+ raise MU::Config::DuplicateNameError, "Saw duplicate #{res_class.cfg_name} name #{orig_name} and couldn't come up with a good way to differentiate them"
950
+ end
951
+ end
952
+
638
953
  # Go through everything we've scraped and update our mappings of cloud ids
639
954
  # and bare name fields, so that resources can reference one another
640
955
  # portably by name.