glowworm 0.3.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 +15 -0
 - data/.gitignore +6 -0
 - data/.yardopts +1 -0
 - data/Gemfile +5 -0
 - data/Gemfile.lock +129 -0
 - data/LICENSE +19 -0
 - data/README.md +326 -0
 - data/Rakefile +29 -0
 - data/bin/basic_server_tester +311 -0
 - data/bin/em_server/config.ru +2 -0
 - data/bin/em_server/em_server.rb +19 -0
 - data/bin/em_server_tester +68 -0
 - data/bin/glowworm +90 -0
 - data/bin/load_tester +84 -0
 - data/ci_jobs/glowworm-continuous-deploy-next-staging/run.sh +10 -0
 - data/ci_jobs/glowworm-integrations/run.sh +15 -0
 - data/ci_jobs/glowworm-performance/run.sh +2 -0
 - data/ci_jobs/glowworm-robustness/run.sh +2 -0
 - data/ci_jobs/glowworm-units/run.sh +13 -0
 - data/ci_jobs/setup.sh +119 -0
 - data/example/example_server.ecology +6 -0
 - data/example/example_server.rb +32 -0
 - data/glowworm.gemspec +54 -0
 - data/lib/glowworm.rb +501 -0
 - data/lib/glowworm/em.rb +8 -0
 - data/lib/glowworm/no_bg.rb +2 -0
 - data/lib/glowworm/version.rb +3 -0
 - data/server/Gemfile +27 -0
 - data/server/Gemfile.lock +87 -0
 - data/server/PROTOCOL +39 -0
 - data/server/check_mk_checks/check_glowworm_server +43 -0
 - data/server/db_migrations/20111004214649_change_feature_accounts_to_string.rb +60 -0
 - data/server/db_migrations/20111028104546_add_value_to_account_set_features.rb +12 -0
 - data/server/db_migrations/20120217090636_add_fully_active_flag_to_features.rb +15 -0
 - data/server/example_test_data.rb +66 -0
 - data/server/glowworm_server.ecology.erb +16 -0
 - data/server/glowworm_server.rb +226 -0
 - data/server/run/server.sh +7 -0
 - data/server/server_test.rb +72 -0
 - data/server/version.rb +3 -0
 - data/test/integration/basic_server_test.rb +90 -0
 - data/test/integration/create_sqlite_data.rb +196 -0
 - data/test/integration/em_server_test.rb +68 -0
 - data/test/integration/gemfile_for_specific_glowworm_version +17 -0
 - data/test/integration/gemfile_for_specific_glowworm_version.lock +55 -0
 - data/test/integration/integration_test_helper.rb +153 -0
 - data/test/integration/load_test.rb +59 -0
 - data/test/integration/nginx.conf +23 -0
 - data/test/integration/server_test.ecology.erb +6 -0
 - data/test/test_helper.rb +47 -0
 - data/test/units/em_test.rb +41 -0
 - data/test/units/feature_flag_test.rb +297 -0
 - data/test/units/no_bg_test.rb +40 -0
 - data/test/units/request_test.rb +51 -0
 - metadata +410 -0
 
    
        data/lib/glowworm/em.rb
    ADDED
    
    
    
        data/server/Gemfile
    ADDED
    
    | 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            source "http://rubygems.org"
         
     | 
| 
      
 2 
     | 
    
         
            +
            source "http://gems.ooyala.com"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            gem "capistrano"
         
     | 
| 
      
 5 
     | 
    
         
            +
            gem "ecology", "~>0.0.14"
         
     | 
| 
      
 6 
     | 
    
         
            +
            gem "multi_json", "~>1.3.2"
         
     | 
| 
      
 7 
     | 
    
         
            +
            gem "mysql"  # Should be mysql2, but sequel doesn't support it right now
         
     | 
| 
      
 8 
     | 
    
         
            +
            gem "railsless-deploy", :require => nil  # see https://github.com/leehambley/railsless-deploy#bundler-usage
         
     | 
| 
      
 9 
     | 
    
         
            +
            gem "sequel"
         
     | 
| 
      
 10 
     | 
    
         
            +
            gem "termite", '~>0.0.14'
         
     | 
| 
      
 11 
     | 
    
         
            +
            gem "yajl-ruby"   # This is much faster than MultiJson's default OKJson
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            group :deploy do
         
     | 
| 
      
 14 
     | 
    
         
            +
              gem "email-utils"
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            group :test do
         
     | 
| 
      
 18 
     | 
    
         
            +
              gem "sqlite3"
         
     | 
| 
      
 19 
     | 
    
         
            +
              gem "trollop"
         
     | 
| 
      
 20 
     | 
    
         
            +
              gem "glowworm"
         
     | 
| 
      
 21 
     | 
    
         
            +
              gem "minitap",  ">= 0.3.5"
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            group :development do
         
     | 
| 
      
 25 
     | 
    
         
            +
              gem "pry"
         
     | 
| 
      
 26 
     | 
    
         
            +
              gem "httparty"
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
    
        data/server/Gemfile.lock
    ADDED
    
    | 
         @@ -0,0 +1,87 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            GEM
         
     | 
| 
      
 2 
     | 
    
         
            +
              remote: http://rubygems.org/
         
     | 
| 
      
 3 
     | 
    
         
            +
              remote: http://gems.ooyala.com/
         
     | 
| 
      
 4 
     | 
    
         
            +
              specs:
         
     | 
| 
      
 5 
     | 
    
         
            +
                ansi (1.4.3)
         
     | 
| 
      
 6 
     | 
    
         
            +
                capistrano (2.14.1)
         
     | 
| 
      
 7 
     | 
    
         
            +
                  highline
         
     | 
| 
      
 8 
     | 
    
         
            +
                  net-scp (>= 1.0.0)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  net-sftp (>= 2.0.0)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  net-ssh (>= 2.0.14)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  net-ssh-gateway (>= 1.1.0)
         
     | 
| 
      
 12 
     | 
    
         
            +
                chronic (0.9.0)
         
     | 
| 
      
 13 
     | 
    
         
            +
                coderay (1.0.8)
         
     | 
