glowworm 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +6 -0
  3. data/.yardopts +1 -0
  4. data/Gemfile +5 -0
  5. data/Gemfile.lock +129 -0
  6. data/LICENSE +19 -0
  7. data/README.md +326 -0
  8. data/Rakefile +29 -0
  9. data/bin/basic_server_tester +311 -0
  10. data/bin/em_server/config.ru +2 -0
  11. data/bin/em_server/em_server.rb +19 -0
  12. data/bin/em_server_tester +68 -0
  13. data/bin/glowworm +90 -0
  14. data/bin/load_tester +84 -0
  15. data/ci_jobs/glowworm-continuous-deploy-next-staging/run.sh +10 -0
  16. data/ci_jobs/glowworm-integrations/run.sh +15 -0
  17. data/ci_jobs/glowworm-performance/run.sh +2 -0
  18. data/ci_jobs/glowworm-robustness/run.sh +2 -0
  19. data/ci_jobs/glowworm-units/run.sh +13 -0
  20. data/ci_jobs/setup.sh +119 -0
  21. data/example/example_server.ecology +6 -0
  22. data/example/example_server.rb +32 -0
  23. data/glowworm.gemspec +54 -0
  24. data/lib/glowworm.rb +501 -0
  25. data/lib/glowworm/em.rb +8 -0
  26. data/lib/glowworm/no_bg.rb +2 -0
  27. data/lib/glowworm/version.rb +3 -0
  28. data/server/Gemfile +27 -0
  29. data/server/Gemfile.lock +87 -0
  30. data/server/PROTOCOL +39 -0
  31. data/server/check_mk_checks/check_glowworm_server +43 -0
  32. data/server/db_migrations/20111004214649_change_feature_accounts_to_string.rb +60 -0
  33. data/server/db_migrations/20111028104546_add_value_to_account_set_features.rb +12 -0
  34. data/server/db_migrations/20120217090636_add_fully_active_flag_to_features.rb +15 -0
  35. data/server/example_test_data.rb +66 -0
  36. data/server/glowworm_server.ecology.erb +16 -0
  37. data/server/glowworm_server.rb +226 -0
  38. data/server/run/server.sh +7 -0
  39. data/server/server_test.rb +72 -0
  40. data/server/version.rb +3 -0
  41. data/test/integration/basic_server_test.rb +90 -0
  42. data/test/integration/create_sqlite_data.rb +196 -0
  43. data/test/integration/em_server_test.rb +68 -0
  44. data/test/integration/gemfile_for_specific_glowworm_version +17 -0
  45. data/test/integration/gemfile_for_specific_glowworm_version.lock +55 -0
  46. data/test/integration/integration_test_helper.rb +153 -0
  47. data/test/integration/load_test.rb +59 -0
  48. data/test/integration/nginx.conf +23 -0
  49. data/test/integration/server_test.ecology.erb +6 -0
  50. data/test/test_helper.rb +47 -0
  51. data/test/units/em_test.rb +41 -0
  52. data/test/units/feature_flag_test.rb +297 -0
  53. data/test/units/no_bg_test.rb +40 -0
  54. data/test/units/request_test.rb +51 -0
  55. metadata +410 -0
