fix(core): update

This commit is contained in:
Philipp Kunz 2019-09-03 11:29:14 +02:00
parent 9ef366eee9
commit 5e5a679f99
9 changed files with 149 additions and 516 deletions

50
package-lock.json generated
View File

@ -144,6 +144,23 @@
"@pushrocks/smartpromise": "^3.0.2" "@pushrocks/smartpromise": "^3.0.2"
} }
}, },
"@pushrocks/smartenv": {
"version": "4.0.7",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartenv/-/smartenv-4.0.7.tgz",
"integrity": "sha512-ThSJMEcUAF1VIa4vAvn1zGnziEuDY4h8T+EQ5JCiKy2qnUM/F6zqzFogyo495X6rg71gC/7RmktQVQaN06gjWA==",
"requires": {
"@pushrocks/smartparam": "^1.0.4",
"@pushrocks/smartpromise": "^3.0.2",
"@types/node": "^12.7.2"
},
"dependencies": {
"@types/node": {
"version": "12.7.3",
"resolved": "https://verdaccio.lossless.one/@types%2fnode/-/node-12.7.3.tgz",
"integrity": "sha512-3SiLAIBkDWDg6vFo0+5YJyHPWU9uwu40Qe+v+0MH8wRKYBimHvvAOyk3EzMrD/TrIlLYfXrqDqrg913PynrMJQ=="
}
}
},
"@pushrocks/smartevent": { "@pushrocks/smartevent": {
"version": "2.0.3", "version": "2.0.3",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartevent/-/smartevent-2.0.3.tgz", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartevent/-/smartevent-2.0.3.tgz",
@ -251,7 +268,6 @@
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartparam/-/smartparam-1.0.4.tgz", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartparam/-/smartparam-1.0.4.tgz",
"integrity": "sha512-UAqhnTQGBaJRptTK3qTd47Yt2ZTrAWByteow4auSNZD+k0xrpY9mTPPgKh0IwsURe0cZhj7zYNpGiekhKkL4rA==", "integrity": "sha512-UAqhnTQGBaJRptTK3qTd47Yt2ZTrAWByteow4auSNZD+k0xrpY9mTPPgKh0IwsURe0cZhj7zYNpGiekhKkL4rA==",
"dev": true,
"requires": { "requires": {
"@pushrocks/smartpromise": "^2.0.5", "@pushrocks/smartpromise": "^2.0.5",
"is-promise": "^2.1.0", "is-promise": "^2.1.0",
@ -261,8 +277,7 @@
"@pushrocks/smartpromise": { "@pushrocks/smartpromise": {
"version": "2.0.5", "version": "2.0.5",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartpromise/-/smartpromise-2.0.5.tgz", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartpromise/-/smartpromise-2.0.5.tgz",
"integrity": "sha512-9j/chLtIiNkR0MDw7Mpxg9slxAVvAQwUZuiaPYX5KpHdKxQaHLI1VZ8IN0vPhwlfgNO4i4vGXV0wB8BvSDj03g==", "integrity": "sha512-9j/chLtIiNkR0MDw7Mpxg9slxAVvAQwUZuiaPYX5KpHdKxQaHLI1VZ8IN0vPhwlfgNO4i4vGXV0wB8BvSDj03g=="
"dev": true
} }
} }
}, },
@ -318,6 +333,32 @@
"which": "^1.3.1" "which": "^1.3.1"
} }
}, },
"@pushrocks/smartsystem": {
"version": "2.0.8",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartsystem/-/smartsystem-2.0.8.tgz",
"integrity": "sha512-lBruo1Ikai35oiDMy3Lx1NhddTMynCCd8MTx4dBU22a0HnNYZRKxTITuPAfIDD3VB94ZpEGbAvzblnZpuFFkMQ==",
"requires": {
"@pushrocks/lik": "^3.0.10",
"@pushrocks/smartenv": "^4.0.7",
"@pushrocks/smartpromise": "^3.0.2"
},
"dependencies": {
"@pushrocks/lik": {
"version": "3.0.11",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2flik/-/lik-3.0.11.tgz",
"integrity": "sha512-SDKRPj9+xBTqozlDPcA7O6BcccM1Tw/sXPVP+OnhNxCubDZ/L2kGNpPpqm43NJUoNxSSo5wdBw4N7MAFYCGdVg==",
"requires": {
"@pushrocks/smartdelay": "^2.0.3",
"@pushrocks/smartpromise": "^3.0.2",
"@pushrocks/smartrx": "^2.0.3",
"@pushrocks/smarttime": "^3.0.12",
"@types/minimatch": "^3.0.3",
"minimatch": "^3.0.4",
"symbol-tree": "^3.2.4"
}
}
}
},
"@pushrocks/smarttime": { "@pushrocks/smarttime": {
"version": "3.0.12", "version": "3.0.12",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmarttime/-/smarttime-3.0.12.tgz", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmarttime/-/smarttime-3.0.12.tgz",
@ -898,8 +939,7 @@
"is-promise": { "is-promise": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://verdaccio.lossless.one/is-promise/-/is-promise-2.1.0.tgz", "resolved": "https://verdaccio.lossless.one/is-promise/-/is-promise-2.1.0.tgz",
"integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o="
"dev": true
}, },
"is-stream": { "is-stream": {
"version": "1.1.0", "version": "1.1.0",

View File

@ -24,6 +24,7 @@
"@pushrocks/smartlog": "^2.0.19", "@pushrocks/smartlog": "^2.0.19",
"@pushrocks/smartlog-destination-local": "^8.0.2", "@pushrocks/smartlog-destination-local": "^8.0.2",
"@pushrocks/smartshell": "^2.0.25", "@pushrocks/smartshell": "^2.0.25",
"@pushrocks/smartsystem": "^2.0.8",
"@types/fs-extra": "^8.0.0", "@types/fs-extra": "^8.0.0",
"fs-extra": "^8.1.0" "fs-extra": "^8.1.0"
}, },

View File

@ -0,0 +1,60 @@
import * as plugins from './smartdaemon.plugins';
import { SmartDaemonTemplateManager } from './smartdaemon.classes.templatemanager';
const smartDaemonNamespace = 'smartdaemon_';
interface SmartDaemonConstructorOptions {
name: string;
scriptPath: string;
workingDir: string;
}
export class SmartDaemon {
private templateManager: SmartDaemonTemplateManager;
private smartshellInstance: plugins.smartshell.Smartshell;
private smartsystem: plugins.smartsystem.Smartsystem;
private shouldExecute: boolean = false;
constructor() {
this.templateManager = new SmartDaemonTemplateManager;
this.smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash'
});
this.smartsystem = new plugins.smartsystem.Smartsystem();
}
public async checkElegibility () {
if (await this.smartsystem.env.isLinuxAsync()) {
this.shouldExecute = true;
} else {
console.log('Smartdaemon can only be used on Linuc systems! Refusing to set up a service.');
this.shouldExecute = false;
}
return this.shouldExecute;
}
/**
* enables the service
*/
public async enable() {
};
/**
* disables the service
*/
public async disable() {
};
/**
* pauses the service
*/
public pause() {};
private async execute(commandArg: string) {
this.smartshellInstance.exec(commandArg);
}
}