| 
      
 14 
     | 
    
         
            +
                ecology (0.0.18)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  erubis
         
     | 
| 
      
 16 
     | 
    
         
            +
                  multi_json
         
     | 
| 
      
 17 
     | 
    
         
            +
                email-utils (0.0.2)
         
     | 
| 
      
 18 
     | 
    
         
            +
                erubis (2.7.0)
         
     | 
| 
      
 19 
     | 
    
         
            +
                glowworm (0.1.8)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  ecology (~> 0.0.18)
         
     | 
| 
      
 21 
     | 
    
         
            +
                  hastur (~> 1.2.0)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  httparty
         
     | 
| 
      
 23 
     | 
    
         
            +
                  multi_json
         
     | 
| 
      
 24 
     | 
    
         
            +
                  termite (~> 0.0.13)
         
     | 
| 
      
 25 
     | 
    
         
            +
                  trollop
         
     | 
| 
      
 26 
     | 
    
         
            +
                hastur (1.2.18)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  chronic
         
     | 
| 
      
 28 
     | 
    
         
            +
                  multi_json (~> 1.3.2)
         
     | 
| 
      
 29 
     | 
    
         
            +
                highline (1.6.15)
         
     | 
| 
      
 30 
     | 
    
         
            +
                httparty (0.10.0)
         
     | 
| 
      
 31 
     | 
    
         
            +
                  multi_json (~> 1.0)
         
     | 
| 
      
 32 
     | 
    
         
            +
                  multi_xml
         
     | 
| 
      
 33 
     | 
    
         
            +
                json (1.7.6)
         
     | 
| 
      
 34 
     | 
    
         
            +
                method_source (0.8.1)
         
     | 
| 
      
 35 
     | 
    
         
            +
                minitap (0.3.5)
         
     | 
| 
      
 36 
     | 
    
         
            +
                  minitest
         
     | 
| 
      
 37 
     | 
    
         
            +
                  tapout (>= 0.3.0)
         
     | 
| 
      
 38 
     | 
    
         
            +
                minitest (4.4.0)
         
     | 
| 
      
 39 
     | 
    
         
            +
                multi_json (1.3.7)
         
     | 
| 
      
 40 
     | 
    
         
            +
                multi_xml (0.5.2)
         
     | 
| 
      
 41 
     | 
    
         
            +
                mysql (2.9.0)
         
     | 
| 
      
 42 
     | 
    
         
            +
                net-scp (1.0.4)
         
     | 
| 
      
 43 
     | 
    
         
            +
                  net-ssh (>= 1.99.1)
         
     | 
| 
      
 44 
     | 
    
         
            +
                net-sftp (2.0.5)
         
     | 
| 
      
 45 
     | 
    
         
            +
                  net-ssh (>= 2.0.9)
         
     | 
| 
      
 46 
     | 
    
         
            +
                net-ssh (2.6.3)
         
     | 
| 
      
 47 
     | 
    
         
            +
                net-ssh-gateway (1.1.0)
         
     | 
| 
      
 48 
     | 
    
         
            +
                  net-ssh (>= 1.99.1)
         
     | 
| 
      
 49 
     | 
    
         
            +
                pry (0.9.10)
         
     | 
| 
      
 50 
     | 
    
         
            +
                  coderay (~> 1.0.5)
         
     | 
| 
      
 51 
     | 
    
         
            +
                  method_source (~> 0.8)
         
     | 
| 
      
 52 
     | 
    
         
            +
                  slop (~> 3.3.1)
         
     | 
| 
      
 53 
     | 
    
         
            +
                railsless-deploy (1.0.2)
         
     | 
| 
      
 54 
     | 
    
         
            +
                rainbow (1.1.4)
         
     | 
| 
      
 55 
     | 
    
         
            +
                sequel (3.43.0)
         
     | 
| 
      
 56 
     | 
    
         
            +
                slop (3.3.3)
         
     | 
| 
      
 57 
     | 
    
         
            +
                sqlite3 (1.3.7)
         
     | 
| 
      
 58 
     | 
    
         
            +
                tapout (0.4.1)
         
     | 
| 
      
 59 
     | 
    
         
            +
                  ansi
         
     | 
| 
      
 60 
     | 
    
         
            +
                  json
         
     | 
| 
      
 61 
     | 
    
         
            +
                termite (0.0.20)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  ecology (~> 0.0.6)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  multi_json
         
     | 
| 
      
 64 
     | 
    
         
            +
                  rainbow (~> 1.1.3)
         
     | 
| 
      
 65 
     | 
    
         
            +
                trollop (2.0)
         
     | 
| 
      
 66 
     | 
    
         
            +
                yajl-ruby (1.1.0)
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
            PLATFORMS
         
     | 
| 
      
 69 
     | 
    
         
            +
              ruby
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            DEPENDENCIES
         
     | 
| 
      
 72 
     | 
    
         
            +
              capistrano
         
     | 
| 
      
 73 
     | 
    
         
            +
              ecology (~> 0.0.14)
         
     | 
| 
      
 74 
     | 
    
         
            +
              email-utils
         
     | 
| 
      
 75 
     | 
    
         
            +
              glowworm
         
     | 
| 
      
 76 
     | 
    
         
            +
              hastur (~> 1.2.0)
         
     | 
| 
      
 77 
     | 
    
         
            +
              httparty
         
     | 
| 
      
 78 
     | 
    
         
            +
              minitap (>= 0.3.5)
         
     | 
| 
      
 79 
     | 
    
         
            +
              multi_json (~> 1.3.2)
         
     | 
| 
      
 80 
     | 
    
         
            +
              mysql
         
     | 
| 
      
 81 
     | 
    
         
            +
              pry
         
     | 
| 
      
 82 
     | 
    
         
            +
              railsless-deploy
         
     | 
| 
      
 83 
     | 
    
         
            +
              sequel
         
     | 
| 
      
 84 
     | 
    
         
            +
              sqlite3
         
     | 
| 
      
 85 
     | 
    
         
            +
              termite (~> 0.0.14)
         
     | 
| 
      
 86 
     | 
    
         
            +
              trollop
         
     | 
