vines-services 0.1.0

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 (69) hide show
  1. data/LICENSE +19 -0
  2. data/README +40 -0
  3. data/Rakefile +130 -0
  4. data/bin/vines-services +95 -0
  5. data/conf/config.rb +25 -0
  6. data/lib/vines/services/command/init.rb +209 -0
  7. data/lib/vines/services/command/restart.rb +14 -0
  8. data/lib/vines/services/command/start.rb +30 -0
  9. data/lib/vines/services/command/stop.rb +20 -0
  10. data/lib/vines/services/command/views.rb +26 -0
  11. data/lib/vines/services/component.rb +26 -0
  12. data/lib/vines/services/config.rb +105 -0
  13. data/lib/vines/services/connection.rb +120 -0
  14. data/lib/vines/services/controller/attributes_controller.rb +19 -0
  15. data/lib/vines/services/controller/base_controller.rb +99 -0
  16. data/lib/vines/services/controller/disco_info_controller.rb +61 -0
  17. data/lib/vines/services/controller/labels_controller.rb +17 -0
  18. data/lib/vines/services/controller/members_controller.rb +44 -0
  19. data/lib/vines/services/controller/messages_controller.rb +66 -0
  20. data/lib/vines/services/controller/probes_controller.rb +45 -0
  21. data/lib/vines/services/controller/services_controller.rb +81 -0
  22. data/lib/vines/services/controller/subscriptions_controller.rb +39 -0
  23. data/lib/vines/services/controller/systems_controller.rb +45 -0
  24. data/lib/vines/services/controller/transfers_controller.rb +58 -0
  25. data/lib/vines/services/controller/uploads_controller.rb +62 -0
  26. data/lib/vines/services/controller/users_controller.rb +127 -0
  27. data/lib/vines/services/core_ext/blather.rb +46 -0
  28. data/lib/vines/services/core_ext/couchrest.rb +33 -0
  29. data/lib/vines/services/indexer.rb +195 -0
  30. data/lib/vines/services/priority_queue.rb +94 -0
  31. data/lib/vines/services/roster.rb +70 -0
  32. data/lib/vines/services/storage/couchdb/fragment.rb +23 -0
  33. data/lib/vines/services/storage/couchdb/service.rb +170 -0
  34. data/lib/vines/services/storage/couchdb/system.rb +141 -0
  35. data/lib/vines/services/storage/couchdb/upload.rb +66 -0
  36. data/lib/vines/services/storage/couchdb/user.rb +137 -0
  37. data/lib/vines/services/storage/couchdb/vcard.rb +13 -0
  38. data/lib/vines/services/storage/couchdb.rb +157 -0
  39. data/lib/vines/services/storage.rb +33 -0
  40. data/lib/vines/services/throttle.rb +26 -0
  41. data/lib/vines/services/version.rb +7 -0
  42. data/lib/vines/services/vql/compiler.rb +94 -0
  43. data/lib/vines/services/vql/vql.citrus +115 -0
  44. data/lib/vines/services/vql/vql.rb +186 -0
  45. data/lib/vines/services.rb +71 -0
  46. data/test/config_test.rb +242 -0
  47. data/test/priority_queue_test.rb +23 -0
  48. data/test/storage/couchdb_test.rb +30 -0
  49. data/test/vql/compiler_test.rb +96 -0
  50. data/test/vql/vql_test.rb +233 -0
  51. data/web/coffeescripts/api.coffee +51 -0
  52. data/web/coffeescripts/commands.coffee +18 -0
  53. data/web/coffeescripts/files.coffee +315 -0
  54. data/web/coffeescripts/init.coffee +21 -0
  55. data/web/coffeescripts/services.coffee +356 -0
  56. data/web/coffeescripts/setup.coffee +503 -0
  57. data/web/coffeescripts/systems.coffee +371 -0
  58. data/web/images/default-service.png +0 -0
  59. data/web/images/linux.png +0 -0
  60. data/web/images/mac.png +0 -0
  61. data/web/images/run.png +0 -0
  62. data/web/images/windows.png +0 -0
  63. data/web/index.html +17 -0
  64. data/web/stylesheets/common.css +52 -0
  65. data/web/stylesheets/files.css +218 -0
  66. data/web/stylesheets/services.css +181 -0
  67. data/web/stylesheets/setup.css +117 -0
  68. data/web/stylesheets/systems.css +142 -0
  69. metadata +230 -0
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2011 Negative Code
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,40 @@
1
+ == Welcome to Vines Services
2
+
3
+ Vines Services are dynamically updated groups of systems based on criteria like
4
+ hostname, installed software, operating system, etc. Send a command to the
5
+ service and it runs on every system in the group. Services, files and permissions
6
+ are managed via the bundled web application.
7
+
8
+ Services are defined using VinesQL, a simple query language that finds systems
9
+ based on their machine attributes. A "Mac OS X Lion" service might be
10
+ defined as: platform is 'mac_os_x' and platform_version starts with '10.7'.
11
+ As machines update to Lion, they will join this service automatically and may
12
+ be managed as one group.
13
+
14
+ == Usage
15
+
16
+ 1. gem install vines-services
17
+ 2. vines-services init wonderland.lit
18
+ 3. cd wonderland.lit && vines-services start
19
+
20
+ == Dependencies
21
+
22
+ * bcrypt-ruby >= 3.0.0
23
+ * blather >= 0.5.4
24
+ * citrus >= 2.4.0
25
+ * eventmachine >= 0.12.10
26
+ * nokogiri >= 1.4.4
27
+ * sqlite3 >= 1.3.4
28
+ * ruby >= 1.9.2
29
+
30
+ == Ubuntu setup
31
+ $ sudo apt-get install build-essential ruby1.9.1 ruby1.9.1-dev libxml2-dev libxslt-dev
32
+
33
+ == Contact
34
+
35
+ * David Graham <david@negativecode.com>
36
+ * Chris Johnson <chris@negativecode.com>
37
+
38
+ == License
39
+
40
+ Vines Services is released under the MIT license. Check the LICENSE file for details.
data/Rakefile ADDED
@@ -0,0 +1,130 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/testtask'
4
+ require 'rubygems/package_task'
5
+ require 'nokogiri'
6
+ require_relative 'lib/vines/services/version'
7
+
8
+ spec = Gem::Specification.new do |s|
9
+ s.name = "vines-services"
10
+ s.version = Vines::Services::VERSION
11
+ s.date = Time.now.strftime("%Y-%m-%d")
12
+
13
+ s.summary = "An XMPP component that broadcasts shell commands to many agents."
14
+ s.description = "Vines Services are dynamically updated groups of systems based
15
+ on criteria like hostname, installed software, operating system, etc. Send a
16
+ command to the service and it runs on every system in the group. Services, files
17
+ and permissions are managed via the bundled web application."
18
+
19
+ s.authors = ["David Graham", "Chris Johnson"]
20
+ s.email = %w[david@negativecode.com chris@negativecode.com]
21
+ s.homepage = "http://www.getvines.com"
22
+
23
+ s.files = FileList['[A-Z]*', '{bin,lib,conf,web}/**/*']
24
+ s.test_files = FileList["test/**/*"]
25
+ s.executables = %w[vines-services]
26
+ s.require_path = "lib"
27
+
28
+ s.add_dependency "bcrypt-ruby", "~> 3.0.0"
29
+ s.add_dependency "blather", "~> 0.5.4"
30
+ s.add_dependency "citrus", "~> 2.4.0"
31
+ s.add_dependency "couchrest_model", "~> 1.1.2"
32
+ s.add_dependency "em-http-request", "~> 0.3.0"
33
+ s.add_dependency "sqlite3", "~> 1.3.4"
34
+ s.add_dependency "vines", "~> 0.3"
35
+
36
+ s.add_development_dependency "minitest"
37
+ s.add_development_dependency "rake"
38
+
39
+ s.required_ruby_version = '>= 1.9.2'
40
+ end
41
+
42
+ Gem::PackageTask.new(spec) do |pkg|
43
+ pkg.need_tar = true
44
+ end
45
+
46
+ Rake::TestTask.new(:test) do |test|
47
+ test.libs << 'test'
48
+ test.pattern = 'test/**/*_test.rb'
49
+ test.warning = false
50
+ end
51
+
52
+ # Return an array of local, non-library, js includes.
53
+ def scripts(doc)
54
+ scripts = []
55
+ doc.css('script').each do |node|
56
+ file = node['src'].split('/').last()
57
+ if node['src'].start_with?('javascripts/')
58
+ scripts << file
59
+ end
60
+ end
61
+ scripts
62
+ end
63
+
64
+ # Replace script tags with combined and minimized files.
65
+ def rewrite_js(doc)
66
+ doc.css('script').each {|node| node.remove }
67
+ doc.css('head').each do |node|
68
+ %w[/lib/javascripts/base.js javascripts/app.js].each do |src|
69
+ script = doc.create_element('script',
70
+ 'type' => 'text/javascript',
71
+ 'src' => src)
72
+ node.add_child(script)
73
+ node.add_child(doc.create_text_node("\n"))
74
+ end
75
+ end
76
+ end
77
+
78
+ def stylesheets(doc)
79
+ sheets = []
80
+ doc.css('link[rel="stylesheet"]').each do |node|
81
+ file = node['href'].split('/').last()
82
+ if node['href'].start_with?('stylesheets/')
83
+ sheets << file
84
+ end
85
+ end
86
+ sheets
87
+ end
88
+
89
+ def rewrite_css(doc)
90
+ doc.css('link[rel="stylesheet"]').each {|node| node.remove }
91
+ doc.css('head').each do |node|
92
+ %w[/lib/stylesheets/base.css /lib/stylesheets/login.css stylesheets/app.css].each do |file|
93
+ link = doc.create_element('link',
94
+ 'rel' => 'stylesheet',
95
+ 'href' => file)
96
+ node.add_child(link)
97
+ node.add_child(doc.create_text_node("\n"))
98
+ end
99
+ end
100
+ end
101
+
102
+ task :compile do
103
+ index = 'web/index.html'
104
+ doc = Nokogiri::HTML(File.read(index))
105
+ scripts, sheets = scripts(doc), stylesheets(doc)
106
+
107
+ rewrite_js(doc)
108
+ rewrite_css(doc)
109
+
110
+ # save index.html before rewriting
111
+ FileUtils.cp(index, '/tmp/index.html')
112
+ File.open(index, 'w') {|f| f.write(doc.to_xml(:indent => 2)) }
113
+
114
+ js_files = scripts.map {|f| "web/javascripts/#{f}"}.join(' ')
115
+ css_files = sheets.map {|f| "web/stylesheets/#{f}"}.join(' ')
116
+
117
+ sh %{coffee -c -b -o web/javascripts web/coffeescripts/*.coffee}
118
+ sh %{cat #{js_files} | uglifyjs -nc > web/javascripts/app.js}
119
+
120
+ sh %{cat #{css_files} > web/stylesheets/app.css}
121
+ end
122
+
123
+ task :cleanup do
124
+ # move index.html back into place after gem packaging
125
+ FileUtils.cp('/tmp/index.html', 'web/index.html')
126
+ File.delete('/tmp/index.html')
127
+ end
128
+
129
+
130
+ task :default => [:clobber, :test, :compile, :gem, :cleanup]
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ require 'optparse'
5
+ require 'vines/services'
6
+
7
+ COMMANDS = %w[start stop restart init views]
8
+
9
+ def parse(args)
10
+ options = {}
11
+ parser = OptionParser.new do |opts|
12
+ opts.banner = "Usage: vines-services [options] #{COMMANDS.join('|')}"
13
+
14
+ opts.separator ""
15
+ opts.separator "Daemon options:"
16
+
17
+ opts.on('-d', '--daemonize', 'Run daemonized in the background') do |daemonize|
18
+ options[:daemonize] = daemonize
19
+ end
20
+
21
+ options[:log] = 'log/vines-services.log'
22
+ opts.on('-l', '--log FILE', 'File to redirect output (default: log/vines-services.log)') do |log|
23
+ options[:log] = log
24
+ end
25
+
26
+ options[:pid] = 'pid/vines-services.pid'
27
+ opts.on('-P', '--pid FILE', 'File to store PID (default: pid/vines-services.pid)') do |pid|
28
+ options[:pid] = pid
29
+ end
30
+
31
+ opts.separator ""
32
+ opts.separator "Common options:"
33
+
34
+ opts.on('-h', '--help', 'Show this message') do |help|
35
+ options[:help] = help
36
+ end
37
+
38
+ opts.on('-v', '--version', 'Show version') do |version|
39
+ options[:version] = version
40
+ end
41
+ end
42
+
43
+ begin
44
+ parser.parse!(args)
45
+ rescue
46
+ puts parser
47
+ exit(1)
48
+ end
49
+
50
+ if options[:version]
51
+ puts Vines::Services::VERSION
52
+ exit
53
+ end
54
+
55
+ if options[:help]
56
+ puts parser
57
+ exit
58
+ end
59
+
60
+ command = args.shift
61
+ unless COMMANDS.include?(command)
62
+ puts parser
63
+ exit(1)
64
+ end
65
+
66
+ options.tap do |opts|
67
+ opts[:args] = args
68
+ opts[:command] = command
69
+ opts[:config] = File.expand_path('conf/config.rb')
70
+ opts[:pid] = File.expand_path(opts[:pid])
71
+ opts[:log] = File.expand_path(opts[:log])
72
+ end
73
+ end
74
+
75
+ def check_config(opts)
76
+ return if %w[init].include?(opts[:command])
77
+ unless File.exists?(opts[:config])
78
+ puts "No config file found at #{opts[:config]}"
79
+ exit(1)
80
+ end
81
+ end
82
+
83
+ VINES_ROOT = Dir.pwd
84
+ opts = parse(ARGV)
85
+ check_config(opts)
86
+ command = Vines::Services::Command.const_get(opts[:command].capitalize).new
87
+ begin
88
+ command.run(opts)
89
+ rescue SystemExit
90
+ # do nothing
91
+ rescue Exception => e
92
+ puts e.message
93
+ puts e.backtrace
94
+ exit(1)
95
+ end
data/conf/config.rb ADDED
@@ -0,0 +1,25 @@
1
+ # encoding: UTF-8
2
+
3
+ # This is the Vines Services configuration file. Restart the service with
4
+ # 'vines-services restart' after updating this file.
5
+
6
+ Vines::Services::Config.configure do
7
+ # Set the logging level to debug, info, warn, error, or fatal. The debug
8
+ # level logs all XML sent and received by the server.
9
+ log :info
10
+
11
+ host 'vines.wonderland.lit' do
12
+ upstream 'localhost', 5347, 'secr3t'
13
+ uploads 'data/upload'
14
+
15
+ storage 'couchdb' do
16
+ host 'localhost'
17
+ port 5984
18
+ database 'xmpp'
19
+ tls false
20
+ username ''
21
+ password ''
22
+ index_dir 'data/index'
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,209 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Services
5
+ module Command
6
+ class Init
7
+ def run(opts)
8
+ raise 'vines-services init [domain]' unless opts[:args].size == 1
9
+ @domain = opts[:args].first.dup
10
+ dir = File.expand_path(@domain)
11
+ @component_password = Kit.generate_password
12
+ create_directories
13
+ initialize_server
14
+ initialize_component
15
+ initialize_agent
16
+ puts "Initialized service, agent, and server directories: #{@domain}"
17
+ end
18
+
19
+ private
20
+
21
+ def initialize_server
22
+ Dir.chdir(File.join(@domain, "server"))
23
+ `vines init #{@domain}`
24
+ configure_database
25
+ FileUtils.mv Dir.glob("#{@domain}/*"), "./"
26
+ FileUtils.remove_dir(@domain)
27
+ `vines start -d`
28
+ FileUtils.cp_r(File.expand_path("../../../../../web/index.html", __FILE__), File.join("web", "index.html"))
29
+ FileUtils.cp_r(File.expand_path("../../../../../web/javascripts", __FILE__), File.join("web", "javascripts"))
30
+ FileUtils.cp_r(File.expand_path("../../../../../web/stylesheets", __FILE__), File.join("web", "stylesheets"))
31
+ FileUtils.cp_r(File.expand_path("../../../../../web/images", __FILE__), File.join("web", "images"))
32
+ FileUtils.cp_r(File.expand_path("../../../../../web/coffeescripts", __FILE__), File.join("web", "coffeescripts"))
33
+ end
34
+
35
+ def initialize_agent
36
+ Dir.chdir("../agent/")
37
+ `vines-agent init #{@domain}`
38
+ FileUtils.cp("../server/conf/certs/#{@domain}.crt", File.join("#{@domain}", "conf", "certs"))
39
+ agent_password = Kit.generate_password
40
+ jid = Vines::JID.new(fqdn, @domain).bare
41
+ id = "user:#{jid}"
42
+ Fiber.new do
43
+ user = Vines::Services::CouchModels::User.new(id: id)
44
+ user.name = jid
45
+ user.system = true
46
+ user.password = agent_password
47
+ if user.valid?
48
+ user.save
49
+ log.debug("Saving user: #{user}")
50
+ end
51
+ end.resume
52
+ update_agent_config(File.join("#{@domain}", "conf", "config.rb"), agent_password)
53
+ FileUtils.mv Dir.glob("#{@domain}/*"), "./"
54
+ FileUtils.remove_dir(@domain)
55
+ `vines-agent start -d`
56
+ end
57
+
58
+ # Coordinate all the configuration required for the service
59
+ def initialize_component
60
+ Dir.chdir("../services/")
61
+ %w[conf].each do |sub|
62
+ FileUtils.cp_r(File.expand_path("../../../../../#{sub}", __FILE__), File.join("."))
63
+ end
64
+ log, pid = %w[log pid data data/index].map do |sub|
65
+ File.join(sub).tap {|subdir| Dir.mkdir(subdir) }
66
+ end
67
+ update_config(File.join("conf", "config.rb"))
68
+ `vines-services start -d`
69
+ end
70
+
71
+ #create all the directories for the server, agent, and service
72
+ def create_directories
73
+ Dir.mkdir(@domain)
74
+ %w[server services agent].each do |sub|
75
+ Dir.mkdir(File.join(@domain, sub))
76
+ end
77
+ end
78
+
79
+ def create_db(host, port, db)
80
+ url = "http://#{host}:#{port}/#{db}"
81
+ begin
82
+ RestClient.head(url)
83
+ rescue RestClient::ResourceNotFound
84
+ RestClient.put(url, nil)
85
+ end
86
+ true
87
+ rescue
88
+ false
89
+ end
90
+
91
+ # This will attempt to create a new couch database. If one exists
92
+ # by the name generated, it will not be deleted. This will loop until
93
+ # it gets a valid couch db address and port
94
+ def configure_database
95
+ server, port = "127.0.0.1", 5984
96
+ db_name = @domain.gsub(".", "_").downcase()
97
+ until create_db(server, port, db_name)
98
+ puts "Unable to connect."
99
+ $stdout.write('CouchDB Server: ')
100
+ server = $stdin.gets.chomp
101
+ $stdout.write('CouchDB Port: ')
102
+ port = $stdin.gets.chomp
103
+ end
104
+ @couch_server = server
105
+ @couch_port = port
106
+ @couch_db = db_name
107
+ update_config(File.join(@domain, "conf", "config.rb"))
108
+ @storage = Vines::Services::Storage::CouchDB.new do
109
+ host server
110
+ port port
111
+ database db_name
112
+ index_dir '.'
113
+ end
114
+ create_user
115
+ end
116
+
117
+ #Create a new vines user object in the couch database
118
+ def create_user
119
+ jid, password = nil, nil
120
+ until jid
121
+ $stdout.write('JID: ')
122
+ if node = $stdin.gets.chomp.split('@').first
123
+ jid = Vines::JID.new(node, @domain) rescue nil
124
+ end
125
+ end
126
+
127
+ until password
128
+ $stdout.write('Password: ')
129
+ `stty -echo`
130
+ password = $stdin.gets.chomp
131
+ password = nil if password.empty?
132
+ `stty echo`
133
+ end
134
+ puts "\nCreated #{jid}"
135
+
136
+ id = "user:#{jid}"
137
+ Fiber.new do
138
+ user = Vines::Services::CouchModels::User.new(id: id)
139
+ user.name = jid
140
+ user.password = password
141
+ if user.valid?
142
+ user.save
143
+ log.debug("\nSaving user: #{user}")
144
+ end
145
+ end.resume
146
+ end
147
+
148
+ # Set the agents password to the one we generated in this initialization
149
+ def update_agent_config(config, password)
150
+ text = File.read(config)
151
+ File.open(config, 'w') do |f|
152
+ f.write(text.gsub(/password.*/, "password '#{password}'"))
153
+ end
154
+ end
155
+
156
+ # Return the fully qualified domain name for this machine. This is used
157
+ # to determine the agent's JID.
158
+ def fqdn
159
+ require 'ohai'
160
+ system = Ohai::System.new
161
+ system.require_plugin('os')
162
+ system.require_plugin('hostname')
163
+ system.fqdn.downcase
164
+ end
165
+
166
+ # Change the config file to contain the values we generated or validated.
167
+ def update_config(config)
168
+ # FIXME vines.local
169
+ orig_config = """
170
+ host 'vines.local' do
171
+ cross_domain_messages false
172
+ private_storage false
173
+ storage 'fs' do
174
+ dir 'data/users'
175
+ end
176
+ # components 'tea' => 'secr3t',
177
+ # 'cake' => 'passw0rd'
178
+ end"""
179
+ new_config = """
180
+ host '#{@domain}' do
181
+ cross_domain_messages false
182
+ private_storage true
183
+ storage 'couchdb' do
184
+ host '#{@couch_server}'
185
+ port #{@couch_port}
186
+ database '#{@couch_db}'
187
+ tls false
188
+ username ''
189
+ password ''
190
+ end
191
+ components 'vines' => '#{@component_password}'
192
+ end"""
193
+ text = File.read(config)
194
+ text = text.gsub(orig_config, new_config)
195
+ text = text.gsub("host 'vines.wonderland.lit'", "host 'vines.#{@domain}'")
196
+ text = text.gsub("secr3t", "#{@component_password}")
197
+ text = text.gsub("host 'localhost'", "host '#{@couch_server}'")
198
+ text = text.gsub("port 5984", "port #{@couch_port}")
199
+ text = text.gsub("database 'xmpp'", "database '#{@couch_db}'")
200
+ File.open(config, 'w') do |f|
201
+ f.write(text)
202
+ end
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+
209
+
@@ -0,0 +1,14 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Services
5
+ module Command
6
+ class Restart
7
+ def run(opts)
8
+ Stop.new.run(opts)
9
+ Start.new.run(opts)
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,30 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Services
5
+ module Command
6
+ class Start
7
+ def run(opts)
8
+ raise 'vines-services [--pid FILE] start' unless opts[:args].size == 0
9
+ require opts[:config]
10
+ component = Vines::Services::Component.new(Config.instance)
11
+ daemonize(opts) if opts[:daemonize]
12
+ component.start
13
+ end
14
+
15
+ private
16
+
17
+ def daemonize(opts)
18
+ daemon = Vines::Daemon.new(:pid => opts[:pid], :stdout => opts[:log],
19
+ :stderr => opts[:log])
20
+ if daemon.running?
21
+ raise "The vines service is running as process #{daemon.pid}"
22
+ else
23
+ puts "The vines service has started"
24
+ daemon.start
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,20 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Services
5
+ module Command
6
+ class Stop
7
+ def run(opts)
8
+ raise 'vines-services [--pid FILE] stop' unless opts[:args].size == 0
9
+ daemon = Vines::Daemon.new(:pid => opts[:pid])
10
+ if daemon.running?
11
+ daemon.stop
12
+ puts 'The vines service has been shutdown'
13
+ else
14
+ puts 'The vines service is not running'
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Services
5
+ module Command
6
+ class Views
7
+ def run(opts)
8
+ raise 'vines-services views <domain>' unless opts[:args].size == 1
9
+ require opts[:config]
10
+ domain = opts[:args].first
11
+ unless host = Config.instance.vhosts[domain]
12
+ raise "#{domain} virtual host not found in conf/config.rb"
13
+ end
14
+ unless host.storage.respond_to?(:create_views)
15
+ raise "CouchDB storage not configured for #{domain} virtual host"
16
+ end
17
+ begin
18
+ host.storage.create_views
19
+ rescue Exception => e
20
+ raise "View creation failed: #{e.message}"
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: UTF-8
2
+
3
+ module Vines
4
+ module Services
5
+ # The main starting point for the Vines Services component process. Starts
6
+ # the EventMachine processing loop and registers the component with the
7
+ # configured upstream servers.
8
+ class Component
9
+ include Vines::Log
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ end
14
+
15
+ def start
16
+ log.info('Vines component started')
17
+ at_exit { log.fatal('Vines component stopped') }
18
+ EM.epoll
19
+ EM.kqueue
20
+ EM.run do
21
+ @config.hosts.each {|host| host.start }
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end