22 Commits

Author SHA1 Message Date
119f20915e 1.0.18 2019-09-06 17:33:02 +02:00
ce1fa6640b fix(core): update 2019-09-06 17:33:01 +02:00
8957e03445 1.0.17 2019-09-05 16:19:18 +02:00
3df86bee10 fix(core): update 2019-09-05 16:19:18 +02:00
65326710ab 1.0.16 2019-09-05 16:14:05 +02:00
2e174c9f55 fix(core): update 2019-09-05 16:14:05 +02:00
9e42910456 1.0.15 2019-09-05 16:04:38 +02:00
ff85cee528 fix(core): update 2019-09-05 16:04:38 +02:00
2a9fff0185 1.0.14 2019-09-05 15:48:00 +02:00
67689d79bd fix(core): update 2019-09-05 15:48:00 +02:00
f8c851de97 1.0.13 2019-09-05 13:50:11 +02:00
09e9d8c190 fix(core): update 2019-09-05 13:50:11 +02:00
80f5df3317 1.0.12 2019-09-05 11:15:17 +02:00
6cf3ff6e83 fix(core): update 2019-09-05 11:15:17 +02:00
ceb30c7ac2 1.0.11 2019-09-04 16:51:58 +02:00
0df90eec5d fix(core): update 2019-09-04 16:51:58 +02:00
261031a49c 1.0.10 2019-09-03 22:09:31 +02:00
615cc6aa7c fix(core): update 2019-09-03 22:09:30 +02:00
c9aa7fed48 1.0.9 2019-09-03 19:58:08 +02:00
44d30fc4d6 fix(core): update 2019-09-03 19:58:08 +02:00
dd5e1a978d 1.0.8 2019-09-03 16:50:25 +02:00
692602b463 fix(core): update 2019-09-03 16:50:24 +02:00
8 changed files with 236 additions and 45 deletions

