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 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