vayacondios-server 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/.gitignore +61 -0
  2. data/.travis.yml +11 -0
  3. data/.yardopts +10 -0
  4. data/CHANGELOG.md +0 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +41 -0
  7. data/LICENSE.md +99 -0
  8. data/Procfile +2 -0
  9. data/README.md +183 -0
  10. data/Rakefile +6 -0
  11. data/app/http_shim.rb +67 -0
  12. data/bin/vcd.sh +27 -0
  13. data/config/http_shim.rb +43 -0
  14. data/config/vayacondios.example.yaml +4 -0
  15. data/config/vayacondios.yaml +4 -0
  16. data/lib/tasks/publish.rake +23 -0
  17. data/lib/tasks/spec.rake +9 -0
  18. data/lib/tasks/yard.rake +2 -0
  19. data/lib/vayacondios/client/configliere.rb +38 -0
  20. data/lib/vayacondios/client/http_client.rb +49 -0
  21. data/lib/vayacondios/client/notifier.rb +84 -0
  22. data/lib/vayacondios/server/handlers/config_handler.rb +35 -0
  23. data/lib/vayacondios/server/handlers/event_handler.rb +30 -0
  24. data/lib/vayacondios/server/model/config_document.rb +94 -0
  25. data/lib/vayacondios/server/model/document.rb +25 -0
  26. data/lib/vayacondios/server/model/event_document.rb +94 -0
  27. data/lib/vayacondios/version.rb +3 -0
  28. data/lib/vayacondios-client.rb +20 -0
  29. data/lib/vayacondios-server.rb +18 -0
  30. data/scripts/hadoop_monitor/configurable.rb +74 -0
  31. data/scripts/hadoop_monitor/hadoop_client.rb +249 -0
  32. data/scripts/hadoop_monitor/hadoop_monitor.rb +91 -0
  33. data/scripts/hadoop_monitor/hadoopable.rb +65 -0
  34. data/scripts/hadoop_monitor/machine_monitor.rb +115 -0
  35. data/scripts/s3_cataloger/buckets +33 -0
  36. data/scripts/s3_cataloger/foreach_bucket +88 -0
  37. data/scripts/s3_cataloger/parse_ls.py +391 -0
  38. data/spec/client/notifier_spec.rb +120 -0
  39. data/spec/server/config_spec.rb +55 -0
  40. data/spec/server/event_spec.rb +44 -0
  41. data/spec/server/server_spec.rb +20 -0
  42. data/spec/spec_helper.rb +10 -0
  43. data/spec/support/mongo_cleaner.rb +26 -0
  44. data/vayacondios-client.gemspec +26 -0
  45. data/vayacondios-server.gemspec +30 -0
  46. 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
@@ -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,4 @@
1
+ mongo:
2
+ host: localhost
3
+ port: 27017
4
+ database: vayacondios_dev
@@ -0,0 +1,4 @@
1
+ mongo:
2
+ host: localhost
3
+ port: 27017
4
+ database: vayacondios_dev
@@ -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
@@ -0,0 +1,9 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new
4
+
5
+ namespace :spec do
6
+ # RSpec::Core::RakeTask.new :coverage do
7
+ # ENV['COVERAGE'] = "true"
8
+ # end
9
+ end
@@ -0,0 +1,2 @@
1
+ require 'yard'
2
+ YARD::Rake::YardocTask.new
@@ -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