mecano
Version:
Common functions for system deployment.
226 lines (220 loc) • 10.1 kB
Markdown
`service([goptions], options, callback)`
----------------------------------------
Install a service. For now, only yum over SSH.
each = require 'each'
misc = require './misc'
conditions = require './misc/conditions'
child = require './misc/child'
execute = require './execute'
`options` Command options include:
* `name` Package name, optional.
* `startup` Run service daemon on startup. If true, startup will be set to '2345', use an empty string to not define any run level.
* `yum_name` Name used by the yum utility, default to "name".
* `chk_name` Name used by the chkconfig utility, default to "srv_name" and "name".
* `srv_name` Name used by the service utility, default to "name".
* `cache` Run entirely from system cache, run install and update checks offline.
* `action` Execute the service with the provided action argument.
* `stdout` Writable Stream in which commands output will be piped.
* `stderr` Writable Stream in which commands error will be piped.
* `installed` Cache a list of installed services. If an object, the service will be installed if a key of the same name exists; if anything else (default), no caching will take place.
* `updates` Cache a list of outdated services. If an object, the service will be updated if a key of the same name exists; If true, the option will be converted to an object with all the outdated service names as keys; if anything else (default), no caching will take place.
`callback` Received parameters are:
* `err` Error object if any.
* `modified` Number of action taken (installed, updated, started or stopped).
* `installed` List of installed services.
* `updates` List of services to update.
module.exports = (goptions, options, callback) ->
[goptions, options, callback] = misc.args arguments, parallel: 1
installed = updates = null
misc.options options, (err, options) ->
return callback err if err
serviced = 0
each( options )
.parallel(goptions.parallel)
.on 'item', (options, next) ->
# Validate parameters
# return next new Error 'Missing service name' unless options.name
return next new Error 'Restricted to Yum over SSH' unless options.ssh
# return next new Error 'Invalid configuration, start conflict with stop' if options.start? and options.start is options.stop
pkgname = options.yum_name or options.name
chkname = options.chk_name or options.srv_name or options.name
srvname = options.srv_name or options.name
if options.startup? and typeof options.startup isnt 'string'
options.startup = if options.startup then '2345' else ''
modified = false
installed ?= options.installed
updates ?= options.updates
# Start real work
chkinstalled = ->
# option name and yum_name are optional, skill installation if not present
return startuped() unless pkgname
cache = ->
options.log? "List installed packages"
c = if options.cache then '-C' else ''
execute
ssh: options.ssh
# cmd: "yum #{c} list installed"
cmd: "yum -C list installed"
# cmd: "rpm -qa"
code_skipped: 1
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err, executed, stdout) ->
return next err if err
stdout = stdout.split '\n'
start = false
installed = []
for pkg in stdout
start = true if pkg.trim() is 'Installed Packages'
continue unless start
installed.push pkg[1] if pkg = /^([^\. ]+?)\./.exec pkg
decide()
decide = ->
if installed.indexOf(pkgname) isnt -1 then chkupdates() else install()
if installed then decide() else cache()
# execute
# ssh: options.ssh
# cmd: "yum list installed | grep ^#{pkgname}\\\\."
# code_skipped: 1
# stdout: options.stdout
# stderr: options.stderr
# , (err, installed) ->
# return next err if err
# if installed then updates() else install()
chkupdates = ->
cache = ->
options.log? "List available updates"
c = if options.cache then '-C' else ''
execute
ssh: options.ssh
cmd: "yum #{c} list updates"
code_skipped: 1
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err, executed, stdout) ->
return next err if err
stdout = stdout.split '\n'
start = false
updates = []
for pkg in stdout
start = true if pkg.trim() is 'Updated Packages'
continue unless start
updates.push pkg[1] if pkg = /^([^\. ]+?)\./.exec pkg
decide()
decide = ->
if updates.indexOf(pkgname) isnt -1 then install() else
options.log? "No available update"
startuped()
if updates then decide() else cache()
install = ->
options.log? "Package installation: #{pkgname}"
execute
ssh: options.ssh
cmd: "yum install -y #{pkgname}"
code_skipped: 1
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err, succeed) ->
return next err if err
installedIndex = installed.indexOf pkgname
installed.push pkgname if installedIndex is -1
if updates
updatesIndex = updates.indexOf pkgname
updates.splice updatesIndex, 1 unless updatesIndex is -1
# Those 2 lines seems all wrong
return next new Error "No package #{pkgname} available." unless succeed
modified = true if installedIndex isnt -1
startuped()
startuped = ->
return started() unless options.startup?
options.log? "List startup services"
execute
ssh: options.ssh
cmd: "chkconfig --list #{chkname}"
code_skipped: 1
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err, registered, stdout, stderr) ->
return next err if err
# Invalid service name return code is 0 and message in stderr start by error
return next new Error "Invalid chkconfig name #{chkname}" if /^error/.test stderr
current_startup = ''
if registered
for c in stdout.split(' ').pop().trim().split '\t'
[level, status] = c.split ':'
current_startup += level if ['on', 'marche'].indexOf(status) > -1
return started() if options.startup is current_startup
modified = true
if options.startup?
then startup_add()
else startup_del()
startup_add = ->
options.log? "Add startup service"
startup_on = startup_off = ''
for i in [0...6]
if options.startup.indexOf(i) isnt -1
then startup_on += i
else startup_off += i
cmd = "chkconfig --add #{chkname};"
cmd += "chkconfig --level #{startup_on} #{chkname} on;" if startup_on
cmd += "chkconfig --level #{startup_off} #{chkname} off;" if startup_off
execute
ssh: options.ssh
cmd: cmd
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err) ->
return next err if err
started()
startup_del = ->
options.log? "Remove startup service"
execute
ssh: options.ssh
cmd: "chkconfig --del #{chkname}"
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err) ->
return next err if err
started()
started = ->
return action() if ['start', 'stop', 'restart'].indexOf(options.action) is -1
options.log? "Check if service #{srvname} is started"
execute
ssh: options.ssh
cmd: "service #{srvname} status"
code_skipped: [3, 1] # ntpd return 1 if pidfile exists without a matching process
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err, started) ->
return next err if err
if started
return action() unless options.action is 'start'
else
return action() unless options.action is 'stop'
finish()
action = ->
return finish() unless options.action
options.log? "Start/stop the service"
execute
ssh: options.ssh
cmd: "service #{srvname} #{options.action}"
log: options.log
stdout: options.stdout
stderr: options.stderr
, (err, executed) ->
return next err if err
modified = true
finish()
finish = ->
serviced++ if modified
next()
conditions.all options, next, chkinstalled
.on 'both', (err) ->
callback err, serviced, installed, updates