web47core 0.0.10 → 0.1.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.
- checksums.yaml +4 -4
 - data/Gemfile.lock +3 -1
 - data/bin/cron_server +6 -0
 - data/lib/app/jobs/application_job.rb +23 -0
 - data/lib/app/jobs/cron/command.rb +79 -0
 - data/lib/app/jobs/cron/job.rb +31 -0
 - data/lib/app/jobs/cron/job_tab.rb +126 -0
 - data/lib/app/jobs/cron/server.rb +285 -0
 - data/lib/app/jobs/cron/switchboard_sync_configuration.rb +66 -0
 - data/lib/app/jobs/cron/switchboard_sync_models.rb +25 -0
 - data/lib/app/jobs/cron/tab.rb +111 -0
 - data/lib/app/jobs/cron/trim_collection.rb +36 -0
 - data/lib/app/jobs/cron/trim_cron_servers.rb +22 -0
 - data/lib/app/jobs/cron/trim_failed_delayed_jobs.rb +40 -0
 - data/lib/app/jobs/cron/trim_notifications.rb +24 -0
 - data/lib/app/jobs/cron/worker.rb +70 -0
 - data/lib/app/models/concerns/switchboard_able.rb +130 -0
 - data/lib/app/models/delayed_job.rb +80 -0
 - data/lib/templates/slack/failed_delayed_job.liquid +7 -0
 - data/lib/web47core.rb +32 -0
 - data/test/jobs/cron/switchboard_sync_configuration_test.rb +64 -0
 - data/test/jobs/cron/trim_cron_servers_test.rb +28 -0
 - data/test/jobs/cron/trim_failed_delayed_jobs_test.rb +71 -0
 - data/test/models/job_cron_tab_test.rb +25 -0
 - data/test/models/web47core_test.rb +18 -0
 - data/web47core.gemspec +4 -1
 - metadata +47 -5
 - /data/test/models/{app47_logger_test.rb → concerns/app47_logger_test.rb} +0 -0
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: b3949ac1edae0d03528d687d4eb39024813fc8fa98325d0407a982800c68ff77
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 250122ca58e430a2a366f3adebba47c7c83e17061a5946690fb8010ccbcc293c
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 74c4529df3ec5931a3b7e1d9f53123253dca0beddf26612db997e08d5ce1950897e3306cf5d10ff56d0be1ee581e077915d9d7a8b141765fcce018b5ee4b856b
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 4b9b67895fba934193b72badc6a4da20d7dce717dd53169152a0445a581278564a027ea42e4b56c3a1f0b97417160e090632ba0f4aabe6a52159c0d26221ed06
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,9 +1,10 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                web47core (0.0 
     | 
| 
      
 4 
     | 
    
         
            +
                web47core (0.1.0)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  activesupport (~> 5.0)
         
     | 
| 
       6 
6 
     | 
    
         
             
                  aws-sdk-ec2 (> 1.140, <= 1.160)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  daemons
         
     | 
| 
       7 
8 
     | 
    
         
             
                  delayed_job_mongoid (~> 2.3)
         
     | 
| 
       8 
9 
     | 
    
         
             
                  email_format
         
     | 
| 
       9 
10 
     | 
    
         
             
                  haml
         
     | 
| 
         @@ -92,6 +93,7 @@ GEM 
     | 
|
| 
       92 
93 
     | 
    
         
             
                crack (0.4.3)
         
     | 
| 
       93 
94 
     | 
    
         
             
                  safe_yaml (~> 1.0.0)
         
     | 
| 
       94 
95 
     | 
    
         
             
                crass (1.0.6)
         
     | 
| 
      
 96 
     | 
    
         
            +
                daemons (1.3.1)
         
     | 
| 
       95 
97 
     | 
    
         
             
                database_cleaner (1.8.3)
         
     | 
| 
       96 
98 
     | 
    
         
             
                delayed_job (4.1.8)
         
     | 
| 
       97 
99 
     | 
    
         
             
                  activesupport (>= 3.0, < 6.1)
         
     | 
    
        data/bin/cron_server
    ADDED
    
    
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Base application job that all jobs extend from
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            class ApplicationJob < ActiveJob::Base
         
     | 
| 
      
 7 
     | 
    
         
            +
              include App47Logger
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              #
         
     | 
| 
      
 10 
     | 
    
         
            +
              # If this job should run in this current environment, defaults to true
         
     | 
| 
      
 11 
     | 
    
         
            +
              #
         
     | 
| 
      
 12 
     | 
    
         
            +
              def self.valid_environments
         
     | 
| 
      
 13 
     | 
    
         
            +
                []
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              #
         
     | 
| 
      
 17 
     | 
    
         
            +
              # Is this job in a valid environment
         
     | 
| 
      
 18 
     | 
    
         
            +
              #
         
     | 
| 
      
 19 
     | 
    
         
            +
              def self.valid_environment?
         
     | 
| 
      
 20 
     | 
    
         
            +
                my_environments = valid_environments
         
     | 
| 
      
 21 
     | 
    
         
            +
                (my_environments.empty? || my_environments.include?(Rails.env))
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,79 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            unless ENV['RAILS_ENV'] == 'test'
         
     | 
| 
      
 2 
     | 
    
         
            +
              begin
         
     | 
| 
      
 3 
     | 
    
         
            +
                require 'daemons'
         
     | 
| 
      
 4 
     | 
    
         
            +
              rescue LoadError
         
     | 
| 
      
 5 
     | 
    
         
            +
                raise "You need to add gem 'daemons' to your Gemfile if you wish to use it."
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
            end
         
     | 
| 
      
 8 
     | 
    
         
            +
            require 'fileutils'
         
     | 
| 
      
 9 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 10 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            module Cron
         
     | 
| 
      
 13 
     | 
    
         
            +
              class Command # rubocop:disable ClassLength
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                DIR_PWD = Pathname.new Dir.pwd
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                def initialize(args) # rubocop:disable MethodLength
         
     | 
| 
      
 18 
     | 
    
         
            +
                  @options = { pid_dir: "#{root}/tmp/pids", log_dir: "#{root}/log", monitor: false }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                  opts = OptionParser.new do |opt|
         
     | 
| 
      
 21 
     | 
    
         
            +
                    opt.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] start|stop|restart|run"
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                    opt.on('-h', '--help', 'Show this message') do
         
     | 
