gorgon 0.4.0 → 0.4.1.rc1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- gorgon (0.4.0)
4
+ gorgon (0.4.1.rc1)
5
5
  amqp (~> 0.9.7)
6
6
  awesome_print
7
7
  bunny (~> 0.8.0)
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](https://github.com/Fitzsimmons/Gorgon/blob/master/gorgon.json.sample) for more details.
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](https://github.com/Fitzsimmons/Gorgon/blob/master/gorgon_listener.json.sample) for more details.
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
@@ -1,3 +1,3 @@
1
1
  module Gorgon
2
- VERSION = "0.4.0"
2
+ VERSION = "0.4.1.rc1"
3
3
  end
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.0
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-08 00:00:00.000000000 Z
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: '0'
289
+ version: 1.3.1
285
290
  requirements: []
286
291
  rubyforge_project: gorgon
287
292
  rubygems_version: 1.8.24