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.
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