View File

@ -0,0 +1,34 @@
import * as plugins from './smartdaemon.plugins';
export class SmartDaemonTemplateManager {
public generateServiceTemplate = (optionsArg: {
serviceName: string;
description: string;
serviceVersion: string;
pathNodeJs: string;
pathWorkkingDir;
pathJsFileToRun;
}) => {
return `
# servicVersion: ${optionsArg.serviceVersion}
[Unit]
Description=${optionsArg.description}
Requires=network.target
After=network.target
[Service]
Type=simple
ExecStart=${optionsArg.pathNodeJs} ${optionsArg.pathJsFileToRun}
WorkingDirectory=${optionsArg.pathWorkkingDir}
Restart=on-failure
LimitNOFILE=infinity
LimitCORE=infinity
StandardInput=null
StandardOutput=syslog
StandardError=syslog
Restart=always
[Install]
WantedBy=multi-user.target
`;
};
}

4
ts/smartdaemon.paths.ts Normal file
View File

@ -0,0 +1,4 @@
import * as plugins from './smartdaemon.plugins';
export const packageDir = plugins.path.join(__dirname, '../');
export const systemdDir = plugins.path.join('/lib/systemd/system/');

View File

@ -7,14 +7,16 @@ export {
}; };
// @pushrocks scope // @pushrocks scope
import * as smartshell from '@pushrocks/smartshell';
import * as smartlog from '@pushrocks/smartlog'; import * as smartlog from '@pushrocks/smartlog';
import * as smartlogDestinationLocal from '@pushrocks/smartlog-destination-local'; import * as smartlogDestinationLocal from '@pushrocks/smartlog-destination-local';
import * as smartshell from '@pushrocks/smartshell';
import * as smartsystem from '@pushrocks/smartsystem';
export { export {
smartshell,
smartlog, smartlog,
smartlogDestinationLocal smartlogDestinationLocal,
smartshell,
smartsystem
}; };
// third party // third party

