jesus 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ *.kpf
2
+ *.tmproj
3
+ .DS_Store
4
+ vendor/gems/*
5
+ bin/*
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source "http://gemcutter.org"
2
+ #
3
+ # Required on every environment
4
+ #
5
+ gem "sinatra"
6
+ gem "sinatra_more"
7
+ gem "god"
8
+
9
+ #
10
+ # Required only for tests
11
+ #
12
+ gem "rspec", :only => :testing
13
+ gem "rack-test", :only => :testing
14
+ gem "mocha", :only => :testing
@@ -0,0 +1,44 @@
1
+ h1. Jesus
2
+
3
+ Jesus is a web interface to "god":http://github.com/mojombo/god
4
+ It allows you to see the process and monitor/unmonitor them.
5
+
6
+ !http://cloud.github.com/downloads/dmathieu/jesus/jesus-0.0.1.thumb.png(Screenshot 0.0.1)!:http://cloud.github.com/downloads/dmathieu/jesus/jesus-0.0.1.png
7
+
8
+ h2. Installation
9
+
10
+ To install the application you first need to have "bundler":http://github.com/wycats/bundler installed on your machine.
11
+ Once that is done, clone the project in the directory of your choice.
12
+
13
+ @git clone git://github.com/dmathieu/jesus.git@
14
+
15
+ Then you should install the dependencies.
16
+
17
+ @gem bundle@
18
+
19
+ You now have Jesus available on your machine. Start your god process.
20
+ And start Jesus.
21
+
22
+ @god -c /path/to/your/god.config@
23
+
24
+ @rackup@
25
+
26
+ Note: Jesus and God should be started with the same user. Otherwise, Jesus won't be able to get access to the God informations.
27
+
28
+ Go to "localhot:9292":http://localhost:9292 to watch the process monitored by god.
29
+
30
+ h2. Contributing
31
+
32
+ If you think Jesus is great but can be improved, feel free to contribute.
33
+ To do so, you can :
34
+
35
+ * "Fork":http://help.github.com/forking/ the project
36
+ * Do your changes and commit them to your repository
37
+ * Test your changes. We won't accept any untested contributions (except if they're not testable).
38
+ * Create an "issue":http://help.github.com/forking/ with a link to your commits.
39
+
40
+ And that's it! We'll soon take a look at your issue and review your changes.
41
+
42
+ h2. Author
43
+
44
+ Damien MATHIEU :: 42 (AT|CHEZ) dmathieu.com
@@ -0,0 +1,33 @@
1
+ require 'vendor/gems/environment'
2
+ require 'spec/rake/spectask'
3
+
4
+ #
5
+ # Jeweler, to execute the tests
6
+ #
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gemspec|
10
+ gemspec.name = "jesus"
11
+ gemspec.summary = "A web interface for god to speak with mankind"
12
+ gemspec.description = "A web interface for god to speak with mankind"
13
+ gemspec.email = "42@dmathieu.com"
14
+ gemspec.homepage = "http://github.com/dmathieu/jesus"
15
+ gemspec.authors = ["Damien MATHIEU"]
16
+ gemspec.version = '0.0.2'
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with:"
20
+ puts "gem install jeweler"
21
+ end
22
+
23
+
24
+ #
25
+ # The rspec tasks
26
+ #
27
+ task :default => :spec
28
+
29
+ desc "Run all specs"
30
+ Spec::Rake::SpecTask.new('spec') do |t|
31
+ t.spec_files = FileList['spec/**/*.rb']
32
+ t.spec_opts = ['-cfs']
33
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'logger'
3
+
4
+ # We require the bundled gems
5
+ require 'vendor/gems/environment'
6
+
7
+ # We require the application
8
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/lib')
9
+ require 'jesus/server'
10
+
11
+ # We start the server
12
+ use Rack::ShowExceptions
13
+ run Jesus::Server.new
@@ -0,0 +1,58 @@
1
+ require 'god'
2
+
3
+ module Jesus
4
+ #
5
+ # Interface between jesus and god
6
+ #
7
+ class Interface
8
+ attr_reader :server
9
+
10
+ def initialize
11
+ @server = DRbObject.new(nil, God::Socket.socket(17165))
12
+ end
13
+
14
+
15
+ #
16
+ # We retrieve the processes by group
17
+ #
18
+ def status
19
+ begin
20
+ statuses = server.status
21
+ rescue
22
+ return nil
23
+ end
24
+ groups = Hash.new
25
+
26
+ statuses.each do |name, status|
27
+ g = status[:group] || ''
28
+ groups[g] ||= Hash.new
29
+ groups[g][name] = status
30
+ end
31
+ groups
32
+ end
33
+
34
+ #
35
+ # We execute a specific command to god
36
+ # Could be "start", "stop", "restart", "quit", "terminate"
37
+ #
38
+ def command(command, name)
39
+ begin
40
+ server.control(name, command)
41
+ rescue
42
+ return nil
43
+ end
44
+ end
45
+
46
+ #
47
+ # Gets the god's logs for one process since some time
48
+ # By default, gets all the logs
49
+ #
50
+ def log(name=nil, start=Time.at(0))
51
+ begin
52
+ server.running_log(name, start).split("\n")
53
+ rescue
54
+ nil
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,50 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra_more/render_plugin'
3
+ require 'jesus/interface'
4
+ require 'jesus/server/helpers'
5
+
6
+ module Jesus
7
+ class Server < Sinatra::Base
8
+ dir = File.dirname(File.expand_path(__FILE__))
9
+ register SinatraMore::RenderPlugin
10
+ include Jesus::Helpers
11
+
12
+ set :views, "#{dir}/server/views"
13
+ set :public, "#{dir}/server/public"
14
+ set :static, true
15
+ enable :sessions
16
+
17
+ #
18
+ # The home
19
+ # Displays the list of the process
20
+ #
21
+ get '/' do
22
+ @status = Jesus::Interface.new.status
23
+ erb_template @status.nil? ? :error : :home
24
+ end
25
+
26
+ #
27
+ # Displays a process log
28
+ #
29
+ get '/logs/:process' do
30
+ @log = Jesus::Interface.new.log(params[:process])
31
+ erb_template @log.nil? ? :error : :log
32
+ end
33
+
34
+ #
35
+ # Displays every page logs
36
+ #
37
+ get '/logs' do
38
+ flash(:notice, 'You currently can not display all the logs. You should select a process first.')
39
+ redirect '/'
40
+ end
41
+
42
+ #
43
+ # Executes a command (start, restart, stop, quit or terminate) on the server
44
+ #
45
+ post '/command/:command/:process' do
46
+ @command = Jesus::Interface.new.command(params[:command], params[:process])
47
+ redirect '/'
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,18 @@
1
+ module Jesus
2
+ module Helpers
3
+ def class_if_current(link)
4
+ link = '^/$' if link == '/'
5
+ 'class="current"' if request.path_info =~ Regexp.new(link)
6
+ end
7
+ def tab(name, link)
8
+ "<li #{class_if_current(link)}><a href='#{link}'>#{name}</a></li>"
9
+ end
10
+
11
+ def flash_tag(type)
12
+ "<div id='flash'>#{session.delete(type)}</div>" unless session[type].nil?
13
+ end
14
+ def flash(type, message)
15
+ session[type] = message
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,131 @@
1
+ /*
2
+ * The global styles
3
+ */
4
+ html {
5
+ background:#efefef;
6
+ font-family:Arial, Verdana, sans-serif;
7
+ font-size:13px;
8
+ }
9
+ body {
10
+ padding:0;
11
+ margin:0;
12
+ }
13
+
14
+ /*
15
+ * The page header
16
+ */
17
+ #header {
18
+ background:#000;
19
+ padding: 8px 5% 0 5%;
20
+ border-bottom:1px solid #444;
21
+ border-bottom:5px solid #ce1212;
22
+ }
23
+ #header ul {
24
+ margin: 0px;
25
+ padding: 0px;
26
+ }
27
+ #header ul li {
28
+ display:inline;
29
+ }
30
+ #header ul li a {
31
+ color:#fff;
32
+ text-decoration:none;
33
+ margin: 0px 10px 0px 0px;
34
+ display:inline-block;
35
+ padding:8px;
36
+
37
+ -webkit-border-top-right-radius:6px;
38
+ -webkit-border-top-left-radius:6px;
39
+ -moz-border-radius-topright:6px;
40
+ -moz-border-radius-topleft:6px;
41
+ border-top-right-radius:6px;
42
+ border-top-left-radius:6px;
43
+ }
44
+ #header ul li a:hover {
45
+ background:#333;
46
+ }
47
+ #header ul li.current a {
48
+ background:#ce1212;
49
+ font-weight:bold;
50
+ color:#fff;
51
+ }
52
+
53
+ /*
54
+ * The page itself
55
+ */
56
+ #page {
57
+ padding: 15px;
58
+ }
59
+
60
+ /*
61
+ * The flash message
62
+ */
63
+ #flash {
64
+ width: 700px;
65
+ text-align: center;
66
+ margin: 10px auto;
67
+ padding: 5px;
68
+ background-color: #FFCFD1;
69
+ border: 1px solid #FF000A;
70
+ }
71
+
72
+ /*
73
+ * The common page info div
74
+ */
75
+ div.page_info h1 {
76
+ color: #FF0000;
77
+ margin: 0px;
78
+ text-align: center;
79
+ }
80
+ div.page_info p {
81
+ text-align: left;
82
+ padding: 0px 15px;
83
+ color: #6F6F6F;
84
+ }
85
+ div.page_info {
86
+ width: 700px;
87
+ text-align: center;
88
+ margin: 0 auto;
89
+ background-color: #D0CFFF;
90
+ border: 1px solid #0300FF;
91
+ }
92
+
93
+ /*
94
+ * The home page (processes list)
95
+ */
96
+ div.group h2 { margin: 5px 30px; }
97
+ div.group ul {
98
+ list-style-type: none;
99
+ padding: 0px;
100
+ }
101
+ div.group li {
102
+ border: 1px solid #CCC;
103
+ margin: 5px 0px;
104
+ padding: 10px;
105
+ }
106
+
107
+ div.group li.up { background-color: #BFFFC8; }
108
+ div.group li.unmonitored { background-color: #FFBFBF; }
109
+
110
+ ul.links {
111
+ list-style-type: none;
112
+ margin: 0px;
113
+ float: right;
114
+ }
115
+ ul.links li {
116
+ display: inline;
117
+ border: 0px;
118
+ }
119
+
120
+
121
+ /*
122
+ * The logs page
123
+ */
124
+ div#logs {
125
+ text-align: center;
126
+ margin: 30px 0px;
127
+ }
128
+ div#logs textarea {
129
+ width: 100%;
130
+ height: 500px;
131
+ }
@@ -0,0 +1,6 @@
1
+ <div class="page_info">
2
+ <h1>An error occured</h1>
3
+ <p>
4
+ God doesn't seem to be started. Or I don't have the privileges to access it.
5
+ </p>
6
+ </div>
@@ -0,0 +1,24 @@
1
+ <div class="page_info">
2
+ <h1>Processes</h1>
3
+ <p>
4
+ In the list below, you see the processes monitored by God.<br />
5
+ If the process is green, it mean it's launched. If it's in red then it's not currently being monitored.
6
+ </p>
7
+ </div>
8
+
9
+ <% @status.each_pair do |group,processes| %>
10
+ <div class="group">
11
+ <h2><%= group %></h2>
12
+ <ul>
13
+ <% processes.each do |process| %>
14
+ <li class="<%= process[1][:state] %>">
15
+ <%= process[0] %>
16
+
17
+ <ul class="links">
18
+ <li><a href="/logs/<%= process[0] %>">View Logs</a></li>
19
+ </ul>
20
+ </li>
21
+ <% end %>
22
+ </ul>
23
+ </div>
24
+ <% end %>
@@ -0,0 +1,20 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Jesus</title>
5
+ <link href="/css/style.css" media="screen" rel="stylesheet" type="text/css" />
6
+ </head>
7
+ <body>
8
+ <div id="header">
9
+ <ul>
10
+ <%= tab 'Home', '/' %>
11
+ <%= tab 'Logs', '/logs' %>
12
+ </ul>
13
+ </div>
14
+ <div id="page">
15
+ <%= flash_tag(:notice) %>
16
+
17
+ <%= yield %>
18
+ </div>
19
+ </body>
20
+ </html>
@@ -0,0 +1,7 @@
1
+ <div class="page_info">
2
+ <h1>Logs for <%= params[:process] %></h1>
3
+ </div>
4
+
5
+ <div id="logs">
6
+ <textarea><%= @log.join("\r\n") %></textarea>
7
+ </div>
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+ require 'jesus/interface'
3
+
4
+ describe "God interface" do
5
+ before(:all) do
6
+ @server = Jesus::Interface.new
7
+ end
8
+
9
+ describe 'no mock' do
10
+ it 'should get the server' do
11
+ @server.server.should be_kind_of DRb::DRbObject
12
+ end
13
+
14
+ it 'should fail because the server is not connected' do
15
+ @server.status.should be_nil
16
+ end
17
+ end
18
+
19
+ describe 'with mocking' do
20
+ before(:all) do
21
+ @server.server.stubs(:status).returns({
22
+ :FirstProcess => { :group => 'GroupName', :status => :up },
23
+ :SecondProcess => { :group => 'Group', :status => :unmonitored },
24
+ })
25
+ end
26
+
27
+ it 'should retrieve the status ordered by group' do
28
+ status = @server.status
29
+
30
+ status.length.should eql(2)
31
+ status['Group'].should eql({ :SecondProcess => { :status => :unmonitored, :group => "Group" }})
32
+ status['GroupName'].should eql({ :FirstProcess => { :status => :up, :group=>"GroupName"}})
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper'
2
+
3
+ describe "The home page" do
4
+ include Rack::Test::Methods
5
+ include Jesus::Spec
6
+
7
+ it "should respond to /" do
8
+ get '/'
9
+ last_response.should be_ok
10
+ end
11
+
12
+ it 'should display a process logs' do
13
+ class Jesus::Interface
14
+ def log(name); ['First Log Line', 'Second Log Line']; end
15
+ end
16
+
17
+ get '/logs/test'
18
+ last_response.should be_ok
19
+ last_response.should match(/<h1>Logs for test<\/h1>/)
20
+ last_response.should match(/<textarea>First Log Line\r\nSecond Log Line<\/textarea>/)
21
+ end
22
+
23
+ it 'should execute the command' do
24
+ post '/command/start/jesus'
25
+ last_response.should be_redirect
26
+ end
27
+ end
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --format progress
3
+ --loadby mtime
4
+ --reverse
@@ -0,0 +1,23 @@
1
+ require 'vendor/gems/environment'
2
+ require 'sinatra'
3
+ require 'rack/test'
4
+ require 'spec'
5
+ require 'mocha'
6
+ require 'spec/autorun'
7
+ require 'spec/interop/test'
8
+
9
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
10
+ require 'jesus/server'
11
+
12
+ # set test environment
13
+ set :environment, :test
14
+ set :run, false
15
+ set :raise_errors, true
16
+ set :logging, false
17
+
18
+ # Set the application
19
+ module Jesus::Spec
20
+ def app
21
+ @app ||= Sinatra.new Jesus::Server
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+ APP_ROOT = File.expand_path(File.dirname(__FILE__))
2
+ preload_app true
3
+ timeout 30
4
+
5
+ # Listen on a Unix data socket
6
+ listen APP_ROOT + '/tmp/jesus.sock', :backlog => 2048
7
+ pid APP_ROOT + '/tmp/jesus.pid'
8
+
9
+ ##
10
+ # REE
11
+
12
+ # http://www.rubyenterpriseedition.com/faq.html#adapt_apps_for_cow
13
+ if GC.respond_to?(:copy_on_write_friendly=)
14
+ GC.copy_on_write_friendly = true
15
+ end
16
+
17
+ before_fork do |server, worker|
18
+ ##
19
+ # When sent a USR2, Unicorn will suffix its pidfile with .oldbin and
20
+ # immediately start loading up a new version of itself (loaded with a new
21
+ # version of our app). When this new Unicorn is completely loaded
22
+ # it will begin spawning workers. The first worker spawned will check to
23
+ # see if an .oldbin pidfile exists. If so, this means we've just booted up
24
+ # a new Unicorn and need to tell the old one that it can now die. To do so
25
+ # we send it a QUIT.
26
+ #
27
+ # Using this method we get 0 downtime deploys.
28
+
29
+ old_pid = APP_ROOT + '/tmp/unicorn.pid.oldbin'
30
+ if File.exists?(old_pid) && server.pid != old_pid
31
+ begin
32
+ Process.kill("QUIT", File.read(old_pid).to_i)
33
+ rescue Errno::ENOENT, Errno::ESRCH
34
+ # someone else did our job for us
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ after_fork do |server, worker|
41
+ ##
42
+ # Unicorn master is started as root, which is fine, but let's
43
+ # drop the workers to www-data:www-data
44
+
45
+ uid, gid = Process.euid, Process.egid
46
+ user, group = 'www-data', 'www-data'
47
+ target_uid = Etc.getpwnam(user).uid
48
+ target_gid = Etc.getgrnam(group).gid
49
+
50
+ worker.tmp.chown(target_uid, target_gid)
51
+ if uid != target_uid || gid != target_gid
52
+ Process.initgroups(user, target_gid)
53
+ Process::GID.change_privilege(target_gid)
54
+ Process::UID.change_privilege(target_uid)
55
+ end
56
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jesus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Damien MATHIEU
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-19 00:00:00 +01:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: A web interface for god to speak with mankind
17
+ email: 42@dmathieu.com
18
+ executables:
19
+ - autospec
20
+ - rake2thor
21
+ - sinatra_gen
22
+ - spec
23
+ - rackup
24
+ - god
25
+ - rake
26
+ - thor
27
+ extensions: []
28
+
29
+ extra_rdoc_files:
30
+ - README.textile
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.textile
35
+ - Rakefile
36
+ - config.ru
37
+ - lib/jesus/interface.rb
38
+ - lib/jesus/server.rb
39
+ - lib/jesus/server/helpers.rb
40
+ - lib/jesus/server/public/css/style.css
41
+ - lib/jesus/server/views/error.erb
42
+ - lib/jesus/server/views/home.erb
43
+ - lib/jesus/server/views/layout.erb
44
+ - lib/jesus/server/views/log.erb
45
+ - spec/lib/god_spec.rb
46
+ - spec/server/home_spec.rb
47
+ - spec/spec.opts
48
+ - spec/spec_helper.rb
49
+ - unicorn.rb
50
+ has_rdoc: true
51
+ homepage: http://github.com/dmathieu/jesus
52
+ licenses: []
53
+
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.3.5
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: A web interface for god to speak with mankind
78
+ test_files:
79
+ - spec/spec_helper.rb
80
+ - spec/server/home_spec.rb
81
+ - spec/lib/god_spec.rb