vines-services 0.1.0

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