smartguard 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,6 +8,7 @@ runner.description = "Smartkiosk services control daemon"
8
8
 
9
9
  runner.with_options do |opts|
10
10
  options[:port] = 10000
11
+ options[:development] = false
11
12
 
12
13
  opts.on("-a", "--app APPLICATION", String, "Application to use") do |x|
13
14
  options[:application] = x
@@ -20,38 +21,40 @@ runner.with_options do |opts|
20
21
  options[:port] = x
21
22
  end
22
23
 
23
- opts.on("-s", "--start", "Start all services and exit") do |x|
24
- options[:start] = x
25
- end
26
-
27
- opts.on("-S", "--stop", "Stop all services and exit") do |x|
28
- options[:stop] = x
29
- end
30
-
31
24
  opts.on("-v", "--version", "Output version") do |x|
32
25
  options[:version] = x
33
26
  end
27
+
28
+ opts.on("-d", "--development", "Run in development environment") do |x|
29
+ options[:development] = x
30
+ end
34
31
  end
35
32
 
36
33
  runner.execute do |opts|
37
34
  abort "Smartguard version: #{Smartguard::VERSION}" if opts[:version]
38
35
 
39
36
  abort "You should specify application (--app)" if opts[:application].blank?
40
- abort "You should specify application path (--path)" if opts[:path].blank?
37
+
38
+ if opts[:path].blank?
39
+ if opts[:development]
40
+ opts[:path] = Dir.getwd
41
+ else
42
+ abort "You should specify application path (--path)" if opts[:path].blank?
43
+ end
44
+ end
41
45
 
42
46
  application = Smartguard::Applications.const_get(opts[:application].camelize) rescue nil
43
47
 
44
48
  abort "Application `#{opts[:application]}` not supported" if application.nil?
45
49
 
46
- application = application.new(opts[:path])
47
-
48
- if opts[:stop]
49
- application.stop_services
50
- elsif opts[:start]
51
- application.start_services
50
+ if opts[:development]
51
+ Smartguard.environment = :development
52
52
  else
53
- application.warm_up
54
- DRb.start_service("druby://localhost:#{opts[:port]}", application)
55
- DRb.thread.join
53
+ Smartguard.environment = :production
56
54
  end
57
- end
55
+
56
+ application = application.new(opts[:path])
57
+ DRb.start_service("druby://localhost:#{opts[:port]}", application)
58
+ application.start_services
59
+ DRb.thread.join
60
+ end
@@ -2,18 +2,23 @@ require 'active_support/all'
2
2
  require 'pathname'
3
3
  require 'fileutils'
4
4
  require 'drb'
5
+ require "socket"
5
6
 
6
7
  require 'smartguard/version'
7
8
  require 'smartguard/logging'
8
9
  require 'smartguard/process'
10
+ require 'smartguard/process_manager'
9
11
  require 'smartguard/application'
10
12
  require 'smartguard/client'
11
13
 
12
14
  require 'smartguard/applications/smartkiosk'
13
15
  require 'smartguard/applications/smartkiosk/sidekiq'
14
16
  require 'smartguard/applications/smartkiosk/smartware'
15
- require 'smartguard/applications/smartkiosk/cronic'
17
+ require 'smartguard/applications/smartkiosk/scheduler'
16
18
  require 'smartguard/applications/smartkiosk/thin'
17
19
 
18
20
  module Smartguard
21
+ class << self
22
+ attr_accessor :environment
23
+ end
19
24
  end
@@ -2,10 +2,17 @@ module Smartguard
2
2
  class Application
3
3
  def initialize(path)
4
4
  @base_path = Pathname.new(File.absolute_path path)
5
- @current_path = @base_path.join('current')
6
- @releases_path = @base_path.join('releases')
7
- @shared_path = @base_path.join('shared')
8
- @active_path = File.readlink @current_path
5
+ if Smartguard.environment == :production
6
+ @current_path = @base_path.join('current')
7
+ @releases_path = @base_path.join('releases')
8
+ @shared_path = @base_path.join('shared')
9
+ @active_path = File.readlink @current_path
10
+ else
11
+ @current_path = @base_path
12
+ @releases_path = @base_path
13
+ @shared_path = @base_path
14
+ @active_path = @base_path
15
+ end
9
16
  end
10
17
  end