| 
      
 24 
     | 
    
         
            +
                      puts opt
         
     | 
| 
      
 25 
     | 
    
         
            +
                      exit 1
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                    opt.on('-e', '--environment=NAME', 'Specifies the environment to run this delayed jobs under (test/development/production).') do |_e|
         
     | 
| 
      
 28 
     | 
    
         
            +
                      STDERR.puts 'The -e/--environment option has been deprecated and has no effect. Use RAILS_ENV and see http://github.com/collectiveidea/delayed_job/issues/7'
         
     | 
| 
      
 29 
     | 
    
         
            +
                    end
         
     | 
| 
      
 30 
     | 
    
         
            +
                    opt.on('--pid-dir=DIR', 'Specifies an alternate directory in which to store the process ids.') do |dir|
         
     | 
| 
      
 31 
     | 
    
         
            +
                      @options[:pid_dir] = dir
         
     | 
| 
      
 32 
     | 
    
         
            +
                    end
         
     | 
| 
      
 33 
     | 
    
         
            +
                    opt.on('--log-dir=DIR', 'Specifies an alternate directory in which to store the delayed_job log.') do |dir|
         
     | 
| 
      
 34 
     | 
    
         
            +
                      @options[:log_dir] = dir
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                    opt.on('-m', '--monitor', 'Start monitor process.') do
         
     | 
| 
      
 37 
     | 
    
         
            +
                      @options[:monitor] = true
         
     | 
| 
      
 38 
     | 
    
         
            +
                    end
         
     | 
| 
      
 39 
     | 
    
         
            +
                    opt.on('--exit-on-complete', 'Exit when no more jobs are available to run. This will exit if all jobs are scheduled to run in the future.') do
         
     | 
| 
      
 40 
     | 
    
         
            +
                      @options[:exit_on_complete] = true
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                    opt.on('--daemon-options a, b, c', Array, 'options to be passed through to daemons gem') do |daemon_options|
         
     | 
| 
      
 43 
     | 
    
         
            +
                      @daemon_options = daemon_options
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @args = opts.parse!(args) + (@daemon_options || [])
         
     | 
| 
      
 47 
     | 
    
         
            +
                end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                def daemonize # rubocop:disable PerceivedComplexity
         
     | 
| 
      
 50 
     | 
    
         
            +
                  dir = @options[:pid_dir]
         
     | 
| 
      
 51 
     | 
    
         
            +
                  FileUtils.mkdir_p(dir) unless File.exist?(dir)
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  Cron::JobTab.ensure_cron_tabs
         
     | 
| 
      
 54 
     | 
    
         
            +
                  Cron::Worker.logger ||= Logger.new(File.join(@options[:log_dir], 'cron.log'))
         
     | 
| 
      
 55 
     | 
    
         
            +
                  Daemons.run_proc('cron_server',
         
     | 
| 
      
 56 
     | 
    
         
            +
                                   dir: options[:pid_dir],
         
     | 
| 
      
 57 
     | 
    
         
            +
                                   dir_mode: :normal,
         
     | 
| 
      
 58 
     | 
    
         
            +
                                   monitor: @options[:monitor],
         
     | 
| 
      
 59 
     | 
    
         
            +
                                   ARGV: @args) do
         
     | 
| 
      
 60 
     | 
    
         
            +
                    run @options
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def run(options = {})
         
     | 
| 
      
 65 
     | 
    
         
            +
                  Dir.chdir(root)
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  Cron::Worker.new(options).start
         
     | 
| 
      
 68 
     | 
    
         
            +
                rescue StandardError => error
         
     | 
| 
      
 69 
     | 
    
         
            +
                  App47Logger.log_error 'Unable to start Cron Server', error
         
     | 
| 
      
 70 
     | 
    
         
            +
                  exit 1
         
     | 
| 
      
 71 
     | 
    
         
            +
                end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                private
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def root
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @root ||= defined?(::Rails.root) ? ::Rails.root : DIR_PWD
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,31 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Cron
         
     | 
| 
      
 4 
     | 
    
         
            +
              #
         
     | 
| 
      
 5 
     | 
    
         
            +
              # Base class for WeeklyJobs.
         
     | 
| 
      
 6 
     | 
    
         
            +
              #
         
     | 
| 
      
 7 
     | 
    
         
            +
              # Support running only on certain days as defined by day_of_week method
         
     | 
| 
      
 8 
     | 
    
         
            +
              #
         
     | 
| 
      
 9 
     | 
    
         
            +
              class Job < ApplicationJob
         
     | 
| 
      
 10 
     | 
    
         
            +
                cattr_accessor :cron_tab
         
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Method to set the cron_tab
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                def self.cron_tab_entry(entry)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  self.cron_tab = entry
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                # Sends support an email if something goes awry
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                def send_support_email(error, event)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  email = EmailNotification.new
         
     | 
| 
      
 23 
     | 
    
         
            +
                  email.to = 'support@app47.com'
         
     | 
| 
      
 24 
     | 
    
         
            +
                  params = { current_date: I18n.l(Time.zone.today, format: :medium),
         
     | 
| 
      
 25 
     | 
    
         
            +
                             event: event,
         
     | 
| 
      
 26 
     | 
    
         
            +
                             error: error }
         
     | 
| 
      
 27 
     | 
    
         
            +
                  email.from_template('support_ticket_notification', params)
         
     | 