| 
      
 87 
     | 
    
         
            +
              yajl-ruby
         
     | 
    
        data/server/PROTOCOL
    ADDED
    
    | 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Glowworm's wire protocol is intentionally very simple, and carried via
         
     | 
| 
      
 2 
     | 
    
         
            +
            HTTP requests.  Some goals, in order of importance:
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            * Simplicity, in order to achieve high Reliability and Availability
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Readability and Debuggability
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Not too unreasonably high bandwidth
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            A given request will be a read of a set of accounts and a set of
         
     | 
| 
      
 9 
     | 
    
         
            +
            features.  Usually, that is one of:
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * One feature for one account
         
     | 
| 
      
 12 
     | 
    
         
            +
            * One feature for all accounts
         
     | 
| 
      
 13 
     | 
    
         
            +
            * All features for one account
         
     | 
| 
      
 14 
     | 
    
         
            +
            * All features for all accounts
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            A request will return data in the following form:
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            * What account_sets the given account(s) are part of
         
     | 
| 
      
 19 
     | 
    
         
            +
            * The setting of each feature for each account_set
         
     | 
| 
      
 20 
     | 
    
         
            +
            * A list of override flags for specific account/feature combinations
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            That data is serialized as JSON, in the following form:
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            {
         
     | 
| 
      
 25 
     | 
    
         
            +
              "version": 1,
         
     | 
| 
      
 26 
     | 
    
         
            +
              "account_sets": {
         
     | 
| 
      
 27 
     | 
    
         
            +
                "13742": 1,  // account 13742 is part of account_set 1
         
     | 
| 
      
 28 
     | 
    
         
            +
                "19244": [7,8],  // account 19244 is part of account_sets 7 and 8
         
     | 
| 
      
 29 
     | 
    
         
            +
                //...
         
     | 
| 
      
 30 
     | 
    
         
            +
              },
         
     | 
| 
      
 31 
     | 
    
         
            +
              "features": {
         
     | 
| 
      
 32 
     | 
    
         
            +
                "turn_foo_widget_orange": [1, 2, 3, 4, 6, 7, 8], // account_sets for which this feature is active by default
         
     | 
| 
      
 33 
     | 
    
         
            +
                //...
         
     | 
| 
      
 34 
     | 
    
         
            +
              },
         
     | 
| 
      
 35 
     | 
    
         
            +
              "overrides": [
         
     | 
| 
      
 36 
     | 
    
         
            +
                "turn_foo_widget_orange": {"19244": false, "742": true},
         
     | 
| 
      
 37 
     | 
    
         
            +
                //...
         
     | 
| 
      
 38 
     | 
    
         
            +
              ]
         
     | 
| 
      
 39 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
            require "time"
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            SERVICE_NAME="GlowwormServerStatus"
         
     | 
| 
      
 5 
     | 
    
         
            +
            HOST="127.0.0.1"
         
     | 
| 
      
 6 
     | 
    
         
            +
            PORT=4999
         
     | 
| 
      
 7 
     | 
    
         
            +
            URI="/healthz"
         
     | 
| 
      
 8 
     | 
    
         
            +
            URL="http://#{HOST}:#{PORT}#{URI}"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            OUTPUT=`curl -I #{URL} 2> /dev/null`
         
     | 
| 
      
 11 
     | 
    
         
            +
            if OUTPUT.empty?
         
     | 
| 
      
 12 
     | 
    
         
            +
              puts "2 #{SERVICE_NAME} - Glowworm Nginx Server Down"
         
     | 
| 
      
 13 
     | 
    
         
            +
              exit
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            MOD_TIME=Time.parse `echo "#{OUTPUT}" |grep Last-Modified |sed -e 's/^Last-Modified: //'`
         
     | 
| 
      
 17 
     | 
    
         
            +
            HTTP_STATUS=OUTPUT.split[1].to_i
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            # TODO(jbhat): We need to check that output's time, which looks like: Thu, 18 Oct 2012 05:56:57 GMT,
         
     | 
| 
      
 20 
     | 
    
         
            +
            # is within 10 minutes of now. If we get a 404, we should return that the glowworm server is down.
         
     | 
| 
      
 21 
     | 
    
         
            +
            # If we get a 500, we should report that there is an error (shouldn't happen).
         
     | 
| 
      
 22 
     | 
    
         
            +
            # If we get a 200, but the file is more than 10 minutes ago, we should return an error of stale data.
         
     | 
| 
      
 23 
     | 
    
         
            +
            # If we get a 200 with fresh file, but the contents are not OOYALA GLOWWORM OK, we should error its contents.
         
     | 
| 
      
 24 
     | 
    
         
            +
            # Finally, we can return ok if the file has last been modified within 10 minutes, and contains the string.
         
     | 
| 
      
 25 
     | 
    
         
            +
            STATUS, OUTPUT_TEXT = case HTTP_STATUS
         
     | 
| 
      
 26 
     | 
    
         
            +
            when 200
         
     | 
| 
      
 27 
     | 
    
         
            +
              AGE=Time.now - MOD_TIME
         
     | 
| 
      
 28 
     | 
    
         
            +
              if(AGE > 600)
         
     | 
| 
      
 29 
     | 
    
         
            +
                [2, "Data Stale, healthz has not been updated in #{AGE} seconds."]
         
     | 
| 
      
 30 
     | 
    
         
            +
              else
         
     | 
| 
      
 31 
     | 
    
         
            +
                HEALTHZ_CONTENTS=`curl #{URL} 2> /dev/null`.chomp
         
     | 
| 
      
 32 
     | 
    
         
            +
                if HEALTHZ_CONTENTS =~ /OOYALA GLOWWORM OK/
         
     | 
| 
      
 33 
     | 
    
         
            +
                  [0, HEALTHZ_CONTENTS]
         
     | 
| 
      
 34 
     | 
    
         
            +
                else
         
     | 
| 
      
 35 
     | 
    
         
            +
                  [2, HEALTHZ_CONTENTS]
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
            when 404
         
     | 
| 
      
 39 
     | 
    
         
            +
              [2, "404 Glowworm Service Down"]
         
     | 
| 
      
 40 
     | 
    
         
            +
            else
         
     | 
| 
      
 41 
     | 
    
         
            +
              [2, "#{STATUS} Error Retrieving Healthz"]
         
     | 
| 
      
 42 
     | 
    
         
            +
            end
         
     | 
| 
      
 43 
     | 
    
         
            +
            puts "#{STATUS} #{SERVICE_NAME} - #{OUTPUT_TEXT}"
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            Sequel.migration do
         
     | 
| 
      
 2 
     | 
    
         
            +
              up do
         
     | 
| 
      
 3 
     | 
    
         
            +
                create_table(:account_sets) do
         
     | 
| 
      
 4 
     | 
    
         
            +
                  primary_key :id
         
     | 
| 
      
 5 
     | 
    
         
            +
                  Datetime :created_at, :null => false
         
     | 
| 
      
 6 
     | 
    
         
            +
                  Datetime :updated_at, :null => false
         
     | 
| 
      
 7 
     | 
    
         
            +
                  String :name, :null => false
         
     | 
| 
      
 8 
     | 
    
         
            +
                  String :description
         
     | 
| 
      
 9 
     | 
    
         
            +
                  index :name, :unique => true
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                create_table(:account_set_accounts) do
         
     | 
| 
      
 13 
     | 
    
         
            +
                  primary_key :id
         
     | 
| 
      
 14 
     | 
    
         
            +
                  Datetime :added_at, :null => false
         
     | 
| 
      
 15 
     | 
    
         
            +
                  Integer :account_set_id, :null => false
         
     | 
| 
      
 16 
     | 
    
         
            +
                  Integer :provider_id, :null => true
         
     | 
| 
      
 17 
     | 
    
         
            +
                  String :account, :limit => 10, :null => false
         
     | 
| 
      
 18 
     | 
    
         
            +
                  index :account_set_id
         
     | 
| 
      
 19 
     | 
    
         
            +
                  index :provider_id
         
     | 
| 
      
 20 
     | 
    
         
            +
                  index :account
         
     | 
| 
      
 21 
     | 
    
         
            +
                  index [:account_set_id, :account], :unique => true
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
                create_table(:features) do
         
     | 
| 
      
 25 
     | 
    
         
            +
                  primary_key :id
         
     | 
| 
      
 26 
     | 
    
         
            +
                  Datetime :created_at, :null => false
         
     | 
| 
      
 27 
     | 
    
         
            +
                  Datetime :updated_at, :null => false
         
     | 
| 
      
 28 
     | 
    
         
            +
                  String :name, :null => false
         
     | 
| 
      
 29 
     | 
    
         
            +
                  String :description
         
     | 
| 
      
 30 
     | 
    
         
            +
                  index :name, :unique => true
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                create_table(:account_set_features) do
         
     | 
| 
      
 34 
     | 
    
         
            +
                  primary_key :id
         
     | 
| 
      
 35 
     | 
    
         
            +
                  Datetime :added_at, :null => false
         
     | 
| 
      
 36 
     | 
    
         
            +
                  Integer :account_set_id, :null => false
         
     | 
| 
      
 37 
     | 
    
         
            +
                  Integer :feature_id, :null => false
         
     | 
| 
      
 38 
     | 
    
         
            +
                  index :feature_id
         
     | 
| 
      
 39 
     | 
    
         
            +
                  index [:account_set_id, :feature_id], :unique => true
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                create_table(:feature_account_overrides) do
         
     | 
| 
      
 43 
     | 
    
         
            +
                  Integer :provider_id, :null => true
         
     | 
| 
      
 44 
     | 
    
         
            +
                  String :account, :limit => 10, :null => false
         
     | 
| 
      
 45 
     | 
    
         
            +
                  Integer :feature_id, :null => false
         
     | 
| 
      
 46 
     | 
    
         
            +
                  Integer :value, :null => false
         
     | 
| 
      
 47 
     | 
    
         
            +
                  index :provider_id
         
     | 
| 
      
 48 
     | 
    
         
            +
                  index :feature_id
         
     | 
| 
      
 49 
     | 
    
         
            +
                  index [:account, :feature_id], :unique => true
         
     | 
| 
      
 50 
     | 
    
         
            +
                end
         
     | 
| 
      
 51 
     | 
    
         
            +
              end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
              down do
         
     | 
| 
      
 54 
     | 
    
         
            +
                drop_table :account_sets
         
     | 
| 
      
 55 
     | 
    
         
            +
                drop_table :account_set_accounts
         
     | 
| 
      
 56 
     | 
    
         
            +
                drop_table :features
         
     | 
| 
      
 57 
     | 
    
         
            +
                drop_table :account_set_features
         
     | 
| 
      
 58 
     | 
    
         
            +
                drop_table :feature_account_overrides
         
     | 
| 
      
 59 
     | 
    
         
            +
              end
         
     | 
| 
      
 60 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), "migration_helper")
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Sequel.migration do
         
     | 
