require_relative 'default'
module Common
# General
def self.packages(ctx, *pkgs, action: :install)
Array(pkgs).flatten.each do |pkg|
ctx.package pkg do
action action
end
end
end
def self.directories(ctx, dirs, opts = {})
dirs = Array(dirs).compact.uniq
owner = opts[:owner] || Default.user
group = opts[:group] || Default.group
mode = opts[:mode] || '0755'
recursive = opts[:recursive] || true
ignore_failure = !!opts[:ignore_failure]
recreate = !!opts[:recreate] || false
if recreate
sort_dir(dirs).each { |dir| delete_dir(ctx, dir) }
end
dirs.each { |dir| create_dir(ctx, dir, owner, group, mode, recursive, ignore_failure) }
end
# System
def self.daemon(ctx, name)
Ctx.find(ctx, :execute, name) do
command 'systemctl daemon-reload'
action :nothing
end
end
def self.application(ctx, name, user: Default.user, group: Default.group,
exec: nil, cwd: nil, unit: {}, actions: [:enable, :start], subscribe: nil, reload: 'systemd_reload',
restart: 'on-failure', restart_delay: 10, restart_limit: 10, restart_max: 600,
verify: true, verify_timeout: 60, verify_interval: 5, verify_cmd: "systemctl is-active --quiet #{name}")
if exec
daemon(ctx, reload)
defaults = {
'Unit' => {
'Description' => name.capitalize, 'After' => 'network.target',
'StartLimitBurst' => restart_limit,
'StartLimitIntervalSec' => restart_max
},
'Service' => ( {
'Type' => 'simple', 'User' => user, 'Group' => group,
'Restart' => restart,
'RestartSec' => restart_delay
}.merge(exec ? { 'ExecStart' => exec } : {})
.merge(cwd ? { 'WorkingDirectory' => cwd } : {}) ),
'Install' => { 'WantedBy' => 'multi-user.target' }
}
unit_config = defaults.dup
unit.each { |section, settings| unit_config[section] = (unit_config[section] || {}).merge(settings) }
unit_content = unit_config.map do |section, settings|
lines = settings.map { |k, v| "#{k}=#{v}" unless v.nil? }.compact.join("\n")
"[#{section}]\n#{lines}"
end.join("\n\n")
# Mask default application service
conflicts = %Q(systemctl list-unit-files '*#{File.basename(exec.split.first)}*.service' --no-legend | awk '$2!="masked" {print $1}')
Ctx.dsl(ctx).execute "application_mask_#{name}" do
command "#{conflicts} | xargs -r -IUNIT sh -c 'systemctl stop UNIT && systemctl mask UNIT'"
only_if conflicts
returns [0, 123] # already masked returns '123'
action :run
end
Ctx.dsl(ctx).file "/etc/systemd/system/#{name}.service" do
owner 'root'
group 'root'
mode '0664'
content unit_content
notifies :run, "execute[application_reset_#{name}]", :immediately
notifies :run, "execute[#{reload}]", :immediately
notifies :restart, "service[#{name}]", :delayed
end
end
Ctx.dsl(ctx).execute "application_reset_#{name}" do
command "systemctl reset-failed #{name}.service"
only_if "systemctl is-failed --quiet #{name}.service"
action :nothing
end
if actions.include?(:force_restart)
Ctx.dsl(ctx).execute "application_restart_#{name}" do
command "systemctl stop #{name} || true && sleep 1 && systemctl start #{name}"
action :run
end
else
Ctx.dsl(ctx).service name do
action actions
Array(subscribe).flatten.each { |ref| subscribes :restart, ref, :delayed } if subscribe
end
end
Ctx.dsl(ctx).ruby_block "application_verify_service_#{name}" do
block do
retry_timeout = Time.now + verify_timeout
ok = false
while Time.now < retry_timeout
(is_active = Mixlib::ShellOut.new(verify_cmd)).run_command
is_active.exitstatus.zero? ? (ok = true; break) : (sleep verify_interval)
end
raise (Logs.debug(["service '#{name}' failed health check",
Mixlib::ShellOut.new("systemctl status #{name} --no-pager").run_command.stdout.strip,
Mixlib::ShellOut.new("journalctl -u #{name} --no-pager").run_command.stdout.strip,
], level: :error)) unless ok
end
only_if { verify }
end
end
def self.create_dir(ctx, dir, owner, group, mode, recursive, ignore_failure = false)
Ctx.dsl(ctx).directory dir do owner owner; group group; mode mode; recursive recursive; ignore_failure ignore_failure; end
rescue => e
Logs.warn("Skip create #{dir}: #{e}")
end
def self.delete_dir(ctx, dir)
Logs.try!("delete dir #{dir}") do
Ctx.dsl(ctx).directory dir do action :delete; recursive true; only_if { ::Dir.exist?(dir) } end
end
end
def self.sort_dir(dirs)
Array(dirs).compact.sort_by { |d| -d.count('/') }
end
end