| 
      
 28 
     | 
    
         
            +
                  email.send_notification
         
     | 
| 
      
 29 
     | 
    
         
            +
                end
         
     | 
| 
      
 30 
     | 
    
         
            +
              end
         
     | 
| 
      
 31 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,126 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            # Value object for a cron tab entry
         
     | 
| 
      
 5 
     | 
    
         
            +
            #
         
     | 
| 
      
 6 
     | 
    
         
            +
            module Cron
         
     | 
| 
      
 7 
     | 
    
         
            +
              class JobTab < Tab
         
     | 
| 
      
 8 
     | 
    
         
            +
                unless defined? FRAMEWORK_CLASSES
         
     | 
| 
      
 9 
     | 
    
         
            +
                  FRAMEWORK_CLASSES = %w[job trim_collection command server tab job_tab worker].freeze
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
                cattr_accessor :jobs
         
     | 
| 
      
 12 
     | 
    
         
            +
                #
         
     | 
| 
      
 13 
     | 
    
         
            +
                # Validations
         
     | 
| 
      
 14 
     | 
    
         
            +
                #
         
     | 
| 
      
 15 
     | 
    
         
            +
                validates :name, presence: true, uniqueness: true
         
     | 
| 
      
 16 
     | 
    
         
            +
                validate :valid_name
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                class << self
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #
         
     | 
| 
      
 20 
     | 
    
         
            +
                  # Pull form system configuration
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #
         
     | 
| 
      
 22 
     | 
    
         
            +
                  def from_string(name, value)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    tab = JobTab.find_or_initialize_by name: name
         
     | 
| 
      
 24 
     | 
    
         
            +
                    return unless tab.valid?
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                    tab.update_from_string(value)
         
     | 
| 
      
 27 
     | 
    
         
            +
                    tab
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                  def ensure_cron_tabs
         
     | 
| 
      
 31 
     | 
    
         
            +
                    self.jobs = []
         
     | 
| 
      
 32 
     | 
    
         
            +
                    Cron.constants.each do |job|
         
     | 
| 
      
 33 
     | 
    
         
            +
                      job_name = job.to_s.underscore
         
     | 
| 
      
 34 
     | 
    
         
            +
                      next if FRAMEWORK_CLASSES.include?(job_name) ||
         
     | 
| 
      
 35 
     | 
    
         
            +
                        job_name.end_with?('_test') ||
         
     | 
| 
      
 36 
     | 
    
         
            +
                        job_name.start_with?('base_')
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                      jobs << job_name
         
     | 
| 
      
 39 
     | 
    
         
            +
                      klass = "cron/#{job_name}".camelize.constantize
         
     | 
| 
      
 40 
     | 
    
         
            +
                      tab = JobTab.find_or_initialize_by name: job_name
         
     | 
| 
      
 41 
     | 
    
         
            +
                      next if tab.persisted?
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                      configure_cron_tab(tab, klass.cron_tab)
         
     | 
| 
      
 44 
     | 
    
         
            +
                    end
         
     | 
| 
      
 45 
     | 
    
         
            +
                    purge_cron_tabs
         
     | 
| 
      
 46 
     | 
    
         
            +
                  end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  private
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  def configure_cron_tab(tab, type)
         
     | 
| 
      
 52 
     | 
    
         
            +
                    case type
         
     | 
| 
      
 53 
     | 
    
         
            +
                    when :always
         
     | 
| 
      
 54 
     | 
    
         
            +
                      tab.update! min: WILDCARD, hour: WILDCARD
         
     | 
| 
      
 55 
     | 
    
         
            +
                    when :hourly
         
     | 
| 
      
 56 
     | 
    
         
            +
                      tab.update! min: 0, hour: WILDCARD
         
     | 
| 
      
 57 
     | 
    
         
            +
                    when :daily
         
     | 
| 
      
 58 
     | 
    
         
            +
                      tab.update! min: 0, hour: 0
         
     | 
| 
      
 59 
     | 
    
         
            +
                    when :weekly
         
     | 
| 
      
 60 
     | 
    
         
            +
                      tab.update! min: 0, hour: 0, wday: 0
         
     | 
| 
      
 61 
     | 
    
         
            +
                    when :monthly
         
     | 
| 
      
 62 
     | 
    
         
            +
                      tab.update! min: 0, hour: 0, mday: 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                    else
         
     | 
| 
      
 64 
     | 
    
         
            +
                      tab.update_from_string(type)
         
     | 
| 
      
 65 
     | 
    
         
            +
                      tab.save!
         
     | 
| 
      
 66 
     | 
    
         
            +
                    end
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def purge_cron_tabs
         
     | 
| 
      
 70 
     | 
    
         
            +
                    JobTab.all.each do |tab|
         
     | 
| 
      
 71 
     | 
    
         
            +
                      tab.destroy unless jobs.include?(tab.name)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    end
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                #
         
     | 
| 
      
 77 
     | 
    
         
            +
                # Update our values based on the value
         
     | 
| 
      
 78 
     | 
    
         
            +
                #
         
     | 
| 
      
 79 
     | 
    
         
            +
                def update_from_string(value)
         
     | 
| 
      
 80 
     | 
    
         
            +
                  values = value.split(' ')
         
     | 
| 
      
 81 
     | 
    
         
            +
                  self.min = values[0]
         
     | 
| 
      
 82 
     | 
    
         
            +
                  self.hour = values[1]
         
     | 
| 
      
 83 
     | 
    
         
            +
                  self.mday = values[2]
         
     | 
| 
      
 84 
     | 
    
         
            +
                  self.month = values[3]
         
     | 
| 
      
 85 
     | 
    
         
            +
                  self.wday = values[4]
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                #
         
     | 
| 
      
 89 
     | 
    
         
            +
                # Run this job cron tab
         
     | 
| 
      
 90 
     | 
    
         
            +
                #
         
     | 