| 
      
 4 
     | 
    
         
            +
              up do
         
     | 
| 
      
 5 
     | 
    
         
            +
                add_column :account_set_features, :value, Integer, :null => false, :default => 1
         
     | 
| 
      
 6 
     | 
    
         
            +
                self[:account_set_features].update(:value => 1)
         
     | 
| 
      
 7 
     | 
    
         
            +
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
              down do
         
     | 
| 
      
 10 
     | 
    
         
            +
                drop_column :account_set_features, :value
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,15 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require File.join(File.dirname(__FILE__), "migration_helper")
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Sequel.migration do
         
     | 
| 
      
 4 
     | 
    
         
            +
              up do
         
     | 
| 
      
 5 
     | 
    
         
            +
                alter_table(:features) do
         
     | 
| 
      
 6 
     | 
    
         
            +
                  add_column :fully_active, :boolean, :default => false
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              down do
         
     | 
| 
      
 11 
     | 
    
         
            +
                alter_table(:features) do
         
     | 
| 
      
 12 
     | 
    
         
            +
                  drop_column :fully_active
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "ecology"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "sequel"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "termite"
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Ecology.read("glowworm_server.ecology")
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            db_spec = Ecology.property("db") || "sqlite:glowworm_server.sqlite"
         
     | 
| 
      
 10 
     | 
    
         
            +
            DB = Sequel.connect db_spec
         
     | 