11
18
  end
@@ -3,43 +3,35 @@ module Smartguard
3
3
  class Smartkiosk < Smartguard::Application
4
4
  def initialize(*args)
5
5
  super
6
- @head_path = @base_path.join('head')
6
+
7
+ if Smartguard.environment == :production
8
+ @head_path = @base_path.join('head')
9
+ else
10
+ @head_path = @base_path
11
+ end
12
+
13
+ @services = {
14
+ smartware: Smartware.new(wrap_path),
15
+ sidekiq: Sidekiq.new(wrap_path),
16
+ thin: Thin.new(wrap_path),
17
+ scheduler: Scheduler.new(wrap_path),
18
+ }
7
19
  end
8
20
 
9
21
  def services
10
- [:sidekiq, :smartware, :cronic, :thin]
22
+ @services.keys
11
23
  end
12
24
 
13
25
  def status
14
26
  data = {}
15
27
 
16
- services.each do |s|
17
- service = send(s)
18
-
19
- data[s] = [service.pid, service.active?]
28
+ @services.each do |key, service|
29
+ data[key] = [ service.pid, service.active? ]
20
30
  end
21
31
 
22
32
  data
23
33
  end
24
34
 
25
- def warm_up
26
- Logging.logger.info 'Warming up'
27
-
28
- services.each do |s|
29
- service = send(s)
30
-
31
- if !service.active?
32
- if !service.start
33
- stop_services
34
- puts "Could not be started: #{s}"
35
- exit
36
- end
37
- else
38
- Logging.logger.info "#{s} is already active: #{service.pid}"
39
- end
40
- end
41
- end
42
-
43
35
  def restart(path=nil)
44
36
  stop_services(path)
45
37
  start_services(path)
@@ -51,6 +43,10 @@ module Smartguard
51
43
  end
52
44
 
53
45
  def switch_release(release)
46
+ if Smartguard.environment != :production
47
+ raise "Release switching is only supported in the production environment."
48
+ end
49
+
54
50
  release = release.is_a?(Symbol) ? @releases_path.join(release.to_s)
55
51
  : @base_path.join(release)
56
52
 
@@ -91,40 +87,30 @@ module Smartguard
91
87
  end
92
88
 
93
89
  def start_services(path=nil, &block)
94
- services.each do |s|
95
- if !send(s, path).start && block_given?
96
- Logging.logger.debug "Startup of #{s} failed: running safety block"
97
- yield
98
- return false
90
+ @services.each do |key, service|
91
+ service.path = wrap_path path
92
+
93
+ if !service.active?
94
+ if !service.start && block_given?
95
+ Logging.logger.debug "Startup of #{s} failed: running safety block"
96
+ yield
97
+ return false
98
+ end
99
99
  end
100
100
  end
101
101
  end
102
102
 
103
103
  def stop_services(path=nil)
104
- services.each do |s|
105
- send(s, path).stop
104
+ @services.each do |key, service|
105
+ service.path = wrap_path path
106
+
107
+ service.stop if service.active?
106
108
  end
107
109
  end
108
110
 
109
111
  protected
110
112
 
111
- def sidekiq(path=nil)
112
- Sidekiq.new wrap_path(path)
113
- end
114
-
115
- def smartware(path=nil)
116
- Smartware.new wrap_path(path)
117
- end
118
-
119
- def cronic(path=nil)
120
- Cronic.new wrap_path(path)
121
- end
122
-
123
- def thin(path=nil)
124
- Thin.new wrap_path(path)
125
- end
126
-
127
- def wrap_path(path)
113
+ def wrap_path(path = nil)
128
114
  path.blank? ? @current_path : Pathname.new(path)
129
115
  end
130
116
  end
@@ -0,0 +1,26 @@
1
+ require "fileutils"
2
+
3
+ module Smartguard
4
+ module Applications
5
+ class Smartkiosk
6
+ class Scheduler < Process
7
+ def start
8
+ super
9
+
10
+ Logging.logger.info "Starting scheduler"
11
+
12
+ run @path, {
13
+ 'RAILS_ENV' => Smartguard.environment.to_s
14
+ }, "rake", "schedule"
15
+ end
16
+
17
+ def stop
18
+ super
19
+
20
+ Logging.logger.info "Stoping scheduler"
21
+ kill_and_wait :TERM, 5
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,32 +1,35 @@
1
1
  module Smartguard
