32 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
382b694027 1.0.7 2019-09-03 15:24:50 +02:00
de831b086f fix(core): update 2019-09-03 15:24:49 +02:00
01c7d2e482 1.0.6 2019-09-03 15:21:30 +02:00
49ebf991a2 fix(core): update 2019-09-03 15:21:30 +02:00
513337355f 1.0.5 2019-09-03 11:29:14 +02:00
5e5a679f99 fix(core): update 2019-09-03 11:29:14 +02:00
9ef366eee9 1.0.4 2019-08-28 11:55:14 +02:00
10562afea1 fix(core): update 2019-08-28 11:55:14 +02:00
c4e3f628cd 1.0.3 2019-08-28 08:27:12 +02:00
40e6a7abe4 fix(core): update 2019-08-28 08:27:11 +02:00
12 changed files with 604 additions and 307 deletions

View File

@ -1,4 +1,5 @@
Copyright (c) 2019 Lossless GmbH (hello@lossless.com) Copyright (c) 2019 Lossless GmbH (hello@lossless.com)
Copyright (c) 2017-2019, braces lab
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal

524
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{ {
"name": "@pushrocks/smartdaemon", "name": "@pushrocks/smartdaemon",
"version": "1.0.2", "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",
@ -13,14 +13,22 @@
"format": "(gitzone format)" "format": "(gitzone format)"
}, },
"devDependencies": { "devDependencies": {
"@gitzone/tsbuild": "^2.0.22", "@gitzone/tsbuild": "^2.1.17",
"@gitzone/tstest": "^1.0.15", "@gitzone/tstest": "^1.0.15",
"@pushrocks/tapbundle": "^3.0.7", "@pushrocks/tapbundle": "^3.0.13",
"@types/node": "^10.11.7", "@types/node": "^10.11.7",
"tslint": "^5.11.0", "tslint": "^5.11.0",
"tslint-config-prettier": "^1.15.0" "tslint-config-prettier": "^1.15.0"
}, },
"dependencies": {}, "dependencies": {
"@pushrocks/lik": "^3.0.11",
"@pushrocks/smartfile": "^7.0.4",
"@pushrocks/smartfm": "^2.0.4",
"@pushrocks/smartlog": "^2.0.19",
"@pushrocks/smartlog-destination-local": "^8.0.2",
"@pushrocks/smartshell": "^2.0.25",
"@pushrocks/smartsystem": "^2.0.8"
},
"files": [ "files": [
"ts/*", "ts/*",
"ts_web/*", "ts_web/*",

View File

@ -1,8 +1,21 @@
import { expect, tap } from '@pushrocks/tapbundle'; import { expect, tap } from '@pushrocks/tapbundle';
import * as smartdaemon from '../ts/index'; import * as smartdaemon from '../ts/index';
tap.test('first test', async () => { let testSmartdaemon: smartdaemon.SmartDaemon;
console.log(smartdaemon.standardExport);
tap.test('should create an instance of smartdaemon', async () => {
testSmartdaemon = new 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

@ -1,3 +1 @@
import * as plugins from './smartdaemon.plugins'; export * from './smartdaemon.classes.smartdaemon';
export let standardExport = 'Hi there! :) This is an exported string';

View File

@ -0,0 +1,84 @@
import * as plugins from './smartdaemon.plugins';
import * as paths from './smartdaemon.paths';
import { SmartDaemon } from './smartdaemon.classes.smartdaemon';
export interface ISmartDaemonServiceConstructorOptions {
name: string;
description: string;
command: string;
workingDir: string;
version: string;
}
/**
* represents a service that is being spawned by SmartDaemon
*/
export class SmartDaemonService implements ISmartDaemonServiceConstructorOptions {
public static async createFromOptions(smartdaemonRef: SmartDaemon, optionsArg: ISmartDaemonServiceConstructorOptions) {
const service = new SmartDaemonService(smartdaemonRef);
for (const key of Object.keys(optionsArg)) {
service[key] = optionsArg[key];
}
return service;
}
public options: ISmartDaemonServiceConstructorOptions;
public alreadyExists = false;
public name: string;
public version: string;
public command: string;
public workingDir: string;
public description: string;
public smartdaemonRef: SmartDaemon;
constructor(smartdaemonRegfArg: SmartDaemon) {
this.smartdaemonRef = smartdaemonRegfArg;
}
/**
* enables the service
*/
public async enable() {
await this.smartdaemonRef.systemdManager.enableService(this);
}
/**
* disables the service
*/
public async disable() {
await this.smartdaemonRef.systemdManager.disableService(this);
}
/**
* starts a service
*/
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

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

View File

@ -0,0 +1,138 @@
import * as plugins from './smartdaemon.plugins';
import * as paths from './smartdaemon.paths';
import { SmartDaemon } from './smartdaemon.classes.smartdaemon';
import { ISmartDaemonServiceConstructorOptions, SmartDaemonService } from './smartdaemon.classes.service';
export class SmartDaemonSystemdManager {
// 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 smartshellInstance: plugins.smartshell.Smartshell;
public smartsystem: plugins.smartsystem.Smartsystem;
public shouldExecute: boolean = false;
constructor(smartdaemonRefArg: SmartDaemon) {
this.smartdaemonRef = smartdaemonRefArg;
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 Linux systems! Refusing to set up a service.');
this.shouldExecute = false;
}
return this.shouldExecute;
}
public async execute(commandArg: string) {
if (await this.checkElegibility()) {
await this.smartshellInstance.exec(commandArg);
}
}
/**
* 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 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

@ -0,0 +1,40 @@
import * as plugins from './smartdaemon.plugins';
import { SmartDaemon } from './smartdaemon.classes.smartdaemon';
import { SmartDaemonService } from './smartdaemon.classes.service';
export class SmartDaemonTemplateManager {
public smartdaemonRef: SmartDaemon;
constructor(smartdaemonRefArg: SmartDaemon) {
this.smartdaemonRef = smartdaemonRefArg;
}
public generateUnitFileForService = (serviceArg: SmartDaemonService) => {
return `# ---
# name: ${serviceArg.name}
# version: ${serviceArg.version}
# description: ${serviceArg.description}
# command: ${serviceArg.command}
# workingDir: ${serviceArg.workingDir}
# ---
[Unit]
Description=${serviceArg.description}
Requires=network.target
After=network.target
[Service]
Type=simple
ExecStart=/bin/bash -c "cd ${serviceArg.workingDir} && ${serviceArg.command}"
WorkingDirectory=${serviceArg.workingDir}
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

@ -1,2 +1,28 @@
const removeme = {}; // node native scope
export { removeme }; import * as path from 'path';
export {
path
};
// @pushrocks scope
import * as lik from '@pushrocks/lik';
import * as smartfile from '@pushrocks/smartfile';
import * as smartfm from '@pushrocks/smartfm';
import * as smartlog from '@pushrocks/smartlog';
import * as smartlogDestinationLocal from '@pushrocks/smartlog-destination-local';
import * as smartshell from '@pushrocks/smartshell';
import * as smartsystem from '@pushrocks/smartsystem';
export {
lik,
smartfile,
smartfm,
smartlog,
smartlogDestinationLocal,
smartshell,
smartsystem
};
// third party

15
ts/smartdamon.logging.ts Normal file
View File

@ -0,0 +1,15 @@
import * as plugins from './smartdaemon.plugins';
export const logger = new plugins.smartlog.Smartlog({
logContext: {
company: 'Some Company',
companyunit: 'Some CompanyUnit',
containerName: 'Some Containername',
environment: 'local',
runtime: 'node',
zone: 'gitzone'
},
minimumLogLevel: 'silly'
});
logger.addLogDestination(new plugins.smartlogDestinationLocal.DestinationLocal());