| 
      
 11 
     | 
    
         
            +
            MyLogger = Termite::Logger.new
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            if ARGV.include?("--clear")
         
     | 
| 
      
 14 
     | 
    
         
            +
              DB[:account_sets].delete
         
     | 
| 
      
 15 
     | 
    
         
            +
              DB[:features].delete
         
     | 
| 
      
 16 
     | 
    
         
            +
              DB[:account_set_accounts].delete
         
     | 
| 
      
 17 
     | 
    
         
            +
              DB[:account_set_features].delete
         
     | 
| 
      
 18 
     | 
    
         
            +
              DB[:feature_account_overrides].delete
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            DB[:account_sets].insert(:name => "Major", :description => "Major accounts", :id => 1,
         
     | 
| 
      
 22 
     | 
    
         
            +
                                     :created_at => Time.now, :updated_at => Time.now)
         
     | 
| 
      
 23 
     | 
    
         
            +
            DB[:account_sets].insert(:name => "Minor", :description => "Minor accounts", :id => 2,
         
     | 
| 
      
 24 
     | 
    
         
            +
                                     :created_at => Time.now, :updated_at => Time.now)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            DB[:account_set_accounts].insert(:account_set_id => 1, :account => "27366", :provider_id => 27366,
         
     | 
| 
      
 28 
     | 
    
         
            +
                                             :added_at => Time.now)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
            # Add account #35 to major accounts
         
     | 
| 
      
 31 
     | 
    
         
            +
            DB[:account_set_accounts].insert(:account_set_id => 1, :account => "35", :provider_id => 35,
         
     | 
| 
      
 32 
     | 
    
         
            +
                                             :added_at => Time.now)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
            # Add account #42 to minor accounts
         
     | 
| 
      
 35 
     | 
    
         
            +
            DB[:account_set_accounts].insert(:account_set_id => 2, :account => "42", :provider_id => 42,
         
     | 
| 
      
 36 
     | 
    
         
            +
                                             :added_at => Time.now)
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
            # Add account #49 to minor accounts
         
     | 
| 
      
 39 
     | 
    
         
            +
            DB[:account_set_accounts].insert(:account_set_id => 2, :account => "49", :provider_id => 49,
         
     | 
| 
      
 40 
     | 
    
         
            +
                                             :added_at => Time.now)
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            DB[:features].insert(:id => 100, :name => "video_rec", :description => "video_rec",
         
     | 
| 
      
 43 
     | 
    
         
            +
                                 :created_at => Time.now, :updated_at => Time.now)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            DB[:features].insert(:id => 1, :name => "foo_feature", :description => "foo",
         
     | 
| 
      
 47 
     | 
    
         
            +
                                 :created_at => Time.now, :updated_at => Time.now)
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            DB[:features].insert(:id => 2, :name => "new_signup_message", :description => "Use new signup message?",
         
     | 
| 
      
 50 
     | 
    
         
            +
                                 :created_at => Time.now, :updated_at => Time.now)
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            # Turn on foo_feature for major accounts, but not minor
         
     | 
| 
      
 53 
     | 
    
         
            +
            DB[:account_set_features].insert(:account_set_id => 1, :feature_id => 1,
         
     | 
| 
      
 54 
     | 
    
         
            +
                                             :added_at => Time.now)
         
     | 
| 
      
 55 
     | 
    
         
            +
            DB[:account_set_features].insert(:account_set_id => 1, :feature_id => 100,
         
     | 
| 
      
 56 
     | 
    
         
            +
                                             :added_at => Time.now)
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            if ARGV.include?("--new-signup")
         
     | 
| 
      
 59 
     | 
    
         
            +
              DB[:account_set_features].insert(:account_set_id => 1, :feature_id => 2,
         
     | 
| 
      
 60 
     | 
    
         
            +
                                               :added_at => Time.now)
         
     | 
| 
      
 61 
     | 
    
         
            +
            end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
            DB[:feature_account_overrides].insert(:account => "49", :provider_id => 49, :feature_id => 1,
         
     | 
| 
      
 64 
     | 
    
         
            +
                                                  :value => 1)
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
            puts "Added test data successfully!"
         
     | 
| 
         @@ -0,0 +1,16 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            <%
         
     | 
| 
      
 2 
     | 
    
         
            +
              begin
         
     | 
| 
      
 3 
     | 
    
         
            +
                require 'config/pushed_environment'
         
     | 
| 
      
 4 
     | 
    
         
            +
              rescue LoadError
         
     | 
| 
      
 5 
     | 
    
         
            +
                nil
         
     | 
| 
      
 6 
     | 
    
         
            +
              end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
              db_user = (DB_USER rescue "root")
         
     | 
| 
      
 9 
     | 
    
         
            +
              db_password = (DB_PASSWORD rescue "")
         
     | 
| 
      
 10 
     | 
    
         
            +
              db_location = (DB_LOCATION rescue "localhost")
         
     | 
| 
      
 11 
     | 
    
         
            +
            %>
         
     | 
| 
      
 12 
     | 
    
         
            +
            {
         
     | 
| 
      
 13 
     | 
    
         
            +
              "application": "glowworm_server",
         
     | 
| 
      
 14 
     | 
    
         
            +
              "db": "mysql://<%= db_user %>:<%= db_password %>@<%= db_location %>/vstreams",
         
     | 
| 
      
 15 
     | 
    
         
            +
              "cache_period": 300
         
     | 
| 
      
 16 
     | 
    
         
            +
            }
         
     | 
| 
         @@ -0,0 +1,226 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            #!/usr/bin/env ruby
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require "sequel"
         
     | 
| 
      
 4 
     | 
    
         
            +
            require "yajl"
         
     | 
| 
      
 5 
     | 
    
         
            +
            require "multi_json"
         
     | 
| 
      
 6 
     | 
    
         
            +
            require "ecology"
         
     | 
| 
      
 7 
     | 
    
         
            +
            require "termite"
         
     | 
