modules/mu/adoption.rb in cloud-mu-3.1.6 vs modules/mu/adoption.rb in cloud-mu-3.2.0
- old
+ new
@@ -28,11 +28,12 @@
GROUPMODES = {
:logical => "Group resources in logical layers (folders and habitats together, users/roles/groups together, network resources together, etc)",
:omnibus => "Jam everything into one monolothic configuration"
}
- 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)
+
+ 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)
@scraped = {}
@clouds = clouds
@types = types
@parent = parent
@boks = {}
@@ -42,20 +43,22 @@
@target_creds = credentials
@group_by = group_by
@savedeploys = savedeploys
@diff = diff
@habitats = habitats
+ @regions = regions
@habitats ||= []
@scrub_mu_isms = scrub_mu_isms
+ @merge = merge
end
# Walk cloud providers with available credentials to discover resources
def scrapeClouds()
@default_parent = nil
@clouds.each { |cloud|
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(cloud)
+ cloudclass = MU::Cloud.cloudClass(cloud)
next if cloudclass.listCredentials.nil?
if cloud == "Google" and !@parent and @target_creds
dest_org = MU::Cloud::Google.getOrg(@target_creds)
if dest_org
@@ -63,11 +66,10 @@
end
end
cloudclass.listCredentials.each { |credset|
next if @sources and !@sources.include?(credset)
-
cfg = cloudclass.credConfig(credset)
if cfg and cfg['restrict_to_habitats']
cfg['restrict_to_habitats'] << cfg['project'] if cfg['project']
end
@@ -88,11 +90,11 @@
end
end
@types.each { |type|
begin
- resclass = Object.const_get("MU").const_get("Cloud").const_get(cloud).const_get(type)
+ resclass = MU::Cloud.resourceClass(cloud, type)
rescue ::MU::Cloud::MuCloudResourceNotImplemented
next
end
if !resclass.instance_methods.include?(:toKitten)
MU.log "Skipping MU::Cloud::#{cloud}::#{type} (resource has not implemented #toKitten)", MU::WARN
@@ -104,27 +106,34 @@
cloud,
type,
credentials: credset,
allow_multi: true,
habitats: @habitats.dup,
+ region: @regions,
dummy_ok: true,
skip_provider_owned: true,
# debug: false#,
)
if found and found.size > 0
if resclass.cfg_plural == "habitats"
- found.reject! { |h| !cloudclass.listHabitats(credset).include?(h) }
+ found.reject! { |h|
+ !cloudclass.listHabitats(credset).include?(h.cloud_id)
+ }
end
MU.log "Found #{found.size.to_s} raw #{resclass.cfg_plural} in #{cloud}"
@scraped[type] ||= {}
found.each { |obj|
if obj.habitat and !cloudclass.listHabitats(credset).include?(obj.habitat)
next
end
# XXX apply any filters (e.g. MU-ID tags)
+ if obj.cloud_id.nil?
+ MU.log "This damn thing gave me no cloud id, what do I even do with that", MU::ERR, details: obj
+ exit
+ end
@scraped[type][obj.cloud_id] = obj
}
end
}
@@ -198,47 +207,74 @@
groupings["services"] = MU::Cloud.resource_types.values.map { |v| v[:cfg_plural] } - groupings.values.flatten
elsif @group_by == :omnibus
prefix = "mu" if prefix.empty? # so that appnames aren't ever empty
end
+ # Find any previous deploys with this particular profile, which we'll use
+ # later for --diff.
+ @existing_deploys = {}
+ @existing_deploys_by_id = {}
+ @origins = {}
+ @types_found_in = {}
groupings.each_pair { |appname, types|
+ allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
+ next if (types & allowed_types).size == 0
+ origin = {
+ "appname" => prefix+appname,
+ "types" => (types & allowed_types).sort,
+ "habitats" => @habitats.sort,
+ "group_by" => @group_by.to_s
+ }
+
+ @existing_deploys[appname] = MU::MommaCat.findMatchingDeploy(origin)
+ if @existing_deploys[appname]
+ @existing_deploys_by_id[@existing_deploys[appname].deploy_id] = @existing_deploys[appname]
+ @origins[appname] = origin
+ origin['types'].each { |t|
+ @types_found_in[t] = @existing_deploys[appname]
+ }
+ end
+ }
+
+ groupings.each_pair { |appname, types|
+ allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
+ next if (types & allowed_types).size == 0
+
bok = { "appname" => prefix+appname }
if @scrub_mu_isms
bok["scrub_mu_isms"] = true
end
if @target_creds
bok["credentials"] = @target_creds
end
count = 0
- allowed_types = @types.map { |t| MU::Cloud.resource_types[t][:cfg_plural] }
- next if (types & allowed_types).size == 0
- origin = {
- "appname" => bok['appname'],
- "types" => (types & allowed_types).sort,
- "habitats" => @habitats.sort,
- "group_by" => @group_by.to_s
- }
-
- deploy = MU::MommaCat.findMatchingDeploy(origin)
- if @diff and !deploy
- MU.log "--diff was set but I failed to find a deploy like me to compare to", MU::ERR, details: origin
- exit 1
+ if @diff
+ if !@existing_deploys[appname]
+ 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]
+ exit 1
+ else
+ MU.log "Will diff current live resources against #{@existing_deploys[appname].deploy_id}", MU::NOTICE, details: @origins[appname]
+ end
end
threads = []
+ timers = {}
+ walltimers = {}
@clouds.each { |cloud|
@scraped.each_pair { |type, resources|
+ typestart = Time.now
res_class = begin
- MU::Cloud.loadCloudType(cloud, type)
+ MU::Cloud.resourceClass(cloud, type)
rescue MU::Cloud::MuCloudResourceNotImplemented
# XXX I don't think this can actually happen
next
end
next if !types.include?(res_class.cfg_plural)
bok[res_class.cfg_plural] ||= []
+ timers[type] ||= {}
class_semaphore = Mutex.new
Thread.abort_on_exception = true
resources.values.each { |obj_thr|
@@ -251,27 +287,33 @@
MU.log cloud+" "+type.to_s+" "+obj_thr.cloud_id+" did not return a cloud descriptor, skipping", MU::WARN
next
end
end
threads << Thread.new(obj_thr) { |obj|
+ start = Time.now
- kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats)
+ kitten_cfg = obj.toKitten(rootparent: @default_parent, billing: @billing, habitats: @habitats, types: @types)
if kitten_cfg
print "."
kitten_cfg.delete("credentials") if @target_creds
class_semaphore.synchronize {
bok[res_class.cfg_plural] << kitten_cfg
+ if !kitten_cfg['cloud_id']
+ MU.log "No cloud id in this #{res_class.cfg_name} kitten!", MU::ERR, details: kitten_cfg
+ end
+ timers[type][kitten_cfg['cloud_id']] = (Time.now - start)
}
count += 1
end
}
}
threads.each { |t|
t.join
}
+
puts ""
bok[res_class.cfg_plural].sort! { |a, b|
strs = [a, b].map { |x|
if x['cloud_id']
x['cloud_id']
@@ -289,60 +331,256 @@
# If we've got duplicate names in here, try to deal with it
bok[res_class.cfg_plural].each { |kitten_cfg|
bok[res_class.cfg_plural].each { |sibling|
next if kitten_cfg == sibling
if sibling['name'] == kitten_cfg['name']
- MU.log "#{res_class.cfg_name} name #{sibling['name']} unavailable, will attempt to rename duplicate object", MU::DEBUG, details: kitten_cfg
- if kitten_cfg['parent'] and kitten_cfg['parent'].respond_to?(:id) and kitten_cfg['parent'].id
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['parent'].id
- elsif kitten_cfg['project']
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['project']
- elsif kitten_cfg['region']
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['region']
- elsif kitten_cfg['cloud_id']
- kitten_cfg['name'] = kitten_cfg['name']+kitten_cfg['cloud_id'].gsub(/[^a-z0-9]/i, "-")
- else
- 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"
- end
+ MU::Adoption.deDuplicateName(kitten_cfg, res_class)
MU.log "De-duplication: Renamed #{res_class.cfg_name} name '#{sibling['name']}' => '#{kitten_cfg['name']}'", MU::NOTICE
break
end
}
}
+ walltimers[type] ||= 0
+ walltimers[type] += (Time.now - typestart)
}
}
+ timers.each_pair { |type, resources|
+ next if resources.empty?
+ total = resources.values.sum
+ top_5 = resources.keys.sort { |a, b|
+ resources[b] <=> resources[a]
+ }.slice(0, 5).map { |k|
+ k.to_s+": "+sprintf("%.2fs", resources[k])
+ }
+ if walltimers[type] < 45
+ MU.log "Kittened #{resources.size.to_s} eligible #{type}s in #{sprintf("%.2fs", walltimers[type])}"
+ else
+ 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
+ end
+ }
+
# No matching resources isn't necessarily an error
next if count == 0 or bok.nil?
# Now walk through all of the Refs in these objects, resolve them, and minimize
# their config footprint
MU.log "Minimizing footprint of #{count.to_s} found resources", MU::DEBUG
- @boks[bok['appname']] = vacuum(bok, origin: origin, save: @savedeploys)
- if @diff and !deploy
+ generated_deploy = generateStubDeploy(bok)
+ @boks[bok['appname']] = vacuum(bok, origin: @origins[appname], deploy: generated_deploy, save: @savedeploys)
+
+ if @diff and !@existing_deploys[appname]
MU.log "diff flag set, but no comparable deploy provided for #{bok['appname']}", MU::ERR
exit 1
end
- if deploy and @diff
- prevcfg = MU::Config.manxify(vacuum(deploy.original_config, deploy: deploy))
+ if @diff
+ prev_vacuumed = vacuum(@existing_deploys[appname].original_config, deploy: @existing_deploys[appname], keep_missing: true, copy_from: generated_deploy)
+ prevcfg = MU::Config.manxify(prev_vacuumed)
if !prevcfg
- MU.log "#{deploy.deploy_id} didn't have a working original config for me to compare", MU::ERR
+ MU.log "#{@existing_deploys[appname].deploy_id} didn't have a working original config for me to compare", MU::ERR
exit 1
end
newcfg = MU::Config.manxify(@boks[bok['appname']])
+ report = prevcfg.diff(newcfg)
- prevcfg.diff(newcfg)
- exit
+ if report
+
+ if MU.muCfg['adopt_change_notify']
+ notifyChanges(@existing_deploys[appname], report.freeze)
+ end
+ if @merge
+ MU.log "Saving changes to #{@existing_deploys[appname].deploy_id}"
+ @existing_deploys[appname].updateBasketofKittens(newcfg, save_now: true)
+ end
+ end
+
end
}
@boks
end
private
+ # @param tier [Hash]
+ # @param parent_key [String]
+ def crawlChangeReport(tier, parent_key = nil, indent: "")
+ report = []
+ if tier.is_a?(Array)
+ tier.each { |a|
+ sub_report = crawlChangeReport(a, parent_key)
+ report.concat(sub_report) if sub_report and !sub_report.empty?
+ }
+ elsif tier.is_a?(Hash)
+ if tier[:action]
+ preposition = if tier[:action] == :added
+ "to"
+ elsif tier[:action] == :removed
+ "from"
+ else
+ "in"
+ end
+
+ name = ""
+ type_of = parent_key.sub(/s$|\[.*/, '') if parent_key
+ loc = tier[:habitat]
+
+ if tier[:value] and tier[:value].is_a?(Hash)
+ name, loc = MU::MommaCat.getChunkName(tier[:value], type_of)
+ elsif parent_key
+ name = parent_key
+ end
+
+ path_str = []
+ slack_path_str = ""
+ if tier[:parents] and tier[:parents].size > 2
+ path = tier[:parents].clone
+ slack_path_str += "#{preposition} \*"+path.join(" ⇨ ")+"\*" if path.size > 0
+ path.shift
+ path.shift
+ path.pop if path.last == name
+ for c in (0..(path.size-1)) do
+ path_str << (" " * (c+2)) + (path[c] || "<nil>")
+ end
+ end
+ path_str << "" if !path_str.empty?
+
+ plain = (name ? name : type_of) if name or type_of
+ plain ||= "" # XXX but this is a problem
+ slack = "`"+plain+"`"
+
+ plain += " ("+loc+")" if loc and !loc.empty?
+ color = plain
+
+ if tier[:action] == :added
+ color = "+ ".green + plain
+ plain = "+ " + plain
+ slack += " added"
+ elsif tier[:action] == :removed
+ color = "- ".red + plain
+ plain = "- " + plain
+ slack += " removed"
+ end
+
+ slack += " #{tier[:action]} #{preposition} \*#{loc}\*" if loc and !loc.empty? and [Array, Hash].include?(tier[:value].class)
+
+ plain = path_str.join(" => \n") + indent + plain
+ color = path_str.join(" => \n") + indent + color
+
+ slack += " "+slack_path_str if !slack_path_str.empty?
+ myreport = {
+ "slack" => slack,
+ "plain" => plain,
+ "color" => color
+ }
+
+ append = ""
+ if tier[:value] and (tier[:value].is_a?(Array) or tier[:value].is_a?(Hash))
+ if tier[:value].is_a?(Hash)
+ if name
+ tier[:value].delete("entity")
+ tier[:value].delete(name.sub(/\[.*/, '')) if name
+ end
+ if (tier[:value].keys - ["id", "name", "type"]).size > 0
+ myreport["details"] = tier[:value].clone
+ append = PP.pp(tier[:value], '').gsub(/(^|\n)/, '\1'+indent)
+ end
+ else
+ append = indent+"["+tier[:value].map { |v| MU::MommaCat.getChunkName(v, type_of).reverse.join("/") || v.to_s.light_blue }.join(", ")+"]"
+ slack += " #{tier[:action].to_s}: "+tier[:value].map { |v| MU::MommaCat.getChunkName(v, type_of).reverse.join("/") || v.to_s }.join(", ")
+ end
+ else
+ tier[:value] ||= "<nil>"
+ if ![:removed].include?(tier[:action])
+ myreport["slack"] += ". New #{tier[:field] ? "`"+tier[:field]+"`" : :value}: \*#{tier[:value]}\*"
+ else
+ myreport["slack"] += " (was \*#{tier[:value]}\*)"
+ end
+ append = tier[:value].to_s.bold
+ end
+
+ if append and !append.empty?
+ myreport["plain"] += " =>\n "+indent+append
+ myreport["color"] += " =>\n "+indent+append
+ end
+
+ report << myreport if tier[:action]
+ end
+
+ # Just because we've got changes at this level doesn't mean there aren't
+ # more further down.
+ tier.each_pair { |k, v|
+ next if !(v.is_a?(Hash) or v.is_a?(Array))
+ sub_report = crawlChangeReport(v, k, indent: indent+" ")
+ report.concat(sub_report) if sub_report and !sub_report.empty?
+ }
+ end
+
+ report
+ end
+
+
+ def notifyChanges(deploy, report)
+ snippet_threshold = (MU.muCfg['adopt_change_notify'] && MU.muCfg['adopt_change_notify']['slack_snippet_threshold']) || 5
+
+ report.each_pair { |res_type, resources|
+ shortclass, _cfg_name, _cfg_plural, _classname = MU::Cloud.getResourceNames(res_type, false)
+ next if !shortclass # we don't really care about Mu metadata changes
+ resources.each_pair { |name, data|
+ if MU::MommaCat.getChunkName(data[:value], res_type).first.nil?
+ symbol = if data[:action] == :added
+ "+".green
+ elsif data[:action] == :removed
+ "-".red
+ else
+ "~".yellow
+ end
+ puts (symbol+" "+res_type+"["+name+"]")
+ end
+
+ noun = shortclass ? shortclass.to_s : res_type.capitalize
+ verb = if data[:action]
+ data[:action].to_s
+ else
+ "modified"
+ end
+
+ changes = crawlChangeReport(data.freeze, res_type)
+
+ slacktext = "#{noun} \*#{name}\* was #{verb}"
+ if data[:habitat]
+ slacktext += " in \*#{data[:habitat]}\*"
+ end
+ snippets = []
+
+ if [:added, :removed].include?(data[:action]) and data[:value]
+ snippets << { text: "```"+JSON.pretty_generate(data[:value])+"```" }
+ else
+ changes.each { |c|
+ slacktext += "\n • "+c["slack"]
+ if c["details"]
+ details = JSON.pretty_generate(c["details"])
+ snippets << { text: "```"+JSON.pretty_generate(c["details"])+"```" }
+ end
+ }
+ end
+
+ changes.each { |c|
+ puts c["color"]
+ }
+ puts ""
+
+ if MU.muCfg['adopt_change_notify'] and MU.muCfg['adopt_change_notify']['slack']
+ deploy.sendAdminSlack(slacktext, scrub_mu_isms: MU.muCfg['adopt_scrub_mu_isms'], snippets: snippets, noop: false)
+ end
+
+ }
+ }
+
+ end
+
def scrubSchemaDefaults(conf_chunk, schema_chunk, depth = 0, type: nil)
return if schema_chunk.nil?
if !conf_chunk.nil? and schema_chunk["properties"].kind_of?(Hash) and conf_chunk.is_a?(Hash)
deletia = []
@@ -370,12 +608,11 @@
conf_chunk.each { |item|
# this bit only happens at the top-level key for a resource type, in
# theory
realschema = if type and schema_chunk["items"] and schema_chunk["items"]["properties"] and item["cloud"] and MU::Cloud.supportedClouds.include?(item['cloud'])
- cloudclass = Object.const_get("MU").const_get("Cloud").const_get(item["cloud"]).const_get(type)
- _toplevel_required, cloudschema = cloudclass.schema(self)
+ _toplevel_required, cloudschema = MU::Cloud.resourceClass(item['cloud'], type).schema(self)
newschema = schema_chunk["items"].dup
newschema["properties"].merge!(cloudschema)
newschema
else
@@ -395,12 +632,11 @@
# representation (remove extraneous attributes that match the parent
# object).
# Do the same for our main objects: if they all use the same credentials,
# for example, remove the explicit +credentials+ attributes and set that
# value globally, once.
- def vacuum(bok, origin: nil, save: false, deploy: nil)
- deploy ||= generateStubDeploy(bok)
+ def vacuum(bok, origin: nil, save: false, deploy: nil, copy_from: nil, keep_missing: false)
globals = {
'cloud' => {},
'credentials' => {},
'region' => {},
@@ -416,62 +652,79 @@
counts[resource[field]] ||= 0
counts[resource[field]] += 1
end
}
obj = deploy.findLitterMate(type: attrs[:cfg_plural], name: resource['name'])
+ inject_metadata = save
+ if obj.nil? and copy_from
+ obj = copy_from.findLitterMate(type: attrs[:cfg_plural], name: resource['name'])
+ if obj
+ inject_metadata = true
+ obj.intoDeploy(deploy, force: true)
+ end
+ end
+
begin
raise Incomplete if obj.nil?
+ if inject_metadata
+ deploydata = obj.notify
+ deploy.notify(attrs[:cfg_plural], resource['name'], deploydata, triggering_node: obj)
+ end
new_cfg = resolveReferences(resource, deploy, obj)
new_cfg.delete("cloud_id")
- cred_cfg = MU::Cloud.const_get(obj.cloud).credConfig(obj.credentials)
+ cred_cfg = MU::Cloud.cloudClass(obj.cloud).credConfig(obj.credentials)
if cred_cfg['region'] == new_cfg['region']
new_cfg.delete('region')
end
if cred_cfg['default']
new_cfg.delete('credentials')
new_cfg.delete('habitat')
end
processed << new_cfg
rescue Incomplete
+ if keep_missing
+ processed << resource
+ else
+ 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'] != "" }
+ end
end
}
deploy.original_config[attrs[:cfg_plural]] = processed
bok[attrs[:cfg_plural]] = processed
end
}
# Pare out global values like +cloud+ or +region+ that appear to be
# universal in the deploy we're creating.
- def scrub_globals(h, field)
+ scrub_globals = Proc.new { |h, field|
if h.is_a?(Hash)
newhash = {}
h.each_pair { |k, v|
next if k == field
- newhash[k] = scrub_globals(v, field)
+ newhash[k] = scrub_globals.call(v, field)
}
h = newhash
elsif h.is_a?(Array)
newarr = []
h.each { |v|
- newarr << scrub_globals(v, field)
+ newarr << scrub_globals.call(v, field)
}
- h = newarr
+ h = newarr.uniq
end
-
h
- end
+ }
globals.each_pair { |field, counts|
next if counts.size != 1
bok[field] = counts.keys.first
MU.log "Setting global default #{field} to #{bok[field]} (#{deploy.deploy_id})", MU::DEBUG
MU::Cloud.resource_types.values.each { |attrs|
if bok[attrs[:cfg_plural]]
new_resources = []
bok[attrs[:cfg_plural]].each { |resource|
- new_resources << scrub_globals(resource, field)
+ new_resources << scrub_globals.call(resource, field)
}
bok[attrs[:cfg_plural]] = new_resources
end
}
}
@@ -485,15 +738,37 @@
bok
end
def resolveReferences(cfg, deploy, parent)
+ mask_deploy_id = false
+
+ check_deploy_id = Proc.new { |cfgblob|
+ (deploy and
+ (cfgblob.is_a?(MU::Config::Ref) or cfgblob.is_a?(Hash)) and
+ cfgblob['deploy_id'] and
+ cfgblob['deploy_id'] != deploy.deploy_id and
+ @diff and
+ @types_found_in[cfgblob['type']] and
+ @types_found_in[cfgblob['type']].deploy_id == cfgblob['deploy_id']
+ )
+ }
+
+ mask_deploy_id = check_deploy_id.call(cfg)
+
if cfg.is_a?(MU::Config::Ref)
- cfg.kitten(deploy) || cfg.kitten
+ if mask_deploy_id
+ cfg.delete("deploy_id")
+ cfg.delete("mommacat")
+ cfg.kitten(deploy)
+ else
+ cfg.kitten(deploy) || cfg.kitten
+ end
+
hashcfg = cfg.to_h
- if cfg.kitten(deploy)
+ if cfg.kitten
littermate = deploy.findLitterMate(type: cfg.type, name: cfg.name, cloud_id: cfg.id, habitat: cfg.habitat)
if littermate and littermate.config['name']
hashcfg['name'] = littermate.config['name']
hashcfg.delete("id") if hashcfg["name"]
@@ -520,11 +795,11 @@
raise Incomplete, "Failed to resolve reference on behalf of #{parent}"
end
hashcfg.delete("deploy_id") if hashcfg['deploy_id'] == deploy.deploy_id
if parent and parent.config
- cred_cfg = MU::Cloud.const_get(parent.cloud).credConfig(parent.credentials)
+ cred_cfg = MU::Cloud.cloudClass(parent.cloud).credConfig(parent.credentials)
if parent.config['region'] == hashcfg['region'] or
cred_cfg['region'] == hashcfg['region']
hashcfg.delete("region")
end
@@ -579,13 +854,18 @@
end
rescue Incomplete
MU.log "Dropping unresolved value", MU::WARN, details: value
end
}
- cfg = new_array
+ cfg = new_array.uniq
end
+ if mask_deploy_id or check_deploy_id.call(cfg)
+ cfg.delete("deploy_id")
+ 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
+ end
+
cfg
end
# @return [MU::MommaCat]
def generateStubDeploy(bok)
@@ -629,26 +909,46 @@
if bok[attrs[:cfg_plural]]
bok[attrs[:cfg_plural]].each { |kitten|
if !@scraped[typename][kitten['cloud_id']]
MU.log "No object in scraped tree for #{attrs[:cfg_name]} #{kitten['cloud_id']} (#{kitten['name']})", MU::ERR, details: kitten
+ if kitten['cloud_id'].nil?
+ pp caller
+ exit
+ end
next
end
MU.log "Inserting #{attrs[:cfg_name]} #{kitten['name']} (#{kitten['cloud_id']}) into stub deploy", MU::DEBUG, details: @scraped[typename][kitten['cloud_id']]
@scraped[typename][kitten['cloud_id']].config!(kitten)
deploy.addKitten(
attrs[:cfg_plural],
kitten['name'],
- @scraped[typename][kitten['cloud_id']]
+ @scraped[typename][kitten['cloud_id']],
+ do_notify: true
)
}
end
}
deploy
+ end
+
+ def self.deDuplicateName(kitten_cfg, res_class)
+ orig_name = kitten_cfg['name'].dup
+ if kitten_cfg['parent'] and kitten_cfg['parent'].respond_to?(:id) and kitten_cfg['parent'].id
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['parent'].id
+ elsif kitten_cfg['project']
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['project']
+ elsif kitten_cfg['region']
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['region']
+ elsif kitten_cfg['cloud_id']
+ kitten_cfg['name'] = kitten_cfg['name']+"-"+kitten_cfg['cloud_id'].gsub(/[^a-z0-9]/i, "-")
+ else
+ 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"
+ end
end
# Go through everything we've scraped and update our mappings of cloud ids
# and bare name fields, so that resources can reference one another
# portably by name.