Initial Commit
This commit is contained in:
111
app/classes/logical/services/ServiceConfig.js
Normal file
111
app/classes/logical/services/ServiceConfig.js
Normal file
@@ -0,0 +1,111 @@
|
||||
const { Injectable } = require('flitter-di')
|
||||
|
||||
class ServiceConfig extends Injectable {
|
||||
static get services() {
|
||||
return [...super.services, 'utility']
|
||||
}
|
||||
|
||||
_data = {
|
||||
after: ['syslog.target', 'network.target'],
|
||||
type: 'simple',
|
||||
restart: 'always',
|
||||
restart_after: '2s',
|
||||
install_to: 'multi-user.target',
|
||||
}
|
||||
|
||||
name(set) { return this._get_or_set('name', set) }
|
||||
|
||||
description(set) { return this._get_or_set('description', set) }
|
||||
|
||||
after(set) { return this._get_or_set('after', set, true) }
|
||||
|
||||
restart(set) { return this._get_or_set('restart', set) }
|
||||
|
||||
restart_after(set) { return this._get_or_set('restart_after', set) }
|
||||
|
||||
type(set) { return this._get_or_set('type', set) }
|
||||
|
||||
user(set) { return this._get_or_set('user', set) }
|
||||
|
||||
group(set) { return this._get_or_set('group', set) }
|
||||
|
||||
directory(set) { return this._get_or_set('directory', set) }
|
||||
|
||||
execute(set) { return this._get_or_set('execute', set) }
|
||||
|
||||
env(set) { return this._get_or_set('env', set) }
|
||||
|
||||
install_to(set) { return this._get_or_set('install_to', set) }
|
||||
|
||||
_export() {
|
||||
return this.utility.deep_copy(this._data)
|
||||
}
|
||||
|
||||
_build() {
|
||||
this._validate()
|
||||
const lines = []
|
||||
lines.push('[Unit]')
|
||||
if ( this.description() ) lines.push(`Description=${this.description()}`)
|
||||
if ( this.after() ) {
|
||||
for ( const item of this.after() ) {
|
||||
lines.push(`After=${item}`)
|
||||
}
|
||||
}
|
||||
lines.push('\n[Service]')
|
||||
if ( this.restart() ) {
|
||||
lines.push(`Restart=${this.restart()}`)
|
||||
|
||||
if ( this.restart_after() ) {
|
||||
lines.push(`RestartSec=${this.restart_after()}`)
|
||||
}
|
||||
}
|
||||
|
||||
if ( this.user() ) {
|
||||
lines.push(`User=${this.user()}`)
|
||||
}
|
||||
|
||||
if ( this.group() ) {
|
||||
lines.push(`Group=${this.group()}`)
|
||||
}
|
||||
|
||||
if ( this.directory() ) {
|
||||
lines.push(`WorkingDirectory=${this.directory()}`)
|
||||
}
|
||||
|
||||
if ( this.execute() ) {
|
||||
lines.push(`ExecStart=${this.execute()}`)
|
||||
}
|
||||
|
||||
if ( this.env() ) {
|
||||
lines.push(`Environment=${this.env()}`)
|
||||
}
|
||||
|
||||
if ( this.install_to() ) {
|
||||
lines.push('\n[Install]')
|
||||
lines.push(`WantedBy=${this.install_to()}`)
|
||||
}
|
||||
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
_validate() {
|
||||
const required_fields = ['name', 'description', 'after', 'type', 'user', 'group', 'directory', 'execute']
|
||||
for ( const field of required_fields ) {
|
||||
if ( !this[field]() ) throw new Error(`Missing required service config field: ${field}`)
|
||||
}
|
||||
}
|
||||
|
||||
_get_or_set(field, value, arr = false) {
|
||||
if ( value ) {
|
||||
if ( arr ) {
|
||||
if ( !this._data[field] ) this._data[field] = []
|
||||
this._data[field].push(value)
|
||||
} else this._data[field] = value
|
||||
return this
|
||||
}
|
||||
|
||||
return this._data[field]
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = ServiceConfig
|
||||
79
app/classes/logical/services/ServiceManager.js
Normal file
79
app/classes/logical/services/ServiceManager.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const { Injectable } = require('flitter-di')
|
||||
const ImplementationError = require('libflitter/errors/ImplementationError')
|
||||
const ServiceConfig = require('./ServiceConfig')
|
||||
|
||||
class ServiceManager extends Injectable {
|
||||
static SERVICE_STATE_STOPPED = 'stopped'
|
||||
static SERVICE_STATE_ERROR = 'error'
|
||||
static SERVICE_STATE_RUNNING = 'running'
|
||||
|
||||
_command_restart_service = '%%SERVICE%%'
|
||||
_command_start_service = '%%SERVICE%%'
|
||||
_command_stop_service = '%%SERVICE%%'
|
||||
_command_reload_service = '%%SERVICE%%'
|
||||
_command_enable_service = '%%SERVICE%%'
|
||||
_command_disable_service = '%%SERVICE%%'
|
||||
_command_daemon_reload = ''
|
||||
|
||||
_group_services_by = ' '
|
||||
|
||||
constructor(host) {
|
||||
super()
|
||||
this._host = host
|
||||
}
|
||||
|
||||
async status(...services) {
|
||||
throw new ImplementationError()
|
||||
}
|
||||
|
||||
async install(config) {
|
||||
throw new ImplementationError()
|
||||
}
|
||||
|
||||
new_config() {
|
||||
return new ServiceConfig()
|
||||
}
|
||||
|
||||
async start(...services) {
|
||||
await this._standard_format_execution(services, this._command_start_service)
|
||||
}
|
||||
|
||||
async stop(...services) {
|
||||
await this._standard_format_execution(services, this._command_stop_service)
|
||||
}
|
||||
|
||||
async restart(...services) {
|
||||
await this._standard_format_execution(services, this._command_restart_service)
|
||||
}
|
||||
|
||||
async reload(...services) {
|
||||
await this._standard_format_execution(services, this._command_reload_service)
|
||||
}
|
||||
|
||||
async enable(...services) {
|
||||
await this._standard_format_execution(services, this._command_enable_service)
|
||||
}
|
||||
|
||||
async disable(...services) {
|
||||
await this._standard_format_execution(services, this._command_disable_service)
|
||||
}
|
||||
|
||||
async daemon_reload() {
|
||||
await this._host.run_line_result(this._command_daemon_reload)
|
||||
}
|
||||
|
||||
async _standard_format_execution(items, command, replace = '%%SERVICE%%') {
|
||||
if ( this._group_services_by ) {
|
||||
items = [items.join(this._group_services_by)]
|
||||
}
|
||||
|
||||
for ( const item of items ) {
|
||||
const result = await this._host.execute(command.replace(replace, item))
|
||||
if ( result.exit_code !== 0 ) {
|
||||
throw new Error('Error encountered while executing command: '+result.stderr.join('\n'))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = ServiceManager
|
||||
55
app/classes/logical/services/SystemDManager.js
Normal file
55
app/classes/logical/services/SystemDManager.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const ServiceManager = require('./ServiceManager')
|
||||
|
||||
class SystemDManager extends ServiceManager {
|
||||
_command_restart_service = 'systemctl restart %%SERVICE%%'
|
||||
_command_start_service = 'systemctl start %%SERVICE%%'
|
||||
_command_stop_service = 'systemctl stop %%SERVICE%%'
|
||||
_command_reload_service = 'systemctl reload %%SERVICE%%'
|
||||
_command_enable_service = 'systemctl enable %%SERVICE%%'
|
||||
_command_disable_service = 'systemctl disable %%SERVICE%%'
|
||||
_command_daemon_reload = 'systemctl daemon-reload'
|
||||
|
||||
async status(...services) {
|
||||
const statuses = []
|
||||
for ( const service_name of services ) {
|
||||
const result = await this._host.execute(`systemctl status -l --value --no-pager ${service_name}`)
|
||||
if ( result.exit_code !== 0 ) {
|
||||
throw new Error(`Unable to determine service state: ${result.stderr.join('\n')}`)
|
||||
}
|
||||
|
||||
const status = result.clean_out.map(x => x.trim().toLowerCase())
|
||||
.filter(x => x.startsWith('active:'))[0]
|
||||
.split(':')[1]
|
||||
.trim()
|
||||
|
||||
|
||||
statuses.push({
|
||||
name: service_name,
|
||||
status: this._determine_status_from_string(status)
|
||||
})
|
||||
}
|
||||
|
||||
return statuses.length === 1 ? statuses[0] : statuses
|
||||
}
|
||||
|
||||
async install(config) {
|
||||
if ( !config._build || !config.name ) throw new Error('Config must be instance of classes/logical/services/ServiceConfig')
|
||||
const file_contents = config._build()
|
||||
const name = config.name()
|
||||
|
||||
const service_file = await this._host.get_path(`/etc/systemd/system/${name}.service`)
|
||||
await service_file.echo(file_contents)
|
||||
}
|
||||
|
||||
_determine_status_from_string(str) {
|
||||
if ( str.startsWith('active') ) {
|
||||
return this.constructor.SERVICE_STATE_RUNNING
|
||||
} else if ( str.startsWith('fail') ) {
|
||||
return this.constructor.SERVICE_STATE_ERROR
|
||||
} else {
|
||||
return this.constructor.SERVICE_STATE_STOPPED
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = exports = SystemDManager
|
||||
Reference in New Issue
Block a user