View File

@ -1,174 +0,0 @@
export const settingsReference = {
name: {
cli: {
short: 'n',
long: 'service',
value: '[name]',
description: 'Service name'
},
mandatory: true
},
description: {
cli: {
short: 'd',
long: 'description',
value: '[description]',
description: 'Service description'
},
default: '{name} service'
},
author: {
cli: {
short: 'k',
long: 'author',
value: '[author]',
description: 'Service author'
}
},
user: {
cli: {
short: 'u',
long: 'user',
value: '[user]',
description: 'User to run service as'
}
},
group: {
cli: {
short: 'g',
long: 'group',
value: '[group]',
description: 'Group to run service as'
}
},
app: {
cli: {
short: 'A',
long: 'app',
value: '[app]',
description: 'Application main file'
},
default: 'main.js'
},
cwd: {
cli: {
short: 'c',
long: 'cwd',
value: '[path]',
description: 'Application cwd'
},
mandatory: true,
fs: true
},
'app.args': {
cli: {
short: 'p',
long: 'app.args',
value: '[args]',
description: 'Application arguments'
}
},
env: {
cli: {
short: 'e',
long: 'env',
value: '[NAME=VALUE]',
description: 'Environment variables to set in systemd job',
function: (v, vars) => {
const _v = v.split('=')
vars[_v[0]] = _v[1]; return vars
},
store: []
}
},
pid: {
cli: {
short: 'P',
long: 'pid',
value: '[file]',
description: 'Service pid file'
},
default: '/var/run/{name}.pid'
},
log: {
cli: {
short: 'L',
long: 'log',
value: '[log]',
description: 'Service log file'
},
default: '/var/log/{name}/log'
},
error: {
cli: {
short: 'E',
long: 'error',
value: '[error]',
description: 'Service error file'
},
default: '/var/log/{name}/error'
},
engine: {
cli: {
short: 'X',
long: 'engine',
value: '[node|forever|pm2]',
description: 'Service engine (node|forever|pm2)'
},
default: 'node',
mandatory: true
},
'engine.bin': {
cli: {
short: 'b',
long: 'engine.bin',
value: '[bin]',
description: 'Service engine bin'
},
default: '/usr/bin/{engine}',
fs: true
},
'engine.args': {
cli: {
short: 't',
long: 'engine.args',
value: '[eargs]',
description: 'Service engine args'
}
},
logrotate: {
cli: {
short: 'l',
long: 'logrotate',
description: 'Add logrotate config'
}
// default false
},
'logrotate.rotate': {
cli: {
short: 'R',
long: 'logrotate.rotate',
value: '[rotate]',
description: 'Logrotate rotations'
},
default: 10
},
'logrotate.frequency': {
cli: {
short: 'F',
long: 'logrotate.frequency',
value: '[frequency]',
description: 'Logrotate frequency'
},
default: 'daily'
},
noroot: {
cli: {
short: 'w',
long: 'noroot',
description: 'Skip check for root permission'
}
}
};

View File

