### Copyright 2016 Pixar ### ### Licensed under the Apache License, Version 2.0 (the "Apache License") ### with the following modification; you may not use this file except in ### compliance with the Apache License and the following modification to it: ### Section 6. Trademarks. is deleted and replaced with: ### ### 6. Trademarks. This License does not grant permission to use the trade ### names, trademarks, service marks, or product names of the Licensor ### and its affiliates, except as required to comply with Section 4(c) of ### the License and to reproduce the content of the NOTICE file. ### ### You may obtain a copy of the Apache License at ### ### http://www.apache.org/licenses/LICENSE-2.0 ### ### Unless required by applicable law or agreed to in writing, software ### distributed under the Apache License with the above modification is ### distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY ### KIND, either express or implied. See the Apache License for the specific ### language governing permissions and limitations under the Apache License. ### ### ### module D3 module PuppyTime ### PuppyQ - the current queue of pending puppy installs. ### ### This class only has one real value: A hash of ### PendingPuppy objects, keyed by basename. ### But, PendingPuppies can be added and removed ### and the queue can be read and written to disk ### class PuppyQueue ################# Mixin Modules ################# include Singleton ################## Class Constants ################# QUEUE_FILE = D3::SUPPORT_DIR + "d3-pending-puppies.yaml" DFT_NOTIFICATION_FREQUENCY = 7 ################# Class Methods ################# ################# Attributes ################# # the queue of PendingPuppy objects # A hash, keyed by basename. attr_reader :q ################# Constructor ################# # start by sett def initialize read_q end # init ################# Public Instance Methods ################# ### Read in the current puppy queue from disk ### or set it to an empty hash if not there. ### ### @return [void] ### def read_q if QUEUE_FILE.exist? @q = YAML.load(QUEUE_FILE.read) D3.log "Puppy queue loaded from disk", :debug else @q = {} D3.log "Created new empty puppy queue", :debug end end # read_q ### Save the current q out to disk ### ### @return [void] ### def save_q D3.log "Saving puppy queue", :debug if @q.empty? D3.log "Puppy queue is empty, deleting from disk", :debug QUEUE_FILE.delete if QUEUE_FILE.exist? else QUEUE_FILE.jss_save YAML.dump(@q) D3.log "Puppy queue saved to disk", :debug end end # save_q ### An array of basenames for all pending puppies. ### ### @return [Array] the basenames of the pending puppies ### def pups @q.keys end ### Add a puppy to the queue ### ### @param puppy[D3::PendingPupppy] the puppy to add. ### ### @return [Boolean] True if the puppy was queued, false if already ### in the queue with a same or newer edition. ### def + (puppy) raise TypeError, "You can only add PendingPuppy ojects to the PuppyQueue" unless puppy.class == D3::PuppyTime::PendingPuppy D3.log "Adding to puppy queue: #{puppy.edition}", :info # does this basename already exist in the queue? # we can only have one edition per basename in the queue at a time if pups.include? puppy.basename in_q = @q[puppy.basename] # if the pre-queued one is older, replace it with this one. if in_q.id < puppy.id D3.log "Replacing older puppy in queue: #{in_q.edition}", :warm self - puppy.basename else if puppy.force D3.log "Puppy already in queue for '#{puppy.basename}' is the same or newer (#{in_q.edition}), but force-adding", :warn self - puppy.basename else D3.log "Puppy already in queue for '#{puppy.basename}' is the same or newer (#{in_q.edition}), not adding", :warn return false end # if force end end # if @pups.include? puppy.basename # note if we're starting with an empty queue started_empty = @q.empty? # Note when we're queueing puppy.queued_at = Time.now # add the new puppy @q[puppy.basename] = puppy D3.log "Added puppy to queue: #{puppy.edition}", :info # save it save_q notify_puppies return true end # + ### Remove a puppy from the queue ### ### @param puppy[String,D3::PendingPupppy] the basename or D3::PendingPupppy ### for the puppy to be removed ### ### @return [Boolean] true if it was removed, false if it wasn't in the queue ### def - (puppy) puppy = puppy.basename unless puppy.is_a? String return false unless pups.include? puppy # remove it @q.delete puppy D3.log "Removed basename #{puppy} from the puppy queue", :debug # save the queue save_q return true end # + ### Should we run the puppytime notification policy? ### returns the policy id or name, or false if we ### shouldn't run. ### ### @return [String,Integer,false] the policy to run, if we should ### def should_run_notification_policy # no puppies, no notify if @q.empty? D3.log "Not running puppytime notification policy: No puppies in queue", :debug return false end # no policy, no notify unless policy = D3::CONFIG.puppy_notification_policy D3.log "Not running puppytime notification policy: No policy in config", :debug return false end # no-notification option was given, no notify unless D3::Client.puppy_notification_ok_with_admin? D3.log "Not running puppytime notification policy: --no-puppy-notification was given", :debug return false end # how many days between notifications? frequency = D3::CONFIG.puppy_notification_frequency frequency ||= DFT_NOTIFICATION_FREQUENCY case frequency # zero means never notify when 0 D3.log "Not running puppytime notification policy: Frequency set to Zero", :debug return false # -1 means always notify when -1 D3.log "Frequency set to -1, always running puppytime notification policy", :debug return policy end unless last_notification = D3::CONFIG.puppy_last_notification # never been notified? always notify. D3.log "Puppytime notification policy never run, running now.", :debug return policy end # if last notiv # not long enough since last? no notify last_notification_days_ago = ((Time.now - last_notification) / 60 / 60 / 24).to_i unless last_notification_days_ago >= frequency D3.log "Not running puppytime notification policy: last notification #{last_notification_days_ago}/#{frequency} days ago", :debug return false end return policy end ### Run the puppy notification policy, if there is one ### and if its been long enough since the last run. ### ### @return [void] ### def notify_puppies(verbose = false) return unless policy = should_run_notification_policy # put the queue editions in the ENV puppies = @q.values.map{|p| p.edition}.join " " D3::Client.set_env :puppytime_notification, puppies D3.run_policy policy, :puppy_notification, verbose update_last_notification D3::Client.unset_env :puppytime_notification end ### Update the last notification date in the config ### to right-now. ### ### @return [void] ### def update_last_notification D3.log "Updating last puppy notification time", :debug D3::CONFIG.puppy_last_notification = Time.now D3::CONFIG.save end # update last notification ################# Method Aliases ################# alias queue q alias pending_puppies q alias puppies pups alias basenames pups alias refresh read_q end # class PuppyQueue end # module puppytime # here's our one queue instance PUPPY_Q = D3::PuppyTime::PuppyQueue.instance end #module d3