vayacondios-server 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +61 -0
- data/.travis.yml +11 -0
- data/.yardopts +10 -0
- data/CHANGELOG.md +0 -0
- data/Gemfile +4 -0
- data/Guardfile +41 -0
- data/LICENSE.md +99 -0
- data/Procfile +2 -0
- data/README.md +183 -0
- data/Rakefile +6 -0
- data/app/http_shim.rb +67 -0
- data/bin/vcd.sh +27 -0
- data/config/http_shim.rb +43 -0
- data/config/vayacondios.example.yaml +4 -0
- data/config/vayacondios.yaml +4 -0
- data/lib/tasks/publish.rake +23 -0
- data/lib/tasks/spec.rake +9 -0
- data/lib/tasks/yard.rake +2 -0
- data/lib/vayacondios/client/configliere.rb +38 -0
- data/lib/vayacondios/client/http_client.rb +49 -0
- data/lib/vayacondios/client/notifier.rb +84 -0
- data/lib/vayacondios/server/handlers/config_handler.rb +35 -0
- data/lib/vayacondios/server/handlers/event_handler.rb +30 -0
- data/lib/vayacondios/server/model/config_document.rb +94 -0
- data/lib/vayacondios/server/model/document.rb +25 -0
- data/lib/vayacondios/server/model/event_document.rb +94 -0
- data/lib/vayacondios/version.rb +3 -0
- data/lib/vayacondios-client.rb +20 -0
- data/lib/vayacondios-server.rb +18 -0
- data/scripts/hadoop_monitor/configurable.rb +74 -0
- data/scripts/hadoop_monitor/hadoop_client.rb +249 -0
- data/scripts/hadoop_monitor/hadoop_monitor.rb +91 -0
- data/scripts/hadoop_monitor/hadoopable.rb +65 -0
- data/scripts/hadoop_monitor/machine_monitor.rb +115 -0
- data/scripts/s3_cataloger/buckets +33 -0
- data/scripts/s3_cataloger/foreach_bucket +88 -0
- data/scripts/s3_cataloger/parse_ls.py +391 -0
- data/spec/client/notifier_spec.rb +120 -0
- data/spec/server/config_spec.rb +55 -0
- data/spec/server/event_spec.rb +44 -0
- data/spec/server/server_spec.rb +20 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/support/mongo_cleaner.rb +26 -0
- data/vayacondios-client.gemspec +26 -0
- data/vayacondios-server.gemspec +30 -0
- metadata +216 -0
data/bin/vcd.sh
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
cmd="${1-help}"
|
4
|
+
|
5
|
+
vcd_server_url='http://localhost:9000'
|
6
|
+
|
7
|
+
case "$cmd" in
|
8
|
+
help|--help)
|
9
|
+
echo "commands: "
|
10
|
+
echo " vcd hola foo/bar '{...json hash...}' -- dispatches the hash to the foo/bar bucket on the vayacondios server"
|
11
|
+
echo " vcd help -- this text"
|
12
|
+
echo ""
|
13
|
+
echo "example: "
|
14
|
+
echo " $0 hola just/fiddlin '{\"hi\":\"there\"}'"
|
15
|
+
echo ""
|
16
|
+
;;
|
17
|
+
|
18
|
+
hola)
|
19
|
+
bucket="$2"
|
20
|
+
facts="$3"
|
21
|
+
if [ -z "$bucket" ] || [ -z "$facts" ] ; then echo "vcd hola foo/bar '{...json hash...}'" ; echo " got '$bucket' '$fact'" ; exit -1 ; fi
|
22
|
+
|
23
|
+
echo -- curl -H 'Content-Type:application/json' -d "'$facts'" "'$vcd_server_url/$bucket'"
|
24
|
+
curl -H 'Content-Type:application/json' -d "$facts" "$vcd_server_url/$bucket"
|
25
|
+
;;
|
26
|
+
|
27
|
+
esac
|
data/config/http_shim.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
def ENV.root_path(*args)
|
2
|
+
File.expand_path(File.join(File.dirname(__FILE__), '..', *args))
|
3
|
+
end
|
4
|
+
|
5
|
+
require 'configliere'
|
6
|
+
Settings.define :app_name, :default => 'vayacondios', :description => 'Name to key on for tracer stats, statsd metrics, etc.'
|
7
|
+
Settings.define 'mongo.host', :default => 'localhost', :description => 'Mongo hostname'
|
8
|
+
Settings.define 'mongo.port', :default => '27017', :description => 'Mongo port'
|
9
|
+
|
10
|
+
Settings.read(ENV.root_path('config/vayacondios.yaml'))
|
11
|
+
Settings.resolve!
|
12
|
+
|
13
|
+
config[:server] = {
|
14
|
+
:nodename => ENV['NODENAME'],
|
15
|
+
:hostname => `hostname`.chomp,
|
16
|
+
:program => File.expand_path($0),
|
17
|
+
:version => `git log | head -n1`.chomp.split[1],
|
18
|
+
:pid => Process.pid,
|
19
|
+
}
|
20
|
+
|
21
|
+
environment(:production) do
|
22
|
+
Settings[:environment] = config[:environment] = 'production'
|
23
|
+
config['mongo'] = EventMachine::Synchrony::ConnectionPool.new(:size => 20) do
|
24
|
+
conn = EM::Mongo::Connection.new(Settings[:mongo][:host], Settings[:mongo][:port], 1, {:reconnect_in => 1})
|
25
|
+
conn.db(Settings[:mongo][:database])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
environment(:development) do
|
30
|
+
Settings[:environment] = config[:environment] = 'development'
|
31
|
+
conn = EM::Mongo::Connection.new(Settings[:mongo][:host],Settings[:mongo][:port], 1, {:reconnect_in => 1})
|
32
|
+
config['mongo'] = conn.db(Settings[:mongo][:database])
|
33
|
+
end
|
34
|
+
|
35
|
+
environment(:test) do
|
36
|
+
Settings[:environment] = config[:environment] = 'test'
|
37
|
+
conn = EM::Mongo::Connection.new(Settings[:mongo][:host],Settings[:mongo][:port], 1, {:reconnect_in => 1})
|
38
|
+
config['mongo'] = conn.db(Settings[:mongo][:database])
|
39
|
+
end
|
40
|
+
|
41
|
+
# def config.inspect
|
42
|
+
# self.reject{|k, v| /#{DB_NAME}/ =~ k.to_s}.inspect
|
43
|
+
# end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
$:.push File.expand_path('..', __FILE__)
|
2
|
+
require 'vayacondios/version'
|
3
|
+
|
4
|
+
Dir['*.gemspec'].each do |gemspec|
|
5
|
+
gem_name = gemspec.gsub(/\.gemspec/, '')
|
6
|
+
|
7
|
+
desc "Build #{gem_name} gem into the pkg directory"
|
8
|
+
task "build_#{gem_name}" do
|
9
|
+
FileUtils.rm_rf('pkg')
|
10
|
+
system "gem build #{gemspec}"
|
11
|
+
FileUtils.mkdir_p('pkg')
|
12
|
+
FileUtils.mv(Dir['*.gem'], 'pkg')
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "Tags version, pushes to remote, and pushes #{gem_name} gem"
|
16
|
+
task "release_#{gem_name}" => "build_#{gem_name}" do
|
17
|
+
sh 'git', 'tag', '-m', "releasing #{gem_name}", "#{gem_name}-v#{Vayacondios::VERSION}"
|
18
|
+
branch = `git branch | awk -F '/* ' '{print $2}'`.strip
|
19
|
+
sh "git push origin #{branch}"
|
20
|
+
sh "git push origin v#{Vayacondios::VERSION}"
|
21
|
+
sh "ls pkg/#{gem_name}*.gem | xargs -n 1 gem push"
|
22
|
+
end
|
23
|
+
end
|
data/lib/tasks/spec.rake
ADDED
data/lib/tasks/yard.rake
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
class Vayacondios
|
2
|
+
module Configliere
|
3
|
+
|
4
|
+
def load_from_vayacondios(organization, id, options = {})
|
5
|
+
options.symbolize_keys!.deep_merge!(organization: organization)
|
6
|
+
|
7
|
+
client = ::Vayacondios::HttpClient.receive(options.deep_compact!)
|
8
|
+
id = [id, options[:env]].compact.join('.')
|
9
|
+
|
10
|
+
begin
|
11
|
+
new_data = client.fetch(:config, id)
|
12
|
+
rescue ::Vayacondios::HttpClient::Error
|
13
|
+
warn "Unable to load vayacondios config '#{id}' for #{organization} at: #{client.host}:#{client.port}"
|
14
|
+
new_data = {}
|
15
|
+
end
|
16
|
+
deep_merge! new_data
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def save_to_vayacondios(organization, id, options = {})
|
21
|
+
options.symbolize_keys!.deep_merge!(organization: organization)
|
22
|
+
|
23
|
+
client = ::Vayacondios::HttpClient.receive(options.deep_compact!)
|
24
|
+
id = [id, options[:env]].compact.join('.')
|
25
|
+
|
26
|
+
begin
|
27
|
+
client.insert(self.to_hash, :config, id)
|
28
|
+
rescue ::Vayacondios::HttpClient::Error
|
29
|
+
warn "Unable to save vayacondios config '#{id}' for #{organization} at: #{client.host}:#{client.port}"
|
30
|
+
end
|
31
|
+
self
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
::Configliere::Param.class_eval do
|
37
|
+
include ::Vayacondios::Configliere
|
38
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Vayacondios
|
2
|
+
class HttpClient
|
3
|
+
include Gorillib::Builder
|
4
|
+
|
5
|
+
field :host, String, :default => 'localhost'
|
6
|
+
field :port, Integer, :default => 8000
|
7
|
+
field :organization, String, :default => 'infochimps'
|
8
|
+
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
def uri
|
12
|
+
return @uri if @uri
|
13
|
+
|
14
|
+
uri_str = "http://#{host}:#{port}/v1"
|
15
|
+
uri_str += "/#{organization}" if organization
|
16
|
+
@uri ||= URI(uri_str)
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch(type, id)
|
20
|
+
request(:get, type, id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def insert(document = {}, type = nil, id = nil)
|
24
|
+
id ||= document.delete(:_id) || document.delete('_id')
|
25
|
+
type ||= document.delete(:_type) || document.delete('_type')
|
26
|
+
|
27
|
+
request(:post, type, id, MultiJson.dump(document))
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def request(method, type, id = nil, document = nil)
|
33
|
+
path = File.join(uri.path, type.to_s, *id.to_s.split(/\W/))
|
34
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
35
|
+
|
36
|
+
params = [method.to_sym, path]
|
37
|
+
params += [document, {'Content-Type' => 'application/json'}] unless document.nil?
|
38
|
+
|
39
|
+
response = http.send *params
|
40
|
+
|
41
|
+
if Net::HTTPSuccess === response
|
42
|
+
MultiJson.load(response.body) rescue response.body
|
43
|
+
else
|
44
|
+
raise Error.new("Error (#{response.code}) while #{method.to_s == 'get' ? 'fetching' : 'inserting'} document: " + response.body)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Vayacondios
|
2
|
+
|
3
|
+
class_attribute :notifier
|
4
|
+
|
5
|
+
class Notifier < Vayacondios
|
6
|
+
attr_accessor :client
|
7
|
+
|
8
|
+
def prepare(obj)
|
9
|
+
case
|
10
|
+
when obj.respond_to?(:to_inspectable) then obj.to_inspectable
|
11
|
+
when obj.respond_to?(:to_wire) then obj.to_wire
|
12
|
+
when obj.respond_to?(:to_hash) then obj.to_hash
|
13
|
+
else
|
14
|
+
raise ArgumentError.new("Cannot notify '#{obj.inspect}' -- require a hash-like object.")
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def notify(topic, cargo = {})
|
19
|
+
NoMethodError.unimplemented_method(self)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class LogNotifier < Notifier
|
24
|
+
|
25
|
+
def initialize(options = {})
|
26
|
+
@client = options[:log] || Log
|
27
|
+
end
|
28
|
+
|
29
|
+
def notify(topic, cargo = {})
|
30
|
+
prepped = prepare(cargo)
|
31
|
+
level = prepped.delete(:level) || :info
|
32
|
+
message = "Notification: #{topic.inspect}."
|
33
|
+
message += " Reason: #{prepped.delete(:reason)}." if prepped[:reason]
|
34
|
+
message += " Cargo: #{prepped.inspect}"
|
35
|
+
client.send(level, message)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class HttpNotifier < Notifier
|
40
|
+
|
41
|
+
def initialize(options = {})
|
42
|
+
@client = Vayacondios::HttpClient.receive(options)
|
43
|
+
end
|
44
|
+
|
45
|
+
def notify(topic, cargo = {})
|
46
|
+
prepped = prepare(cargo)
|
47
|
+
client.insert(prepped, :event, topic)
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class NotifierFactory
|
53
|
+
def self.receive(attrs = {})
|
54
|
+
type = attrs.delete(:type)
|
55
|
+
case type
|
56
|
+
when 'http' then HttpNotifier.new(attrs)
|
57
|
+
when 'log' then LogNotifier.new(attrs)
|
58
|
+
else
|
59
|
+
raise ArgumentError, "<#{type}> is not a valid build option"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.default_notifier() NotifierFactory.receive(type: 'http') ; end
|
65
|
+
|
66
|
+
module Notifications
|
67
|
+
extend Gorillib::Concern
|
68
|
+
include Gorillib::Configurable
|
69
|
+
|
70
|
+
def notify(topic, cargo = {})
|
71
|
+
notifier.notify(topic, cargo)
|
72
|
+
end
|
73
|
+
|
74
|
+
included do
|
75
|
+
class_eval do
|
76
|
+
config(:notifier, Vayacondios::NotifierFactory, default: Vayacondios.default_notifier)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
extend Notifications
|
83
|
+
self.notifier = default_notifier
|
84
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Vayacondios::ConfigHandler
|
2
|
+
#
|
3
|
+
# This handler will accept requests to update config for an organization. All
|
4
|
+
# updates will merge with an existing document.
|
5
|
+
|
6
|
+
class Vayacondios
|
7
|
+
|
8
|
+
class ConfigHandler
|
9
|
+
|
10
|
+
def initialize(mongodb)
|
11
|
+
@mongo = mongodb
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(document, options={})
|
15
|
+
existing_document = ConfigDocument.find(@mongo, options)
|
16
|
+
if existing_document
|
17
|
+
existing_document.update(document)
|
18
|
+
else
|
19
|
+
existing_document = ConfigDocument.create(@mongo, document, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
{
|
23
|
+
topic: existing_document.topic,
|
24
|
+
cargo: existing_document.body,
|
25
|
+
status: :success
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.find(mongodb, options)
|
30
|
+
existing_document = ConfigDocument.find(mongodb, options)
|
31
|
+
return nil unless existing_document
|
32
|
+
existing_document.body
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# Vayacondios::EventHandler
|
2
|
+
#
|
3
|
+
# This handler will accept requests to update Event for an organization. All
|
4
|
+
# updates will overwrite an existing document.
|
5
|
+
|
6
|
+
class Vayacondios
|
7
|
+
|
8
|
+
class EventHandler
|
9
|
+
|
10
|
+
def initialize(mongodb)
|
11
|
+
@mongo = mongodb
|
12
|
+
end
|
13
|
+
|
14
|
+
def update(document, options={})
|
15
|
+
existing_document = EventDocument.find(@mongo, options)
|
16
|
+
if existing_document
|
17
|
+
existing_document.update(document)
|
18
|
+
else
|
19
|
+
existing_document = EventDocument.create(@mongo, document, options)
|
20
|
+
end
|
21
|
+
existing_document.body
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.find(mongodb, options)
|
25
|
+
existing_document = EventDocument.find(mongodb, options)
|
26
|
+
return nil unless existing_document
|
27
|
+
existing_document.body
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require 'vayacondios/server/model/document'
|
2
|
+
|
3
|
+
# The configuration model
|
4
|
+
#
|
5
|
+
# Configuration documents are key-value pairs, represented in JSON. A document
|
6
|
+
# consists of a primary key called the topic (_id in mongodb). It belongs to a
|
7
|
+
# collection named "#{organization_name}.config"
|
8
|
+
#
|
9
|
+
# Note: mongodb is passed in beacuse Goliath makes Thread lookups will not
|
10
|
+
# work while Goliath is in a streaming context.
|
11
|
+
|
12
|
+
class Vayacondios::ConfigDocument < Vayacondios::Document
|
13
|
+
attr_reader :organization, :topic, :body
|
14
|
+
|
15
|
+
def initialize(mongodb, options = {})
|
16
|
+
super options
|
17
|
+
@mongo = mongodb
|
18
|
+
options = sanitize_options(options)
|
19
|
+
|
20
|
+
@body = nil
|
21
|
+
@field = options[:field] ||= options[:id]
|
22
|
+
@mongo = mongodb
|
23
|
+
|
24
|
+
collection_name = [organization.to_s, 'config'].join('.')
|
25
|
+
@collection = @mongo.collection(collection_name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.create(mongodb, document, options={})
|
29
|
+
self.new(mongodb, options).update(document)
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.find(mongodb, options={})
|
33
|
+
self.new(mongodb, options).find
|
34
|
+
end
|
35
|
+
|
36
|
+
def find
|
37
|
+
fields = {}
|
38
|
+
fields[@field] = 1 if @field
|
39
|
+
|
40
|
+
result = @collection.find_one({_id: @topic}, {fields: @fields})
|
41
|
+
if result.present?
|
42
|
+
result.delete("_id")
|
43
|
+
@body = result
|
44
|
+
# @body = @field.split('.').inject(result){|acc, attr| acc = acc[attr]} if @field.present?
|
45
|
+
self
|
46
|
+
else
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def update(document)
|
52
|
+
# MongoDB always wants a Hash. If we POST to /org/topic/users/jim/username
|
53
|
+
# with "jimmy", we transform the document to {username: "jimmy"}
|
54
|
+
if !document.is_a?(Hash)
|
55
|
+
document = {@field.split(/\W/).last.to_sym => document}
|
56
|
+
end
|
57
|
+
|
58
|
+
# Merge ourselves
|
59
|
+
document = deep_merge(document)
|
60
|
+
|
61
|
+
fields = document
|
62
|
+
# fields = {@field => document} if @field.present?
|
63
|
+
|
64
|
+
@body = document
|
65
|
+
@collection.update({:_id => @topic}, {'$set' => fields}, {upsert: true})
|
66
|
+
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def destroy(document)
|
71
|
+
super
|
72
|
+
end
|
73
|
+
|
74
|
+
protected
|
75
|
+
|
76
|
+
def sanitize_options(options)
|
77
|
+
options = options.symbolize_keys
|
78
|
+
|
79
|
+
topic = options[:topic]
|
80
|
+
|
81
|
+
if (topic.is_a?(Hash) && topic["$oid"].present?)
|
82
|
+
topic = BSON::ObjectId(topic["$oid"])
|
83
|
+
elsif topic.is_a?(String)
|
84
|
+
topic = topic.gsub(/\W/,'')
|
85
|
+
if topic.to_s.match(/^[a-f0-9]{24}$/)
|
86
|
+
topic = BSON::ObjectId(topic)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
field = options[:field].gsub(/\W/, '') if options[:field].present?
|
91
|
+
|
92
|
+
options.merge(topic: topic, field: field)
|
93
|
+
end
|
94
|
+
end
|