@ -1,259 +0,0 @@
import * as plugins from './smartdaemon.plugins';
import { logger } from './smartdamon.logging';
import { settingsReference } from './smartdaemon.settings';
import { templateReference } from './smartdaemon.templates';
const smartshellInstance = new plugins.smartshell.Smartshell({
executor: 'bash'
});
/**
* print success message
* @method setup.success
* @param {string} message
*/
export const setupSuccess = (message: string) => {
logger.log('success', `service-systemd: ${message}`);
};
/**
* print error message
* @method setup.fail
* @param {string} message
*/
export const setupFail = (message) => {
logger.log('error', `service-systemd ${message}`);
};
/**
* install the service
* can also be used to update the service
* @method setup.add
* @param {object} settings
* @return {string}
*/
export const setupAdd = async (settings) => {
setupCheckSettings(settings);
const contents = setupParse(settings);
await setupCheckPaths(settings);
await setupAddLog(settings);
await setupAddScripts(settings, contents);
await setupAddLogrotate(settings, contents);
return `service ${settings.name} installed`;
};
/**
* remove the service
* @method setup.remove
* @param {string} service service name
* @return {string}
*/
export const setupRemove = async (service) => {
if (!service) {
throw new Error('Missing argument: service name');
}
const cmd = `systemctl disable ${service}.service`;
logger.log('info', `service-systemd > ${cmd}`);
await smartshellInstance.exec(cmd);
let file = plugins.path.join('/etc/systemd/system', `${service}.service`);
logger.log('info', `service-systemd remove ${file}`);
await plugins.fs.unlink(file);
file = plugins.path.join('/usr/local/bin', `systemd-${service}-start`);
logger.log('info', `service-systemd remove ${file}`);
await plugins.fs.unlink(file);
file = plugins.path.join('/etc/logrotate.d', service);
logger.log('info', `service-systemd remove ${file}`);
await plugins.fs.unlink(file);
return `service ${service} uninstalled`;
};
/**
* check mandatories params and paths
* @method setup.checkSettings
* @param {object} settings
*/
export const setupCheckSettings = (settings) => {
if (!settings.name) {
settings.name = settings.service;
}
delete settings.service;
const paths = [];
for (const optionArg of Object.keys(settingsReference)) {
const option = settingsReference[optionArg];
if (option.mandatory && !settings[optionArg]) {
throw new Error(`Missing ${optionArg} in settings file or arguments`);
}
if (option.fs) {
settings[optionArg] = plugins.path.resolve(settings[optionArg]);
}
}
};
/**
* check mandatories params and paths
* @method setup.checkPaths
* @param {object} settings
*/
export const setupCheckPaths = async (settings) => {
const exists = [];
for (const optionArg of Object.keys(settingsReference)) {
const option = settingsReference[optionArg];
if (option.fs) {
exists.push((async () => {
const exists2 = await plugins.fs.pathExists(settings[optionArg]);
if (!exists2) {
throw Error(`path ${settings[optionArg]} (${optionArg}) does not exists`);
}
})());
}
}
await Promise.all(exists);
};
/**
* merge settings with templates
* create scripts contents
* @method setup.parse
* @param {object} settings
*/
export const setupParse = (settings) => {
if (settings.env) {
settings.envs = '';
for (const key of Object.keys(settings.env)) {
settings.envs += 'Environment=' + key + '=' + settings.env[key] + '\n';
}
}
settings.user = settings.user
? `User=${settings.user}`
: '';
settings.group = settings.group
? `Group=${settings.group}`
: '';
settings.date = (new Date()).toString();
for (const key in settings) {
if (typeof settings[key] === 'string') {
settings[key] = plugins.string.template(settings[key], settings, true);
}
}
const _service = plugins.string.template(templateReference.engines[settings.engine].service, settings, true);
const _start = plugins.string.template(templateReference.engines[settings.engine].start, settings, true);
const _stop = plugins.string.template(templateReference.engines[settings.engine].stop, settings, true);
const _logrotate = settings.logrotate ? plugins.string.template(templateReference.logrotate, settings, true) : '';
return {
service: _service,
start: _start,
stop: _stop,
logrotate: _logrotate
};
};
/**
* write scripts and run commands to install the service
* @method setup.addScripts
* @param {object} settings
* @param {object} contents scripts contents
* @param {string} contents.start
* @param {string} contents.stop
* @param {string} contents.service
*/
export const setupAddScripts = async (settings, contents) => {
const service = plugins.path.join('/etc/systemd/system', `${settings.name}.service`);
const tasks = [];
if (contents.start) {
tasks.push(() => {
const start = plugins.path.join('/usr/local/bin', `systemd-${settings.name}-start`);
logger.log('info', `service-systemd: write file ${start}`);
return plugins.fs.writeFile(start, contents.start, 'utf8');
});
}
if (contents.stop) {
tasks.push(() => {
const stop = plugins.path.join('/usr/local/bin', `systemd-${settings.name}-stop`);
logger.log('info', `service-systemd: write file ${stop}`);
return plugins.fs.writeFile(stop, contents.stop, {
encoding: 'utf8'
});
});
}
if (contents.start || contents.stop) {
tasks.push(() => {
const cmd = `chmod a+x /usr/local/bin/systemd-${settings.name}*`;
logger.log('info', `service-systemd > ${cmd}`);
return smartshellInstance.exec(cmd);
});
}
tasks.push(() => {
logger.log('info', `service-systemd write file ${service}`);
return plugins.fs.writeFile(service, contents.service, 'utf8');
});
tasks.push(() => {
const cmd = `systemctl enable ${service};systemctl daemon-reload`;
logger.log('info', `service-systemd > ${cmd}`);
return smartshellInstance.exec(cmd);
});
for (const task of tasks) {
await task();
}
};
/**
* ensure dirs for log files
* @method setup.addLog
* @param {object} settings
*/
export const setupAddLog = async (settings) => {
const tasks = [];
let dirLog;
let dirError;
if (settings.log) {
dirLog = plugins.path.dirname(settings.log);
logger.log('info', `service-systemd: ensure dir ${dirLog}`);
tasks.push(plugins.fs.ensureDir(dirLog));
}
if (settings.error) {
dirError = plugins.path.dirname(settings.error);
if (dirError !== dirLog) {
logger.log('info', `service-systemd ensure dir ${dirError}`);
tasks.push(plugins.fs.ensureDir(dirError));
}
}
await Promise.all(tasks);
};
/**
* write logrotate conf script
* @method setup.addLogrotate
* @param {object} settings
* @param {object} contents scripts contents
* @param {string} contents.start
* @param {string} contents.stop
* @param {string} contents.service
*/
export const setupAddLogrotate = async (settings, contents) => {
if (!settings.logrotate) {
return;
}
const file = plugins.path.join('/etc/logrotate.d/', settings.name);
logger.log('info', `service-systemd: write logrotate file ${file}`);
await plugins.fs.writeFile(file, contents.logrotate, 'utf8');
};