| 
      
 8 
     | 
    
         
            +
            require "mysql"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            require_relative "config/pushed_environment"
         
     | 
| 
      
 11 
     | 
    
         
            +
            require "digest/md5"
         
     | 
| 
      
 12 
     | 
    
         
            +
            require "fileutils"
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            $LOAD_PATH << File.dirname(__FILE__)
         
     | 
| 
      
 15 
     | 
    
         
            +
            # Try to load the symlinked Glowworm version file, if it rsync'd
         
     | 
| 
      
 16 
     | 
    
         
            +
            (require "version") rescue nil
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            GLOWWORM_VERSION = (Glowworm::VERSION rescue "Unknown")
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            # Be explicit about engine
         
     | 
| 
      
 21 
     | 
    
         
            +
            MultiJson.use :yajl
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            # Calculate the md5 from the data passed in.  Assumes a hash with no md5 unless told otherwise
         
     | 
| 
      
 24 
     | 
    
         
            +
            def md5_from_hash(json_hash, options = { :json => false, :md5 => false })
         
     | 
| 
      
 25 
     | 
    
         
            +
              if options[:json]
         
     | 
| 
      
 26 
     | 
    
         
            +
                json_hash = MultiJson.load json_hash rescue {} # Assume empty hash if there is a problem
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
              json_hash.delete "md5" if options[:md5]
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              dumped_json = MultiJson.dump json_hash
         
     | 
| 
      
 32 
     | 
    
         
            +
              md5 = Digest::MD5.hexdigest(dumped_json)
         
     | 
| 
      
 33 
     | 
    
         
            +
              md5
         
     | 
| 
      
 34 
     | 
    
         
            +
            end
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
            # accounts - hash of account names to account set IDs
         
     | 
| 
      
 37 
     | 
    
         
            +
            # features - hash of feature names to arrays of account sets that set it
         
     | 
| 
      
 38 
     | 
    
         
            +
            # overrides - hash of feature names to hashes of {account => value}
         
     | 
| 
      
 39 
     | 
    
         
            +
            def format_json(accounts, features, overrides, fully_active)
         
     | 
| 
      
 40 
     | 
    
         
            +
              json_hash = {
         
     | 
| 
      
 41 
     | 
    
         
            +
                "version" => 1,
         
     | 
| 
      
 42 
     | 
    
         
            +
                "account_sets" => accounts,
         
     | 
| 
      
 43 
     | 
    
         
            +
                "features" => features,
         
     | 
| 
      
 44 
     | 
    
         
            +
                "overrides" => overrides,
         
     | 
| 
      
 45 
     | 
    
         
            +
                "fully_active" => fully_active,
         
     | 
| 
      
 46 
     | 
    
         
            +
              }
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              return json_hash
         
     | 
| 
      
 49 
     | 
    
         
            +
            end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
            class DBException < Exception
         
     | 
| 
      
 52 
     | 
    
         
            +
            end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
            def try_db
         
     | 
| 
      
 55 
     | 
    
         
            +
              yield
         
     | 
| 
      
 56 
     | 
    
         
            +
            rescue => e
         
     | 
| 
      
 57 
     | 
    
         
            +
              MyLogger.error "Error talking to database. Message: #{e.message}. Backtrace: #{e.backtrace}"
         
     | 
| 
      
 58 
     | 
    
         
            +
              raise DBException
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            def json_for_account_and_feature
         
     | 
| 
      
 62 
     | 
    
         
            +
              MyLogger.info "Querying DB for all accounts and features"
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              # If there has previously been a DB failure, try to reconnect right away
         
     | 
| 
      
 65 
     | 
    
         
            +
              try_db do
         
     | 
| 
      
 66 
     | 
    
         
            +
                DB.connect DB_SPEC
         
     | 
| 
      
 67 
     | 
    
         
            +
                @failures = 0
         
     | 
| 
      
 68 
     | 
    
         
            +
              end if @failures > 0
         
     | 
| 
      
 69 
     | 
    
         
            +
             
     | 
| 
      
 70 
     | 
    
         
            +
              # Right now, account_sets with no accounts won't be returned.  I think that's fine.
         
     | 
| 
      
 71 
     | 
    
         
            +
              accounts = {}
         
     | 
| 
      
 72 
     | 
    
         
            +
              try_db do
         
     | 
| 
      
 73 
     | 
    
         
            +
                DB[:account_set_accounts].each do |row|
         
     | 
| 
      
 74 
     | 
    
         
            +
                  accounts[row[:account]] ||= []
         
     | 
| 
      
 75 
     | 
    
         
            +
                  accounts[row[:account]] << row[:account_set_id].to_s
         
     | 
| 
      
 76 
     | 
    
         
            +
                end
         
     | 
| 
      
 77 
     | 
    
         
            +
              end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
              features = {}
         
     | 
| 
      
 80 
     | 
    
         
            +
              fully_active = {}
         
     | 
| 
      
 81 
     | 
    
         
            +
              try_db do
         
     | 
| 
      
 82 
     | 
    
         
            +
                DB[:features].left_join(:account_set_features, :feature_id => :id).each do |row|
         
     | 
| 
      
 83 
     | 
    
         
            +
                  features[row[:name]] ||= {}
         
     | 
| 
      
 84 
     | 
    
         
            +
                  features[row[:name]][row[:account_set_id].to_s] = row[:value] if row[:account_set_id]
         
     | 
| 
      
 85 
     | 
    
         
            +
                  fully_active[row[:name]] = row[:fully_active]
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
              overrides = {}
         
     | 
| 
      
 90 
     | 
    
         
            +
              try_db do
         
     | 
| 
      
 91 
     | 
    
         
            +
                DB[:feature_account_overrides].join(:features, :id => :feature_id).each do |row|
         
     | 
| 
      
 92 
     | 
    
         
            +
                  overrides[row[:name]] ||= {}
         
     | 
| 
      
 93 
     | 
    
         
            +
                  overrides[row[:name]][row[:account]] = row[:value]
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              json = format_json accounts, features, overrides, fully_active
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
              MyLogger.debug "Returning JSON: #{json}"
         
     | 
| 
      
 100 
     | 
    
         
            +
             
     | 