2
2
  module Applications
3
3
  class Smartkiosk
4
- class Sidekiq < Smartguard::Process
5
- def initialize(path)
6
- @path = path
7
- @config_path = path.join('config/sidekiq.yml')
8
- @log_path = path.join('log/sidekiq_log')
9
- @config = YAML.load_file(@config_path) rescue {}
10
- end
11
-
12
- def pid
13
- File.read(@path.join @config[:pidfile]).to_i rescue nil
14
- end
15
-
4
+ class Sidekiq < Process
16
5
  def start
6
+ super
7
+
17
8
  Logging.logger.info "Starting sidekiq"
18
9
 
19
- if @config[:pidfile].blank?
20
- Logging.logger.warn "Sidekiq's config was not found"
10
+ config_path = @path.join('config/sidekiq.yml')
11
+ log_path = @path.join('log/sidekiq_log')
12
+ pidfile = @path.join('tmp/pids/sidekiq.pid')
13
+
14
+ FileUtils.rm_f pidfile
15
+ if !run(@path,
16
+ {},
17
+ "bundle", "exec",
18
+ "sidekiq", "-e", Smartguard.environment.to_s, "--config=#{config_path}", "--pidfile=#{pidfile}"
19
+ )
21
20
  return false
22
21
  end
23
22
 
24
- run @path, "bundle exec sidekiq -e production --config #{@config_path} >> #{@log_path} 2>&1 &"
23
+ without_respawn do
24
+ wait_for_file pidfile
25
+ end
25
26
  end
26
27
 
27
28
  def stop
28
- Logging.logger.info "Stoping sidekiq"
29
- run @path, "bundle exec sidekiqctl stop #{@config[:pidfile]} 60"
29
+ super
30
+
31
+ Logging.logger.info "Stopping sidekiq"
32
+ kill_and_wait :TERM, 60
30
33
  end
31
34
  end
32
35
  end
@@ -1,26 +1,28 @@
1
1
  module Smartguard
2
2
  module Applications
3
3
  class Smartkiosk
4
- class Smartware < Smartguard::Process
5
- def initialize(path)
6
- @path = path
7
- @pid_file = path.join('tmp/pids/smartware.pid')
8
- @log_file = path.join('log/smartware.log')
9
- @config_file = path.join('config/smartware.yml')
10
- end
4
+ class Smartware < Process
5
+ def start
6
+ super
11
7
 
12
- def pid
13
- File.read(@pid_file).to_i rescue nil
14
- end
8
+ log_file = @path.join('log/smartware.log')
9
+ config_file = @path.join('config/smartware.yml')
15
10
 
16
- def start
17
11
  Logging.logger.info "Starting smartware"
18
- run @path, "bundle exec smartware -d --pid=#{@pid_file} --log=#{@log_file} --config-file=#{@config_file}"
12
+ if !run(@path, {}, "bundle", "exec", "smartware", "--log=#{log_file}", "--config-file=#{config_file}")
13
+ return false
14
+ end
15
+
16
+ without_respawn do
17
+ wait_for_port 6001
18
+ end
19
19
  end
20
20
 
21
21
  def stop
22
+ super
23
+
22
24
  Logging.logger.info "Stoping smartware"
23
- kill unless pid.blank?
25
+ kill_and_wait :TERM, 15
24
26
  end
25
27
  end
26
28
  end
@@ -1,24 +1,30 @@
1
1
  module Smartguard
2
2
  module Applications
3
3
  class Smartkiosk
4
- class Thin < Smartguard::Process
5
- def initialize(path)
6
- @path = path
7
- @pid_file = path.join('tmp/pids/thin.pid')
8
- end
9
-
10
- def pid
11
- File.read(@pid_file).to_i rescue nil
12
- end
13
-
4
+ class Thin < Process
14
5
  def start
6
+ super
7
+
15
8
  Logging.logger.info "Starting thin"
16
- run @path, "bundle exec thin -d -e production start"
9
+
10
+ if !run(@path,
11
+ {},
12
+ "bundle", "exec",
13
+ "thin", "-e", Smartguard.environment.to_s, "start"
14
+ )
15
+ return false
16
+ end
17
+
18
+ without_respawn do
19
+ wait_for_port 3000
20
+ end
17
21
  end