| 
      
 91 
     | 
    
         
            +
                def run
         
     | 
| 
      
 92 
     | 
    
         
            +
                  return unless valid_environment?
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                  cron_job_class.perform_later
         
     | 
| 
      
 95 
     | 
    
         
            +
                  super
         
     | 
| 
      
 96 
     | 
    
         
            +
                end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                #
         
     | 
| 
      
 99 
     | 
    
         
            +
                # Is this enabled and a valid environment
         
     | 
| 
      
 100 
     | 
    
         
            +
                #
         
     | 
| 
      
 101 
     | 
    
         
            +
                def valid_environment?
         
     | 
| 
      
 102 
     | 
    
         
            +
                  enabled? && cron_job_class.valid_environment?
         
     | 
| 
      
 103 
     | 
    
         
            +
                end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                #
         
     | 
| 
      
 106 
     | 
    
         
            +
                # Return the class associated with this job cron tab
         
     | 
| 
      
 107 
     | 
    
         
            +
                #
         
     | 
| 
      
 108 
     | 
    
         
            +
                def cron_job_class
         
     | 
| 
      
 109 
     | 
    
         
            +
                  @cron_job_class ||= "cron/#{name}".camelize.constantize
         
     | 
| 
      
 110 
     | 
    
         
            +
                end
         
     | 
| 
      
 111 
     | 
    
         
            +
             
     | 
| 
      
 112 
     | 
    
         
            +
                #
         
     | 
| 
      
 113 
     | 
    
         
            +
                # Convert back to a standard string value
         
     | 
| 
      
 114 
     | 
    
         
            +
                #
         
     | 
| 
      
 115 
     | 
    
         
            +
                def to_string
         
     | 
| 
      
 116 
     | 
    
         
            +
                  [min, hour, mday, month, wday].join(' ')
         
     | 
| 
      
 117 
     | 
    
         
            +
                end
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                #
         
     | 
| 
      
 120 
     | 
    
         
            +
                # Test the name is the list of jobs discovered
         
     | 
| 
      
 121 
     | 
    
         
            +
                #
         
     | 
| 
      
 122 
     | 
    
         
            +
                def valid_name
         
     | 
| 
      
 123 
     | 
    
         
            +
                  errors.add(:name, 'Invalid Tab') unless jobs.include?(name)
         
     | 
| 
      
 124 
     | 
    
         
            +
                end
         
     | 
| 
      
 125 
     | 
    
         
            +
              end
         
     | 
| 
      
 126 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,285 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Handle the coordination of which server should be running the cron jobs
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            module Cron
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Server
         
     | 
| 
      
 6 
     | 
    
         
            +
                include StandardModel
         
     | 
| 
      
 7 
     | 
    
         
            +
                #
         
     | 
| 
      
 8 
     | 
    
         
            +
                # Constants
         
     | 
| 
      
 9 
     | 
    
         
            +
                #
         
     | 
| 
      
 10 
     | 
    
         
            +
                STATE_PRIMARY = 'primary'.freeze unless defined? STATE_PRIMARY
         
     | 
| 
      
 11 
     | 
    
         
            +
                STATE_SECONDARY = 'secondary'.freeze unless defined? STATE_SECONDARY
         
     | 
| 
      
 12 
     | 
    
         
            +
                ALL_STATES = [STATE_PRIMARY, STATE_SECONDARY].freeze unless defined? ALL_STATES
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                # Fields
         
     | 
| 
      
 15 
     | 
    
         
            +
                #
         
     | 
| 
      
 16 
     | 
    
         
            +
                field :host_name, type: String
         
     | 
| 
      
 17 
     | 
    
         
            +
                field :pid, type: Integer
         
     | 
| 
      
 18 
     | 
    
         
            +
                field :desired_server_count, type: Integer, default: 0
         
     | 
| 
      
 19 
     | 
    
         
            +
                field :current_server_count, type: Integer, default: 0
         
     | 
| 
      
 20 
     | 
    
         
            +
                field :last_check_in_at, type: Time, default: Time.now.utc
         
     | 
| 
      
 21 
     | 
    
         
            +
                field :state, type: String, default: STATE_SECONDARY
         
     | 
| 
      
 22 
     | 
    
         
            +
                #
         
     | 
| 
      
 23 
     | 
    
         
            +
                # Validations
         
     | 
| 
      
 24 
     | 
    
         
            +
                #
         
     | 
| 
      
 25 
     | 
    
         
            +
                validates :host_name, presence: true
         
     | 
| 
      
 26 
     | 
    
         
            +
                validates :pid, presence: true
         
     | 
| 
      
 27 
     | 
    
         
            +
                validates :last_check_in_at, presence: true
         
     | 
| 
      
 28 
     | 
    
         
            +
                validates :state, inclusion: { in: ALL_STATES }
         
     | 
| 
      
 29 
     | 
    
         
            +
                validate :high_lander
         
     | 
| 
      
 30 
     | 
    
         
            +
                #
         
     | 
| 
      
 31 
     | 
    
         
            +
                # Go through the logic once a minute
         
     | 
| 
      
 32 
     | 
    
         
            +
                #
         
     | 
| 
      
 33 
     | 
    
         
            +
                def execute
         
     | 
| 
      
 34 
     | 
    
         
            +
                  if primary?
         
     | 
| 
      
 35 
     | 
    
         
            +
                    run_cron_jobs
         
     | 
| 
      
 36 
     | 
    
         
            +
                  else
         
     | 
| 
      
 37 
     | 
    
         
            +
                    primary = Cron::Server.where(state: STATE_PRIMARY).first
         
     | 
| 
      
 38 
     | 
    
         
            +
                    if primary.blank? || primary.dead?
         
     | 
| 
      
 39 
     | 
    
         
            +
                      become_primary
         
     | 
| 
      
 40 
     | 
    
         
            +
                      run_cron_jobs
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
                  end
         
     | 