| 
      
 101 
     | 
    
         
            +
              [200, json]
         
     | 
| 
      
 102 
     | 
    
         
            +
            rescue DBException
         
     | 
| 
      
 103 
     | 
    
         
            +
              [500, ""]
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
            Ecology.read("glowworm_server.ecology")
         
     | 
| 
      
 107 
     | 
    
         
            +
            SERVING_DIRECTORY = Ecology.property("nginx_directory") || "#{SHARED_PATH}/www"
         
     | 
| 
      
 108 
     | 
    
         
            +
            LIVE_FILENAME = "#{SHARED_PATH}/server_is_live" unless defined? LIVE_FILENAME
         
     | 
| 
      
 109 
     | 
    
         
            +
            FileUtils.mkdir_p SERVING_DIRECTORY
         
     | 
| 
      
 110 
     | 
    
         
            +
            AIM_FILE = ENV['AIM_FILE'] || "#{SERVING_DIRECTORY}/all_if_modified"
         
     | 
| 
      
 111 
     | 
    
         
            +
            STATUSZ_FILE = "#{SERVING_DIRECTORY}/statusz"
         
     | 
| 
      
 112 
     | 
    
         
            +
            HEALTHZ_FILE = "#{SERVING_DIRECTORY}/healthz"
         
     | 
| 
      
 113 
     | 
    
         
            +
            DB_SPEC = Ecology.property("db") || "sqlite:glowworm_server.sqlite"
         
     | 
| 
      
 114 
     | 
    
         
            +
            cache_period = Ecology.property("cache_period") || 60
         
     | 
| 
      
 115 
     | 
    
         
            +
            MyLogger = Termite::Logger.new nil, nil, nil, :use_logger_prefix => true, :stderr_level => "info"
         
     | 
| 
      
 116 
     | 
    
         
            +
            @failures = 0
         
     | 
| 
      
 117 
     | 
    
         
            +
            begin
         
     | 
| 
      
 118 
     | 
    
         
            +
              DB = try_db { Sequel.connect DB_SPEC }
         
     | 
| 
      
 119 
     | 
    
         
            +
            rescue DBException
         
     | 
| 
      
 120 
     | 
    
         
            +
              @failures += 1
         
     | 
| 
      
 121 
     | 
    
         
            +
            end
         
     | 
| 
      
 122 
     | 
    
         
            +
            md5_hash = if File.exists? AIM_FILE
         
     | 
| 
      
 123 
     | 
    
         
            +
              contents = File.open(AIM_FILE).read
         
     | 
| 
      
 124 
     | 
    
         
            +
              md5_from_hash(contents, :json => true, :md5 => true)
         
     | 
| 
      
 125 
     | 
    
         
            +
            else
         
     | 
| 
      
 126 
     | 
    
         
            +
              nil
         
     | 
| 
      
 127 
     | 
    
         
            +
            end
         
     | 
| 
      
 128 
     | 
    
         
            +
             
     | 
| 
      
 129 
     | 
    
         
            +
            def write_statusz_file(status)
         
     | 
| 
      
 130 
     | 
    
         
            +
              str = "Glowworm server from gem version '#{GLOWWORM_VERSION}' Running on Ruby #{RUBY_VERSION}.\n"
         
     | 
| 
      
 131 
     | 
    
         
            +
              str += if status == 200 && @failures.zero?
         
     | 
| 
      
 132 
     | 
    
         
            +
                "Last modified #{@last_modified || "in previous run period"}."
         
     | 
| 
      
 133 
     | 
    
         
            +
              else
         
     | 
| 
      
 134 
     | 
    
         
            +
                "Last update failed due to database connection error at #{Time.now}.\n" +
         
     | 
| 
      
 135 
     | 
    
         
            +
                "#{@failures} consecutive connection errors encountered."
         
     | 
| 
      
 136 
     | 
    
         
            +
              end
         
     | 
| 
      
 137 
     | 
    
         
            +
              File.open(STATUSZ_FILE, "w") do |file|
         
     | 
| 
      
 138 
     | 
    
         
            +
                file.puts str
         
     | 
| 
      
 139 
     | 
    
         
            +
              end
         
     | 
| 
      
 140 
     | 
    
         
            +
            end
         
     | 
| 
      
 141 
     | 
    
         
            +
             
     | 
| 
      
 142 
     | 
    
         
            +
            def write_healthz_file(status)
         
     | 
| 
      
 143 
     | 
    
         
            +
              # Return 404 if LIVE_FILENAME is not present
         
     | 
| 
      
 144 
     | 
    
         
            +
              if !File.exists?(LIVE_FILENAME)
         
     | 
| 
      
 145 
     | 
    
         
            +
                File.delete HEALTHZ_FILE if File.exists?(HEALTHZ_FILE)
         
     | 
| 
      
 146 
     | 
    
         
            +
                return
         
     | 
| 
      
 147 
     | 
    
         
            +
              end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
              str = if status == 200 && @failures.zero?
         
     | 
| 
      
 150 
     | 
    
         
            +
                "OOYALA GLOWWORM OK"
         
     | 
| 
      
 151 
     | 
    
         
            +
              else
         
     | 
| 
      
 152 
     | 
    
         
            +
                "Error reconnecting to database. #{@failures} consecutive connection errors encountered."
         
     | 
| 
      
 153 
     | 
    
         
            +
              end
         
     | 
| 
      
 154 
     | 
    
         
            +
              File.open(HEALTHZ_FILE, "w") do |file|
         
     | 
| 
      
 155 
     | 
    
         
            +
                file.puts str
         
     | 
| 
      
 156 
     | 
    
         
            +
              end
         
     | 
| 
      
 157 
     | 
    
         
            +
            end
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
            unless File.exists? STATUSZ_FILE
         
     | 
| 
      
 160 
     | 
    
         
            +
              # Write statusz file. Parameter is status code, but will output the right thing unless passed 500.
         
     | 
| 
      
 161 
     | 
    
         
            +
              write_statusz_file nil
         
     | 
| 
      
 162 
     | 
    
         
            +
            end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
            if File.exists?(HEALTHZ_FILE) && !File.exists?(LIVE_FILENAME)
         
     | 
