cryptic-hoffa 0.0.16

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.
Files changed (31) hide show
  1. data/.gitignore +17 -0
  2. data/.ruby-version +1 -0
  3. data/Gemfile +7 -0
  4. data/LICENSE.txt +1 -0
  5. data/RBPKG +5 -0
  6. data/README.md +22 -0
  7. data/Rakefile +1 -0
  8. data/bin/dev-glu-tunnel.sh +5 -0
  9. data/bin/highway-from-sql +76 -0
  10. data/bin/hoffa +195 -0
  11. data/bin/prod-glu-tunnel.sh +5 -0
  12. data/binstubs/.gitkeep +0 -0
  13. data/cassandra_migrations/20131024000000_create_keyspace_Cryptic_down.cql.erb +1 -0
  14. data/cassandra_migrations/20131024000000_create_keyspace_Cryptic_up.cql.erb +2 -0
  15. data/cassandra_migrations/20131025000000_create_table_overlay_actions_down.cql.erb +3 -0
  16. data/cassandra_migrations/20131025000000_create_table_overlay_actions_up.cql.erb +15 -0
  17. data/cassandra_migrations/20131114488001_create_client_survey_user_site_tables_down.cql.erb +9 -0
  18. data/cassandra_migrations/20131114488001_create_client_survey_user_site_tables_up.cql.erb +94 -0
  19. data/cassandra_migrations/20131206944013_create_table_cryptic_place_marker_down.cql.erb +3 -0
  20. data/cassandra_migrations/20131206944013_create_table_cryptic_place_marker_up.cql.erb +10 -0
  21. data/cassandra_migrations/20140115150807_add_place_marker_to_place_markers_down.cql +3 -0
  22. data/cassandra_migrations/20140115150807_add_place_marker_to_place_markers_up.cql +3 -0
  23. data/cassandra_migrations/20140116281758_repartition_place_markers_table_down.cql +10 -0
  24. data/cassandra_migrations/20140116281758_repartition_place_markers_table_up.cql +10 -0
  25. data/cryptic-hoffa.gemspec +34 -0
  26. data/database.yml +13 -0
  27. data/jenkins.recipe +67 -0
  28. data/lib/cryptic/hoffa.rb +183 -0
  29. data/lib/cryptic/hoffa/version.rb +6 -0
  30. data/vendor/.gitkeep +0 -0
  31. metadata +240 -0
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.9.3-p327
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+ source 'http://gems.onlive.com'
3
+
4
+ # Specify your gem's dependencies in cryptic-hoffa.gemspec
5
+ gemspec
6
+
7
+ gem "cryptic-highway", ">= 0.0.8"
data/LICENSE.txt ADDED
@@ -0,0 +1 @@
1
+ Copyright (c) 2013 OL2, Inc. All Rights Reserved.
data/RBPKG ADDED
@@ -0,0 +1,5 @@
1
+ name: ol_cryptic_hoffa
2
+ version: %gitver%
3
+ dest: /opt/cryptic_hoffa
4
+ rvm: ruby-1.9.3-p327
5
+ binstubs_dir: binstubs
data/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # Cryptic::Hoffa
2
+
3
+ Cryptic Highway is OnLive's Cassandra series reader/writer code. The
4
+ cryptic-highway gem is the "public" part, in that it could be made
5
+ open-source without a problem.
6
+
7
+ Other parts of our code are very OnLive-specific, such as reading
8
+ messages from OMQ and our specific user sites. This code lives in the
9
+ cryptic-hoffa gem.
10
+
11
+ This gem currently includes two programs:
12
+
13
+ * hoffa - an OMQ reader that writes into Cassandra
14
+ * highway-from-sql - an ActiveRecord-based SQL reader that writes into Cassandra
15
+
16
+ ## Installation
17
+
18
+ "gem install cryptic-hoffa", if you have gems.onlive.com set up as a gem server.
19
+
20
+ ## Usage
21
+
22
+ (To be added)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,5 @@
1
+ #! /bin/sh
2
+ # Copyright (C) 2013 OL2, inc. All Rights Reserved.
3
+
4
+ echo Starting ssh to manage the tunnel to site $1
5
+ ssh shell01.aav.onlive.net -L 61618:glu01.$1:61618 -L 60080:rpcweb.$1:62999
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Copyright (C) 2013-2014 OL2, Inc. All Rights Reserved.
4
+
5
+ require "trollop"
6
+ require "yaml"
7
+ require "active_record"
8
+ require "erubis"
9
+
10
+ require "cryptic/highway"
11
+ require "cryptic/hoffa"
12
+
13
+ opts = Trollop::options do
14
+ banner <<BANNER
15
+ Highway-from-sql is a tool to move database information from SQL DBs into Cassandra.
16
+ Its database.yml file must include a "sql_db" database connection in standard ActiveRecord format
17
+
18
+ Specific options:
19
+ BANNER
20
+ opt :db_file, "Database YAML file", :default => "database.yml", :type => String
21
+ opt :site, "User site", :required => true, :type => String
22
+ opt :cass_host, "Cassandra host(s)", :type => :strings, :default => [ "localhost" ]
23
+ opt :cass_key, "Name for Cass keyspace", :type => String, :default => "cryptic"
24
+ end
25
+
26
+ SITE = opts[:site]
27
+
28
+ raise "Wrong args!" if ARGV.size > 0
29
+
30
+ Cryptic.redirect_user_sites! [opts[:site]]
31
+ site_port = Cryptic.redirected_database_port_for_user_site opts[:site]
32
+
33
+ eruby = Erubis::Eruby.new File.read(opts[:db_file])
34
+ eruby_string = eruby.evaluate :site => SITE, :site_port => site_port
35
+ db_yml = YAML.load eruby_string
36
+
37
+ STDERR.puts "Connecting with DB options: #{db_yml["sql_db"].inspect}"
38
+ ActiveRecord::Base.establish_connection db_yml["sql_db"]
39
+
40
+ class ClientSurveyUSBDevice < ActiveRecord::Base
41
+ self.table_name = "client_survey_usb_device"
42
+ end
43
+
44
+ ramp = Cryptic::Highway::Ramp.new("client_survey_usb_device") do
45
+ hosts opts[:cass_host]
46
+ keyspace opts[:cass_key]
47
+ site opts[:site]
48
+ marker_name "client_survey_usb_device-#{opts[:site]}"
49
+ to_update do |place_marker|
50
+ STDERR.puts "Updating forward from #{place_marker.inspect}!"
51
+ sql_query = <<-QUERY
52
+ SELECT client_survey_timestamp, u.* FROM client_survey_usb_device u
53
+ JOIN client_survey cs ON cs.client_survey_id = u.client_survey_id
54
+ WHERE client_survey_timestamp >= '2014-01-14 00:00:00'
55
+ #{ place_marker.nil? ? "" : "AND client_survey_timestamp >= '#{place_marker}'" }
56
+ LIMIT #{ db_yml["block_size"] || 100 }
57
+ QUERY
58
+ STDERR.puts "Query: #{sql_query}"
59
+ objs = ClientSurveyUSBDevice.find_by_sql sql_query
60
+ STDERR.puts "Query returned #{objs.size} records!"
61
+
62
+ data = objs.map do |o|
63
+ item = o.attributes
64
+ item["timestamp"] = item.delete "client_survey_timestamp"
65
+ item
66
+ end
67
+ ramp.insert_rows data
68
+
69
+ STDERR.puts "New last marker: #{objs.last["client_survey_timestamp"].to_s.inspect}"
70
+ objs.last["client_survey_timestamp"].to_s
71
+ end
72
+ end
73
+
74
+ ramp.update_until_done
75
+
76
+ puts "Highway: Finished. Exiting."
data/bin/hoffa ADDED
@@ -0,0 +1,195 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "trollop"
4
+ require "cryptic/highway"
5
+ require "cryptic/highway/utils"
6
+ require "cryptic/hoffa"
7
+ require "rubyglu"
8
+
9
+ require "logger"
10
+ ERR_LOG = Logger.new STDERR
11
+ ERR_LOG.level = Logger::INFO
12
+ OUT_LOG = Logger.new STDOUT
13
+ OUT_LOG.level = Logger::INFO
14
+
15
+ # Make a fake logger object that just passes all operations to both log objects
16
+ BOTH_LOGS = Cryptic::Highway::Utils::MultiDispatcher.new(ERR_LOG, OUT_LOG)
17
+
18
+ BOTH_LOGS.info "Started hoffa with arguments: #{ARGV.inspect}"
19
+
20
+ opts = Trollop::options do
21
+ banner <<-BANNER
22
+ Read JSON messages from OMQ (GLU), insert them into Cassandra.
23
+ BANNER
24
+ opt :omq_host, "OMQ broker hostname", :type => String
25
+ opt :omq_host_site, "OMQ site of specified host", :type => String
26
+ opt :omq_port, "OMQ broker port", :type => Integer
27
+ opt :omq_debug, "OMQ debug mode"
28
+ opt :cass_host, "Cassandra host", :type => :strings, :default => [ "localhost" ]
29
+ #opt :cass_port, "Cassandra port", :type => Integer, :default => 9042
30
+
31
+ opt :cass_table, "Name for Cass table/CF", :type => String, :required => true
32
+ opt :cass_key, "Name for Cass keyspace", :type => String, :default => "Cryptic"
33
+
34
+ opt :duplication,"Number of times to duplicate each site.", :type => Integer, :default => 1
35
+
36
+ opt :user_sites, "OnLive User site(s) for reading data, comma-separated.", :type => String
37
+ end
38
+
39
+ if opts[:user_sites] && (opts[:omq_host] || opts[:omq_port])
40
+ ERR_LOG.error <<-ERR_STRING
41
+ Can't specify user site(s) with individual host/port for OMQ!
42
+ Sites: #{opts[:user_sites].inspect}
43
+ Host: #{opts[:omq_host]}
44
+ Port: #{opts[:omq_port]}"
45
+ ERR_STRING
46
+ raise "Can't specify user site(s) with individual host/port for OMQ!"
47
+ end
48
+
49
+ # Doing a kill -9 on this should kill the parent and all children, but nobody else.
50
+ Process.setpgrp
51
+
52
+ if opts[:user_sites]
53
+ if sites == "query"
54
+ sites = Cryptic.user_sites
55
+ else
56
+ sites = opts[:user_sites].split ","
57
+ end
58
+ omq_brokers = sites.map { |s| ["omq://omq.#{s}.onlive.net:61618", s] }
59
+ else
60
+ # Use omq_host and omq_port, defaulting to localhost:61618
61
+ omq_brokers = [["omq://#{opts[:omq_host] || "localhost"}:#{opts[:omq_port] || 61618}", opts[:omq_host_site]]]
62
+ end
63
+
64
+ BOTH_LOGS.warn "(Re)starting Hoffa."
65
+
66
+ broker_list = omq_brokers.flat_map { |broker| (1..opts[:duplication]).map { |i| broker + [ i ] } }
67
+
68
+ opts[:slots] = ARGV.length > 0 ? ARGV : [ "*" ]
69
+ puts "Connecting to OMQ broker(s): #{broker_list.inspect}"
70
+
71
+ processes = []
72
+
73
+ # Create a new process, return the pid.
74
+ def new_process(omq_broker, user_site, dup_num, opts = {})
75
+ fork do
76
+ $0 = "Hoffa worker (ruby), Site: #{user_site || "none"} Dup Num: #{dup_num || "none"}"
77
+ Process.setrlimit :RSS, 10_000_000 # This should be way higher than needed
78
+
79
+ consecutive_failures = 0
80
+ loop do
81
+ # Re-instantiate Cassandra connection in each process and after OMQ errors.
82
+ # This works around problems with down nodes not always being marked up
83
+ # again after Cassandra recovery. We want to prevent cases where we keep
84
+ # looping and keep failing... Also, for the same reason, don't share
85
+ # the connection between processes.
86
+ ramp = Cryptic::Highway::Ramp.new(opts[:cass_table]) do
87
+ hosts opts[:cass_host]
88
+ keyspace opts[:cass_key]
89
+ site user_site
90
+ end
91
+
92
+ begin
93
+ GLU::services do
94
+ debug_mode if opts[:omq_debug]
95
+ use_broker omq_broker
96
+ use_name "ruby_omq_tail"
97
+ no_clear
98
+
99
+ consumer opts[:slots] do |message|
100
+ if message.type == 'Log'
101
+ puts "#{message[:timestamp]} LOG-#{message[:level]}(#{message[:componentName]}.#{message[:componentVersion]}) " +
102
+ "#{message[:sourceFileName]}@#{message[:sourceHostName]}: #{message[:message]}"
103
+ elsif not message.name.nil?
104
+ data = message.message_parameters
105
+ sentTo = message.headers["sentTo"]
106
+
107
+ puts "Data to insert: #{sentTo} / #{data.inspect}" # In case of error on insert, put it in the logfile
108
+
109
+ actions = data['actions']
110
+ base = {
111
+ "userSessionKey" => data["userSessionKey"],
112
+ "appSessionId" => data["appSessionId"],
113
+ "topic" => sentTo,
114
+ "sequenceNum" => message.headers["sequenceNum"],
115
+ }
116
+ rows = actions.map { |action| action.merge base }
117
+
118
+ ramp.insert_rows rows
119
+ consecutive_failures = 0 # Reset number of consecutive failures
120
+ else
121
+ BOTH_LOGS.warn "Nil name on message? #{message.inspect}"
122
+ end
123
+ end
124
+ end
125
+ rescue
126
+ # One more failure since the last success
127
+ consecutive_failures += 1
128
+
129
+ is_in_maint = Cryptic.sites_in_maintenance & [ user_site ]
130
+ maint_msg = is_in_maint ? "(In maint)" : "(Not in maint)"
131
+
132
+ OUT_LOG.error "Error caused GLU exit: #{$!.class} / #{$!.message}!"
133
+ ERR_LOG.error <<-ERROR_MESSAGE
134
+ #{maint_msg} Error caused GLU exit: #{$!.class} / #{$!.message}
135
+ #{$!.backtrace.join(" \n")}
136
+ ERROR_MESSAGE
137
+ puts "#{maint_msg} Error caused GLU exit: #{$!.class}: #{$!.message}"
138
+
139
+ if consecutive_failures >= 10
140
+ # That's nearly a minute of consecutive failure, or maybe
141
+ # even longer. Kill the process, in case something is
142
+ # royally and globally screwed. It may just be that
143
+ # Cassandra or OMQ is down, in which case this restarting is
144
+ # fine too.
145
+ exit! # Always use "exit!" at end of a forked Ruby process
146
+ end
147
+
148
+ BOTH_LOGS.warn "#{maint_msg} Looping after 5 second delay (60 in maint)..."
149
+ puts "#{maint_msg} Looping after 5 second delay (60 in maint)..."
150
+ sleep is_in_maint ? 60.0 : 5.0
151
+ end
152
+ end # loop do
153
+ end # fork
154
+ end
155
+
156
+ broker_list.each do |omq_broker, user_site, dup_num|
157
+ pid = new_process omq_broker, user_site, dup_num, opts
158
+ processes << { pid: pid, omq_broker: omq_broker, site: user_site, dup_num: dup_num }
159
+ end
160
+
161
+ $0 = "Hoffa master (ruby), Sites: #{opts[:user_sites].inspect}"
162
+
163
+ Signal.trap("TERM") do
164
+ processes.each { |process| `kill -TERM #{process[:pid]}` }
165
+ shutdown()
166
+ end
167
+
168
+ loop do
169
+
170
+ # Default: reap children every 5 seconds, check for data every 1 minute.
171
+ # That means 12 5-second iterations.
172
+ 12.times do
173
+ tries = 0
174
+ pid, status = Process.waitpid2 0, Process::WNOHANG # Did any child die?
175
+
176
+ # Reap child processes until nobody dies, or up to 10
177
+ while pid != nil && tries < 10
178
+ # Find the descriptor for the dead child process
179
+ proc = processes.detect { |p| p[:pid] == pid }
180
+
181
+ processes -= [ proc ]
182
+ pid = new_process proc[:omq_broker], proc[:site], opts
183
+ processes << { pid: pid, omq_broker: proc[:omq_broker], site: proc[:site] }
184
+ tries += 1
185
+ pid, status = Process.waitpid2 0, Process::WNOHANG # Did any child die?
186
+ end
187
+
188
+ sleep 5 # Reap children and check data no more often than this
189
+ end
190
+
191
+ # TODO: check if process is writing, somehow, and restart if not.
192
+ end
193
+
194
+ ERR_LOG.error "Finished! Shouldn't get here."
195
+ raise "Finished! Shouldn't get here."
@@ -0,0 +1,5 @@
1
+ #! /bin/sh
2
+ # Copyright (C) 2013 OL2, inc. All Rights Reserved.
3
+
4
+ echo Starting ssh to manage the tunnel to site $1
5
+ ssh shell01.mca.onlive.net -L 61618:omq.$1:61618 -L 60080:rpcweb.$1:62999
data/binstubs/.gitkeep ADDED
File without changes
@@ -0,0 +1 @@
1
+ DROP KEYSPACE "cryptic";
@@ -0,0 +1,2 @@
1
+ CREATE KEYSPACE "cryptic" WITH REPLICATION =
2
+ { 'class' : 'SimpleStrategy', 'replication_factor' : <%= ENV['CASS_REPLICATION'] || 1 %> };
@@ -0,0 +1,3 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE "overlay_actions";
@@ -0,0 +1,15 @@
1
+ -- Create table for purchase-flow analytics and other actions in the overlay.
2
+
3
+ use "cryptic";
4
+
5
+ CREATE TABLE overlay_actions (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "topic" varchar, // OMQ topic
8
+ "name" text, // overlay action name
9
+ "timestamp" bigint, // nanosec since the epoch
10
+ "sequenceNum" int,
11
+ "appSessionId" varchar,
12
+ "userSessionKey" varchar,
13
+ "actionKey" varchar,
14
+ "tags" map<text,text>,
15
+ PRIMARY KEY ("partition_key", "topic", "actionKey"));
@@ -0,0 +1,9 @@
1
+ -- Create tables for user-site client_survey data
2
+
3
+ use "cryptic";
4
+
5
+ DROP TABLE client_survey;
6
+ DROP TABLE client_survey_usb_device;
7
+ DROP TABLE client_survey_monitor;
8
+ DROP TABLE network_test_result;
9
+ DROP TABLE spi_profile_log;
@@ -0,0 +1,94 @@
1
+ -- Create tables for user-site client_survey data
2
+
3
+ use "cryptic";
4
+
5
+ CREATE TABLE client_survey (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "client_survey_id" varchar,
8
+ "client_survey_timestamp" bigint,
9
+ "user_id" varchar,
10
+ "user_session_key" varchar,
11
+ "aero" boolean,
12
+ "audio_driver" blob,
13
+ "client_version" varchar,
14
+ "decode_time" float,
15
+ "input_keyboard" boolean,
16
+ "input_mouse" boolean,
17
+ "operating_system" varchar,
18
+ "operating_system_build" int,
19
+ "operating_system_build_str" varchar,
20
+ "pixel_shader_version" float,
21
+ "processor_cores" int,
22
+ "processor_current_frequency" int,
23
+ "processor_details" varchar,
24
+ "processor_features" varchar,
25
+ "processor_frequency" int,
26
+ "processor_name" varchar,
27
+ "processor_vendor" varchar,
28
+ "system_page_used" int,
29
+ "system_ram" int,
30
+ "system_ram_available" int,
31
+ "video_card" varchar,
32
+ "video_card_driver" varchar,
33
+ "video_memory" varchar,
34
+ "video_modes" varchar,
35
+ "client_survey_auto_id" bigint,
36
+ PRIMARY KEY ("partition_key","client_survey_id"));
37
+
38
+ CREATE TABLE client_survey_monitor (
39
+ "partition_key" varchar, // Partition by site and time bucket
40
+ "client_survey_monitor_id" bigint,
41
+ "client_survey_id" varchar,
42
+ "timestamp" bigint,
43
+ "edid" blob,
44
+ "monitor_name" blob,
45
+ "manufacture_date" blob,
46
+ "horizontal_cm" int,
47
+ "vertical_cm" int,
48
+ "name" blob,
49
+ "primary" boolean,
50
+ "refresh_rate" int,
51
+ "resolution" blob,
52
+ PRIMARY KEY ("partition_key","client_survey_monitor_id"));
53
+
54
+ CREATE TABLE client_survey_usb_device (
55
+ "partition_key" varchar, // Partition by site and time bucket
56
+ "client_survey_usb_device_id" bigint,
57
+ "timestamp" bigint,
58
+ "client_survey_id" varchar,
59
+ "client_survey_usb_device_type_id" int,
60
+ "name" blob, // overlay action name
61
+ PRIMARY KEY ("partition_key","client_survey_usb_device_id"));
62
+
63
+ CREATE TABLE network_test_result (
64
+ "partition_key" varchar, // Partition by site and time bucket
65
+ "network_test_result_id" bigint,
66
+ "test_timestamp" timeuuid,
67
+ "user_session_key" varchar,
68
+ "user_id" varchar,
69
+ "bandwidth" bigint,
70
+ "connection" varchar,
71
+ "decode_time" double,
72
+ "jitter" double,
73
+ "latency" double,
74
+ "machine_power_save_possible" boolean,
75
+ "packet_loss" double,
76
+ "error" blob,
77
+ PRIMARY KEY ("partition_key","network_test_result_id"));
78
+
79
+ CREATE TABLE spi_profile_log (
80
+ "partition_key" varchar, // Partition by site and time bucket
81
+ "id" bigint,
82
+ "timestamp" bigint,
83
+ "hw_id" varchar,
84
+ "hw_id_type" varchar,
85
+ "device_type" varchar,
86
+ "slice_id" varchar,
87
+ "session_key" varchar,
88
+ "user_id" varchar,
89
+ "connection_type" varchar,
90
+ "tested_rate_kbps" int,
91
+ "desired_rate_kbps" int,
92
+ "decode_time_ms" bigint,
93
+ "computer_speed" varchar,
94
+ PRIMARY KEY ("partition_key","id"));
@@ -0,0 +1,3 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE "place_markers";
@@ -0,0 +1,10 @@
1
+ -- Create tables for place markers for Cryptic Highway and other Cryptic scripts and processes
2
+
3
+ use "cryptic";
4
+
5
+ CREATE TABLE "place_markers" (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "marker_type" varchar,
8
+ "timestamp" bigint,
9
+ "notes" varchar,
10
+ PRIMARY KEY ("partition_key","marker_type","timestamp"));
@@ -0,0 +1,3 @@
1
+ use cryptic;
2
+
3
+ ALTER TABLE place_markers DROP place_marker;
@@ -0,0 +1,3 @@
1
+ use cryptic;
2
+
3
+ ALTER TABLE place_markers ADD place_marker blob;
@@ -0,0 +1,10 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE place_markers;
4
+
5
+ CREATE TABLE "place_markers" (
6
+ "partition_key" varchar, // Partition by site and time bucket
7
+ "marker_type" varchar,
8
+ "timestamp" bigint,
9
+ "notes" varchar,
10
+ PRIMARY KEY ("partition_key","marker_type","timestamp"));
@@ -0,0 +1,10 @@
1
+ use "cryptic";
2
+
3
+ DROP TABLE place_markers;
4
+
5
+ CREATE TABLE "place_markers" (
6
+ "place_marker" blob,
7
+ "marker_type" varchar,
8
+ "timestamp" bigint,
9
+ "notes" varchar,
10
+ PRIMARY KEY ("marker_type"));
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cryptic/hoffa/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "cryptic-hoffa"
8
+ spec.version = Cryptic::Hoffa::VERSION
9
+ spec.authors = ["Noah Gibbs"]
10
+ spec.email = ["noah@onlive.com"]
11
+ spec.description = %q{Gateway OMQ data to Cassandra}
12
+ spec.summary = %q{Gateway OMQ data to Cassandra}
13
+ spec.homepage = ""
14
+ spec.license = "Proprietary"
15
+
16
+ spec.files = (File.exist?("MANIFEST") ? File.read("MANIFEST") : `git ls-files`).split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "cassandra_migrate"
24
+
25
+ spec.add_runtime_dependency "trollop"
26
+ spec.add_runtime_dependency "cryptic-highway"
27
+ spec.add_runtime_dependency "rubyglu"
28
+ spec.add_runtime_dependency "multi_json"
29
+ spec.add_runtime_dependency "erubis"
30
+
31
+ # For Highway-from-SQL and querying user sites
32
+ spec.add_runtime_dependency "activerecord"
33
+ spec.add_runtime_dependency "mysql2"
34
+ end
data/database.yml ADDED
@@ -0,0 +1,13 @@
1
+ ---
2
+ sql_db:
3
+ adapter: mysql2
4
+ database: analytics
5
+ host: 127.0.0.1
6
+ port: <%= @site_port %>
7
+ username: analytics
8
+ password: 5parklin3
9
+ secure_auth: false
10
+ socket: /tmp/mysql.sock
11
+ pool: 5
12
+ timeout: 5000
13
+ block_size: 1500
data/jenkins.recipe ADDED
@@ -0,0 +1,67 @@
1
+ #!/bin/bash
2
+ # Jenkins recipe to build rpm via rbpkgbuild
3
+
4
+ ### SETUP ENVIRONMENT ###
5
+ : ${RVM_SH_PATH:=/etc/profile.d/rvm.sh}
6
+ . $RVM_SH_PATH
7
+ progname=$(basename $0)
8
+
9
+ # Should this use the specific ruby version specified?
10
+ cd $WORKSPACE
11
+ rvm use 1.9.3-p392
12
+
13
+ log() {
14
+ echo "$progname: ""$@"
15
+ }
16
+
17
+ err() {
18
+ log "$@" >&2
19
+ }
20
+
21
+ ### UPDATE TAGS ###
22
+ git fetch --tags
23
+
24
+ ### INSTALL GEMS ###
25
+ rm -rf .bundle
26
+ rm -rf Gemfile.lock
27
+ rm -rf vendor/bundle
28
+ cp -r bin bindir # bin gets replaced with stubs by rbpkgbuild
29
+ echo "Do bundle install --path=vendor/bundle"
30
+ bundle install --path=vendor/bundle
31
+
32
+ ### RUN TESTS ###
33
+
34
+
35
+ ### CREATE MANIFEST ###
36
+ echo "Creating MANIFEST for deployment"
37
+ git ls-files > MANIFEST
38
+
39
+ ### CREATE RPM ###
40
+ log "Creating rpm with rbpkgbuild"
41
+ /usr/local/bin/rbpkgbuild
42
+ if [ $? -ne 0 ]; then
43
+ err "Error creating rpms"
44
+ exit 2
45
+ else
46
+ log "rpmpkgbuild completed without error."
47
+ fi
48
+
49
+ ### PUBLISH RPM ###
50
+ # Define RPM destinations
51
+ EL5REPO='mm-apt.mmm.onlive.net::incoming-rpm/integration/'
52
+ EL6REPO='mm-apt.mmm.onlive.net::incoming-rpm/integration6/'
53
+
54
+ # Push rpms to repo
55
+ ## depending on build server, el5 or el6 rpms are generated
56
+ log "Publishing rpms to mm-apt via rsync"
57
+ rsync *el6*.rpm $EL6REPO || rsync *el5*.rpm $EL5REPO
58
+ if [ $? -ne 0 ]; then
59
+ err "Error pushing rpm to repo"
60
+ exit 3
61
+ else
62
+ log "Publishing completed without error."
63
+ fi
64
+
65
+ ### END ###
66
+ log "Congratulations! We made it to the end of the jenkins recipe."
67
+ exit 0
@@ -0,0 +1,183 @@
1
+ # Copyright (C) 2013-2015 OL2, Inc. All Rights Reserved.
2
+
3
+ require "cryptic/hoffa/version"
4
+ require "mysql2"
5
+
6
+ # This allows redirecting user sites and querying them.
7
+ # You can pass the following options into the
8
+ # various methods:
9
+ #
10
+ # :master_site - the site ID (aav, mca, etc.) of the master site
11
+ # :grok_master - the URL of the Grok master host
12
+ #
13
+
14
+ module Cryptic
15
+ extend self
16
+
17
+ BASE_PORT=28808
18
+ DEFAULT_OPTIONS = {
19
+ :host => "127.0.0.1",
20
+ :port => 3306,
21
+ :username => "analytics",
22
+ :password => "5parklin3",
23
+ :secure_auth => false,
24
+ :database => "analytics",
25
+ }
26
+
27
+ def user_sites(master_options = {})
28
+ return @user_sites.keys if @user_sites
29
+ conn = slave_connection master_options
30
+ # @user_sites maps site name to grok-master DB ID in the etl_control table. This ensures consistent local port mappings.
31
+ @user_sites = {}
32
+ conn.query("SELECT id, site FROM etl_control WHERE level = 'user' AND status = 'up'").each { |row| @user_sites[row["site"]] = row["id"] }
33
+ @user_sites.keys
34
+ end
35
+
36
+ def master_site(options = {})
37
+ return ENV['CRYPTIC_MASTER_SITE'] if ENV['CRYPTIC_MASTER_SITE']
38
+ return options[:master_site] if options[:master_site]
39
+
40
+ reference_site = options[:grok_master] || `hostname -f`.chomp
41
+ onlive_user_site = reference_site.split(".")[1]
42
+ if onlive_user_site && (onlive_user_site =~ /^[A-Za-z]{3}$/ || onlive_user_site == "prod")
43
+ nil # Use local
44
+ else
45
+ "aav"
46
+ end
47
+
48
+ end
49
+
50
+ def master_host(options = {})
51
+ if options[:grok_master]
52
+ options[:grok_master]
53
+ else
54
+ site = master_site(options)
55
+
56
+ site ? "grok-master.#{site}.onlive.net" : "grok-master"
57
+ end
58
+ end
59
+
60
+ def slave_host(options = {})
61
+ if options[:grok_slave]
62
+ options[:grok_slave]
63
+ else
64
+ site = master_site(options) # There's not a separate "slave site" - just master & slave in the same master site.
65
+
66
+ site ? "grokdbro.#{site}.onlive.net" : "grokdbro"
67
+ end
68
+ end
69
+
70
+ def master_connection(options = {})
71
+ @master_conn ||= Mysql2::Client.new(DEFAULT_OPTIONS.merge(:host => master_host(options)).merge(options))
72
+ end
73
+
74
+ def slave_connection(options = {})
75
+ @master_conn ||= Mysql2::Client.new(DEFAULT_OPTIONS.merge(:host => slave_host(options)).merge(options))
76
+ end
77
+
78
+ def dont_redirect_user_sites!(master_options = {})
79
+ @already_redirected_user_sites = true
80
+ end
81
+
82
+ def redirect_user_sites!(sites = nil, master_options = {})
83
+ return if @already_redirected_user_sites
84
+ @already_redirected_user_sites = true
85
+
86
+ sites = user_sites(master_options) unless sites
87
+
88
+ user = `whoami`.chomp
89
+ sites.each do |site|
90
+ local_port = redirected_database_port_for_user_site(site, master_options)
91
+ process = `pgrep -f #{local_port}`
92
+
93
+ # TODO: use slaves where available (currently not for user sites)
94
+ hostname = "grok.#{site}"
95
+
96
+ ms = master_site(master_options)
97
+ master_domain = ms ? "#{ms}.onlive.net" : ""
98
+
99
+ cmd = "ssh -Nf -L#{local_port}:#{hostname}:3306 -o \"StrictHostKeyChecking no\" #{user}@shell.#{master_domain}"
100
+ if process && !process.strip.empty? # Redirection already active
101
+ process.strip.split("\n").each do |p|
102
+ pid = p.to_i
103
+ cur_cmd = `ps -p #{pid}`.split("\n")[1].split(/\s+/, 4)[3]
104
+
105
+ # If this same command was issued then we're already redirecting the site to the right port number.
106
+ # But the double-quotes go away in the ps output.
107
+ if cur_cmd != cmd.gsub('"', "")
108
+ Process.kill("INT", pid)
109
+ STDERR.puts "Remapping port #{local_port} for site #{site}. Command: #{cmd} / Old Command: #{cur_cmd}"
110
+ system(cmd) || raise("Failed port-map for site #{site}, port #{local_port}!")
111
+ end
112
+ end
113
+ else
114
+ STDERR.puts "Mapping port #{local_port} for site #{site}. Command: #{cmd}"
115
+ system(cmd) || raise("Failed port-map for site #{site}, port #{local_port}!")
116
+ end
117
+ end
118
+ end
119
+
120
+ def redirected_database_port_for_user_site(site, master_options = {})
121
+ user_sites(master_options) unless @user_sites
122
+ index = @user_sites[site]
123
+ raise "No index for site #{site.inspect}!" unless index
124
+ BASE_PORT + index
125
+ end
126
+
127
+ def user_site_connection(site = nil, master_options = {})
128
+ @user_site_connections ||= {}
129
+ return @user_site_connections[site] if @user_site_connections[site]
130
+
131
+ local_port = redirected_database_port_for_user_site(site)
132
+
133
+ @user_site_connections[site] = Mysql2::Client.new DEFAULT_OPTIONS.merge(:port => local_port)
134
+ @user_site_connections[site]
135
+ end
136
+
137
+ # Default timeout for queries: 10 minutes
138
+ SELECT_TIMEOUT = 600
139
+
140
+ def query_on_user_sites(query, sites = nil, master_options = {}, &block)
141
+ redirect_user_sites!(sites, master_options)
142
+
143
+ sites = user_sites(master_options) unless sites
144
+ conns = sites.map { |site| user_site_connection(site, master_options) }
145
+
146
+ results = {}
147
+ threads = []
148
+ sites.zip(conns).each do |site, conn|
149
+ t = nil
150
+ begin
151
+ t = Thread.new do
152
+ # Global Interpreter Lock means we don't need to synchronize this
153
+ results[site] = conn.query(query)
154
+ block.call(results[site]) if block
155
+ end
156
+ rescue
157
+ STDERR.puts "Errors on query for site #{site}:\n#{query}\n\n"
158
+ STDERR.puts "Backtrace:\n#{$!.backtrace.join("\n")}\n"
159
+ end
160
+ threads.push(t)
161
+ end
162
+ threads.each(&:join)
163
+
164
+ results
165
+ end
166
+
167
+ # TODO: figure out how to do this outside prod
168
+ def sites_in_maintenance
169
+ begin
170
+ stat = `curl http://mastermon.mca.onlive.net/glustats/glustats.json 2>/dev/null`
171
+ glustat = JSON.parse(stat)
172
+ sites = glustat.keys
173
+ sites = sites.select do |site|
174
+ glustat[site].is_a?(Hash) && glustat[site]["drain_state"] == "drain"
175
+ end
176
+ return sites
177
+ rescue
178
+ STDERR.puts "Error querying sites in maint: #{$!.message}\n#{$!.backtrace.join "\n"}"
179
+ return []
180
+ end
181
+ end
182
+
183
+ end
@@ -0,0 +1,6 @@
1
+ # Copyright (C) 2013-2015 OL2, Inc. All Rights Reserved.
2
+ module Cryptic
3
+ module Hoffa
4
+ VERSION = "0.0.16"
5
+ end
6
+ end
data/vendor/.gitkeep ADDED
File without changes
metadata ADDED
@@ -0,0 +1,240 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cryptic-hoffa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.16
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Noah Gibbs
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2015-02-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: cassandra_migrate
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: trollop
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: cryptic-highway
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rubyglu
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: multi_json
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: erubis
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: activerecord
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: mysql2
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ description: Gateway OMQ data to Cassandra
175
+ email:
176
+ - noah@onlive.com
177
+ executables:
178
+ - dev-glu-tunnel.sh
179
+ - highway-from-sql
180
+ - hoffa
181
+ - prod-glu-tunnel.sh
182
+ extensions: []
183
+ extra_rdoc_files: []
184
+ files:
185
+ - .gitignore
186
+ - .ruby-version
187
+ - Gemfile
188
+ - LICENSE.txt
189
+ - RBPKG
190
+ - README.md
191
+ - Rakefile
192
+ - bin/dev-glu-tunnel.sh
193
+ - bin/highway-from-sql
194
+ - bin/hoffa
195
+ - bin/prod-glu-tunnel.sh
196
+ - binstubs/.gitkeep
197
+ - cassandra_migrations/20131024000000_create_keyspace_Cryptic_down.cql.erb
198
+ - cassandra_migrations/20131024000000_create_keyspace_Cryptic_up.cql.erb
199
+ - cassandra_migrations/20131025000000_create_table_overlay_actions_down.cql.erb
200
+ - cassandra_migrations/20131025000000_create_table_overlay_actions_up.cql.erb
201
+ - cassandra_migrations/20131114488001_create_client_survey_user_site_tables_down.cql.erb
202
+ - cassandra_migrations/20131114488001_create_client_survey_user_site_tables_up.cql.erb
203
+ - cassandra_migrations/20131206944013_create_table_cryptic_place_marker_down.cql.erb
204
+ - cassandra_migrations/20131206944013_create_table_cryptic_place_marker_up.cql.erb
205
+ - cassandra_migrations/20140115150807_add_place_marker_to_place_markers_down.cql
206
+ - cassandra_migrations/20140115150807_add_place_marker_to_place_markers_up.cql
207
+ - cassandra_migrations/20140116281758_repartition_place_markers_table_down.cql
208
+ - cassandra_migrations/20140116281758_repartition_place_markers_table_up.cql
209
+ - cryptic-hoffa.gemspec
210
+ - database.yml
211
+ - jenkins.recipe
212
+ - lib/cryptic/hoffa.rb
213
+ - lib/cryptic/hoffa/version.rb
214
+ - vendor/.gitkeep
215
+ homepage: ''
216
+ licenses:
217
+ - Proprietary
218
+ post_install_message:
219
+ rdoc_options: []
220
+ require_paths:
221
+ - lib
222
+ required_ruby_version: !ruby/object:Gem::Requirement
223
+ none: false
224
+ requirements:
225
+ - - ! '>='
226
+ - !ruby/object:Gem::Version
227
+ version: '0'
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
229
+ none: false
230
+ requirements:
231
+ - - ! '>='
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ requirements: []
235
+ rubyforge_project:
236
+ rubygems_version: 1.8.23
237
+ signing_key:
238
+ specification_version: 3
239
+ summary: Gateway OMQ data to Cassandra
240
+ test_files: []