gorgon 0.4.0 → 0.4.1.rc1
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.
- data/Gemfile.lock +1 -1
- data/README.md +3 -3
- data/bin/gorgon +15 -0
- data/lib/gorgon/listener_installer.rb +153 -0
- data/lib/gorgon/settings/files_content.rb +26 -0
- data/lib/gorgon/settings/initial_files_creator.rb +77 -0
- data/lib/gorgon/settings/rails_project_files_content.rb +95 -0
- data/lib/gorgon/settings/simple_project_files_content.rb +12 -0
- data/lib/gorgon/version.rb +1 -1
- metadata +10 -5
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -26,7 +26,7 @@ This file contains project-specific settings for gorgon, such as:
|
|
26
26
|
* A glob for generating the list of test files
|
27
27
|
* The file used for Originator's logs
|
28
28
|
|
29
|
-
See [gorgon.json example](
|
29
|
+
See [gorgon.json example](/Fitzsimmons/Gorgon/blob/master/gorgon.json.sample) for more details.
|
30
30
|
|
31
31
|
### gorgon_listener.json
|
32
32
|
This file contains the listener-specific settings, such as:
|
@@ -35,7 +35,7 @@ This file contains the listener-specific settings, such as:
|
|
35
35
|
* How many worker slots are provided by this listener
|
36
36
|
* The file used for logs
|
37
37
|
|
38
|
-
See [gorgon_listener.json example](
|
38
|
+
See [gorgon_listener.json example](/Fitzsimmons/Gorgon/blob/master/gorgon_listener.json.sample) for more details.
|
39
39
|
|
40
40
|
### Setting up gorgon listener as a daemon process using Upstart
|
41
41
|
For an example on how to run listener as a daemon process, follow [these steps](/Fitzsimmons/Gorgon/blob/master/daemon_with_upstart_and_rvm.md)
|
@@ -54,4 +54,4 @@ Gorgon is maintained by:
|
|
54
54
|
* Victor Savkin
|
55
55
|
|
56
56
|
Gorgon is funded by [Nulogy Corp](http://www.nulogy.com/).
|
57
|
-
Thank you to all the [contributors](/contributors).
|
57
|
+
Thank you to all the [contributors](/Fitzsimmons/Gorgon/graphs/contributors).
|
data/bin/gorgon
CHANGED
@@ -5,6 +5,8 @@ require 'gorgon/worker_manager'
|
|
5
5
|
require 'gorgon/ping_service'
|
6
6
|
require 'gorgon/gem_service'
|
7
7
|
require 'gorgon/version'
|
8
|
+
require 'gorgon/listener_installer'
|
9
|
+
require 'gorgon/settings/initial_files_creator'
|
8
10
|
|
9
11
|
WELCOME_MSG = "Welcome to Gorgon #{Gorgon::VERSION}"
|
10
12
|
|
@@ -36,11 +38,20 @@ def run_gem command
|
|
36
38
|
GemService.new.run command
|
37
39
|
end
|
38
40
|
|
41
|
+
def init framework
|
42
|
+
Settings::InitialFilesCreator.run framework
|
43
|
+
end
|
44
|
+
|
45
|
+
def install_listener
|
46
|
+
ListenerInstaller.install
|
47
|
+
end
|
48
|
+
|
39
49
|
def usage
|
40
50
|
#print instructions on how to use gorgon
|
41
51
|
puts "\tstart - remotely runs all tests specified in gorgon.json"
|
42
52
|
puts "\tlisten - starts a listener process using the settings in gorgon_listener.json"
|
43
53
|
puts "\tping - pings listeners and shows hosts and gorgon's version they are running"
|
54
|
+
puts "\tinstall_listener - runs gorgon listener as a daemon process"
|
44
55
|
puts "\tgem command [options...] - execute the gem command on every listener and shutdown listener. e.g. 'gorgon gem install --version 1.0.0'"
|
45
56
|
end
|
46
57
|
|
@@ -62,6 +73,10 @@ when "gem"
|
|
62
73
|
run_gem ARGV.join(' ')
|
63
74
|
when "help"
|
64
75
|
usage
|
76
|
+
when "init"
|
77
|
+
init ARGV[1]
|
78
|
+
when "install_listener"
|
79
|
+
install_listener
|
65
80
|
else
|
66
81
|
puts "Unknown command!"
|
67
82
|
usage
|
@@ -0,0 +1,153 @@
|
|
1
|
+
require 'etc'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
class ListenerInstaller
|
5
|
+
include Configuration
|
6
|
+
|
7
|
+
GORGON_DIR=".gorgon"
|
8
|
+
DEFAULT_NO_WORKERS = 4
|
9
|
+
LISTENER_CONFIG_FILE = "gorgon_listener.json"
|
10
|
+
GORGON_INIT_FILE = "/etc/init/gorgon.conf"
|
11
|
+
|
12
|
+
def self.install
|
13
|
+
ListenerInstaller.new.install
|
14
|
+
end
|
15
|
+
|
16
|
+
def install
|
17
|
+
@configuration = load_configuration_from_file("gorgon.json")
|
18
|
+
|
19
|
+
FileUtils.mkdir_p gorgon_dir_path
|
20
|
+
Dir.chdir gorgon_dir_path
|
21
|
+
|
22
|
+
create_listener_config_file
|
23
|
+
|
24
|
+
create_gorgon_service
|
25
|
+
|
26
|
+
start_gorgon_daemon
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def gorgon_dir_path
|
32
|
+
@gorgon_dir_path ||= "#{Dir.home}/#{GORGON_DIR}"
|
33
|
+
end
|
34
|
+
|
35
|
+
def create_listener_config_file
|
36
|
+
worker_slots = worker_slots_prompt
|
37
|
+
puts "Using #{worker_slots} worker slots"
|
38
|
+
amqp_host = @configuration[:connection][:host]
|
39
|
+
puts "Amqp host is '#{amqp_host}'"
|
40
|
+
|
41
|
+
puts "Creating #{LISTENER_CONFIG_FILE} in #{Dir.pwd}"
|
42
|
+
File.open(LISTENER_CONFIG_FILE, 'w') do |f|
|
43
|
+
f.write listener_config(amqp_host, worker_slots)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_gorgon_service
|
48
|
+
params = {}
|
49
|
+
params[:username] = Etc.getlogin
|
50
|
+
params[:rvm_bin_path] = rvm_bin_path
|
51
|
+
params[:gemset] = get_current_gemset
|
52
|
+
|
53
|
+
tmp_gorgon_conf = '/tmp/gorgon.conf'
|
54
|
+
File.open(tmp_gorgon_conf, 'w') do |f|
|
55
|
+
f.write gorgon_conf(params)
|
56
|
+
end
|
57
|
+
|
58
|
+
puts "Creating '#{GORGON_INIT_FILE}'"
|
59
|
+
|
60
|
+
system("sudo cp /tmp/gorgon.conf #{GORGON_INIT_FILE}")
|
61
|
+
end
|
62
|
+
|
63
|
+
def rvm_bin_path
|
64
|
+
cmd = 'which rvm'
|
65
|
+
path = get_shell_cmd_output cmd, "Error getting rvm path. Make sure '#{cmd}' works"
|
66
|
+
puts "Using '#{path}'"
|
67
|
+
path
|
68
|
+
end
|
69
|
+
|
70
|
+
def get_current_gemset
|
71
|
+
return @gemset unless @gemset.nil?
|
72
|
+
|
73
|
+
cmd = 'rvm current'
|
74
|
+
@gemset = get_shell_cmd_output cmd, "Error getting current gem. Make sure '#{cmd}' works"
|
75
|
+
puts "Using gemset '#{@gemset}'."
|
76
|
+
@gemset
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_shell_cmd_output cmd, error_message
|
80
|
+
result = `#{cmd}`.strip
|
81
|
+
if $?.exitstatus != 0
|
82
|
+
$stderr.puts "#{error_message}"
|
83
|
+
$stderr.puts "Aborting installation"
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def start_gorgon_daemon
|
90
|
+
system("sudo start gorgon")
|
91
|
+
end
|
92
|
+
|
93
|
+
def gem_available?(name)
|
94
|
+
Gem::Specification.find_by_name(name)
|
95
|
+
rescue Gem::LoadError
|
96
|
+
false
|
97
|
+
rescue
|
98
|
+
Gem.available?(name)
|
99
|
+
end
|
100
|
+
|
101
|
+
def worker_slots_prompt
|
102
|
+
begin
|
103
|
+
puts "Number of worker slots (default #{DEFAULT_NO_WORKERS})?"
|
104
|
+
input = $stdin.gets.chomp
|
105
|
+
if input == ""
|
106
|
+
worker_slots = DEFAULT_NO_WORKERS
|
107
|
+
break
|
108
|
+
end
|
109
|
+
|
110
|
+
worker_slots = input.to_i
|
111
|
+
if worker_slots <= 0 || worker_slots >= 20
|
112
|
+
puts "Please, enter a valid number between 0 and 19"
|
113
|
+
end
|
114
|
+
end while worker_slots <= 0 || worker_slots >= 20
|
115
|
+
worker_slots
|
116
|
+
end
|
117
|
+
|
118
|
+
def listener_config amqp_host, worker_slots
|
119
|
+
<<-CONFIG
|
120
|
+
{
|
121
|
+
"connection": {
|
122
|
+
"host": "#{amqp_host}"
|
123
|
+
},
|
124
|
+
|
125
|
+
"worker_slots": #{worker_slots},
|
126
|
+
"log_file": "/tmp/gorgon-remote.log"
|
127
|
+
}
|
128
|
+
CONFIG
|
129
|
+
end
|
130
|
+
|
131
|
+
def gorgon_conf params
|
132
|
+
<<-CONF_FILE
|
133
|
+
description "Start gorgon listener"
|
134
|
+
|
135
|
+
start on filesystem or runlevel [2345]
|
136
|
+
stop on runlevel [!2345]
|
137
|
+
|
138
|
+
respawn
|
139
|
+
respawn limit 10 10
|
140
|
+
|
141
|
+
pre-start script
|
142
|
+
|
143
|
+
bash << "EOF"
|
144
|
+
mkdir -p /var/log/gorgon
|
145
|
+
chown -R #{params[:username]} /var/log/gorgon
|
146
|
+
EOF
|
147
|
+
|
148
|
+
end script
|
149
|
+
|
150
|
+
exec su - #{params[:username]} -c 'cd #{gorgon_dir_path} && #{params[:rvm_bin_path]} #{params[:gemset]} do gorgon listen >> /var/log/gorgon/gorgon.log 2>&1'
|
151
|
+
CONF_FILE
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Settings
|
2
|
+
class FilesContent
|
3
|
+
attr_accessor :amqp_host, :sync_exclude, :files, :originator_log_file,
|
4
|
+
:callbacks, :callbacks_dir
|
5
|
+
|
6
|
+
TEST_UNIT_GLOB = "test/**/*_test.rb"
|
7
|
+
RSPEC_GLOB = "spec/**/*_spec.rb"
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@files = []
|
11
|
+
@files << FilesContent::TEST_UNIT_GLOB if Dir.exist?('test')
|
12
|
+
@files << FilesContent::RSPEC_GLOB if Dir.exist?('spec')
|
13
|
+
end
|
14
|
+
|
15
|
+
DEFAULT_AMQP_HOST = 'localhost'
|
16
|
+
def self.get_amqp_host
|
17
|
+
puts "AMQP host (default '#{DEFAULT_AMQP_HOST}')? "
|
18
|
+
input = $stdin.gets.chomp
|
19
|
+
if input == ""
|
20
|
+
return DEFAULT_AMQP_HOST
|
21
|
+
else
|
22
|
+
return input
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'gorgon/settings/simple_project_files_content'
|
2
|
+
require 'gorgon/settings/rails_project_files_content'
|
3
|
+
|
4
|
+
module Settings
|
5
|
+
class InitialFilesCreator
|
6
|
+
GORGON_JSON_FILE = 'gorgon.json'
|
7
|
+
|
8
|
+
# TODO: we may change this, so it knows what Creator to use according to the Gemfile, so the user doesn't need to specify 'framework'
|
9
|
+
def self.run framework
|
10
|
+
if framework.nil?
|
11
|
+
create_files SimpleProjectFilesContent.new
|
12
|
+
elsif framework == 'rails'
|
13
|
+
create_files RailsProjectFilesContent.new
|
14
|
+
else
|
15
|
+
$stderr.puts "Unknown framework"
|
16
|
+
exit(1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.create_files content
|
21
|
+
self.create_gorgon_json content
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def self.create_gorgon_json content
|
26
|
+
if File.exist? GORGON_JSON_FILE
|
27
|
+
puts "gorgo.json exists. Skipping..."
|
28
|
+
return
|
29
|
+
end
|
30
|
+
|
31
|
+
config = {
|
32
|
+
connection: {host: content.amqp_host},
|
33
|
+
job: {
|
34
|
+
sync_exclude: content.sync_exclude
|
35
|
+
},
|
36
|
+
files: content.files
|
37
|
+
}
|
38
|
+
|
39
|
+
log_file = content.originator_log_file
|
40
|
+
config[:originator_log_file] = log_file if log_file
|
41
|
+
|
42
|
+
if content.callbacks
|
43
|
+
create_callback_files(content)
|
44
|
+
|
45
|
+
config[:job][:callbacks] = content.callbacks.inject({}) do |callbacks, e|
|
46
|
+
callbacks[e[:name]] = "#{content.callbacks_dir}/#{e[:file_name]}"
|
47
|
+
callbacks
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
puts "Creating #{GORGON_JSON_FILE}..."
|
52
|
+
File.open(GORGON_JSON_FILE, 'w') do |f|
|
53
|
+
Yajl::Encoder.encode(config, f, :pretty => true, :indent => " ")
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.create_callback_files content
|
58
|
+
FileUtils.mkdir_p content.callbacks_dir
|
59
|
+
content.callbacks.each do |callback|
|
60
|
+
create_callback_file content.callbacks_dir, callback
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def self.create_callback_file dir, callback
|
65
|
+
file_path = "#{dir}/#{callback[:file_name]}"
|
66
|
+
if File.exist? file_path
|
67
|
+
puts "#{file_path} already exists. Skipping..."
|
68
|
+
return
|
69
|
+
end
|
70
|
+
|
71
|
+
puts "Creating #{file_path}..."
|
72
|
+
File.open(file_path, 'w') do |f|
|
73
|
+
f.write callback[:content]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'gorgon/settings/files_content'
|
2
|
+
|
3
|
+
module Settings
|
4
|
+
class RailsProjectFilesContent < FilesContent
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@amqp_host = FilesContent.get_amqp_host
|
8
|
+
@sync_exclude = [".git", ".rvmrc","tmp","log","doc"]
|
9
|
+
@originator_log_file = 'log/gorgon-originator.log'
|
10
|
+
create_callbacks
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def create_callbacks
|
16
|
+
@callbacks_dir = "#{get_app_subdir}gorgon_callbacks"
|
17
|
+
@callbacks = [{name: :after_sync, file_name: "after_sync.rb",
|
18
|
+
content: after_sync_content},
|
19
|
+
{name: :before_creating_workers, file_name: "before_creating_workers.rb",
|
20
|
+
content: before_creating_workers_content},
|
21
|
+
{name: :before_start, file_name: "before_start.rb",
|
22
|
+
content: before_start_content},
|
23
|
+
{name: :after_complete, file_name: "after_complete.rb",
|
24
|
+
content: after_complete_content}]
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_app_subdir
|
28
|
+
if Dir.exist? "test"
|
29
|
+
"test/"
|
30
|
+
elsif Dir.exist? "spec"
|
31
|
+
"spec/"
|
32
|
+
elsif Dir.exist? "lib"
|
33
|
+
"lib/"
|
34
|
+
else
|
35
|
+
""
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def after_sync_content
|
40
|
+
<<-CONTENT
|
41
|
+
require 'bundler'
|
42
|
+
require 'open4'
|
43
|
+
|
44
|
+
Bundler.with_clean_env do
|
45
|
+
BUNDLE_LOG_FILE||="/tmp/gorgon-bundle-install.log "
|
46
|
+
|
47
|
+
pid, stdin, stdout, stderr = Open4::popen4 "bundle install > \#\{BUNDLE_LOG_FILE\} 2>&1 "
|
48
|
+
|
49
|
+
ignore, status = Process.waitpid2 pid
|
50
|
+
|
51
|
+
if status.exitstatus != 0
|
52
|
+
raise "ERROR: 'bundle install' failed.\n\#\{stderr.read\}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
CONTENT
|
56
|
+
end
|
57
|
+
|
58
|
+
def before_creating_workers_content
|
59
|
+
content = ""
|
60
|
+
content << "require './test/test_helper'\n" if File.exist?("test/test_helper.rb")
|
61
|
+
content << "require './spec/spec_helper'\n" if File.exist?("spec/spec_helper.rb")
|
62
|
+
content
|
63
|
+
end
|
64
|
+
|
65
|
+
def before_start_content
|
66
|
+
<<-CONTENT
|
67
|
+
require 'rake'
|
68
|
+
load './Rakefile'
|
69
|
+
|
70
|
+
begin
|
71
|
+
Rails.env = 'remote_test'
|
72
|
+
ENV['TEST_ENV_NUMBER'] = Process.pid.to_s
|
73
|
+
|
74
|
+
Rake::Task['db:reset'].invoke
|
75
|
+
end
|
76
|
+
|
77
|
+
CONTENT
|
78
|
+
end
|
79
|
+
|
80
|
+
def after_complete_content
|
81
|
+
<<-CONTENT
|
82
|
+
require 'rake'
|
83
|
+
load './Rakefile'
|
84
|
+
|
85
|
+
begin
|
86
|
+
if Rails.env = 'remote_test'
|
87
|
+
Rake::Task['db:drop'].execute
|
88
|
+
end
|
89
|
+
rescue Exception => ex
|
90
|
+
puts "Error dropping test database:\n \#\{ex\}"
|
91
|
+
end
|
92
|
+
CONTENT
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'gorgon/settings/files_content'
|
2
|
+
|
3
|
+
module Settings
|
4
|
+
class SimpleProjectFilesContent < FilesContent
|
5
|
+
def initialize
|
6
|
+
super
|
7
|
+
@amqp_host = FilesContent.get_amqp_host
|
8
|
+
@sync_exclude = [".git", ".rvmrc"]
|
9
|
+
@originator_log_file = 'gorgon-originator.log'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/gorgon/version.rb
CHANGED
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gorgon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.1.rc1
|
5
|
+
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Justin Fitzsimmons
|
@@ -13,7 +13,7 @@ authors:
|
|
13
13
|
autorequire:
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
|
-
date: 2012-12-
|
16
|
+
date: 2012-12-18 00:00:00.000000000 Z
|
17
17
|
dependencies:
|
18
18
|
- !ruby/object:Gem::Dependency
|
19
19
|
name: rake
|
@@ -228,6 +228,7 @@ files:
|
|
228
228
|
- lib/gorgon/job_definition.rb
|
229
229
|
- lib/gorgon/job_state.rb
|
230
230
|
- lib/gorgon/listener.rb
|
231
|
+
- lib/gorgon/listener_installer.rb
|
231
232
|
- lib/gorgon/mini_test_runner.rb
|
232
233
|
- lib/gorgon/originator.rb
|
233
234
|
- lib/gorgon/originator_logger.rb
|
@@ -236,6 +237,10 @@ files:
|
|
236
237
|
- lib/gorgon/pipe_forker.rb
|
237
238
|
- lib/gorgon/progress_bar_view.rb
|
238
239
|
- lib/gorgon/rspec_runner.rb
|
240
|
+
- lib/gorgon/settings/files_content.rb
|
241
|
+
- lib/gorgon/settings/initial_files_creator.rb
|
242
|
+
- lib/gorgon/settings/rails_project_files_content.rb
|
243
|
+
- lib/gorgon/settings/simple_project_files_content.rb
|
239
244
|
- lib/gorgon/source_tree_syncer.rb
|
240
245
|
- lib/gorgon/test_unit_runner.rb
|
241
246
|
- lib/gorgon/unknown_runner.rb
|
@@ -279,9 +284,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
279
284
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
280
285
|
none: false
|
281
286
|
requirements:
|
282
|
-
- - ! '
|
287
|
+
- - ! '>'
|
283
288
|
- !ruby/object:Gem::Version
|
284
|
-
version:
|
289
|
+
version: 1.3.1
|
285
290
|
requirements: []
|
286
291
|
rubyforge_project: gorgon
|
287
292
|
rubygems_version: 1.8.24
|