| 
      
 43 
     | 
    
         
            +
                  time_to_next_run
         
     | 
| 
      
 44 
     | 
    
         
            +
                rescue StandardError => error
         
     | 
| 
      
 45 
     | 
    
         
            +
                  App47Logger.log_error 'Unable to run cron jobs', error
         
     | 
| 
      
 46 
     | 
    
         
            +
                  time_to_next_run
         
     | 
| 
      
 47 
     | 
    
         
            +
                ensure
         
     | 
| 
      
 48 
     | 
    
         
            +
                  check_in
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                def run_cron_jobs
         
     | 
| 
      
 52 
     | 
    
         
            +
                  run_jobs
         
     | 
| 
      
 53 
     | 
    
         
            +
                  check_auto_scale
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                #
         
     | 
| 
      
 57 
     | 
    
         
            +
                # Run all cron tab jobs
         
     | 
| 
      
 58 
     | 
    
         
            +
                #
         
     | 
| 
      
 59 
     | 
    
         
            +
                def run_jobs
         
     | 
| 
      
 60 
     | 
    
         
            +
                  now = Time.now.utc
         
     | 
| 
      
 61 
     | 
    
         
            +
                  CronTab.all.each { |tab| tab.run if tab.time_to_run?(now) }
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                #
         
     | 
| 
      
 65 
     | 
    
         
            +
                # Determine the next minute to run,
         
     | 
| 
      
 66 
     | 
    
         
            +
                #
         
     | 
| 
      
 67 
     | 
    
         
            +
                def time_to_next_run
         
     | 
| 
      
 68 
     | 
    
         
            +
                  60 - Time.now.utc.to_i % 60
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                #
         
     | 
| 
      
 72 
     | 
    
         
            +
                # Find a record for this server
         
     | 
| 
      
 73 
     | 
    
         
            +
                #
         
     | 
| 
      
 74 
     | 
    
         
            +
                def self.find_or_create_server
         
     | 
| 
      
 75 
     | 
    
         
            +
                  Cron::Server.find_or_create_by!(host_name: Socket.gethostname, pid: Process.pid)
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
             
     | 
| 
      
 78 
     | 
    
         
            +
                #
         
     | 
| 
      
 79 
     | 
    
         
            +
                # Find a the current master
         
     | 
| 
      
 80 
     | 
    
         
            +
                #
         
     | 
| 
      
 81 
     | 
    
         
            +
                def self.primary_server
         
     | 
| 
      
 82 
     | 
    
         
            +
                  Cron::Server.where(state: STATE_PRIMARY).first
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                #
         
     | 
| 
      
 86 
     | 
    
         
            +
                # Warm up a server on the next evaluation
         
     | 
| 
      
 87 
     | 
    
         
            +
                #
         
     | 
| 
      
 88 
     | 
    
         
            +
                def self.warm_up_server
         
     | 
| 
      
 89 
     | 
    
         
            +
                  return unless SystemConfiguration.auto_scaling_configured?
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                  primary_server.auto_scale([primary_server.desired_server_count + 1, 10].min)
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                #
         
     | 
| 
      
 95 
     | 
    
         
            +
                # Become primary, making others secondary
         
     | 
| 
      
 96 
     | 
    
         
            +
                #
         
     | 
| 
      
 97 
     | 
    
         
            +
                def become_primary
         
     | 
| 
      
 98 
     | 
    
         
            +
                  Cron::Server.each(&:become_secondary)
         
     | 
| 
      
 99 
     | 
    
         
            +
                  # sleep a small amount of time to randomize a new primary
         
     | 
| 
      
 100 
     | 
    
         
            +
                  sleep rand(1..15)
         
     | 
| 
      
 101 
     | 
    
         
            +
                  # Check to see if another node already became primary
         
     | 
| 
      
 102 
     | 
    
         
            +
                  primary = Cron::Server.primary_server
         
     | 
| 
      
 103 
     | 
    
         
            +
                  return if primary.present? && primary.alive?
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
      
 105 
     | 
    
         
            +
                  # no one else is in, so become primary
         
     | 
| 
      
 106 
     | 
    
         
            +
                  update_attributes! state: STATE_PRIMARY, last_check_in_at: Time.now.utc
         
     | 
| 
      
 107 
     | 
    
         
            +
                end
         
     | 
| 
      
 108 
     | 
    
         
            +
             
     | 
| 
      
 109 
     | 
    
         
            +
                #
         
     | 
| 
      
 110 
     | 
    
         
            +
                # Become secondary node
         
     | 
| 
      
 111 
     | 
    
         
            +
                #
         
     | 
| 
      
 112 
     | 
    
         
            +
                def become_secondary(user = nil)
         
     | 
| 
      
 113 
     | 
    
         
            +
                  if user.present?
         
     | 
| 
      
 114 
     | 
    
         
            +
                    update_attributes_and_log! user, state: STATE_SECONDARY
         
     | 
| 
      
 115 
     | 
    
         
            +
                  else
         
     | 
| 
      
 116 
     | 
    
         
            +
                    update_attributes! state: STATE_SECONDARY
         
     | 
| 
      
 117 
     | 
    
         
            +
                  end
         
     | 
| 
      
 118 
     | 
    
         
            +
                end
         
     | 
| 
      
 119 
     | 
    
         
            +
             
     | 
| 
      
 120 
     | 
    
         
            +
                #
         
     | 
| 
      
 121 
     | 
    
         
            +
                # Am I the primary server
         
     | 
| 
      
 122 
     | 
    
         
            +
                #
         
     | 
| 
      
 123 
     | 
    
         
            +
                def primary?
         
     | 
| 
      
 124 
     | 
    
         
            +
                  alive? && STATE_PRIMARY.eql?(state)
         
     | 
| 
      
 125 
     | 
    
         
            +
                end
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                #
         
     | 
