Class: Sunshine::Daemon

Inherits:
Object
  • Object
show all
Defined in:
lib/sunshine/daemon.rb

Overview

An abstract class to wrap simple daemon software setup and start/stop.

Child classes are expected to at least provide a start and stop bash script by either overloading the start_cmd and stop_cmd methods, or by setting may also be specified if restart requires more functionality than simply calling start_cmd && stop_cmd.

Direct Known Subclasses

ARSendmail, DelayedJob, Server

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(app, options = {}) ⇒ Daemon

Daemon objects need only an App object to be instantiated but many options are available for customization:

:pid

pid_path - set the pid; default: app.shared_path/pids/svr_name.pid

defaults to app.shared_path/pids/svr_name.pid
:bin

bin_path - set the daemon app bin path (e.g. usr/local/nginx)

defaults to svr_name
:sudo

bool|str - define if sudo should be used and with what user

:timeout

int|str - timeout to use for daemon config

defaults to 1
:processes

prcss_num - number of processes daemon should run

defaults to 0
:config_template

path - glob path to tempates to render and upload

defaults to sunshine_path/templates/svr_name/*
:config_path

path - remote path daemon configs will be uploaded to

defaults to app.current_path/daemons/svr_name
:config_file

name - remote file name the daemon should load

defaults to svr_name.conf
:log_path

path - path to where the log files should be output

defaults to app.log_path
:point_to

app|daemon - an abstract target to point to

defaults to the passed app

The Daemon constructor also supports any App#find options to narrow the server apps to use. Note: subclasses such as Server already have a default :role that can be overridden.



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/sunshine/daemon.rb', line 79

def initialize app, options={}
  @options = options

  @app    = app
  @target = options[:point_to] || @app

  @short_class_name = self.class.underscore self.class.to_s.split("::").last

  @name        = options[:name] || @short_class_name

  @pid         = options[:pid] || "#{@app.shared_path}/pids/#{@name}.pid"
  @bin         = options[:bin] || @name
  @sudo        = options[:sudo]
  @timeout     = options[:timeout] || 0
  @dep_name    = options[:dep_name] || @name
  @processes   = options[:processes] || 1

  @config_template   = options[:config_template]
  @config_template ||= "#{Sunshine::ROOT}/templates/#{@short_class_name}/*"

  @config_path     = options[:config_path] ||
    "#{@app.current_path}/daemons/#{@name}"
  @config_file     = options[:config_file] || "#{@name}.conf"

  log_path  = options[:log_path] || @app.log_path
  @log_files = {
    :stderr => "#{log_path}/#{@name}_stderr.log",
    :stdout => "#{log_path}/#{@name}_stdout.log"
  }

  @start_cmd = @stop_cmd = @restart_cmd = @status_cmd = nil

  @setup_successful = nil

  register_after_user_script
end

Instance Attribute Details

#appObject (readonly)

Returns the value of attribute app.



34
35
36
# File 'lib/sunshine/daemon.rb', line 34

def app
  @app
end

#binObject

Returns the value of attribute bin.



36
37
38
# File 'lib/sunshine/daemon.rb', line 36

def bin
  @bin
end

#config_fileObject

Returns the value of attribute config_file.



38
39
40
# File 'lib/sunshine/daemon.rb', line 38

def config_file
  @config_file
end

#config_pathObject

Returns the value of attribute config_path.



38
39
40
# File 'lib/sunshine/daemon.rb', line 38

def config_path
  @config_path
end

#config_templateObject

Returns the value of attribute config_template.



38
39
40
# File 'lib/sunshine/daemon.rb', line 38

def config_template
  @config_template
end

#nameObject (readonly)

Returns the value of attribute name.



34
35
36
# File 'lib/sunshine/daemon.rb', line 34

def name
  @name
end

#pidObject

Returns the value of attribute pid.



36
37
38
# File 'lib/sunshine/daemon.rb', line 36

def pid
  @pid
end

#processesObject

Returns the value of attribute processes.



36
37
38
# File 'lib/sunshine/daemon.rb', line 36

def processes
  @processes
end

#restart_cmdObject

Gets the command that restarts the daemon.



263
264
265
# File 'lib/sunshine/daemon.rb', line 263

def restart_cmd
  @restart_cmd || [stop_cmd, start_cmd].map{|c| "(#{c})"}.join(" && ")
end

#server_appsObject

Returns the value of attribute server_apps.



36
37
38
# File 'lib/sunshine/daemon.rb', line 36

def server_apps
  @server_apps
end

#start_cmdObject

Gets the command that starts the daemon. Should be overridden by child classes.



244
245
246
247
# File 'lib/sunshine/daemon.rb', line 244

def start_cmd
  return @start_cmd ||
    raise(CriticalDeployError, "@start_cmd undefined. Can't start #{@name}")
end

#status_cmdObject

Get the command to check if the daemon is running.



271
272
273
# File 'lib/sunshine/daemon.rb', line 271

def status_cmd
  @status_cmd || "test -f #{@pid}"
end

#stop_cmdObject

Gets the command that stops the daemon. Should be overridden by child classes.



254
255
256
257
# File 'lib/sunshine/daemon.rb', line 254

def stop_cmd
  return @stop_cmd ||
    raise(CriticalDeployError, "@stop_cmd undefined. Can't stop #{@name}")
end

#sudoObject

Returns the value of attribute sudo.



36
37
38
# File 'lib/sunshine/daemon.rb', line 36

def sudo
  @sudo
end

#targetObject (readonly)

Returns the value of attribute target.



34
35
36
# File 'lib/sunshine/daemon.rb', line 34

def target
  @target
end

#timeoutObject

Returns the value of attribute timeout.



36
37
38
# File 'lib/sunshine/daemon.rb', line 36

def timeout
  @timeout
end

Class Method Details

.binder_methodsObject

Returns an array of method names to assign to the binder for template rendering.



19
20
21
22
# File 'lib/sunshine/daemon.rb', line 19

def self.binder_methods
  [:app, :name, :target, :bin, :pid, :port,
  :processes, :config_path, :log_file, :timeout]
end

.underscore(str) ⇒ Object

Turn camelcase into underscore. Used for Daemon#name.



28
29
30
31
# File 'lib/sunshine/daemon.rb', line 28

def self.underscore str
  str.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
   gsub(/([a-z\d])([A-Z])/,'\1_\2').downcase
end

Instance Method Details

#config_file_pathObject

Get the file path to the daemon’s config file.



298
299
300
# File 'lib/sunshine/daemon.rb', line 298

def config_file_path
  "#{@config_path}/#{@config_file}"
end

#config_template_filesObject

Get the array of local config template files needed by the daemon.



325
326
327
# File 'lib/sunshine/daemon.rb', line 325

def config_template_files
  @config_template_files ||= Dir[@config_template].select{|f| File.file?(f)}
end

#each_server_app(&block) ⇒ Object

Do something with each server app used by the daemon.



120
121
122
# File 'lib/sunshine/daemon.rb', line 120

def each_server_app(&block)
  @app.each(@options, &block)
end

#has_setup?(force = false) ⇒ Boolean

Check if setup was run successfully.

Returns:

  • (Boolean)


163
164
165
166
167
168
169
170
171
172
173
174
# File 'lib/sunshine/daemon.rb', line 163

def has_setup? force=false
  return @setup_successful unless @setup_successful.nil? || force

  each_server_app do |server_app|

    unless server_app.shell.file? config_file_path
      return @setup_successful = false
    end
  end

  @setup_successful = true
end

#log_file(key) ⇒ Object

Get the path of a log file:

daemon.log_file(:stderr)
#=> "/all_logs/stderr.log"


290
291
292
# File 'lib/sunshine/daemon.rb', line 290

def log_file(key)
  @log_files[key]
end

#log_files(hash) ⇒ Object

Append or override daemon log file paths:

daemon.log_files :stderr => "/all_logs/stderr.log"


280
281
282
# File 'lib/sunshine/daemon.rb', line 280

def log_files(hash)
  @log_files.merge!(hash)
end

#restartObject

Restarts the daemon using the restart_cmd attribute if provided. If restart_cmd is not provided, calls stop and start.



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/sunshine/daemon.rb', line 222

def restart
  self.setup unless has_setup?

  Sunshine.logger.info @name, "Restarting #{@name} daemon" do
    each_server_app do |server_app|
      begin
        server_app.shell.call restart_cmd,
          :sudo => pick_sudo(server_app.shell)

        yield(server_app) if block_given?
      rescue => e
        raise CriticalDeployError.new(e, "Could not restart #{@name}")
      end
    end
  end
end

#setupObject

Setup the daemon, parse and upload config templates. If a dependency with the daemon name exists in Sunshine.dependencies, setup will attempt to install the dependency before uploading configs. If a block is given it will be passed each server_app and binder object which will be used for the building erb config templates. See the ConfigBinding class for more information.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/sunshine/daemon.rb', line 133

def setup
  Sunshine.logger.info @name, "Setting up #{@name} daemon" do

    each_server_app do |server_app|

      # Build erb binding
      binder = config_binding server_app.shell

      server_app.shell.call "mkdir -p #{remote_dirs.join(" ")}",
        :sudo => binder.sudo

      yield(server_app, binder) if block_given?

      server_app.install_deps @dep_name if
        Sunshine.dependencies.exist?(@dep_name)

      upload_config_files(server_app.shell, binder.get_binding)
    end
  end

  @setup_successful = true

rescue => e
  raise CriticalDeployError.new(e, "Could not setup #{@name}")
end

#startObject

Start the daemon app after running setup.



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/sunshine/daemon.rb', line 180

def start
  self.setup unless has_setup?
  Sunshine.logger.info @name, "Starting #{@name} daemon" do

    each_server_app do |server_app|
      begin
        server_app.shell.call start_cmd,
          :sudo => pick_sudo(server_app.shell)

        yield(server_app) if block_given?
      rescue => e
        raise CriticalDeployError.new(e, "Could not start #{@name}")
      end
    end
  end
end

#stopObject

Stop the daemon app.



201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/sunshine/daemon.rb', line 201

def stop
  Sunshine.logger.info @name, "Stopping #{@name} daemon" do

    each_server_app do |server_app|
      begin
        server_app.shell.call stop_cmd,
          :sudo => pick_sudo(server_app.shell)

        yield(server_app) if block_given?
      rescue => e
        raise CriticalDeployError.new(e, "Could not stop #{@name}")
      end
    end
  end
end

#upload_config_files(shell, setup_binding = binding) ⇒ Object

Upload config files and run them through erb with the provided binding if necessary.



307
308
309
310
311
312
313
314
315
316
317
318
319
# File 'lib/sunshine/daemon.rb', line 307

def upload_config_files(shell, setup_binding=binding)
  config_template_files.each do |config_file|

    if File.extname(config_file) == ".erb"
      filename = File.basename(config_file[0..-5])
      parsed_config = @app.build_erb(config_file, setup_binding)
      shell.make_file "#{@config_path}/#{filename}", parsed_config
    else
      filename = File.basename(config_file)
      shell.upload config_file, "#{@config_path}/#{filename}"
    end
  end
end