dynamo-autoscale 0.3.6 → 0.4.1
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.
- data/.gitignore +1 -0
 - data/Gemfile +1 -0
 - data/Gemfile.lock +13 -8
 - data/README.md +5 -1
 - data/bin/dynamo-autoscale +30 -19
 - data/config/environment/common.rb +73 -18
 - data/config/environment/test.rb +25 -2
 - data/config/services/aws.rb +18 -2
 - data/config/services/logger.rb +20 -21
 - data/lib/dynamo-autoscale/actioner.rb +1 -0
 - data/lib/dynamo-autoscale/dynamo_actioner.rb +3 -2
 - data/lib/dynamo-autoscale/fake_poller.rb +40 -0
 - data/lib/dynamo-autoscale/log_collector.rb +18 -0
 - data/lib/dynamo-autoscale/logger.rb +5 -1
 - data/lib/dynamo-autoscale/metrics.rb +11 -28
 - data/lib/dynamo-autoscale/poller.rb +17 -6
 - data/lib/dynamo-autoscale/random_data_generator.rb +51 -0
 - data/lib/dynamo-autoscale/scale_report.rb +2 -2
 - data/lib/dynamo-autoscale/table_tracker.rb +56 -13
 - data/lib/dynamo-autoscale/unit_cost.rb +42 -14
 - data/lib/dynamo-autoscale/version.rb +1 -1
 - data/script/historic_data +1 -1
 - data/script/random_test +68 -0
 - data/script/test +4 -3
 - data/spec/config_spec.rb +72 -0
 - data/spec/dispatcher_spec.rb +56 -0
 - data/spec/helpers/environment_helper.rb +15 -0
 - data/spec/helpers/logger.rb +20 -0
 - data/spec/poller_spec.rb +39 -0
 - data/spec/spec_helper.rb +0 -3
 - data/spec/table_tracker_spec.rb +163 -0
 - data/spec/unit_cost_spec.rb +39 -0
 - metadata +13 -3
 
    
        data/.gitignore
    CHANGED
    
    
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                dynamo-autoscale (0. 
     | 
| 
      
 4 
     | 
    
         
            +
                dynamo-autoscale (0.4.1)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  activesupport
         
     | 
| 
       6 
6 
     | 
    
         
             
                  aws-sdk
         
     | 
| 
       7 
7 
     | 
    
         
             
                  colored
         
     | 
| 
         @@ -12,28 +12,28 @@ PATH 
     | 
|
| 
       12 
12 
     | 
    
         
             
            GEM
         
     | 
| 
       13 
13 
     | 
    
         
             
              remote: https://rubygems.org/
         
     | 
| 
       14 
14 
     | 
    
         
             
              specs:
         
     | 
| 
       15 
     | 
    
         
            -
                activesupport (4.0. 
     | 
| 
      
 15 
     | 
    
         
            +
                activesupport (4.0.2)
         
     | 
| 
       16 
16 
     | 
    
         
             
                  i18n (~> 0.6, >= 0.6.4)
         
     | 
| 
       17 
17 
     | 
    
         
             
                  minitest (~> 4.2)
         
     | 
| 
       18 
18 
     | 
    
         
             
                  multi_json (~> 1.3)
         
     | 
| 
       19 
19 
     | 
    
         
             
                  thread_safe (~> 0.1)
         
     | 
| 
       20 
20 
     | 
    
         
             
                  tzinfo (~> 0.3.37)
         
     | 
| 
       21 
21 
     | 
    
         
             
                atomic (1.1.14)
         
     | 
| 
       22 
     | 
    
         
            -
                aws-sdk (1. 
     | 
| 
      
 22 
     | 
    
         
            +
                aws-sdk (1.19.0)
         
     | 
| 
       23 
23 
     | 
    
         
             
                  json (~> 1.4)
         
     | 
| 
       24 
     | 
    
         
            -
                  nokogiri (< 1.6.0)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  nokogiri (>= 1.4.4, < 1.6.0)
         
     | 
| 
       25 
25 
     | 
    
         
             
                  uuidtools (~> 2.1)
         
     | 
| 
       26 
26 
     | 
    
         
             
                bond (0.4.3)
         
     | 
| 
       27 
27 
     | 
    
         
             
                coderay (1.0.9)
         
     | 
| 
       28 
28 
     | 
    
         
             
                colored (1.2)
         
     | 
| 
       29 
29 
     | 
    
         
             
                diff-lcs (1.2.4)
         
     | 
| 
       30 
     | 
    
         
            -
                i18n (0.6. 
     | 
| 
       31 
     | 
    
         
            -
                json (1.8. 
     | 
| 
      
 30 
     | 
    
         
            +
                i18n (0.6.9)
         
     | 
| 
      
 31 
     | 
    
         
            +
                json (1.8.1)
         
     | 
| 
       32 
32 
     | 
    
         
             
                mail (2.5.4)
         
     | 
| 
       33 
33 
     | 
    
         
             
                  mime-types (~> 1.16)
         
     | 
| 
       34 
34 
     | 
    
         
             
                  treetop (~> 1.4.8)
         
     | 
| 
       35 
35 
     | 
    
         
             
                method_source (0.8.1)
         
     | 
| 
       36 
     | 
    
         
            -
                mime-types (1. 
     | 
| 
      
 36 
     | 
    
         
            +
                mime-types (1.25)
         
     | 
| 
       37 
37 
     | 
    
         
             
                minitest (4.7.5)
         
     | 
| 
       38 
38 
     | 
    
         
             
                multi_json (1.8.0)
         
     | 
| 
       39 
39 
     | 
    
         
             
                nokogiri (1.5.10)
         
     | 
| 
         @@ -57,6 +57,10 @@ GEM 
     | 
|
| 
       57 
57 
     | 
    
         
             
                  diff-lcs (>= 1.1.3, < 2.0)
         
     | 
| 
       58 
58 
     | 
    
         
             
                rspec-mocks (2.13.1)
         
     | 
| 
       59 
59 
     | 
    
         
             
                ruby-prof (0.13.0)
         
     | 
| 
      
 60 
     | 
    
         
            +
                simplecov (0.7.1)
         
     | 
| 
      
 61 
     | 
    
         
            +
                  multi_json (~> 1.0)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  simplecov-html (~> 0.7.1)
         
     | 
| 
      
 63 
     | 
    
         
            +
                simplecov-html (0.7.1)
         
     | 
| 
       60 
64 
     | 
    
         
             
                slop (3.4.5)
         
     | 
| 
       61 
65 
     | 
    
         
             
                thread_safe (0.1.3)
         
     | 
| 
       62 
66 
     | 
    
         
             
                  atomic
         
     | 
| 
         @@ -64,7 +68,7 @@ GEM 
     | 
|
| 
       64 
68 
     | 
    
         
             
                treetop (1.4.14)
         
     | 
| 
       65 
69 
     | 
    
         
             
                  polyglot
         
     | 
| 
       66 
70 
     | 
    
         
             
                  polyglot (>= 0.3.1)
         
     | 
| 
       67 
     | 
    
         
            -
                tzinfo (0.3. 
     | 
| 
      
 71 
     | 
    
         
            +
                tzinfo (0.3.38)
         
     | 
| 
       68 
72 
     | 
    
         
             
                uuidtools (2.1.4)
         
     | 
| 
       69 
73 
     | 
    
         | 
| 
       70 
74 
     | 
    
         
             
            PLATFORMS
         
     | 
| 
         @@ -76,4 +80,5 @@ DEPENDENCIES 
     | 
|
| 
       76 
80 
     | 
    
         
             
              rake
         
     | 
| 
       77 
81 
     | 
    
         
             
              ripl
         
     | 
| 
       78 
82 
     | 
    
         
             
              rspec
         
     | 
| 
      
 83 
     | 
    
         
            +
              simplecov
         
     | 
| 
       79 
84 
     | 
    
         
             
              timecop
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -78,7 +78,11 @@ tables only. 
     | 
|
| 
       78 
78 
     | 
    
         
             
            :aws:
         
     | 
| 
       79 
79 
     | 
    
         
             
              :access_key_id:      "your_id"
         
     | 
| 
       80 
80 
     | 
    
         
             
              :secret_access_key:  "your_key"
         
     | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
              # Currently, dynamo-autoscale can only operate in one region at a time. If you
         
     | 
| 
      
 83 
     | 
    
         
            +
              # want to track tables in multiple regions, you will have to run multiple
         
     | 
| 
      
 84 
     | 
    
         
            +
              # instances of dynamo-autoscale.
         
     | 
| 
      
 85 
     | 
    
         
            +
              :region:             "us-east-1"
         
     | 
| 
       82 
86 
     | 
    
         | 
| 
       83 
87 
     | 
    
         
             
            # There are some example rulesets in the rulesets/ directory of this project.
         
     | 
| 
       84 
88 
     | 
    
         
             
            :ruleset: "path_to_your_ruleset.rb"
         
     | 
    
        data/bin/dynamo-autoscale
    CHANGED
    
    | 
         @@ -2,6 +2,8 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            require 'optparse'
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            USAGE_PATH = File.join(File.dirname(__FILE__), '..', 'USAGE')
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
       5 
7 
     | 
    
         
             
            # This OptionParser block is here to ensure the queries to --version are
         
     | 
| 
       6 
8 
     | 
    
         
             
            # actually fast and don't have to go through all of the Ruby requiring lark.
         
     | 
| 
       7 
9 
     | 
    
         
             
            begin
         
     | 
| 
         @@ -13,55 +15,64 @@ begin 
     | 
|
| 
       13 
15 
     | 
    
         
             
                end
         
     | 
| 
       14 
16 
     | 
    
         | 
| 
       15 
17 
     | 
    
         
             
                opts.on('--help', 'Shows this documentation.') do
         
     | 
| 
       16 
     | 
    
         
            -
                  puts File.read( 
     | 
| 
      
 18 
     | 
    
         
            +
                  puts File.read(USAGE_PATH)
         
     | 
| 
       17 
19 
     | 
    
         
             
                  exit 0
         
     | 
| 
       18 
20 
     | 
    
         
             
                end
         
     | 
| 
       19 
21 
     | 
    
         
             
              end.parse!
         
     | 
| 
       20 
22 
     | 
    
         
             
            rescue OptionParser::InvalidOption => e
         
     | 
| 
       21 
     | 
    
         
            -
              STDERR.puts 
     | 
| 
       22 
     | 
    
         
            -
              STDERR.puts
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
              STDERR.puts File.read(File.join(File.dirname(__FILE__), '..', 'USAGE'))
         
     | 
| 
      
 23 
     | 
    
         
            +
              STDERR.puts("#{e.message}\n\n#{File.read(USAGE_PATH)}")
         
     | 
| 
       25 
24 
     | 
    
         
             
              exit 1
         
     | 
| 
       26 
25 
     | 
    
         
             
            end
         
     | 
| 
       27 
26 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
             
     | 
| 
      
 27 
     | 
    
         
            +
            begin
         
     | 
| 
      
 28 
     | 
    
         
            +
              require_relative '../config/environment/common'
         
     | 
| 
      
 29 
     | 
    
         
            +
            rescue DynamoAutoscale::Error::InvalidConfigurationError => e
         
     | 
| 
      
 30 
     | 
    
         
            +
              STDERR.puts e.message
         
     | 
| 
      
 31 
     | 
    
         
            +
              exit 1
         
     | 
| 
      
 32 
     | 
    
         
            +
            end
         
     | 
| 
       29 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
            # Typing out DynamoAutoscale so many times is tedious.
         
     | 
| 
      
 35 
     | 
    
         
            +
            DA = DynamoAutoscale
         
     | 
| 
       30 
36 
     | 
    
         | 
| 
       31 
37 
     | 
    
         
             
            if ARGV[0]
         
     | 
| 
       32 
     | 
    
         
            -
               
     | 
| 
      
 38 
     | 
    
         
            +
              begin
         
     | 
| 
      
 39 
     | 
    
         
            +
                DA.setup_from_config(ARGV[0])
         
     | 
| 
      
 40 
     | 
    
         
            +
              rescue DA::Error::InvalidConfigurationError => e
         
     | 
| 
      
 41 
     | 
    
         
            +
                STDERR.puts e.message
         
     | 
| 
      
 42 
     | 
    
         
            +
                exit 1
         
     | 
| 
      
 43 
     | 
    
         
            +
              end
         
     | 
| 
       33 
44 
     | 
    
         
             
            elsif ARGV[0].nil?
         
     | 
| 
       34 
     | 
    
         
            -
              STDERR.puts File.read( 
     | 
| 
      
 45 
     | 
    
         
            +
              STDERR.puts File.read(USAGE_PATH)
         
     | 
| 
       35 
46 
     | 
    
         | 
| 
       36 
47 
     | 
    
         
             
              exit 1
         
     | 
| 
       37 
48 
     | 
    
         
             
            elsif ARGV[0] and !File.exists?(ARGV[0])
         
     | 
| 
       38 
49 
     | 
    
         
             
              STDERR.puts "Error: The path you specified is to a file that does not exist."
         
     | 
| 
       39 
50 
     | 
    
         
             
              STDERR.puts
         
     | 
| 
       40 
     | 
    
         
            -
              STDERR.puts File.read( 
     | 
| 
      
 51 
     | 
    
         
            +
              STDERR.puts File.read(USAGE_PATH)
         
     | 
| 
       41 
52 
     | 
    
         | 
| 
       42 
53 
     | 
    
         
             
              exit 1
         
     | 
| 
       43 
54 
     | 
    
         
             
            end
         
     | 
| 
       44 
55 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
             
     | 
| 
      
 56 
     | 
    
         
            +
            DA.logger.info "Ensuring tables exist in DynamoDB..."
         
     | 
| 
       46 
57 
     | 
    
         
             
            dynamo = AWS::DynamoDB.new
         
     | 
| 
       47 
58 
     | 
    
         | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
      
 59 
     | 
    
         
            +
            DA.poller_opts[:tables].select! do |table_name|
         
     | 
| 
       49 
60 
     | 
    
         
             
              if dynamo.tables[table_name].exists?
         
     | 
| 
       50 
61 
     | 
    
         
             
                true
         
     | 
| 
       51 
62 
     | 
    
         
             
              else
         
     | 
| 
       52 
     | 
    
         
            -
                 
     | 
| 
      
 63 
     | 
    
         
            +
                DA.logger.error "Table #{table_name} does not exist inside your DynamoDB."
         
     | 
| 
       53 
64 
     | 
    
         
             
                false
         
     | 
| 
       54 
65 
     | 
    
         
             
              end
         
     | 
| 
       55 
66 
     | 
    
         
             
            end
         
     | 
| 
       56 
67 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
      
 68 
     | 
    
         
            +
            DA.poller_class = DA::CWPoller
         
     | 
| 
       58 
69 
     | 
    
         | 
| 
       59 
     | 
    
         
            -
            unless  
     | 
| 
       60 
     | 
    
         
            -
               
     | 
| 
      
 70 
     | 
    
         
            +
            unless DA.config[:dry_run]
         
     | 
| 
      
 71 
     | 
    
         
            +
              DA.actioner_class = DA::DynamoActioner
         
     | 
| 
       61 
72 
     | 
    
         
             
            end
         
     | 
| 
       62 
73 
     | 
    
         | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
      
 74 
     | 
    
         
            +
            DA.logger.info "Finished setup. Backdating..."
         
     | 
| 
      
 75 
     | 
    
         
            +
            DA.poller.backdate
         
     | 
| 
       65 
76 
     | 
    
         | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
            DA.logger.info "Starting polling loop..."
         
     | 
| 
      
 78 
     | 
    
         
            +
            DA.poller.run
         
     | 
| 
         @@ -1,4 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require 'logger'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
       2 
3 
     | 
    
         
             
            require 'fileutils'
         
     | 
| 
       3 
4 
     | 
    
         
             
            require 'time'
         
     | 
| 
       4 
5 
     | 
    
         
             
            require 'csv'
         
     | 
| 
         @@ -18,14 +19,32 @@ require_relative '../../lib/dynamo-autoscale/actioner' 
     | 
|
| 
       18 
19 
     | 
    
         
             
            module DynamoAutoscale
         
     | 
| 
       19 
20 
     | 
    
         
             
              include DynamoAutoscale::Logger
         
     | 
| 
       20 
21 
     | 
    
         | 
| 
       21 
     | 
    
         
            -
               
     | 
| 
      
 22 
     | 
    
         
            +
              module Error
         
     | 
| 
      
 23 
     | 
    
         
            +
                InvalidConfigurationError = Class.new(StandardError)
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
       22 
25 
     | 
    
         | 
| 
       23 
26 
     | 
    
         
             
              def self.root
         
     | 
| 
       24 
     | 
    
         
            -
                File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
         
     | 
| 
      
 27 
     | 
    
         
            +
                @@root ||= File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
              def self.root_dir *args
         
     | 
| 
      
 31 
     | 
    
         
            +
                File.join(self.root, *args)
         
     | 
| 
      
 32 
     | 
    
         
            +
              end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
              def self.data_dir *args
         
     | 
| 
      
 35 
     | 
    
         
            +
                root_dir 'data', *args
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              def self.config_dir *args
         
     | 
| 
      
 39 
     | 
    
         
            +
                root_dir 'config', *args
         
     | 
| 
       25 
40 
     | 
    
         
             
              end
         
     | 
| 
       26 
41 
     | 
    
         | 
| 
       27 
     | 
    
         
            -
              def self. 
     | 
| 
       28 
     | 
    
         
            -
                 
     | 
| 
      
 42 
     | 
    
         
            +
              def self.rlib_dir *args
         
     | 
| 
      
 43 
     | 
    
         
            +
                root_dir 'rlib', *args
         
     | 
| 
      
 44 
     | 
    
         
            +
              end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
              def self.template_dir *args
         
     | 
| 
      
 47 
     | 
    
         
            +
                root_dir 'templates', *args
         
     | 
| 
       29 
48 
     | 
    
         
             
              end
         
     | 
| 
       30 
49 
     | 
    
         | 
| 
       31 
50 
     | 
    
         
             
              def self.config
         
     | 
| 
         @@ -36,18 +55,29 @@ module DynamoAutoscale 
     | 
|
| 
       36 
55 
     | 
    
         
             
                @@config = new_config
         
     | 
| 
       37 
56 
     | 
    
         
             
              end
         
     | 
| 
       38 
57 
     | 
    
         | 
| 
      
 58 
     | 
    
         
            +
              def self.require_in_order *files
         
     | 
| 
      
 59 
     | 
    
         
            +
                expand_paths(*files).each { |path| require path }
         
     | 
| 
      
 60 
     | 
    
         
            +
              end
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
              def self.load_in_order *files
         
     | 
| 
      
 63 
     | 
    
         
            +
                expand_paths(*files).each { |path| load path }
         
     | 
| 
      
 64 
     | 
    
         
            +
              end
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
       39 
66 
     | 
    
         
             
              def self.setup_from_config path, overrides = {}
         
     | 
| 
       40 
67 
     | 
    
         
             
                logger.debug "[setup] Loading config..."
         
     | 
| 
       41 
68 
     | 
    
         
             
                self.config = YAML.load_file(path).merge(overrides)
         
     | 
| 
       42 
69 
     | 
    
         | 
| 
       43 
70 
     | 
    
         
             
                if config[:tables].nil? or config[:tables].empty?
         
     | 
| 
       44 
     | 
    
         
            -
                   
     | 
| 
       45 
     | 
    
         
            -
                    ":tables section."
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
                  exit 1
         
     | 
| 
      
 71 
     | 
    
         
            +
                  raise Error::InvalidConfigurationError.new("You need to specify at " +
         
     | 
| 
      
 72 
     | 
    
         
            +
                    "least one table in your config's :tables section.")
         
     | 
| 
       48 
73 
     | 
    
         
             
                end
         
     | 
| 
       49 
74 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
                filters = config[:dry_run] 
     | 
| 
      
 75 
     | 
    
         
            +
                filters = if config[:dry_run]
         
     | 
| 
      
 76 
     | 
    
         
            +
                            DynamoAutoscale::LocalActioner.faux_provisioning_filters
         
     | 
| 
      
 77 
     | 
    
         
            +
                          else
         
     | 
| 
      
 78 
     | 
    
         
            +
                            []
         
     | 
| 
      
 79 
     | 
    
         
            +
                          end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
       51 
81 
     | 
    
         
             
                if filters.empty?
         
     | 
| 
       52 
82 
     | 
    
         
             
                  logger.debug "[setup] Not running as a dry run. Hitting production Dynamo."
         
     | 
| 
       53 
83 
     | 
    
         
             
                else
         
     | 
| 
         @@ -89,14 +119,11 @@ module DynamoAutoscale 
     | 
|
| 
       89 
119 
     | 
    
         
             
                DynamoAutoscale.load_services
         
     | 
| 
       90 
120 
     | 
    
         
             
              end
         
     | 
| 
       91 
121 
     | 
    
         | 
| 
       92 
     | 
    
         
            -
              def self.require_all path
         
     | 
| 
       93 
     | 
    
         
            -
                Dir[File.join(root, path, '*.rb')].each { |file| require file }
         
     | 
| 
       94 
     | 
    
         
            -
              end
         
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
122 
     | 
    
         
             
              def self.load_services
         
     | 
| 
       97 
     | 
    
         
            -
                 
     | 
| 
       98 
     | 
    
         
            -
                   
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
      
 123 
     | 
    
         
            +
                load_in_order(
         
     | 
| 
      
 124 
     | 
    
         
            +
                  'config/services/logger.rb',
         
     | 
| 
      
 125 
     | 
    
         
            +
                  'config/services/*.rb'
         
     | 
| 
      
 126 
     | 
    
         
            +
                )
         
     | 
| 
       100 
127 
     | 
    
         
             
              end
         
     | 
| 
       101 
128 
     | 
    
         | 
| 
       102 
129 
     | 
    
         
             
              def self.dispatcher= new_dispatcher
         
     | 
| 
         @@ -153,6 +180,10 @@ module DynamoAutoscale 
     | 
|
| 
       153 
180 
     | 
    
         
             
                end
         
     | 
| 
       154 
181 
     | 
    
         
             
              end
         
     | 
| 
       155 
182 
     | 
    
         | 
| 
      
 183 
     | 
    
         
            +
              def self.actioners= new_actioners
         
     | 
| 
      
 184 
     | 
    
         
            +
                @@actioners = new_actioners
         
     | 
| 
      
 185 
     | 
    
         
            +
              end
         
     | 
| 
      
 186 
     | 
    
         
            +
             
     | 
| 
       156 
187 
     | 
    
         
             
              def self.reset_tables
         
     | 
| 
       157 
188 
     | 
    
         
             
                @@tables = Hash.new { |h, k| h[k] = TableTracker.new(k) }
         
     | 
| 
       158 
189 
     | 
    
         
             
              end
         
     | 
| 
         @@ -180,9 +211,33 @@ module DynamoAutoscale 
     | 
|
| 
       180 
211 
     | 
    
         
             
              def self.current_table
         
     | 
| 
       181 
212 
     | 
    
         
             
                @@current_table ||= nil
         
     | 
| 
       182 
213 
     | 
    
         
             
              end
         
     | 
| 
      
 214 
     | 
    
         
            +
             
     | 
| 
      
 215 
     | 
    
         
            +
              private
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
              # Expands strings given to it as paths relative to the project root
         
     | 
| 
      
 218 
     | 
    
         
            +
              def self.expand_paths *files
         
     | 
| 
      
 219 
     | 
    
         
            +
                files.inject([]) do |memo, path|
         
     | 
| 
      
 220 
     | 
    
         
            +
                  full_path = root_dir(path)
         
     | 
| 
      
 221 
     | 
    
         
            +
             
     | 
| 
      
 222 
     | 
    
         
            +
                  if (paths = Dir.glob(full_path)).length > 0
         
     | 
| 
      
 223 
     | 
    
         
            +
                    memo += paths.select { |p| File.file?(p) }
         
     | 
| 
      
 224 
     | 
    
         
            +
                  elsif File.exist?("#{full_path}.rb")
         
     | 
| 
      
 225 
     | 
    
         
            +
                    memo << "#{full_path}.rb"
         
     | 
| 
      
 226 
     | 
    
         
            +
                  elsif File.exist?(full_path)
         
     | 
| 
      
 227 
     | 
    
         
            +
                    memo << full_path
         
     | 
| 
      
 228 
     | 
    
         
            +
                  else
         
     | 
| 
      
 229 
     | 
    
         
            +
                    logger.warn "[load] Could not load file #{full_path}"
         
     | 
| 
      
 230 
     | 
    
         
            +
                    STDERR.puts Kernel.caller
         
     | 
| 
      
 231 
     | 
    
         
            +
                    exit 1
         
     | 
| 
      
 232 
     | 
    
         
            +
                  end
         
     | 
| 
      
 233 
     | 
    
         
            +
             
     | 
| 
      
 234 
     | 
    
         
            +
                  memo
         
     | 
| 
      
 235 
     | 
    
         
            +
                end
         
     | 
| 
      
 236 
     | 
    
         
            +
              end
         
     | 
| 
       183 
237 
     | 
    
         
             
            end
         
     | 
| 
       184 
238 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
            DynamoAutoscale. 
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
      
 239 
     | 
    
         
            +
            DynamoAutoscale.require_in_order(
         
     | 
| 
      
 240 
     | 
    
         
            +
              'lib/dynamo-autoscale/**.rb',
         
     | 
| 
      
 241 
     | 
    
         
            +
            )
         
     | 
| 
       187 
242 
     | 
    
         | 
| 
       188 
243 
     | 
    
         
             
            DynamoAutoscale.load_services
         
     | 
    
        data/config/environment/test.rb
    CHANGED
    
    | 
         @@ -1,6 +1,29 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ENV['RACK_ENV'] = "test"
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # SimpleCov seems to get confused when you load a file into your code multiple
         
     | 
| 
      
 4 
     | 
    
         
            +
            # times. It will wipe all of its current data about that file when it gets
         
     | 
| 
      
 5 
     | 
    
         
            +
            # reloaded, so some of our coverage stats are less than they should be.
         
     | 
| 
      
 6 
     | 
    
         
            +
            require 'simplecov'
         
     | 
| 
      
 7 
     | 
    
         
            +
            SimpleCov.start
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
       2 
9 
     | 
    
         
             
            require_relative 'common'
         
     | 
| 
       3 
10 
     | 
    
         
             
            require 'timecop'
         
     | 
| 
       4 
11 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
            DynamoAutoscale.setup_from_config( 
     | 
| 
      
 12 
     | 
    
         
            +
            TEST_CONFIG_PATH = DynamoAutoscale.config_dir('dynamo-autoscale-test.yml')
         
     | 
| 
      
 13 
     | 
    
         
            +
            DynamoAutoscale.setup_from_config(TEST_CONFIG_PATH)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            DynamoAutoscale.require_in_order(
         
     | 
| 
      
 16 
     | 
    
         
            +
              'spec/helpers/**.rb'
         
     | 
| 
      
 17 
     | 
    
         
            +
            )
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            RSpec.configure do |config|
         
     | 
| 
      
 20 
     | 
    
         
            +
              config.include DynamoAutoscale::Helpers::LoggerHelper
         
     | 
| 
      
 21 
     | 
    
         
            +
              config.include DynamoAutoscale::Helpers::EnvironmentHelper
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
              config.before(:each) do
         
     | 
| 
      
 24 
     | 
    
         
            +
                DynamoAutoscale.reset_tables
         
     | 
| 
      
 25 
     | 
    
         
            +
                DynamoAutoscale.dispatcher = nil
         
     | 
| 
      
 26 
     | 
    
         
            +
                DynamoAutoscale.actioners  = nil
         
     | 
| 
      
 27 
     | 
    
         
            +
                DynamoAutoscale.poller     = nil
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
    
        data/config/services/aws.rb
    CHANGED
    
    | 
         @@ -1,3 +1,19 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            if DynamoAutoscale.config[:aws]
         
     | 
| 
       2 
     | 
    
         
            -
               
     | 
| 
      
 1 
     | 
    
         
            +
            if config = DynamoAutoscale.config[:aws]
         
     | 
| 
      
 2 
     | 
    
         
            +
              valid_regions = [
         
     | 
| 
      
 3 
     | 
    
         
            +
                "us-east-1", "us-west-1", "us-west-2", "eu-west-1", "ap-southeast-1",
         
     | 
| 
      
 4 
     | 
    
         
            +
                "ap-southeast-2", "ap-northeast-1", "sa-east-1",
         
     | 
| 
      
 5 
     | 
    
         
            +
              ]
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
              unless config[:region]
         
     | 
| 
      
 8 
     | 
    
         
            +
                raise DynamoAutoscale::Error::InvalidConfigurationError.new("You must " +
         
     | 
| 
      
 9 
     | 
    
         
            +
                  "specify a :region key in the :aws section of your dynamo-autoscale " +
         
     | 
| 
      
 10 
     | 
    
         
            +
                  "configuration file!")
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              unless valid_regions.include?(config[:region])
         
     | 
| 
      
 14 
     | 
    
         
            +
                DynamoAutoscale::Logger.logger.warn "Specified region \"#{config[:region]}\"" +
         
     | 
| 
      
 15 
     | 
    
         
            +
                  " does not appear in the valid list of regions. Proceed with caution."
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              AWS.config(config)
         
     | 
| 
       3 
19 
     | 
    
         
             
            end
         
     | 
    
        data/config/services/logger.rb
    CHANGED
    
    | 
         @@ -1,31 +1,30 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            config = DynamoAutoscale.config[:logger] 
     | 
| 
      
 1 
     | 
    
         
            +
            if config = DynamoAutoscale.config[:logger]
         
     | 
| 
      
 2 
     | 
    
         
            +
              if config[:sync]
         
     | 
| 
      
 3 
     | 
    
         
            +
                STDOUT.sync = true
         
     | 
| 
      
 4 
     | 
    
         
            +
                STDERR.sync = true
         
     | 
| 
      
 5 
     | 
    
         
            +
              end
         
     | 
| 
       2 
6 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            if config[: 
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
     | 
    
         
            -
             
     | 
| 
       6 
     | 
    
         
            -
            end
         
     | 
| 
      
 7 
     | 
    
         
            +
              if config[:log_to]
         
     | 
| 
      
 8 
     | 
    
         
            +
                STDOUT.reopen(config[:log_to])
         
     | 
| 
      
 9 
     | 
    
         
            +
                STDERR.reopen(config[:log_to])
         
     | 
| 
      
 10 
     | 
    
         
            +
              end
         
     | 
| 
       7 
11 
     | 
    
         | 
| 
       8 
     | 
    
         
            -
             
     | 
| 
       9 
     | 
    
         
            -
              STDOUT.reopen(config[:log_to])
         
     | 
| 
       10 
     | 
    
         
            -
              STDERR.reopen(config[:log_to])
         
     | 
| 
       11 
     | 
    
         
            -
            end
         
     | 
| 
      
 12 
     | 
    
         
            +
              DynamoAutoscale::Logger.logger = ::Logger.new(STDOUT)
         
     | 
| 
       12 
13 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
      
 14 
     | 
    
         
            +
              if config[:style] == "pretty"
         
     | 
| 
      
 15 
     | 
    
         
            +
                DynamoAutoscale::Logger.logger.formatter = DynamoAutoscale::PrettyFormatter.new
         
     | 
| 
      
 16 
     | 
    
         
            +
              else
         
     | 
| 
      
 17 
     | 
    
         
            +
                DynamoAutoscale::Logger.logger.formatter = DynamoAutoscale::StandardFormatter.new
         
     | 
| 
      
 18 
     | 
    
         
            +
              end
         
     | 
| 
       14 
19 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
            if config[: 
     | 
| 
       16 
     | 
    
         
            -
             
     | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
              DynamoAutoscale::Logger.logger.formatter = DynamoAutoscale::StandardFormatter.new
         
     | 
| 
      
 20 
     | 
    
         
            +
              if config[:level]
         
     | 
| 
      
 21 
     | 
    
         
            +
                DynamoAutoscale::Logger.logger.level = ::Logger.const_get(config[:level])
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
       19 
23 
     | 
    
         
             
            end
         
     | 
| 
       20 
24 
     | 
    
         | 
| 
       21 
25 
     | 
    
         
             
            if ENV['DEBUG']
         
     | 
| 
       22 
26 
     | 
    
         
             
              DynamoAutoscale::Logger.logger.level = ::Logger::DEBUG
         
     | 
| 
       23 
     | 
    
         
            -
             
     | 
| 
       24 
     | 
    
         
            -
             
     | 
| 
       25 
     | 
    
         
            -
            end
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
            if ENV['SILENT']
         
     | 
| 
      
 27 
     | 
    
         
            +
              AWS.config(logger: DynamoAutoscale::Logger.logger)
         
     | 
| 
      
 28 
     | 
    
         
            +
            elsif ENV['SILENT']
         
     | 
| 
       28 
29 
     | 
    
         
             
              DynamoAutoscale::Logger.logger.level = ::Logger::FATAL
         
     | 
| 
       29 
30 
     | 
    
         
             
            end
         
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
            AWS.config(logger: DynamoAutoscale::Logger.logger) if ENV['DEBUG']
         
     | 
| 
         @@ -86,13 +86,14 @@ module DynamoAutoscale 
     | 
|
| 
       86 
86 
     | 
    
         
             
                  aws_description = self.class.describe_table(table)
         
     | 
| 
       87 
87 
     | 
    
         
             
                  decreases_today = aws_description[:provisioned_throughput][:number_of_decreases_today]
         
     | 
| 
       88 
88 
     | 
    
         | 
| 
       89 
     | 
    
         
            -
                  downscales( 
     | 
| 
      
 89 
     | 
    
         
            +
                  downscales(decreases_today)
         
     | 
| 
       90 
90 
     | 
    
         
             
                  logger.warn "[#{e.class}] #{e.message}"
         
     | 
| 
       91 
91 
     | 
    
         
             
                  return false
         
     | 
| 
       92 
92 
     | 
    
         
             
                end
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
       94 
94 
     | 
    
         
             
                def self.describe_table table
         
     | 
| 
       95 
     | 
    
         
            -
                   
     | 
| 
      
 95 
     | 
    
         
            +
                  client = AWS::DynamoDB::Client.new(:api_version => '2012-08-10')
         
     | 
| 
      
 96 
     | 
    
         
            +
                  data   = client.describe_table(table_name: table.name)
         
     | 
| 
       96 
97 
     | 
    
         | 
| 
       97 
98 
     | 
    
         
             
                  data[:table]
         
     | 
| 
       98 
99 
     | 
    
         
             
                end
         
     |