| 
      
 128 
     | 
    
         
            +
                # Am I a secondary server
         
     | 
| 
      
 129 
     | 
    
         
            +
                #
         
     | 
| 
      
 130 
     | 
    
         
            +
                def secondary?
         
     | 
| 
      
 131 
     | 
    
         
            +
                  STATE_SECONDARY.eql?(state)
         
     | 
| 
      
 132 
     | 
    
         
            +
                end
         
     | 
| 
      
 133 
     | 
    
         
            +
             
     | 
| 
      
 134 
     | 
    
         
            +
                #
         
     | 
| 
      
 135 
     | 
    
         
            +
                # Return true if I've reported in the last two minutes
         
     | 
| 
      
 136 
     | 
    
         
            +
                #
         
     | 
| 
      
 137 
     | 
    
         
            +
                def alive?
         
     | 
| 
      
 138 
     | 
    
         
            +
                  last_check_in_at >= 90.seconds.ago.utc
         
     | 
| 
      
 139 
     | 
    
         
            +
                end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
                #
         
     | 
| 
      
 142 
     | 
    
         
            +
                # Is the server dead, meaning is it not reporting within the last two minutes
         
     | 
| 
      
 143 
     | 
    
         
            +
                #
         
     | 
| 
      
 144 
     | 
    
         
            +
                def dead?
         
     | 
| 
      
 145 
     | 
    
         
            +
                  !alive?
         
     | 
| 
      
 146 
     | 
    
         
            +
                end
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                #
         
     | 
| 
      
 149 
     | 
    
         
            +
                # Perform a check in for the server
         
     | 
| 
      
 150 
     | 
    
         
            +
                #
         
     | 
| 
      
 151 
     | 
    
         
            +
                def check_in
         
     | 
| 
      
 152 
     | 
    
         
            +
                  set last_check_in_at: Time.now.utc
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                #
         
     | 
| 
      
 156 
     | 
    
         
            +
                # Auto scale environment
         
     | 
| 
      
 157 
     | 
    
         
            +
                #
         
     | 
| 
      
 158 
     | 
    
         
            +
                def check_auto_scale
         
     | 
| 
      
 159 
     | 
    
         
            +
                  return unless SystemConfiguration.auto_scaling_configured?
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
                  if delayed_jobs_count.eql?(0)
         
     | 
| 
      
 162 
     | 
    
         
            +
                    handle_zero_job_count
         
     | 
| 
      
 163 
     | 
    
         
            +
                  else
         
     | 
| 
      
 164 
     | 
    
         
            +
                    handle_auto_scale_jobs
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
                #
         
     | 
| 
      
 169 
     | 
    
         
            +
                # Returns the AWS AutoScaling Client
         
     | 
| 
      
 170 
     | 
    
         
            +
                #
         
     | 
| 
      
 171 
     | 
    
         
            +
                def client
         
     | 
| 
      
 172 
     | 
    
         
            +
                  credentials = { access_key_id: sys_config.access_key_id,
         
     | 
| 
      
 173 
     | 
    
         
            +
                                  secret_access_key: sys_config.secret_access_key,
         
     | 
| 
      
 174 
     | 
    
         
            +
                                  region: sys_config.region }
         
     | 
| 
      
 175 
     | 
    
         
            +
                  @client ||= Aws::AutoScaling::Client.new(credentials)
         
     | 
| 
      
 176 
     | 
    
         
            +
                end
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
                def sys_config
         
     | 
| 
      
 179 
     | 
    
         
            +
                  @sys_config ||= SystemConfiguration.configuration
         
     | 
| 
      
 180 
     | 
    
         
            +
                end
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                #
         
     | 
| 
      
 183 
     | 
    
         
            +
                # Returns the AutoScalingGroup associated with the account
         
     | 
| 
      
 184 
     | 
    
         
            +
                #
         
     | 
| 
      
 185 
     | 
    
         
            +
                def auto_scaling_group
         
     | 
| 
      
 186 
     | 
    
         
            +
                  filter = { auto_scaling_group_names: [sys_config.auto_scaling_group_name] }
         
     | 
| 
      
 187 
     | 
    
         
            +
                  @auto_scaling_group ||= client.describe_auto_scaling_groups(filter).auto_scaling_groups.first
         
     | 
| 
      
 188 
     | 
    
         
            +
                end
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                #
         
     | 
| 
      
 191 
     | 
    
         
            +
                # Returns a count of the Delayed Jobs in queue that have not failed
         
     | 
| 
      
 192 
     | 
    
         
            +
                #
         
     | 
| 
      
 193 
     | 
    
         
            +
                def delayed_jobs_count
         
     | 
| 
      
 194 
     | 
    
         
            +
                  @delayed_jobs_count ||= Delayed::Backend::Mongoid::Job.where(failed_at: nil).read(mode: :primary).count
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                #
         
     | 
| 
      
 198 
     | 
    
         
            +
                # Returns the current value of 'desired capacity' for the AutoScalingGroup
         
     | 
| 
      
 199 
     | 
    
         
            +
                #
         
     | 
| 
      
 200 
     | 
    
         
            +
                def current_desired_capacity
         
     | 
| 
      
 201 
     | 
    
         
            +
                  current = auto_scaling_group.desired_capacity
         
     | 
| 
      
 202 
     | 
    
         
            +
                  set current_server_count: current
         
     | 
| 
      
 203 
     | 
    
         
            +
                  current
         
     | 
| 
      
 204 
     | 
    
         
            +
                rescue StandardError
         
     | 
| 
      
 205 
     | 
    
         
            +
                  0
         
     | 
| 
      
 206 
     | 
    
         
            +
                end
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
                #
         
     | 
| 
      
 209 
     | 
    
         
            +
                # Calls the 'auto_scale' method with a 'desired_count' of 0 unless the capacity is already at 0
         
     | 
