cryptic-hoffa 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +1 -0
- data/RBPKG +5 -0
- data/README.md +22 -0
- data/Rakefile +1 -0
- data/bin/dev-glu-tunnel.sh +5 -0
- data/bin/highway-from-sql +76 -0
- data/bin/hoffa +195 -0
- data/bin/prod-glu-tunnel.sh +5 -0
- data/binstubs/.gitkeep +0 -0
- data/cassandra_migrations/20131024000000_create_keyspace_Cryptic_down.cql.erb +1 -0
- data/cassandra_migrations/20131024000000_create_keyspace_Cryptic_up.cql.erb +2 -0
- data/cassandra_migrations/20131025000000_create_table_overlay_actions_down.cql.erb +3 -0
- data/cassandra_migrations/20131025000000_create_table_overlay_actions_up.cql.erb +15 -0
- data/cassandra_migrations/20131114488001_create_client_survey_user_site_tables_down.cql.erb +9 -0
- data/cassandra_migrations/20131114488001_create_client_survey_user_site_tables_up.cql.erb +94 -0
- data/cassandra_migrations/20131206944013_create_table_cryptic_place_marker_down.cql.erb +3 -0
- data/cassandra_migrations/20131206944013_create_table_cryptic_place_marker_up.cql.erb +10 -0
- data/cassandra_migrations/20140115150807_add_place_marker_to_place_markers_down.cql +3 -0
- data/cassandra_migrations/20140115150807_add_place_marker_to_place_markers_up.cql +3 -0
- data/cassandra_migrations/20140116281758_repartition_place_markers_table_down.cql +10 -0
- data/cassandra_migrations/20140116281758_repartition_place_markers_table_up.cql +10 -0
- data/cryptic-hoffa.gemspec +34 -0
- data/database.yml +13 -0
- data/jenkins.recipe +67 -0
- data/lib/cryptic/hoffa.rb +183 -0
- data/lib/cryptic/hoffa/version.rb +6 -0
- data/vendor/.gitkeep +0 -0
- metadata +240 -0
data/.gitignore
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.9.3-p327
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Copyright (c) 2013 OL2, Inc. All Rights Reserved.
|
data/RBPKG
ADDED
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,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."
|
data/binstubs/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
DROP KEYSPACE "cryptic";
|
@@ -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,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,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,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,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
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
|
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: []
|