vayacondios-server 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|