| 
      
 210 
     | 
    
         
            +
                #
         
     | 
| 
      
 211 
     | 
    
         
            +
                def handle_zero_job_count
         
     | 
| 
      
 212 
     | 
    
         
            +
                  return if current_desired_capacity.eql?(0)
         
     | 
| 
      
 213 
     | 
    
         
            +
             
     | 
| 
      
 214 
     | 
    
         
            +
                  auto_scale
         
     | 
| 
      
 215 
     | 
    
         
            +
                end
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                #
         
     | 
| 
      
 218 
     | 
    
         
            +
                # Calls the 'auto_scale' method with a variable 'desired_count' based on how many jobs are running
         
     | 
| 
      
 219 
     | 
    
         
            +
                # We don't need any more workers if the job count is less than 1,000
         
     | 
| 
      
 220 
     | 
    
         
            +
                #
         
     | 
| 
      
 221 
     | 
    
         
            +
                def handle_auto_scale_jobs
         
     | 
| 
      
 222 
     | 
    
         
            +
                  return if delayed_jobs_count < 50
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                  case delayed_jobs_count
         
     | 
| 
      
 225 
     | 
    
         
            +
                  when 50..250
         
     | 
| 
      
 226 
     | 
    
         
            +
                    auto_scale(1)
         
     | 
| 
      
 227 
     | 
    
         
            +
                  when 251..500
         
     | 
| 
      
 228 
     | 
    
         
            +
                    auto_scale(2)
         
     | 
| 
      
 229 
     | 
    
         
            +
                  when 501..1_000
         
     | 
| 
      
 230 
     | 
    
         
            +
                    auto_scale(3)
         
     | 
| 
      
 231 
     | 
    
         
            +
                  when 1_001..2_000
         
     | 
| 
      
 232 
     | 
    
         
            +
                    auto_scale(4)
         
     | 
| 
      
 233 
     | 
    
         
            +
                  when 2_001..3_999
         
     | 
| 
      
 234 
     | 
    
         
            +
                    auto_scale(4)
         
     | 
| 
      
 235 
     | 
    
         
            +
                  when 4_000..7_999
         
     | 
| 
      
 236 
     | 
    
         
            +
                    auto_scale(5)
         
     | 
| 
      
 237 
     | 
    
         
            +
                  when 8_000..10_999
         
     | 
| 
      
 238 
     | 
    
         
            +
                    auto_scale(5)
         
     | 
| 
      
 239 
     | 
    
         
            +
                  when 11_000..13_999
         
     | 
| 
      
 240 
     | 
    
         
            +
                    auto_scale(6)
         
     | 
| 
      
 241 
     | 
    
         
            +
                  when 14_000..17_999
         
     | 
| 
      
 242 
     | 
    
         
            +
                    auto_scale(6)
         
     | 
| 
      
 243 
     | 
    
         
            +
                  else
         
     | 
| 
      
 244 
     | 
    
         
            +
                    auto_scale(7)
         
     | 
| 
      
 245 
     | 
    
         
            +
                  end
         
     | 
| 
      
 246 
     | 
    
         
            +
                end
         
     | 
| 
      
 247 
     | 
    
         
            +
             
     | 
| 
      
 248 
     | 
    
         
            +
                #
         
     | 
| 
      
 249 
     | 
    
         
            +
                # Sets the desired and minimum number of EC2 instances to run
         
     | 
| 
      
 250 
     | 
    
         
            +
                #
         
     | 
| 
      
 251 
     | 
    
         
            +
                def auto_scale(desired_count = 0)
         
     | 
| 
      
 252 
     | 
    
         
            +
                  set desired_server_count: desired_count
         
     | 
| 
      
 253 
     | 
    
         
            +
                  # Make sure we don't remove any workers with assigned jobs by accident
         
     | 
| 
      
 254 
     | 
    
         
            +
                  return if desired_count.positive? && desired_count <= current_desired_capacity
         
     | 
| 
      
 255 
     | 
    
         
            +
             
     | 
| 
      
 256 
     | 
    
         
            +
                  client.update_auto_scaling_group(auto_scaling_group_name: sys_config.auto_scaling_group_name,
         
     | 
| 
      
 257 
     | 
    
         
            +
                                                   min_size: desired_count,
         
     | 
| 
      
 258 
     | 
    
         
            +
                                                   desired_capacity: desired_count)
         
     | 
| 
      
 259 
     | 
    
         
            +
                end
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
                #
         
     | 
| 
      
 262 
     | 
    
         
            +
                # Look to make sure there is only one primary
         
     | 
| 
      
 263 
     | 
    
         
            +
                #
         
     | 
| 
      
 264 
     | 
    
         
            +
                def high_lander
         
     | 
| 
      
 265 
     | 
    
         
            +
                  return if secondary? # Don't need to check if not primary
         
     | 
| 
      
 266 
     | 
    
         
            +
             
     | 
| 
      
 267 
     | 
    
         
            +
                  primary = Cron::Server.where(state: STATE_PRIMARY).first
         
     | 
| 
      
 268 
     | 
    
         
            +
                  errors.add(:state, 'there can only be one primary') unless primary.blank? || primary.eql?(self)
         
     | 
| 
      
 269 
     | 
    
         
            +
                end
         
     | 
| 
      
 270 
     | 
    
         
            +
             
     | 
| 
      
 271 
     | 
    
         
            +
                #
         
     | 
| 
      
 272 
     | 
    
         
            +
                # Returns the count of active servers
         
     | 
| 
      
 273 
     | 
    
         
            +
                #
         
     | 
| 
      
 274 
     | 
    
         
            +
                def active_count
         
     | 
| 
      
 275 
     | 
    
         
            +
                  current_server_count
         
     | 
| 
      
 276 
     | 
    
         
            +
                end
         
     | 
| 
      
 277 
     | 
    
         
            +
             
     | 