View File

@ -1,75 +0,0 @@
export const templateReference = {
engines: {
node: {
service:
'# Generated by service-systemd on {{date}}\n' +
'[Unit]\n' +
'Description={description}\n' +
'Requires=network.target\n' +
'After=network.target\n' +
'\n' +
'[Service]\n' +
'Type=simple\n' +
'ExecStart={engine.bin} {engine.args} {app} {app.args}\n' +
'WorkingDirectory={cwd}\n' +
'Restart=on-failure\n' +
'{envs}\n' +
'LimitNOFILE=infinity\n' +
'LimitCORE=infinity\n' +
'StandardInput=null\n' +
'StandardOutput=syslog\n' +
'StandardError=syslog\n' +
'Restart=always\n' +
'SyslogIdentifier={name}\n' +
'PIDFile={pid}\n' +
'{user}\n' +
'{group}\n' +
'[Install]\n' +
'WantedBy=multi-user.target\n'
},
forever: {
service:
'# Generated by service-systemd on {date}\n' +
'[Unit]\n' +
'Description={description}\n' +
'Requires=network.target\n' +
'After=network.target\n' +
'\n' +
'[Service]\n' +
'Type=forking\n' +
'WorkingDirectory={cwd}\n' +
'{envs}\n' +
'ExecStart=/usr/local/bin/systemd-{name}-start\n' +
'ExecStop=/usr/local/bin/systemd-{name}-stop\n' +
'PIDFile={pid}\n' +
'{user}\n' +
'{group}\n' +
'[Install]\n' +
'WantedBy=multi-user.target\n',
start:
'#!/bin/bash\n' +
'{engine.bin} start ' +
'--pidFile {pid} ' +
'--uid {name} ' +
'--sourceDir {cwd} ' +
'-l {log} ' +
'-e {error} ' +
'--append --minUptime 5000 --spinSleepTime 2000 ' +
'{engine.args} ' +
'{app} {app.args}\n' +
'exit 0',
stop: '#!/bin/bash\n' + '{engine.bin} stop {name}\n' + 'exit 0'
}
},
logrotate:
'{log}\n{error} {\n' +
' {logrotate.frequency}\n' +
' rotate {logrotate.rotate}\n' +
' create\n' +
' missingok\n' +
' notifempty\n' +
' compress\n' +
' sharedscripts\n' +
// ' postrotate\n{restart}\n' +
' endscript\n}'
};