@@ -0,0 +1,7 @@
1
+ #!/bin/bash -e
2
+
3
+ APP_ROOT=/opt/ooyala/glowworm/current
4
+
5
+ cd $APP_ROOT && \
6
+ source $APP_ROOT/config/pushed_environment.rb && \
7
+ /opt/ruby/1.9/bin/bundle exec /opt/ruby/1.9/bin/ruby glowworm_server.rb 2>&1 |cronolog --symlink=$SHARED_PATH/log/current $SHARED_PATH/log/glowworm.log.%Y-%m-%d
@@ -0,0 +1,72 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.require(:default, :development)
4
+ require "minitest/autorun"
5
+
6
+ # For testing Glowworm itself, use the local version *first*.
7
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), "..", "lib")
8
+
9
+ require "glowworm"
10
+
11
+ class Scope::TestCase
12
+ def set_up_ecology(file_contents, filename = "some.ecology")
13
+ ENV["ECOLOGY_SPEC"] = filename
14
+ File.expects(:exist?).with(filename).returns(true)
15
+ File.expects(:read).with(filename).returns(file_contents)
16
+ end
17
+ end
18
+
19
+ class GlowwormServerTest < Scope::TestCase
20
+ def mock_sequel_query(mock_db, name, data)
21
+ data_stub = stub(:all => data) do
22
+ stubs(:select).returns(self)
23
+ stubs(:filter).returns(self)
24
+ stubs(:join).returns(self)
25
+ stubs(:right_join).returns(self)
26
+ end
27
+ mock_db.stubs(:[]).with(name).returns(data_stub)
28
+
29
+ # Yield each line of data in turn when somebody calls .each
30
+ data[1..-1].inject(mock_db.stubs(:each).yields(data[0])) do |expectation, data_item|
31
+ expectation.then.yields(data_item)
32
+ end
33
+ end
34
+
35
+ setup do
36
+ set_up_ecology <<JSON
37
+ {
38
+ "application": "Glowworm Server Test",
39
+ "features": {
40
+ }
41
+ }
42
+ JSON
43
+ end
44
+
45
+ context "with mocked test data" do
46
+ setup do
47
+ @mock_db = mock("Sequel Database object")
48
+ Sequel.expects(:connect).returns(@mock_db)
49
+ @logger_mock = mock("Termite logger")
50
+ @logger_mock.stubs(:warn => nil, :info => nil, :debug => nil)
51
+ Termite::Logger.expects(:new).returns(@logger_mock)
52
+ end
53
+
54
+ should "Return results for all accounts and features" do
55
+ mock_sequel_query(@mock_db, :account_set_accounts,
56
+ [
57
+ {:account => "123", :account_set_id => 1},
58
+ {:account => "456", :account_set_id => 2},
59
+ ])
60
+ mock_sequel_query(@mock_db, :account_set_features,
61
+ [
62
+ {:name => "foo_feature", :account_set_id => 1}
63
+ ])
64
+ mock_sequel_query(@mock_db, :feature_account_overrides,
65
+ [
66
+ {:name => "foo_feature", :account => "456", :value => true}
67
+ ])
68
+
69
+ assert true
70
+ end
71
+ end
72
+ end
data/server/version.rb ADDED
@@ -0,0 +1,3 @@
1
+ module Glowworm
2
+ VERSION = "0.3.0"
3
+ end
@@ -0,0 +1,90 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), "integration_test_helper")
4
+ require "net/http"
5
+ require 'multi_json'
6
+ require "trollop"
7
+ require "psych"
8
+
9
+ BASIC_SERVER_TESTER_BIN="#{GLOWWORM_ROOT}/bin/basic_server_tester"
10
+
11
+ class BasicServerTest < Scope::TestCase
12
+ setup_once do
13
+ system("ECOLOGY_SPEC=\"#{GLOWWORM_ECOLOGY_FILE}\" #{GLOWWORM_ROOT}/test/integration/create_sqlite_data.rb")
14
+ fail "Couldn't create SQLite data successfully!" unless $?.success?
15
+ end
16
+
17
+ context "with Glowworm server and tester" do
18
+ setup_once do
19
+ tester_tapy = results_path('basic_server_test.yaml')
20
+
21
+ # The tester command outputs results via TAP-Y and diagnostics to STDERR
22
+ tester_cmd = cmd_in_client_ruby_dist(BASIC_SERVER_TESTER_BIN, "--server #{GW_URI} | tee #{tester_tapy}")
23
+ @topology = start_tester_topology(tester_cmd)
24
+
25
+ STDERR.puts "tester status #{@topology[:gw_tester].status}"
26
+
27
+ @tester_results = Psych.parse_stream(IO.read(tester_tapy)).to_ruby
28
+ end
29
+
30
+ teardown_once do
31
+ @topology.stop_all
32
+
33
+ # TODO(noah): Hideous hack until we have real RVM/Nodule integration
34
+ system("pkill -9 -f glowworm_server.rb")
35
+ end
36
+
37
+ should "test server response" do
38
+ contained_summary = false
39
+ @tester_results.each do |tap_entry|
40
+ if "final" == tap_entry["type"]
41
+ contained_summary = true
42
+ counts = tap_entry["counts"]
43
+
44
+ assert_equal(0, counts["fail"], "tester should have no assertion failures")
45
+ assert_equal(0, counts["error"], "tester should not have encountered any errors")
46
+ assert(counts["total"] > 0, "tester should have performed at least a single test")
47
+ end
48
+ end
49
+
50
+ assert(contained_summary, "the tester must have produced a complete output stream")
51
+ end
52
+ end
53
+
54
+ context "with Glowworm server and threadless tester" do
55
+ setup_once do
56
+ tester_tapy = results_path('basic_server_nbg_test.yaml')
57
+
58
+ # The tester command outputs results via TAP-Y and diagnostics to STDERR
59
+ tester_cmd = cmd_in_client_ruby_dist(BASIC_SERVER_TESTER_BIN, "--server #{GW_URI} --nbg true | tee #{tester_tapy}")
60
+ @topology = start_tester_topology(tester_cmd)
61
+
62
+ STDERR.puts "tester status #{@topology[:gw_tester].status}"
63
+
64
+ @tester_results = Psych.parse_stream(IO.read(tester_tapy)).to_ruby
65
+ end
66
+
67
+ teardown_once do
68
+ @topology.stop_all
69
+
70
+ # TODO(noah): Hideous hack until we have real RVM/Nodule integration
71
+ system("pkill -9 -f glowworm_server.rb")
72
+ end
73
+
74
+ should "test server response" do
75
+ contained_summary = false
76
+ @tester_results.each do |tap_entry|
77
+ if "final" == tap_entry["type"]
78
+ contained_summary = true
79
+ counts = tap_entry["counts"]
80
+
81
+ assert_equal(0, counts["fail"], "tester should have no assertion failures")
82
+ assert_equal(0, counts["error"], "tester should not have encountered any errors")
83
+ assert(counts["total"] > 0, "tester should have performed at least a single test")
84
+ end
85
+ end
86
+
87
+ assert(contained_summary, "the tester must have produced a complete output stream") unless ENV["TEST_GLOWWORM_VERSION"].nil? || ENV["TEST_GLOWWORM_VERSION"] < "0.1.6"
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "ecology"
4
+ require "sequel"
5
+ require "termite"
6
+
7
+ # Can be overridden with ECOLOGY_SPEC
8
+ Ecology.read("glowworm_server.ecology")
9
+
10
+ db_spec = Ecology.property("db") || { :adapter => 'sqlite', :database => 'test_data.sqlite' }
11
+ STDERR.puts "Connecting to #{db_spec.inspect}"
12
+ DB = Sequel.connect db_spec
13
+ MyLogger = Termite::Logger.new
14
+
15
+ # The table creation below duplicates a lot of stuff from
16
+ # server/db_migrations. Alas.
17
+
18
+ DB.create_table!(:account_sets) do
19
+ primary_key :id
20
+ Datetime :created_at, :null => false
21
+ Datetime :updated_at, :null => false
22
+ String :name, :null => false
23
+ String :description
24
+ index :name, :unique => true
25
+ end
26
+
27
+ DB.create_table!(:features) do
28
+ primary_key :id
29
+ Datetime :created_at, :null => false
30
+ Datetime :updated_at, :null => false
31
+ String :name, :null => false
32
+ String :description
33
+ Boolean :fully_active, :default => false
34
+ index :name, :unique => true
35
+ end
36
+
37
+ DB.create_table!(:account_set_accounts) do
38
+ primary_key :id
39
+ Datetime :added_at, :null => false
40
+ Integer :account_set_id, :null => false
41
+ Integer :provider_id, :null => true
42
+ String :account, :limit => 10, :null => false
43
+ index :account_set_id
44
+ index :provider_id
45
+ index :account
46
+ index [:account_set_id, :account], :unique => true
47
+ end
48
+
49
+ DB.create_table!(:account_set_features) do
50
+ primary_key :id
51
+ Datetime :added_at, :null => false
52
+ Integer :account_set_id, :null => false
53
+ Integer :feature_id, :null => false
54
+ Integer :value, :null => false, :default => 1
55
+ index :feature_id
56
+ index [:account_set_id, :feature_id], :unique => true
57
+ end
58
+
59
+ DB.create_table!(:feature_account_overrides) do
60
+ Integer :provider_id, :null => true
61
+ String :account, :limit => 10, :null => false
62
+ Integer :feature_id, :null => false
63
+ Integer :value, :null => false
64
+ index :provider_id
65
+ index :feature_id
66
+ index [:account, :feature_id], :unique => true
67
+ end
68
+
69
+ # Probably gone already, but let's be sure.
70
+ DB[:account_sets].delete
71
+ DB[:features].delete
72
+ DB[:account_set_accounts].delete
73
+ DB[:account_set_features].delete
74
+ DB[:feature_account_overrides].delete
75
+
76
+ def unique_id
77
+ @id_counter ||= 10_000
78
+ @id_counter += 1
79
+ @id_counter
80
+ end
81
+
82
+ # "Hello, world" for features - feature_flag(123, "foo") should return true.
83
+ FOO_FEATURE = 1
84
+ DB[:features].insert(:id => FOO_FEATURE, :name => "foo", :description => "hello world feature",
85
+ :created_at => Time.now, :updated_at => Time.now)
86
+ DB[:feature_account_overrides].insert(:account => "123", :provider_id => 123, :feature_id => FOO_FEATURE,
87
+ :value => 1)
88
+
89
+ # Accounts sets for basic testing
90
+ ONE_HORSE_TOWN = 1
91
+ DB[:account_sets].insert(:id => ONE_HORSE_TOWN, :name => "solo", :description => "one-horse town",
92
+ :created_at => Time.now, :updated_at => Time.now)
93
+ TWO_HORSE_TOWN = 2
94
+ DB[:account_sets].insert(:id => TWO_HORSE_TOWN, :name => "duo", :description => "two-horse town",
95
+ :created_at => Time.now, :updated_at => Time.now)
96
+ NO_HORSE_TOWN = 3
97
+ DB[:account_sets].insert(:id => NO_HORSE_TOWN, :name => "nowhere", :description => "no-horse town",
98
+ :created_at => Time.now, :updated_at => Time.now)
99
+
100
+ # Account sets for get_value testing
101
+ LOW_ID_SET = 4
102
+ DB[:account_sets].insert(:id => LOW_ID_SET, :name => "low-id", :description => "acct set, low id #",
103
+ :created_at => Time.now, :updated_at => Time.now)
104
+ BIG_VALUE_SET = 5
105
+ DB[:account_sets].insert(:id => BIG_VALUE_SET, :name => "big-val",
106
+ :description => "acct set, big value",
107
+ :created_at => Time.now, :updated_at => Time.now)
108
+ LITTLE_VALUE_SET = 6
109
+ DB[:account_sets].insert(:id => LITTLE_VALUE_SET, :name => "little-val",
110
+ :description => "acct set, little value",
111
+ :created_at => Time.now, :updated_at => Time.now)
112
+
113
+ VALUE_FOR_ACCT_SET = {
114
+ ONE_HORSE_TOWN => 1,
115
+ TWO_HORSE_TOWN => 2,
116
+ BIG_VALUE_SET => 100,
117
+ LITTLE_VALUE_SET => 7,
118
+ LOW_ID_SET => 21,
119
+ }
120
+
121
+ # Features in 0, 1 and 2 account sets, with no override, true override and false override
122
+ [false, true].each do |fully_active|
123
+ [nil, true, false].each do |override|
124
+ [[],
125
+ [ONE_HORSE_TOWN],
126
+ [ONE_HORSE_TOWN, TWO_HORSE_TOWN],
127
+ ].each do |acct_sets|
128
+ offset = override.nil? ? 0 : (override == true ? 1 : 2)
129
+ offset += 3 if fully_active
130
+ num_accts = acct_sets.size
131
+ id = 1000 + offset * 100 + num_accts
132
+ desc = "#{fully_active ? "Fully Active " : ""}Feature w/ #{num_accts} acct sets and override #{override.inspect}"
133
+ account = "account_for_acct_sets_#{num_accts}_#{override.inspect}"
134
+ feature = "#{fully_active ? "fully_active_" : ""}feature_for_acct_sets_#{num_accts}_#{override.inspect}"
135
+
136
+ DB[:features].insert(:id => id, :name => feature, :description => desc,
137
+ :created_at => Time.now, :updated_at => Time.now, :fully_active => fully_active)
138
+
139
+ acct_sets.each do |acct_set|
140
+ DB[:account_set_accounts].insert(:id => unique_id, :account_set_id => acct_set, :account => account,
141
+ :added_at => Time.now) unless fully_active # Only need to do this once
142
+ DB[:account_set_features].insert(:id => unique_id, :account_set_id => acct_set, :feature_id => id,
143
+ :value => fully_active ? 0 : 1, :added_at => Time.now) # Opposite of default
144
+ end
145
+
146
+ case override
147
+ when true
148
+ DB[:feature_account_overrides].insert(:account => account, :feature_id => id, :value => 1)
149
+ when false
150
+ DB[:feature_account_overrides].insert(:account => account, :feature_id => id, :value => 0)
151
+ when nil
152
+ # No-op
153
+ else
154
+ raise "Illegal value for override!"
155
+ end
156
+ end
157
+ end
158
+ end
159
+
160
+ [nil, 0, 1, 2, 5].each do |override|
161
+ counter = 0
162
+ [[],
163
+ [LITTLE_VALUE_SET],
164
+ [BIG_VALUE_SET, LITTLE_VALUE_SET],
165
+ [BIG_VALUE_SET, LITTLE_VALUE_SET, LOW_ID_SET]].each do |acct_sets|
166
+ acct_key = acct_sets.map(&:to_s).join("/")
167
+ id = 2000 + (override.nil? ? 0 : override + 1) * 100 + counter
168
+ counter += 1
169
+ desc = "Feature w/ value and w/ acct sets #{acct_key} and override #{override.inspect}"
170
+ account = "account_for_value_acct_sets_#{acct_key}_#{override.inspect}"
171
+ feature = "feature_for_value_acct_sets_#{acct_key}_#{override.inspect}"
172
+
173
+ DB[:features].insert(:id => id, :name => feature, :description => desc,
174
+ :created_at => Time.now, :updated_at => Time.now)
175
+
176
+ acct_sets.each do |acct_set|
177
+ DB[:account_set_accounts].insert(:id => unique_id, :account_set_id => acct_set, :account => account,
178
+ :added_at => Time.now)
179
+ DB[:account_set_features].insert(:id => unique_id, :account_set_id => acct_set, :feature_id => id,
180
+ :value => VALUE_FOR_ACCT_SET[acct_set], :added_at => Time.now)
181
+ end
182
+
183
+ if override != nil
184
+ DB[:feature_account_overrides].insert(:account => account, :feature_id => id,
185
+ :value => override)
186
+ end
187
+ end
188
+ end
189
+
190
+ $stderr.puts "Features: #{DB[:features].count}"
191
+ $stderr.puts "Account Sets: #{DB[:account_sets].count}"
192
+ $stderr.puts "Accounts/set joins: #{DB[:account_set_accounts].count}"
193
+ $stderr.puts "Set/feature joins: #{DB[:account_set_features].count}"
194
+ $stderr.puts "Overrides: #{DB[:feature_account_overrides].count}"
195
+
196
+ $stderr.puts "Added test data successfully!"
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), "integration_test_helper")
4
+ require "net/http"
5
+ require 'multi_json'
6
+ require "trollop"
7
+ require "psych"
8
+
9
+ EM_SERVER_TESTER_BIN = "#{GLOWWORM_ROOT}/bin/em_server_tester"
10
+ EM_SERVER_CONFIG = "#{GLOWWORM_ROOT}/bin/em_server/config.ru"
11
+ EM_PORT = Nodule::Util.random_tcp_port
12
+ EM_HOST = "http://localhost:#{EM_PORT}"
13
+
14
+ class EMServerTest < Scope::TestCase
15
+ setup_once do
16
+ system("ECOLOGY_SPEC=\"#{GLOWWORM_ECOLOGY_FILE}\" #{GLOWWORM_ROOT}/test/integration/create_sqlite_data.rb")
17
+ fail "Couldn't create SQLite data successfully!" unless $?.success?
18
+ end
19
+
20
+ context "with Glowworm server and tester" do
21
+ setup_once do
22
+ tester_tapy = results_path('em_server_test.yaml')
23
+
24
+ # The tester command outputs results via TAP-Y and diagnostics to STDERR
25
+ tester_cmd = cmd_in_client_ruby_dist(EM_SERVER_TESTER_BIN, "--server #{EM_HOST} | tee #{tester_tapy}")
26
+ # The tester may be run under ruby 1.8. Fake like 1.9 by using RUBYOPT to include rubygems
27
+ # support
28
+ em_server = {
29
+ :em_server => Nodule::Process.new({
30
+ "ECOLOGY_SPEC" => GLOWWORM_ECOLOGY_FILE,
31
+ "GW_URI" => GW_URI,
32
+ "GLOWWORM_ROOT" => GLOWWORM_ROOT },
33
+ "bash", "-l", "-c", "cd #{GLOWWORM_ROOT} && bundle exec thin -p #{EM_PORT} -R #{EM_SERVER_CONFIG} start",
34
+ :stdout => :capture, :stderr => :capture, :verbose => :cyanio
35
+ )
36
+ }
37
+ @topology = start_tester_topology(tester_cmd, 10, em_server)
38
+
39
+ STDERR.puts "tester status #{@topology[:gw_tester].status}"
40
+
41
+ @tester_results = Psych.parse_stream(IO.read(tester_tapy)).to_ruby
42
+ end
43
+
44
+ teardown_once do
45
+ @topology.stop_all
46
+
47
+ # TODO(noah): Hideous hack until we have real RVM/Nodule integration
48
+ system("pkill -9 -f glowworm_server.rb")
49
+ system("pkill -9 -f thin")
50
+ end
51
+
52
+ should "test server response" do
53
+ contained_summary = false
54
+ @tester_results.each do |tap_entry|
55
+ if "final" == tap_entry["type"]
56
+ contained_summary = true
57
+ counts = tap_entry["counts"]
58
+
59
+ assert_equal(0, counts["fail"], "tester should have no assertion failures")
60
+ assert_equal(0, counts["error"], "tester should not have encountered any errors")
61
+ assert(counts["total"] > 0, "tester should have performed at least a single test")
62
+ end
63
+ end
64
+
65
+ assert(contained_summary, "the tester must have produced a complete output stream")
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,17 @@
1
+ source "http://rubygems.org"
2
+ source "http://gems.sv2" # Ooyala-specific!
3
+ source "http://gems.us-east-1.ooyala.com:8080"
4
+
5
+ fail "requires TEST_GLOWWORM_VERSION in environment" unless ENV['TEST_GLOWWORM_VERSION']
6
+
7
+ gem "glowworm", ENV['TEST_GLOWWORM_VERSION']
8
+
9
+ # plus development dependencies
10
+ gem "ansi"
11
+ gem "minitap", ">=0.3.5"
12
+ gem "minitest"
13
+ gem "mocha"
14
+ gem "rake"
15
+ gem "scope"
16
+ gem "tapout"
17
+ gem "httparty"