modules/mu/mommacat.rb in cloud-mu-3.1.6 vs modules/mu/mommacat.rb in cloud-mu-3.2.0

- old
+ new

@@ -165,10 +165,11 @@ @secret_semaphore = Mutex.new @notify_semaphore = Mutex.new @need_deploy_flush = false @node_cert_semaphore = Mutex.new @deployment = deployment_data + @deployment['mu_public_ip'] = MU.mu_public_ip @private_key = nil @public_key = nil @secrets = Hash.new @secrets['instance_secret'] = Hash.new @@ -180,10 +181,11 @@ @handle = MU.handle # pass this in @appname = appname @appname ||= @original_config['name'] if @original_config @timestamp = timestamp @environment = environment + @original_config['environment'] ||= @environment if @original_config if set_context_to_me MU::MommaCat.setThreadContext(self) end @@ -251,12 +253,11 @@ @original_config[type].each { |resource| if resource['credentials'] seen << resource['credentials'] else cloudconst = @original_config['cloud'] ? @original_config['cloud'] : MU::Config.defaultCloud - Object.const_get("MU").const_get("Cloud").const_get(cloudconst) - seen << cloudclass.credConfig(name_only: true) + seen << MU::Cloud.cloudClass(cloudconst).credConfig(name_only: true) end } end } # XXX insert default for each cloud provider if not explicitly seen @@ -287,15 +288,14 @@ hab_ref = MU::Config::Ref.get(resource['habitat']) if hab_ref and hab_ref.id habitats << hab_ref.id end elsif resource['cloud'] - cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud']) # XXX this should be a general method implemented by each cloud # provider if resource['cloud'] == "Google" - habitats << cloudclass.defaultProject(resource['credentials']) + habitats << MU::Cloud.cloudClass(resource['cloud']).defaultProject(resource['credentials']) end end } end } @@ -315,17 +315,15 @@ MU::Cloud.resource_types.each_pair { |res_type, attrs| type = attrs[:cfg_plural] if @original_config[type] @original_config[type].each { |resource| if resource['cloud'] - cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud']) - resclass = Object.const_get("MU").const_get("Cloud").const_get(resource['cloud']).const_get(res_type.to_s) - if resclass.isGlobal? + if MU::Cloud.resourceClass(resource['cloud'], res_type).isGlobal? # XXX why was I doing this, urgh next elsif !resource['region'] - regions << cloudclass.myRegion + regions << MU::Cloud.cloudClass(resource['cloud']).myRegion(resource['credentials']) end end if resource['region'] regions << resource['region'] if resource['region'] else @@ -399,28 +397,41 @@ # Keep tabs on a {MU::Cloud} object so that it can be found easily by # #findLitterMate. # @param type [String]: # @param name [String]: # @param object [MU::Cloud]: - def addKitten(type, name, object) + def addKitten(type, name, object, do_notify: false) if !type or !name or !object or !object.mu_name raise MuError, "Nil arguments to addKitten are not allowed (got type: #{type}, name: #{name}, and '#{object}' to add)" end _shortclass, _cfg_name, type, _classname, attrs = MU::Cloud.getResourceNames(type) object.intoDeploy(self) - @kitten_semaphore.synchronize { + add_block = Proc.new { @kittens[type] ||= {} @kittens[type][object.habitat] ||= {} if attrs[:has_multiples] @kittens[type][object.habitat][name] ||= {} @kittens[type][object.habitat][name][object.mu_name] = object else @kittens[type][object.habitat][name] = object end + if do_notify + notify(type, name, object.notify, triggering_node: object, delayed_save: true) + end } + + begin + @kitten_semaphore.synchronize { + add_block.call() + } + rescue ThreadError => e + # already locked by a parent call to this method, so this should be safe + raise e if !e.message.match(/recursive locking/) + add_block.call() + end end # Encrypt a string with the deployment's public key. # @param ciphertext [String]: The string to encrypt def encryptWithDeployKey(ciphertext) @@ -534,14 +545,13 @@ # @param data [Hash]: The resource's metadata. # @param triggering_node [MU::Cloud]: A cloud object calling this notify, usually on behalf of itself # @param remove [Boolean]: Remove this resource from the deploy structure, instead of adding it. # @return [void] def notify(type, key, data, mu_name: nil, remove: false, triggering_node: nil, delayed_save: false) - return if @no_artifacts begin - MU::MommaCat.lock("deployment-notification") + MU::MommaCat.lock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts if !@need_deploy_flush or @deployment.nil? or @deployment.empty? loadDeploy(true) # make sure we're saving the latest and greatest end @@ -576,11 +586,11 @@ MU.log "Adding to @deployment[#{type}][#{key}][#{mu_name}]", MU::DEBUG, details: data else @deployment[type][key] = data MU.log "Adding to @deployment[#{type}][#{key}]", MU::DEBUG, details: data end - save!(key) if !delayed_save + save!(key) if !delayed_save and !@no_artifacts else have_deploy = true if @deployment[type].nil? or @deployment[type][key].nil? MU.log "MU::MommaCat.notify called to remove #{type} #{key}#{has_multiples ? " "+mu_name : ""} deployment struct, but no such data exist", MU::DEBUG return @@ -601,33 +611,52 @@ if @deployment[type].empty? @deployment.delete(type) end } end - save! if !delayed_save + save! if !delayed_save and !@no_artifacts end ensure - MU::MommaCat.unlock("deployment-notification") + MU::MommaCat.unlock("deployment-notification", deploy_id: @deploy_id) if !@no_artifacts end end # Send a Slack notification to a deployment's administrators. # @param subject [String]: The subject line of the message. # @param msg [String]: The message body. # @return [void] - def sendAdminSlack(subject, msg: "") - if $MU_CFG['slack'] and $MU_CFG['slack']['webhook'] and - (!$MU_CFG['slack']['skip_environments'] or !$MU_CFG['slack']['skip_environments'].any?{ |s| s.casecmp(MU.environment)==0 }) + def sendAdminSlack(subject, msg: "", scrub_mu_isms: true, snippets: [], noop: false) + if MU.muCfg['slack'] and MU.muCfg['slack']['webhook'] and + (!MU.muCfg['slack']['skip_environments'] or !MU.muCfg['slack']['skip_environments'].any?{ |s| s.casecmp(MU.environment)==0 }) require 'slack-notifier' - slack = Slack::Notifier.new $MU_CFG['slack']['webhook'] + slackargs = nil + keyword_args = { channel: MU.muCfg['slack']['channel'] } + begin + slack = Slack::Notifier.new MU.muCfg['slack']['webhook'] + prefix = scrub_mu_isms ? subject : "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}" - if msg and !msg.empty? - slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}:\n\n```#{msg}\n```", channel: $MU_CFG['slack']['channel'] - else - slack.ping "#{MU.appname} \*\"#{MU.handle}\"\* (`#{MU.deploy_id}`) - #{subject}", channel: $MU_CFG['slack']['channel'] + text = if msg and !msg.empty? + "#{prefix}:\n\n```#{msg}```" + else + prefix + end + + if snippets and snippets.size > 0 + keyword_args[:attachments] = snippets + end + + if !noop + slack.ping(text, **keyword_args) + else + MU.log "Would send to #{MU.muCfg['slack']['channel']}", MU::NOTICE, details: [ text, keyword_args ] + end + rescue Slack::Notifier::APIError => e + MU.log "Failed to send message to slack: #{e.message}", MU::ERR, details: keyword_args + return false end end + true end # Send an email notification to a deployment's administrators. # @param subject [String]: The subject line of the message. # @param msg [String]: The message body. @@ -752,11 +781,11 @@ if !triggering_node.nil? and triggering_node.is_a?(MU::Cloud::Server) triggering_node = triggering_node.mu_name end siblings = findLitterMate(type: "server", return_all: true) - return if siblings.nil? or siblings.empty? + return if siblings.nil? or (siblings.respond_to?(:empty?) and siblings.empty?) update_servers = [] siblings.each_pair { |mu_name, node| next if mu_name == triggering_node or node.groomer.nil? next if nodeclasses.size > 0 and !nodeclasses.include?(node.config['name']) @@ -836,10 +865,10 @@ winrm_cert = MU::Master::SSL.getCert(cert_cn+"-winrm", "/CN=#{resource.config['windows_admin_username']}/O=Mu/C=US", sans: ["otherName:1.3.6.1.4.1.311.20.2.3;UTF8:#{resource.config['windows_admin_username']}@localhost"], pfx: true)[0] results[cert_cn+"-winrm"] = [winrm_key, winrm_cert] end if resource and resource.config and resource.config['cloud'] - cloudclass = Object.const_get("MU").const_get("Cloud").const_get(resource.config['cloud']) + cloudclass = MU::Cloud.cloudClass(resource.config['cloud']) cloudclass.writeDeploySecret(@deploy_id, cert.to_pem, cert_cn+".crt", credentials: resource.config['credentials']) cloudclass.writeDeploySecret(@deploy_id, key.to_pem, cert_cn+".key", credentials: resource.config['credentials']) if pfx_cert cloudclass.writeDeploySecret(@deploy_id, pfx_cert.to_der, cert_cn+".pfx", credentials: resource.config['credentials'])