| 
      
 165 
     | 
    
         
            +
              # Write statusz file. Parameter is status code, but will output the right thing unless passed 500
         
     | 
| 
      
 166 
     | 
    
         
            +
              write_healthz_file nil
         
     | 
| 
      
 167 
     | 
    
         
            +
            end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
            # This is an implementation of "do while": run once if ENV["RUN_ONCE"] is set, loop otherwise
         
     | 
| 
      
 170 
     | 
    
         
            +
            run_repeatedly = !ENV.has_key?("RUN_ONCE")
         
     | 
| 
      
 171 
     | 
    
         
            +
            begin
         
     | 
| 
      
 172 
     | 
    
         
            +
              status, json_hash = json_for_account_and_feature
         
     | 
| 
      
 173 
     | 
    
         
            +
             
     | 
| 
      
 174 
     | 
    
         
            +
              new_md5_hash = md5_from_hash(json_hash)
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
              if status == 200 && @failures.zero?
         
     | 
| 
      
 177 
     | 
    
         
            +
                @failures = 0
         
     | 
| 
      
 178 
     | 
    
         
            +
                @last_success = Time.now
         
     | 
| 
      
 179 
     | 
    
         
            +
                if md5_hash != new_md5_hash || !File.exists?(AIM_FILE)
         
     | 
| 
      
 180 
     | 
    
         
            +
                  @last_modified = Time.now
         
     | 
| 
      
 181 
     | 
    
         
            +
             
     | 
| 
      
 182 
     | 
    
         
            +
                  json_hash[:md5] = new_md5_hash
         
     | 
| 
      
 183 
     | 
    
         
            +
                  # Yes, this is reencoding all the JSON.  We *could* just do hideous string
         
     | 
| 
      
 184 
     | 
    
         
            +
                  # surgery and replace the final close-brace with a comma, the MD5 bit, and
         
     | 
| 
      
 185 
     | 
    
         
            +
                  # a new close-brace.  For now, clarity beats efficiency since this runs rarely.
         
     | 
| 
      
 186 
     | 
    
         
            +
                  json_string = MultiJson.dump(json_hash)
         
     | 
| 
      
 187 
     | 
    
         
            +
                  # If we get to this point, then the file should have been modified, otherwise the hashes would match
         
     | 
| 
      
 188 
     | 
    
         
            +
                  File.open(AIM_FILE, "w") do |file|
         
     | 
| 
      
 189 
     | 
    
         
            +
                    file.write json_string
         
     | 
| 
      
 190 
     | 
    
         
            +
                  end
         
     | 
| 
      
 191 
     | 
    
         
            +
                  md5_hash = new_md5_hash
         
     | 
| 
      
 192 
     | 
    
         
            +
                  MyLogger.info "NEW_DATA - #{json_string[0..40]}..., hash #{md5_hash}"
         
     | 
| 
      
 193 
     | 
    
         
            +
                else
         
     | 
| 
      
 194 
     | 
    
         
            +
                  MyLogger.info "No new data - checksum #{md5_hash}"
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
              else # status == 500
         
     | 
| 
      
 197 
     | 
    
         
            +
                @failures += 1
         
     | 
| 
      
 198 
     | 
    
         
            +
                @retry_time = case @failures
         
     | 
| 
      
 199 
     | 
    
         
            +
                when 0..1
         
     | 
| 
      
 200 
     | 
    
         
            +
                  0 # retry immediately on first failure
         
     | 
| 
      
 201 
     | 
    
         
            +
                when 2..7
         
     | 
| 
      
 202 
     | 
    
         
            +
                  2 ** (@failures - 2) # 1, 2, 4, 8, 16, 32
         
     | 
| 
      
 203 
     | 
    
         
            +
                else
         
     | 
| 
      
 204 
     | 
    
         
            +
                  55 # sleep for 55 seconds after 7th failure (because cmk retry is 60 seconds)
         
     | 
| 
      
 205 
     | 
    
         
            +
                end
         
     | 
| 
      
 206 
     | 
    
         
            +
                MyLogger.error "#{@failures} consecutive database failure(s). Retrying in #{@retry_time} seconds."
         
     | 
| 
      
 207 
     | 
    
         
            +
              end
         
     | 
| 
      
 208 
     | 
    
         
            +
              write_statusz_file status
         
     | 
| 
      
 209 
     | 
    
         
            +
              write_healthz_file status if status != 500 || (Time.now - @last_success) > 60 * 10
         
     | 
| 
      
 210 
     | 
    
         
            +
              if run_repeatedly
         
     | 
| 
      
 211 
     | 
    
         
            +
                # Randomize the cache period so we don't get a thundering herd of
         
     | 
| 
      
 212 
     | 
    
         
            +
                # Glowworm servers all updating on the same five-minute
         
     | 
| 
      
 213 
     | 
    
         
            +
                # boundary every five minutes.  This is a big DB query.
         
     | 
| 
      
 214 
     | 
    
         
            +
                #
         
     | 
| 
      
 215 
     | 
    
         
            +
                # We continue to randomize as we go forward instead of starting
         
     | 
| 
      
 216 
     | 
    
         
            +
                # with a random delay.  This is because delays in service will
         
     | 
| 
      
 217 
     | 
    
         
            +
                # otherwise tend to clump servers up over time, causing an
         
     | 
| 
      
 218 
     | 
    
         
            +
                # emergent thundering herd.
         
     | 
| 
      
 219 
     | 
    
         
            +
                sleep_period = if status == 500
         
     | 
| 
      
 220 
     | 
    
         
            +
                  @retry_time
         
     | 
| 
      
 221 
     | 
    
         
            +
                else
         
     | 
| 
      
 222 
     | 
    
         
            +
                  (cache_period / 2) + Random.rand(cache_period + 1)
         
     | 
| 
      
 223 
     | 
    
         
            +
                end
         
     | 
| 
      
 224 
     | 
    
         
            +
                sleep sleep_period
         
     | 
| 
      
 225 
     | 
    
         
            +
              end
         
     | 
| 
      
 226 
     | 
    
         
            +
            end while run_repeatedly
         
     |