| 
      
 278 
     | 
    
         
            +
                #
         
     | 
| 
      
 279 
     | 
    
         
            +
                # Returns the count of inactive servers
         
     | 
| 
      
 280 
     | 
    
         
            +
                #
         
     | 
| 
      
 281 
     | 
    
         
            +
                def inactive_count
         
     | 
| 
      
 282 
     | 
    
         
            +
                  desired_server_count
         
     | 
| 
      
 283 
     | 
    
         
            +
                end
         
     | 
| 
      
 284 
     | 
    
         
            +
              end
         
     | 
| 
      
 285 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Run in cron
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            module Cron
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Sync configuration with switchboard
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              class SwitchboardSyncConfiguration < Job
         
     | 
| 
      
 9 
     | 
    
         
            +
                cron_tab_entry :hourly
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Only run in environments where switchboard is configured
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                def self.valid_environment?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  SystemConfiguration.switchboard_configured?
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                # Cycle through all configuration keys
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                def perform
         
     | 
| 
      
 22 
     | 
    
         
            +
                  Rails.cache.reconnect
         
     | 
| 
      
 23 
     | 
    
         
            +
                  RestClient.get(switchboard_url,
         
     | 
| 
      
 24 
     | 
    
         
            +
                                 ACCESS_TOKEN: SystemConfiguration.switchboard_stack_api_token,
         
     | 
| 
      
 25 
     | 
    
         
            +
                                 content_type: 'application/json') do |response, _request, _result, &block|
         
     | 
| 
      
 26 
     | 
    
         
            +
                    case response.code
         
     | 
| 
      
 27 
     | 
    
         
            +
                    when 200
         
     | 
| 
      
 28 
     | 
    
         
            +
                      json = JSON.parse(response.body)
         
     | 
| 
      
 29 
     | 
    
         
            +
                      config = SystemConfiguration.configuration
         
     | 
| 
      
 30 
     | 
    
         
            +
                      json['results'].each { |key, value| update_config(config, key, value) }
         
     | 
| 
      
 31 
     | 
    
         
            +
                      config.save!
         
     | 
| 
      
 32 
     | 
    
         
            +
                    else
         
     | 
| 
      
 33 
     | 
    
         
            +
                      App47Logger.log_error "Unable to fetch switchboard config, #{response.inspect}"
         
     | 
| 
      
 34 
     | 
    
         
            +
                      response.return!(&block)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  end
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                #
         
     | 
| 
      
 40 
     | 
    
         
            +
                # First see if it's updateable against system config, then see if it's in JobCronTab
         
     | 
| 
      
 41 
     | 
    
         
            +
                #
         
     | 
| 
      
 42 
     | 
    
         
            +
                def update_config(config, key, value)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  config.send("#{key}=", value) if config.respond_to?("#{key}=")
         
     | 
| 
      
 44 
     | 
    
         
            +
                  return unless key.end_with?('_crontab')
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  name = key.chomp('_crontab')
         
     | 
| 
      
 47 
     | 
    
         
            +
                  tab = Cron::JobTab.from_string(name, value)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  if tab.present? && tab.valid?
         
     | 
| 
      
 49 
     | 
    
         
            +
                    tab.save
         
     | 
| 
      
 50 
     | 
    
         
            +
                    App47Logger.log_debug "Crontab #{name} updated with #{value}"
         
     | 
| 
      
 51 
     | 
    
         
            +
                  else
         
     | 
| 
      
 52 
     | 
    
         
            +
                    App47Logger.log_warn "Unable to update crontab #{name} updated with #{value}"
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
                end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                #
         
     | 
| 
      
 57 
     | 
    
         
            +
                # Generate the switchboard URL
         
     | 
| 
      
 58 
     | 
    
         
            +
                #
         
     | 
| 
      
 59 
     | 
    
         
            +
                def switchboard_url
         
     | 
| 
      
 60 
     | 
    
         
            +
                  [SystemConfiguration.switchboard_base_url,
         
     | 
| 
      
 61 
     | 
    
         
            +
                   'stacks',
         
     | 
| 
      
 62 
     | 
    
         
            +
                   SystemConfiguration.switchboard_stack_id,
         
     | 
| 
      
 63 
     | 
    
         
            +
                   'items.json'].join('/')
         
     | 
| 
      
 64 
     | 
    
         
            +
                end
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #
         
     | 
| 
      
 2 
     | 
    
         
            +
            # Run in cron
         
     | 
| 
      
 3 
     | 
    
         
            +
            #
         
     | 
| 
      
 4 
     | 
    
         
            +
            module Cron
         
     | 
| 
      
 5 
     | 
    
         
            +
              #
         
     | 
| 
      
 6 
     | 
    
         
            +
              # Cycle through all members and tell them to sync with with switchboard
         
     | 
| 
      
 7 
     | 
    
         
            +
              #
         
     | 
| 
      
 8 
     | 
    
         
            +
              class SwitchboardSyncModels <Job
         
     | 
| 
      
 9 
     | 
    
         
            +
                cron_tab_entry :daily
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                #
         
     | 
| 
      
 12 
     | 
    
         
            +
                # Only run in environments where switchboard is configured
         
     | 
| 
      
 13 
     | 
    
         
            +
                #
         
     | 
| 
      
 14 
     | 
    
         
            +
                def self.valid_environment?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  SystemConfiguration.switchboard_configured? && Web47core.config.switchboard_able_models.present?
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                #
         
     | 
| 
      
 19 
     | 
    
         
            +
                # Cycle through the collection and perform an upsert on it
         
     | 
| 
      
 20 
     | 
    
         
            +
                #
         
     | 
| 
      
 21 
     | 
    
         
            +
                def perform
         
     | 
| 
      
 22 
     | 
    
         
            +
                  Web47core.config.switchboard_able_models.each { |model| model.each(&:switchboard_upsert) }
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     |