53
package-lock.json generated
View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartdaemon", "name": "@pushrocks/smartdaemon",
"version": "1.0.7", "version": "1.0.18",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@ -215,6 +215,14 @@
} }
} }
}, },
"@pushrocks/smartfm": {
"version": "2.0.4",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartfm/-/smartfm-2.0.4.tgz",
"integrity": "sha512-QYxmIGhRfnE57rTCjsDBilPTrO/3VmajDxQt5z14pxAm9CeZypyGc4N6+Ts3KT1VGbs68NzCsGOM5ZYJW0Wmfg==",
"requires": {
"gray-matter": "^4.0.2"
}
},
"@pushrocks/smartlog": { "@pushrocks/smartlog": {
"version": "2.0.19", "version": "2.0.19",
"resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartlog/-/smartlog-2.0.19.tgz", "resolved": "https://verdaccio.lossless.one/@pushrocks%2fsmartlog/-/smartlog-2.0.19.tgz",
@ -770,6 +778,14 @@
"strip-eof": "^1.0.0" "strip-eof": "^1.0.0"
} }
}, },
"extend-shallow": {
"version": "2.0.1",
"resolved": "https://verdaccio.lossless.one/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
"requires": {
"is-extendable": "^0.1.0"
}
},
"figures": { "figures": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://verdaccio.lossless.one/figures/-/figures-3.0.0.tgz", "resolved": "https://verdaccio.lossless.one/figures/-/figures-3.0.0.tgz",
@ -850,6 +866,17 @@
"resolved": "https://verdaccio.lossless.one/graceful-fs/-/graceful-fs-4.2.2.tgz", "resolved": "https://verdaccio.lossless.one/graceful-fs/-/graceful-fs-4.2.2.tgz",
"integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q==" "integrity": "sha512-IItsdsea19BoLC7ELy13q1iJFNmd7ofZH5+X/pJr90/nRoPEX0DJo1dHDbgtYWOhJhcCgMDTOw84RZ72q6lB+Q=="
}, },
"gray-matter": {
"version": "4.0.2",
"resolved": "https://verdaccio.lossless.one/gray-matter/-/gray-matter-4.0.2.tgz",
"integrity": "sha512-7hB/+LxrOjq/dd8APlK0r24uL/67w7SkYnfwhNFwg/VDIGWGmduTDYf3WNstLW2fbbmRwrDGCVSJ2isuf2+4Hw==",
"requires": {
"js-yaml": "^3.11.0",
"kind-of": "^6.0.2",
"section-matter": "^1.0.0",
"strip-bom-string": "^1.0.0"
}
},
"has-flag": { "has-flag": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://verdaccio.lossless.one/has-flag/-/has-flag-3.0.0.tgz", "resolved": "https://verdaccio.lossless.one/has-flag/-/has-flag-3.0.0.tgz",
@ -875,6 +902,11 @@
"integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==", "integrity": "sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==",
"dev": true "dev": true
}, },
"is-extendable": {
"version": "0.1.1",
"resolved": "https://verdaccio.lossless.one/is-extendable/-/is-extendable-0.1.1.tgz",
"integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik="
},
"is-fullwidth-code-point": { "is-fullwidth-code-point": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://verdaccio.lossless.one/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", "resolved": "https://verdaccio.lossless.one/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",
@ -930,6 +962,11 @@
"graceful-fs": "^4.1.6" "graceful-fs": "^4.1.6"
} }
}, },
"kind-of": {
"version": "6.0.2",
"resolved": "https://verdaccio.lossless.one/kind-of/-/kind-of-6.0.2.tgz",
"integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA=="
},
"lcid": { "lcid": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://verdaccio.lossless.one/lcid/-/lcid-2.0.0.tgz", "resolved": "https://verdaccio.lossless.one/lcid/-/lcid-2.0.0.tgz",
@ -1286,6 +1323,15 @@
"resolved": "https://verdaccio.lossless.one/safe-buffer/-/safe-buffer-5.1.2.tgz", "resolved": "https://verdaccio.lossless.one/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
}, },
"section-matter": {
"version": "1.0.0",
"resolved": "https://verdaccio.lossless.one/section-matter/-/section-matter-1.0.0.tgz",
"integrity": "sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==",
"requires": {
"extend-shallow": "^2.0.1",
"kind-of": "^6.0.0"
}
},
"semver": { "semver": {
"version": "5.7.0", "version": "5.7.0",
"resolved": "https://verdaccio.lossless.one/semver/-/semver-5.7.0.tgz", "resolved": "https://verdaccio.lossless.one/semver/-/semver-5.7.0.tgz",
@ -1405,6 +1451,11 @@
"strip-bom": "^2.0.0" "strip-bom": "^2.0.0"
} }
}, },
"strip-bom-string": {
"version": "1.0.0",
"resolved": "https://verdaccio.lossless.one/strip-bom-string/-/strip-bom-string-1.0.0.tgz",
"integrity": "sha1-5SEekiQ2n7uB1jOi8ABE3IztrZI="
},
"strip-eof": { "strip-eof": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://verdaccio.lossless.one/strip-eof/-/strip-eof-1.0.0.tgz", "resolved": "https://verdaccio.lossless.one/strip-eof/-/strip-eof-1.0.0.tgz",

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartdaemon", "name": "@pushrocks/smartdaemon",
"version": "1.0.7", "version": "1.0.18",
"private": false, "private": false,
"description": "start scripts as long running daemons and manage them", "description": "start scripts as long running daemons and manage them",
"main": "dist/index.js", "main": "dist/index.js",
@ -23,6 +23,7 @@
"dependencies": { "dependencies": {
"@pushrocks/lik": "^3.0.11", "@pushrocks/lik": "^3.0.11",
"@pushrocks/smartfile": "^7.0.4", "@pushrocks/smartfile": "^7.0.4",
"@pushrocks/smartfm": "^2.0.4",
"@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",

View File

@ -8,4 +8,14 @@ tap.test('should create an instance of smartdaemon', async () => {
expect(testSmartdaemon).to.be.instanceOf(smartdaemon.SmartDaemon); expect(testSmartdaemon).to.be.instanceOf(smartdaemon.SmartDaemon);
}); });
tap.test('should create a service', async () => {
testSmartdaemon.addService({
name: 'npmversion',
version: 'x.x.x',
command: 'npm -v',
description: 'displays the npm version',
workingDir: __dirname
});
});
tap.start(); tap.start();

View File

@ -2,29 +2,34 @@ import * as plugins from './smartdaemon.plugins';
import * as paths from './smartdaemon.paths'; import * as paths from './smartdaemon.paths';
import { SmartDaemon } from './smartdaemon.classes.smartdaemon'; import { SmartDaemon } from './smartdaemon.classes.smartdaemon';
export interface SmartDaemonServiceConstructorOptions { export interface ISmartDaemonServiceConstructorOptions {
name: string; name: string;
description: string;
command: string; command: string;
workingDir: string; workingDir: string;
version: string;
} }
/** /**
* represents a service that is being spawned by SmartDaemon * represents a service that is being spawned by SmartDaemon
*/ */
export class SmartDaemonService implements SmartDaemonServiceConstructorOptions { export class SmartDaemonService implements ISmartDaemonServiceConstructorOptions {
public static async createFromOptions(smartdaemonRef: SmartDaemon, optionsArg: SmartDaemonServiceConstructorOptions) { public static async createFromOptions(smartdaemonRef: SmartDaemon, optionsArg: ISmartDaemonServiceConstructorOptions) {
const service = new SmartDaemonService(smartdaemonRef); const service = new SmartDaemonService(smartdaemonRef);
for (const key of Object.keys(optionsArg)) { for (const key of Object.keys(optionsArg)) {
service[key] = optionsArg[key]; service[key] = optionsArg[key];
} }
return service;
} }
public options: SmartDaemonServiceConstructorOptions; public options: ISmartDaemonServiceConstructorOptions;
public alreadyExists = false;
public name: string; public name: string;
public version: string;
public command: string; public command: string;
public workingDir: string; public workingDir: string;
public description: string;
public smartdaemonRef: SmartDaemon; public smartdaemonRef: SmartDaemon;
@ -36,18 +41,44 @@ export class SmartDaemonService implements SmartDaemonServiceConstructorOptions
* enables the service * enables the service
*/ */
public async enable() { public async enable() {
this.smartdaemonRef await this.smartdaemonRef.systemdManager.enableService(this);
} }
/** /**
* disables the service * disables the service
*/ */
public async disable() { public async disable() {
await this.smartdaemonRef.systemdManager.disableService(this);
} }
/** /**
* pauses the service * starts a service
*/ */
public pause() {}; public async start() {
await this.smartdaemonRef.systemdManager.startService(this);
}
/**
* stops a service
*/
public async stop() {
await this.smartdaemonRef.systemdManager.stopService(this);
}
// Save and Delete
public async save() {
await this.smartdaemonRef.systemdManager.saveService(this);
}
/**
* deletes the service
*/
public async delete() {
await this.smartdaemonRef.systemdManager.deleteService(this);
}
public async reload() {
await this.smartdaemonRef.systemdManager.reload();
}
} }

View File

@ -1,36 +1,38 @@
import * as plugins from './smartdaemon.plugins'; import * as plugins from './smartdaemon.plugins';
import { SmartDaemonTemplateManager } from './smartdaemon.classes.templatemanager'; import { SmartDaemonTemplateManager } from './smartdaemon.classes.templatemanager';
import { SmartDaemonService } from './smartdaemon.classes.service'; import { SmartDaemonService, ISmartDaemonServiceConstructorOptions } from './smartdaemon.classes.service';
import { SmartDaemonSystemdManager } from './smartdaemon.classes.systemdmanager'; import { SmartDaemonSystemdManager } from './smartdaemon.classes.systemdmanager';
export class SmartDaemon { export class SmartDaemon {
public serviceMap: plugins.lik.Objectmap<SmartDaemonService>;
public templateManager: SmartDaemonTemplateManager; public templateManager: SmartDaemonTemplateManager;
public systemdManager: SmartDaemonSystemdManager; public systemdManager: SmartDaemonSystemdManager;
constructor() { constructor() {
this.serviceMap = new plugins.lik.Objectmap<SmartDaemonService>();
this.templateManager = new SmartDaemonTemplateManager(this); this.templateManager = new SmartDaemonTemplateManager(this);
this.systemdManager = new SmartDaemonSystemdManager(this); this.systemdManager = new SmartDaemonSystemdManager(this);
} }
public async addService(serviceNameArg: string, commandArg: string, workingDirectory?: string): Promise<SmartDaemonService> { /**
* adds a service
* @param nameArg
* @param commandArg
* @param workingDirectoryArg
*/
public async addService(optionsArg: ISmartDaemonServiceConstructorOptions): Promise<SmartDaemonService> {
let serviceToAdd: SmartDaemonService; let serviceToAdd: SmartDaemonService;
const existingService = this.serviceMap.find(serviceArg => { const existingServices = await this.systemdManager.getServices();
return serviceArg.name === serviceNameArg; const existingService = existingServices.find(serviceArg => {
return serviceArg.name === optionsArg.name;
}); });
if (!existingService) { if (!existingService) {
serviceToAdd = await SmartDaemonService.createFromOptions(this, optionsArg);
} else { } else {
serviceToAdd = existingService;
Object.assign(serviceToAdd, optionsArg);
} }
await serviceToAdd.save();
return serviceToAdd; return serviceToAdd;
};
public async init() {
await this.systemdManager.init();
} }
} }

View File

@ -1,9 +1,28 @@
import * as plugins from './smartdaemon.plugins'; import * as plugins from './smartdaemon.plugins';
import * as paths from './smartdaemon.paths'; import * as paths from './smartdaemon.paths';
import { SmartDaemon } from './smartdaemon.classes.smartdaemon'; import { SmartDaemon } from './smartdaemon.classes.smartdaemon';
import { ISmartDaemonServiceConstructorOptions, SmartDaemonService } from './smartdaemon.classes.service';
export class SmartDaemonSystemdManager { export class SmartDaemonSystemdManager {
private smartDaemonNamespace = 'smartdaemon_'; // STATIC
private static smartDaemonNamespace = 'smartdaemon';
public static createServiceNameFromServiceName (serviceNameArg: string) {
return `${SmartDaemonSystemdManager.smartDaemonNamespace}_${serviceNameArg}`;
}
public static createFileNameFromServiceName (serviceNameArg: string) {
return `${SmartDaemonSystemdManager.createServiceNameFromServiceName(serviceNameArg)}.service`;
};
public static createFilePathFromServiceName (serviceNameArg: string) {
return plugins.path.join(
paths.systemdDir,
SmartDaemonSystemdManager.createFileNameFromServiceName(serviceNameArg)
);
}
// INSTANCE
public smartdaemonRef: SmartDaemon; public smartdaemonRef: SmartDaemon;
public smartshellInstance: plugins.smartshell.Smartshell; public smartshellInstance: plugins.smartshell.Smartshell;
public smartsystem: plugins.smartsystem.Smartsystem; public smartsystem: plugins.smartsystem.Smartsystem;
@ -22,22 +41,98 @@ export class SmartDaemonSystemdManager {
if (await this.smartsystem.env.isLinuxAsync()) { if (await this.smartsystem.env.isLinuxAsync()) {
this.shouldExecute = true; this.shouldExecute = true;
} else { } else {
console.log('Smartdaemon can only be used on Linuc systems! Refusing to set up a service.'); console.log('Smartdaemon can only be used on Linux systems! Refusing to set up a service.');
this.shouldExecute = false; this.shouldExecute = false;
} }
return this.shouldExecute; return this.shouldExecute;
} }
public async execute(commandArg: string) { public async execute(commandArg: string) {
(await this.checkElegibility()) ? await this.smartshellInstance.exec(commandArg) : null; if (await this.checkElegibility()) {
await this.smartshellInstance.exec(commandArg);
}
} }
public async getServices () { /**
const availableServices = plugins.smartfile.fs.listAllItems(paths.systemdDir, new RegExp(`${this.smartDaemonNamespace}`)); * gets all services that are already present
*/
public async getServices() {
const existingServices: SmartDaemonService[] = [];
if (await this.checkElegibility()) {
const smartfmInstance = new plugins.smartfm.Smartfm({
fmType: 'yaml'
});
const availableServices = await plugins.smartfile.fs.fileTreeToObject(
paths.systemdDir,
'smartdaemon_*.service'
);
for (const serviceFile of availableServices) {
const data = smartfmInstance.parseFromComments('# ', serviceFile.contentBuffer.toString())
.data as ISmartDaemonServiceConstructorOptions;
const service = await SmartDaemonService.createFromOptions(this.smartdaemonRef, data);
service.alreadyExists = true;
existingServices.push(service);
}
}
return existingServices;
} }
public async init() { public async startService(serviceArg: SmartDaemonService) {
if (await this.checkElegibility()) {
await this.execute(
`systemctl start ${SmartDaemonSystemdManager.createServiceNameFromServiceName(serviceArg.name)}`
);
}
}
public async stopService(serviceArg: SmartDaemonService) {
if (await this.checkElegibility()) {
await this.execute(
`systemctl stop ${SmartDaemonSystemdManager.createServiceNameFromServiceName(serviceArg.name)}`
);
}
}
}; public async saveService(serviceArg: SmartDaemonService) {
if (await this.checkElegibility()) {
await plugins.smartfile.memory.toFs(
this.smartdaemonRef.templateManager.generateUnitFileForService(serviceArg),
SmartDaemonSystemdManager.createFilePathFromServiceName(serviceArg.name)
);
}
}
} public async deleteService(serviceArg: SmartDaemonService) {
if (await this.checkElegibility()) {
await plugins.smartfile.fs.remove(
SmartDaemonSystemdManager.createServiceNameFromServiceName(serviceArg.name)
);
}
}
public async enableService(serviceArg: SmartDaemonService) {
if (await this.checkElegibility()) {
await this.saveService(serviceArg);
if (serviceArg.alreadyExists) {
await this.execute(`systemctl daemon-reload`);
}
await this.execute(
`systemctl enable ${SmartDaemonSystemdManager.createServiceNameFromServiceName(serviceArg.name)}`
);
}
}
public async disableService(serviceArg: SmartDaemonService) {
if (await this.checkElegibility()) {
await this.execute(
`systemctl disable ${SmartDaemonSystemdManager.createServiceNameFromServiceName(serviceArg.name)}`
);
}
}
public async reload() {
if (await this.checkElegibility()) {
await this.execute(
`systemctl daemon-reload`
);
}
}
}

