smartguard 0.1.8 → 0.2.0

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