18
22
 
19
23
  def stop
24
+ super
25
+
20
26
  Logging.logger.info "Stoping thin"
21
- run(@path, "bundle exec thin -d -e production stop") unless pid.blank?
27
+ kill_and_wait :TERM, 15
22
28
  end
23
29
  end
24
30
  end
@@ -2,34 +2,157 @@ module Smartguard
2
2
  class Process
3
3
  include DRb::DRbUndumped
4
4
 
5
- def run(path, command, env={})
6
- result = false
5
+ attr_reader :pid
6
+ attr_accessor :path
7
7
 
8
- if defined?(Bundler)
9
- Bundler.with_clean_env do
10
- env = ENV.to_hash.merge(env)
8
+ def initialize(path)
9
+ @active = false
10
+ @pid = nil
11
+ @path = path
12
+ @wanted = false
13
+ @starting = false
14
+ end
15
+
16
+ private
17
+
18
+ def run_clean(path, env={}, *command)
19
+ raise "process is already active" if @active
20
+
21
+ begin
22
+ @pid = ::Process.spawn env, *command, chdir: path
23
+ ProcessManager.track @pid, method(:process_died)
24
+ @active = true
25
+
26
+ true
27
+ rescue => e
28
+ Logging.logger.error "unable to spawn #{command[0]}: #{e}"
29
+ false
30
+ end
31
+ end
32
+
33
+ public
34
+
35
+ def start
36
+ @wanted = true
37
+ end
38
+
39
+ def stop
40
+ @wanted = false
41
+ end
42
+
43
+ def active?
44
+ @active
45
+ end
46
+
47
+ def wanted?
48
+ @wanted
49
+ end
50
+
51
+ protected
52
+
53
+ def died
54
+ Thread.new do
55
+ Logging.logger.warn "#{self.class.name} died, respawning"
56
+ start
57
+ end
58
+ end
59
+
60
+ def without_respawn(&block)
61
+ begin
62
+ @starting = true
63
+
64
+ yield
65
+ ensure
66
+ @starting = false
67
+ end
68
+ end
11
69
 
12
- FileUtils.cd(path) do
13
- result = Kernel.system env, command
70
+ def wait_for_port(port)
71
+ while active?
72
+ socket = nil
73
+ alive = false
74
+ Addrinfo.foreach("localhost", port) do |addr|
75
+ begin
76
+ socket = Socket.new addr.afamily, :STREAM
77
+
78
+ socket.connect addr.to_sockaddr
79
+ alive = true
80
+ rescue
81
+ ensure
82
+ socket.close unless socket.nil?
14
83
  end
84
+
85
+ break if alive
15
86
  end
16
- else
17
- env = ENV.to_hash.merge(env)
18
87
 
19
- FileUtils.cd(path) do
20
- result = Kernel.system env, command
88
+ break if alive
89
+ sleep 0.5
90
+ end
91
+
92
+ active?
93
+ end
94
+
95
+ def wait_for_file(file)
96
+ while active?
97
+ break if File.exists? file
98
+ sleep 0.5
99
+ end
100
+
101
+ active?
102
+ end
103
+
104
+ def process_died(pid)
105
+ @active = false
106
+ @pid = nil
107
+
108
+ died if @wanted && !@starting
109
+ end
110
+
111
+ if defined? Bundler
112
+ def run(*args)
113
+ Bundler.with_clean_env do
114
+ run_clean *args
21
115
  end
22
116
  end
117
+ else
118
+ alias :run :run_clean
119
+ end
23
120
 
24
- result
121
+ def kill_and_wait(signal = :TERM, timeout = nil)
122
+ kill signal
123
+ wait timeout
25
124
  end
26
125
 
27
- def kill
28
- run @path, "kill -9 #{pid}"
126
+ def kill(signal = :TERM)
127
+ if active?
128
+ ::Process.kill signal, @pid
129
+ end
130
+
131
+ true
132
+ rescue
133
+ true
29
134
  end
30
135
 
31
- def active?
32
- !!::Process.getpgid(pid) rescue false
136
+ def wait(timeout = nil)
137
+ started = Time.now
138
+
139
+ while active?
140
+ sleep 0.5
141
+
142
+ now = Time.now
143
+ if !timeout.nil? && now - started > timeout
144
+ begin
145
+ @active = false
146
+ ProcessManager.untrack @pid
147
+ Process.kill :KILL, @pid
148
+ rescue
149
+ end
150
+
151
+ return true
152
+ end
153
+ end
154
+
155
+ true
33
156
  end