View File

@ -1,5 +1,6 @@
import * as plugins from './smartdaemon.plugins'; import * as plugins from './smartdaemon.plugins';
import { SmartDaemon } from './smartdaemon.classes.smartdaemon'; import { SmartDaemon } from './smartdaemon.classes.smartdaemon';
import { SmartDaemonService } from './smartdaemon.classes.service';
export class SmartDaemonTemplateManager { export class SmartDaemonTemplateManager {
public smartdaemonRef: SmartDaemon; public smartdaemonRef: SmartDaemon;
@ -8,25 +9,23 @@ export class SmartDaemonTemplateManager {
this.smartdaemonRef = smartdaemonRefArg; this.smartdaemonRef = smartdaemonRefArg;
} }
public generateServiceTemplate = (optionsArg: { public generateUnitFileForService = (serviceArg: SmartDaemonService) => {
serviceName: string; return `# ---
description: string; # name: ${serviceArg.name}
serviceVersion: string; # version: ${serviceArg.version}
command: string; # description: ${serviceArg.description}
pathWorkkingDir; # command: ${serviceArg.command}
pathJsFileToRun; # workingDir: ${serviceArg.workingDir}
}) => { # ---
return `
# servicVersion: ${optionsArg.serviceVersion}
[Unit] [Unit]
Description=${optionsArg.description} Description=${serviceArg.description}
Requires=network.target Requires=network.target
After=network.target After=network.target
[Service] [Service]
Type=simple Type=simple
ExecStart=/bin/bash -c "cd ${optionsArg.pathWorkkingDir} && ${optionsArg.command}" ExecStart=/bin/bash -c "cd ${serviceArg.workingDir} && ${serviceArg.command}"
WorkingDirectory=${optionsArg.pathWorkkingDir} WorkingDirectory=${serviceArg.workingDir}
Restart=on-failure Restart=on-failure
LimitNOFILE=infinity LimitNOFILE=infinity
LimitCORE=infinity LimitCORE=infinity

View File

@ -9,6 +9,7 @@ export {
// @pushrocks scope // @pushrocks scope
import * as lik from '@pushrocks/lik'; import * as lik from '@pushrocks/lik';
import * as smartfile from '@pushrocks/smartfile'; import * as smartfile from '@pushrocks/smartfile';
import * as smartfm from '@pushrocks/smartfm';
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 smartshell from '@pushrocks/smartshell';
@ -17,6 +18,7 @@ import * as smartsystem from '@pushrocks/smartsystem';
export { export {
lik, lik,
smartfile, smartfile,
smartfm,
smartlog, smartlog,
smartlogDestinationLocal, smartlogDestinationLocal,
smartshell, smartshell,