34
157
  end
35
158
  end
@@ -0,0 +1,39 @@
1
+ module Smartguard
2
+ module ProcessManager
3
+ def self.track(pid, method)
4
+ @tracked ||= {}
5
+ @tracked[pid] = method
6
+ end
7
+
8
+ def self.untrack(pid)
9
+ @tracked ||= {}
10
+ @tracked.delete pid
11
+ end
12
+
13
+ def self.handle_sigchld(signal)
14
+ pids = []
15
+ @tracked ||= {}
16
+
17
+ begin
18
+ loop do
19
+ pid = ::Process.wait(-1, ::Process::WNOHANG)
20
+ break if pid.nil?
21
+
22
+ pids << pid
23
+ end
24
+ rescue => e
25
+ end
26
+
27
+ pids.each do |pid|
28
+ tracker = @tracked[pid]
29
+ unless tracker.nil?
30
+ @tracked.delete pid
31
+ tracker.call pid
32
+ end
33
+ end
34
+ end
35
+
36
+ end
37
+
38
+ trap 'CHLD', ProcessManager.method(:handle_sigchld)
39
+ end
@@ -1,3 +1,3 @@
1
1
  module Smartguard
2
- VERSION = "0.1.8"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: smartguard
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-19 00:00:00.000000000 Z
12
+ date: 2013-01-13 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: i18n
16
- requirement: &70096502060460 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70096502060460
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
31
  name: activesupport
27
- requirement: &70096502060040 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *70096502060040
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: dante
38
- requirement: &70096487986260 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :runtime
45
55
  prerelease: false
46
- version_requirements: *70096487986260
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: pry
49
- requirement: &70096487985840 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,7 +69,12 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *70096487985840
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  description: Smartguard is the Smartkiosk services control daemon
59
79
  email:
60
80
  - boris@roundlake.ru
@@ -72,13 +92,14 @@ files:
72
92
  - lib/smartguard.rb
73
93
  - lib/smartguard/application.rb
74
94
  - lib/smartguard/applications/smartkiosk.rb
75
- - lib/smartguard/applications/smartkiosk/cronic.rb
95
+ - lib/smartguard/applications/smartkiosk/scheduler.rb
76
96
  - lib/smartguard/applications/smartkiosk/sidekiq.rb
77
97
  - lib/smartguard/applications/smartkiosk/smartware.rb
78
98
  - lib/smartguard/applications/smartkiosk/thin.rb
79
99
  - lib/smartguard/client.rb
80
100
  - lib/smartguard/logging.rb
81
101
  - lib/smartguard/process.rb
102
+ - lib/smartguard/process_manager.rb
82
103
  - lib/smartguard/version.rb
83
104
  - smartguard.gemspec
84
105
  homepage: https://github.com/roundlake/smartguard
@@ -101,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
101
122
  version: '0'
102
123
  requirements: []
103
124
  rubyforge_project:
104
- rubygems_version: 1.8.15
125
+ rubygems_version: 1.8.24
105
126
  signing_key:
106
127
  specification_version: 3
107
128
  summary: Smartguard is the Smartkiosk services control daemon
@@ -1,27 +0,0 @@
1
- module Smartguard
2
- module Applications
3
- class Smartkiosk
4
- class Cronic < Smartguard::Process
5
- def initialize(path)
6
- @path = path
7
- @pid_file = path.join('tmp/pids/cronic.pid')
8
- @log_file = path.join('log/cronic.log')
9
- end
10
-
11
- def pid
12
- File.read(@pid_file).to_i rescue nil
13
- end
14
-
15
- def start
16
- Logging.logger.info "Starting cronic"
17
- run @path, "script/cronic -d -l #{@log_file} -P #{@pid_file}", 'RAILS_ENV' => 'production'
18
- end
19
-
20
- def stop
21
- Logging.logger.info "Stoping cronic"
22
- run @path, "script/cronic -k -P #{@pid_file}", 'RAILS_ENV' => 'production'
23
- end
24
- end
25
- end
26
- end
27
- end