From b8c13535e45acb906216f58fbbe89c5fa17f81f3 Mon Sep 17 00:00:00 2001 From: Igor Kulikov Date: Thu, 27 Sep 2018 12:15:48 +0300 Subject: [PATCH] Add Microservices module. Add ThingsBoard JS Executor Microservice. --- msa/js-executor/.gitignore | 31 ++ msa/js-executor/api/jsMessageConsumer.js | 25 ++ msa/js-executor/build.gradle | 120 +++++++ .../config/custom-environment-variables.yml | 25 ++ msa/js-executor/config/default.yml | 26 ++ msa/js-executor/config/logger.js | 59 ++++ msa/js-executor/config/tb-js-executor.conf | 19 ++ msa/js-executor/install.js | 42 +++ msa/js-executor/package.json | 35 ++ msa/js-executor/pom.xml | 310 ++++++++++++++++++ msa/js-executor/server.js | 83 +++++ msa/js-executor/src/main/assembly/windows.xml | 71 ++++ .../src/main/filters/unix.properties | 1 + .../src/main/filters/windows.properties | 2 + .../src/main/scripts/control/deb/postinst | 6 + .../src/main/scripts/control/deb/postrm | 3 + .../src/main/scripts/control/deb/preinst | 18 + .../src/main/scripts/control/deb/prerm | 5 + .../src/main/scripts/control/rpm/postinst | 9 + .../src/main/scripts/control/rpm/postrm | 6 + .../src/main/scripts/control/rpm/preinst | 6 + .../src/main/scripts/control/rpm/prerm | 6 + .../scripts/control/tb-js-executor.service | 11 + .../src/main/scripts/init/tb-js-executor | 233 +++++++++++++ .../src/main/scripts/windows/install.bat | 31 ++ .../src/main/scripts/windows/service.xml | 29 ++ .../src/main/scripts/windows/uninstall.bat | 25 ++ msa/pom.xml | 41 +++ pom.xml | 1 + 29 files changed, 1279 insertions(+) create mode 100644 msa/js-executor/.gitignore create mode 100644 msa/js-executor/api/jsMessageConsumer.js create mode 100644 msa/js-executor/build.gradle create mode 100644 msa/js-executor/config/custom-environment-variables.yml create mode 100644 msa/js-executor/config/default.yml create mode 100644 msa/js-executor/config/logger.js create mode 100644 msa/js-executor/config/tb-js-executor.conf create mode 100644 msa/js-executor/install.js create mode 100644 msa/js-executor/package.json create mode 100644 msa/js-executor/pom.xml create mode 100644 msa/js-executor/server.js create mode 100644 msa/js-executor/src/main/assembly/windows.xml create mode 100644 msa/js-executor/src/main/filters/unix.properties create mode 100644 msa/js-executor/src/main/filters/windows.properties create mode 100644 msa/js-executor/src/main/scripts/control/deb/postinst create mode 100644 msa/js-executor/src/main/scripts/control/deb/postrm create mode 100644 msa/js-executor/src/main/scripts/control/deb/preinst create mode 100644 msa/js-executor/src/main/scripts/control/deb/prerm create mode 100644 msa/js-executor/src/main/scripts/control/rpm/postinst create mode 100644 msa/js-executor/src/main/scripts/control/rpm/postrm create mode 100644 msa/js-executor/src/main/scripts/control/rpm/preinst create mode 100644 msa/js-executor/src/main/scripts/control/rpm/prerm create mode 100644 msa/js-executor/src/main/scripts/control/tb-js-executor.service create mode 100644 msa/js-executor/src/main/scripts/init/tb-js-executor create mode 100644 msa/js-executor/src/main/scripts/windows/install.bat create mode 100644 msa/js-executor/src/main/scripts/windows/service.xml create mode 100644 msa/js-executor/src/main/scripts/windows/uninstall.bat create mode 100644 msa/pom.xml diff --git a/msa/js-executor/.gitignore b/msa/js-executor/.gitignore new file mode 100644 index 0000000000..c5cfd3d322 --- /dev/null +++ b/msa/js-executor/.gitignore @@ -0,0 +1,31 @@ +*.toDelete +output/** +*.class +*~ +*.iml +*/.idea/** +.idea/** +.idea +*.log +*.log.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9] +*/.classpath +.classpath +*/.project +.project +.cache/** +target/ +logs/ +build/ +.settings/ +/bin +bin/ +**/dependency-reduced-pom.xml +pom.xml.versionsBackup +.DS_Store +**/.gradle +**/local.properties +**/build +**/target +**/.env +node_modules +package-lock.json diff --git a/msa/js-executor/api/jsMessageConsumer.js b/msa/js-executor/api/jsMessageConsumer.js new file mode 100644 index 0000000000..43d3c781e4 --- /dev/null +++ b/msa/js-executor/api/jsMessageConsumer.js @@ -0,0 +1,25 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +'use strict'; + +var logger = require('../config/logger')('JsMessageConsumer'); + +exports.onJsInvokeMessage = function(message, producer) { + + logger.info('Received message: %s', message); + +} diff --git a/msa/js-executor/build.gradle b/msa/js-executor/build.gradle new file mode 100644 index 0000000000..f29dbeca79 --- /dev/null +++ b/msa/js-executor/build.gradle @@ -0,0 +1,120 @@ +/** + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import org.apache.tools.ant.filters.ReplaceTokens + +buildscript { + ext { + osPackageVersion = "3.8.0" + } + repositories { + jcenter() + } + dependencies { + classpath("com.netflix.nebula:gradle-ospackage-plugin:${osPackageVersion}") + } +} + +apply plugin: "nebula.ospackage" + +buildDir = projectBuildDir +version = projectVersion +distsDirName = "./" + +// OS Package plugin configuration +ospackage { + packageName = pkgName + version = "${project.version}" + release = 1 + os = LINUX + type = BINARY + + into pkgInstallFolder + + user pkgUser + permissionGroup pkgUser + + // Copy the executable file + from("target/package/linux/bin/${pkgName}") { + fileMode 0500 + into "bin" + } + + // Copy the init file + from("target/package/linux/init/${pkgName}") { + fileMode 0500 + into "init" + } + + // Copy the config files + from("target/package/linux/conf") { + fileType CONFIG | NOREPLACE + fileMode 0754 + into "conf" + } + +} + +// Configure our RPM build task +buildRpm { + + arch = X86_64 + + version = projectVersion.replace('-', '') + archiveName = "${pkgName}.rpm" + + preInstall file("${buildDir}/control/rpm/preinst") + postInstall file("${buildDir}/control/rpm/postinst") + preUninstall file("${buildDir}/control/rpm/prerm") + postUninstall file("${buildDir}/control/rpm/postrm") + + user pkgUser + permissionGroup pkgUser + + // Copy the system unit files + from("${buildDir}/control/${pkgName}.service") { + addParentDirs = false + fileMode 0644 + into "/usr/lib/systemd/system" + } + + directory(pkgLogFolder, 0755) + link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf") +} + +// Same as the buildRpm task +buildDeb { + + arch = "amd64" + + archiveName = "${pkgName}.deb" + + configurationFile("${pkgInstallFolder}/conf/${pkgName}.conf") + configurationFile("${pkgInstallFolder}/conf/custom-environment-variables.yml") + configurationFile("${pkgInstallFolder}/conf/default.yml") + configurationFile("${pkgInstallFolder}/conf/logger.js") + + preInstall file("${buildDir}/control/deb/preinst") + postInstall file("${buildDir}/control/deb/postinst") + preUninstall file("${buildDir}/control/deb/prerm") + postUninstall file("${buildDir}/control/deb/postrm") + + user pkgUser + permissionGroup pkgUser + + directory(pkgLogFolder, 0755) + link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/init/${pkgName}") + link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf") +} diff --git a/msa/js-executor/config/custom-environment-variables.yml b/msa/js-executor/config/custom-environment-variables.yml new file mode 100644 index 0000000000..d63a50461b --- /dev/null +++ b/msa/js-executor/config/custom-environment-variables.yml @@ -0,0 +1,25 @@ +# +# Copyright © 2016-2018 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +kafka: + request_topic: "REMOTE_JS_EVAL_REQUEST_TOPIC" + bootstrap: + # Kafka Bootstrap Servers + servers: "TB_KAFKA_SERVERS" +logger: + level: "LOGGER_LEVEL" + path: "LOG_FOLDER" + filename: "LOGGER_FILENAME" diff --git a/msa/js-executor/config/default.yml b/msa/js-executor/config/default.yml new file mode 100644 index 0000000000..5933780bdf --- /dev/null +++ b/msa/js-executor/config/default.yml @@ -0,0 +1,26 @@ +# +# Copyright © 2016-2018 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +kafka: + request_topic: "js.eval.requests" + bootstrap: + # Kafka Bootstrap Servers + servers: "localhost:9092" + +logger: + level: "info" + path: "logs" + filename: "tb-js-executor-%DATE%.log" diff --git a/msa/js-executor/config/logger.js b/msa/js-executor/config/logger.js new file mode 100644 index 0000000000..86df82777d --- /dev/null +++ b/msa/js-executor/config/logger.js @@ -0,0 +1,59 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var config = require('config'), + path = require('path'), + DailyRotateFile = require('winston-daily-rotate-file'); + +const { createLogger, format, transports } = require('winston'); +const { combine, timestamp, label, printf, splat } = format; + +var loggerTransports = []; + +if (process.env.NODE_ENV !== 'production') { + loggerTransports.push(new transports.Console({ + handleExceptions: true + })); +} else { + var filename = path.join(config.get('logger.path'), config.get('logger.filename')); + var transport = new (DailyRotateFile)({ + filename: filename, + datePattern: 'YYYY-MM-DD-HH', + zippedArchive: true, + maxSize: '20m', + maxFiles: '14d', + handleExceptions: true + }); + loggerTransports.push(transport); +} + +const tbFormat = printf(info => { + return `${info.timestamp} [${info.label}] ${info.level.toUpperCase()}: ${info.message}`; +}); + +function _logger(moduleLabel) { + return createLogger({ + level: config.get('logger.level'), + format:combine( + splat(), + label({ label: moduleLabel }), + timestamp({format: 'YYYY-MM-DD HH:mm:ss,SSS'}), + tbFormat + ), + transports: loggerTransports + }); +} + +module.exports = _logger; \ No newline at end of file diff --git a/msa/js-executor/config/tb-js-executor.conf b/msa/js-executor/config/tb-js-executor.conf new file mode 100644 index 0000000000..71f829d270 --- /dev/null +++ b/msa/js-executor/config/tb-js-executor.conf @@ -0,0 +1,19 @@ +# +# Copyright © 2016-2018 The Thingsboard Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +export NODE_CONFIG_DIR=${pkg.installFolder}/conf +export LOG_FOLDER=${pkg.logFolder} +export NODE_ENV=production diff --git a/msa/js-executor/install.js b/msa/js-executor/install.js new file mode 100644 index 0000000000..a6bf8d8ff7 --- /dev/null +++ b/msa/js-executor/install.js @@ -0,0 +1,42 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +const fs = require('fs'); +const fse = require('fs-extra'); +const path = require('path'); + +let _projectRoot = null; + + +(async() => { + await fse.move(path.join(projectRoot(), 'target', 'thingsboard-js-executor-linux'), + path.join(targetPackageDir('linux'), 'bin', 'tb-js-executor'), + {overwrite: true}); + await fse.move(path.join(projectRoot(), 'target', 'thingsboard-js-executor-win.exe'), + path.join(targetPackageDir('windows'), 'bin', 'tb-js-executor.exe'), + {overwrite: true}); +})(); + + +function projectRoot() { + if (!_projectRoot) { + _projectRoot = __dirname; + } + return _projectRoot; +} + +function targetPackageDir(platform) { + return path.join(projectRoot(), 'target', 'package', platform); +} diff --git a/msa/js-executor/package.json b/msa/js-executor/package.json new file mode 100644 index 0000000000..870986a24f --- /dev/null +++ b/msa/js-executor/package.json @@ -0,0 +1,35 @@ +{ + "name": "thingsboard-js-executor", + "private": true, + "version": "2.2.0", + "description": "ThingsBoard JavaScript Executor Microservice", + "main": "server.js", + "bin": "server.js", + "scripts": { + "install": "pkg -t node8-linux-x64,node8-win-x64 --out-path ./target . && node install.js", + "test": "echo \"Error: no test specified\" && exit 1", + "start": "nodemon server.js", + "start-prod": "NODE_ENV=production nodemon server.js" + }, + "dependencies": { + "config": "^1.30.0", + "js-yaml": "^3.12.0", + "kafka-node": "^3.0.1", + "winston": "^3.0.0", + "winston-daily-rotate-file": "^3.2.1" + }, + "engine": "node >= 5.9.0", + "nyc": { + "exclude": [ + "test", + "__tests__", + "node_modules", + "target" + ] + }, + "devDependencies": { + "fs-extra": "^6.0.1", + "nodemon": "^1.17.5", + "pkg": "^4.3.3" + } +} diff --git a/msa/js-executor/pom.xml b/msa/js-executor/pom.xml new file mode 100644 index 0000000000..2dd03a4a8b --- /dev/null +++ b/msa/js-executor/pom.xml @@ -0,0 +1,310 @@ + + + 4.0.0 + + org.thingsboard + 2.2.0-SNAPSHOT + msa + + org.thingsboard.msa + js-executor + pom + + ThingsBoard JavaScript Executor Microservice + https://thingsboard.io + Service executing JavaScript functions in sandboxed environment + + + UTF-8 + ${basedir}/../.. + tb-js-executor + thingsboard + /var/log/${pkg.name} + /usr/share/${pkg.name} + ${project.build.directory}/package/linux + ${project.build.directory}/package/windows + + + + + com.sun.winsw + winsw + bin + exe + provided + + + + + + + com.github.eirslett + frontend-maven-plugin + 1.0 + + target + ${basedir} + + + + install node and npm + + install-node-and-npm + + + v8.11.3 + 5.6.0 + + + + npm install + + npm + + + install + + + + + + org.apache.maven.plugins + maven-dependency-plugin + + + copy-winsw-service + package + + copy + + + + + com.sun.winsw + winsw + bin + exe + service.exe + + + ${pkg.win.dist} + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + copy-linux-conf + process-resources + + copy-resources + + + ${pkg.linux.dist}/conf + + + config + true + + + + src/main/filters/unix.properties + + + + + copy-linux-init + process-resources + + copy-resources + + + ${pkg.linux.dist}/init + + + src/main/scripts/init + true + + + + src/main/filters/unix.properties + + + + + copy-win-conf + process-resources + + copy-resources + + + ${pkg.win.dist}/conf + + + config + + tb-js-executor.conf + + true + + + + src/main/filters/windows.properties + + + + + copy-control + process-resources + + copy-resources + + + ${project.build.directory}/control + + + src/main/scripts/control + true + + + + src/main/filters/unix.properties + + + + + copy-windows-control + process-resources + + copy-resources + + + ${pkg.win.dist} + + + src/main/scripts/windows + true + + + + src/main/filters/windows.properties + + + + + + + org.fortasoft + gradle-maven-plugin + + + build + buildDeb + buildRpm + + + -PprojectBuildDir=${project.build.directory} + -PprojectVersion=${project.version} + -PpkgName=${pkg.name} + -PpkgUser=${pkg.user} + -PpkgInstallFolder=${pkg.installFolder} + -PpkgLogFolder=${pkg.unixLogFolder} + + + + + package + + invoke + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.0.0 + + ${pkg.name} + + src/main/assembly/windows.xml + + + + + assembly + package + + single + + + + + + + + + npm-start + + + npm-start + + + + + + com.github.eirslett + frontend-maven-plugin + 1.0 + + target + ${basedir} + + + + npm start + + npm + + + + start + + + + + + + + + + + jenkins + Jenkins Repository + http://repo.jenkins-ci.org/releases + + false + + + + diff --git a/msa/js-executor/server.js b/msa/js-executor/server.js new file mode 100644 index 0000000000..2a3b24f30e --- /dev/null +++ b/msa/js-executor/server.js @@ -0,0 +1,83 @@ +/* + * Copyright © 2016-2018 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +var config = require('config'), + kafka = require('kafka-node'), + Consumer = kafka.Consumer, + Producer = kafka.Producer, + JsMessageConsumer = require('./api/jsMessageConsumer'); + +var logger = require('./config/logger')('main'); + +var kafkaBootstrapServers = config.get('kafka.bootstrap.servers'); +var kafkaRequestTopic = config.get('kafka.request_topic'); + +logger.info('Kafka Bootstrap Servers: %s', kafkaBootstrapServers); +logger.info('Kafka Requests Topic: %s', kafkaRequestTopic); + +var kafkaClient; + +(async() => { + try { + logger.info('Starting ThingsBoard JavaScript Executor Microservice...'); + + kafkaClient = new kafka.KafkaClient({kafkaHost: kafkaBootstrapServers}); + + var consumer = new Consumer( + kafkaClient, + [ + { topic: kafkaRequestTopic, partition: 0 } + ], + { + autoCommit: true + } + ); + + var producer = new Producer(kafkaClient); + producer.on('error', (err) => { + logger.error('Unexpected kafka producer error'); + logger.error(err); + }); + + producer.on('ready', () => { + consumer.on('message', (message) => { + JsMessageConsumer.onJsInvokeMessage(message, producer); + }); + }); + + logger.info('Started ThingsBoard JavaScript Executor Microservice.'); + } catch (e) { + logger.error('Failed to start ThingsBoard JavaScript Executor Microservice: %s', e.message); + logger.error(e); + exit(-1); + } +})(); + +process.on('exit', function () { + exit(0); +}); + +function exit(status) { + logger.info('Exiting with status: %d ...', status); + if (kafkaClient) { + logger.info('Stopping Kafka Client...'); + kafkaClient.close(() => { + logger.info('Kafka Client stopped.'); + process.exit(status); + }); + } else { + process.exit(status); + } +} \ No newline at end of file diff --git a/msa/js-executor/src/main/assembly/windows.xml b/msa/js-executor/src/main/assembly/windows.xml new file mode 100644 index 0000000000..7d137157b4 --- /dev/null +++ b/msa/js-executor/src/main/assembly/windows.xml @@ -0,0 +1,71 @@ + + + windows + + + zip + + + + + + ${pkg.win.dist} + logs + + */** + + + + ${pkg.win.dist}/conf + conf + windows + + + + + + ${pkg.win.dist}/bin/${pkg.name}.exe + bin + ${pkg.name}.exe + + + ${pkg.win.dist}/service.exe + + ${pkg.name}.exe + + + ${pkg.win.dist}/service.xml + + ${pkg.name}.xml + windows + + + ${pkg.win.dist}/install.bat + + windows + + + ${pkg.win.dist}/uninstall.bat + + windows + + + diff --git a/msa/js-executor/src/main/filters/unix.properties b/msa/js-executor/src/main/filters/unix.properties new file mode 100644 index 0000000000..8967278673 --- /dev/null +++ b/msa/js-executor/src/main/filters/unix.properties @@ -0,0 +1 @@ +pkg.logFolder=${pkg.unixLogFolder} \ No newline at end of file diff --git a/msa/js-executor/src/main/filters/windows.properties b/msa/js-executor/src/main/filters/windows.properties new file mode 100644 index 0000000000..a6e48d91ba --- /dev/null +++ b/msa/js-executor/src/main/filters/windows.properties @@ -0,0 +1,2 @@ +pkg.logFolder=${BASE}\\logs +pkg.winWrapperLogFolder=%BASE%\\logs diff --git a/msa/js-executor/src/main/scripts/control/deb/postinst b/msa/js-executor/src/main/scripts/control/deb/postinst new file mode 100644 index 0000000000..0767d3f2c7 --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/deb/postinst @@ -0,0 +1,6 @@ +#!/bin/sh + +chown -R ${pkg.user}: ${pkg.logFolder} +chown -R ${pkg.user}: ${pkg.installFolder} +update-rc.d ${pkg.name} defaults + diff --git a/msa/js-executor/src/main/scripts/control/deb/postrm b/msa/js-executor/src/main/scripts/control/deb/postrm new file mode 100644 index 0000000000..61865803c3 --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/deb/postrm @@ -0,0 +1,3 @@ +#!/bin/sh + +update-rc.d -f ${pkg.name} remove diff --git a/msa/js-executor/src/main/scripts/control/deb/preinst b/msa/js-executor/src/main/scripts/control/deb/preinst new file mode 100644 index 0000000000..d2ebea46d7 --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/deb/preinst @@ -0,0 +1,18 @@ +#!/bin/sh + +if ! getent group ${pkg.user} >/dev/null; then + addgroup --system ${pkg.user} +fi + +if ! getent passwd ${pkg.user} >/dev/null; then + adduser --quiet \ + --system \ + --ingroup ${pkg.user} \ + --quiet \ + --disabled-login \ + --disabled-password \ + --home ${pkg.installFolder} \ + --no-create-home \ + -gecos "Thingsboard application" \ + ${pkg.user} +fi diff --git a/msa/js-executor/src/main/scripts/control/deb/prerm b/msa/js-executor/src/main/scripts/control/deb/prerm new file mode 100644 index 0000000000..898d3efd5c --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/deb/prerm @@ -0,0 +1,5 @@ +#!/bin/sh + +if [ -e /var/run/${pkg.name}/${pkg.name}.pid ]; then + service ${pkg.name} stop +fi diff --git a/msa/js-executor/src/main/scripts/control/rpm/postinst b/msa/js-executor/src/main/scripts/control/rpm/postinst new file mode 100644 index 0000000000..d8021e2dd9 --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/rpm/postinst @@ -0,0 +1,9 @@ +#!/bin/sh + +chown -R ${pkg.user}: ${pkg.logFolder} +chown -R ${pkg.user}: ${pkg.installFolder} + +if [ $1 -eq 1 ] ; then + # Initial installation + systemctl --no-reload enable ${pkg.name}.service >/dev/null 2>&1 || : +fi diff --git a/msa/js-executor/src/main/scripts/control/rpm/postrm b/msa/js-executor/src/main/scripts/control/rpm/postrm new file mode 100644 index 0000000000..8e1f8a2048 --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/rpm/postrm @@ -0,0 +1,6 @@ +#!/bin/sh + +if [ $1 -ge 1 ] ; then + # Package upgrade, not uninstall + systemctl try-restart ${pkg.name}.service >/dev/null 2>&1 || : +fi diff --git a/msa/js-executor/src/main/scripts/control/rpm/preinst b/msa/js-executor/src/main/scripts/control/rpm/preinst new file mode 100644 index 0000000000..db6306e4ac --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/rpm/preinst @@ -0,0 +1,6 @@ +#!/bin/sh + +getent group ${pkg.user} >/dev/null || groupadd -r ${pkg.user} +getent passwd ${pkg.user} >/dev/null || \ +useradd -d ${pkg.installFolder} -g ${pkg.user} -M -r ${pkg.user} -s /sbin/nologin \ +-c "Thingsboard application" diff --git a/msa/js-executor/src/main/scripts/control/rpm/prerm b/msa/js-executor/src/main/scripts/control/rpm/prerm new file mode 100644 index 0000000000..accb487b8e --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/rpm/prerm @@ -0,0 +1,6 @@ +#!/bin/sh + +if [ $1 -eq 0 ] ; then + # Package removal, not upgrade + systemctl --no-reload disable --now ${pkg.name}.service > /dev/null 2>&1 || : +fi diff --git a/msa/js-executor/src/main/scripts/control/tb-js-executor.service b/msa/js-executor/src/main/scripts/control/tb-js-executor.service new file mode 100644 index 0000000000..f542dd0f92 --- /dev/null +++ b/msa/js-executor/src/main/scripts/control/tb-js-executor.service @@ -0,0 +1,11 @@ +[Unit] +Description=${pkg.name} +After=syslog.target + +[Service] +User=${pkg.user} +ExecStart=${pkg.installFolder}/init/${pkg.name} +SuccessExitStatus=143 + +[Install] +WantedBy=multi-user.target diff --git a/msa/js-executor/src/main/scripts/init/tb-js-executor b/msa/js-executor/src/main/scripts/init/tb-js-executor new file mode 100644 index 0000000000..6e8fa52cd3 --- /dev/null +++ b/msa/js-executor/src/main/scripts/init/tb-js-executor @@ -0,0 +1,233 @@ +#!/bin/bash +# + + +### BEGIN INIT INFO +# Provides: tb-js-executor +# Required-Start: $remote_fs $syslog $network +# Required-Stop: $remote_fs $syslog $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: ${project.name} +# Description: ${project.description} +# chkconfig: 2345 99 01 +### END INIT INFO + +[[ -n "$DEBUG" ]] && set -x + +# Initialize variables that cannot be provided by a .conf file +WORKING_DIR="$(pwd)" +# shellcheck disable=SC2153 + +mainfile=${pkg.installFolder}/bin/${pkg.name} +configfile=${pkg.name}.conf + +# Follow symlinks to find the real script and detect init.d script +cd "$(dirname "$0")" || exit 1 +[[ -z "$initfile" ]] && initfile=$(pwd)/$(basename "$0") +while [[ -L "$initfile" ]]; do + [[ "$initfile" =~ init\.d ]] && init_script=$(basename "$initfile") + initfile=$(readlink "$initfile") + cd "$(dirname "$initfile")" || exit 1 + initfile=$(pwd)/$(basename "$initfile") +done +initfolder="$( (cd "$(dirname "initfile")" && pwd -P) )" +cd "$WORKING_DIR" || exit 1 + +# Initialize CONF_FOLDER location +[[ -z "$CONF_FOLDER" ]] && CONF_FOLDER="${pkg.installFolder}/conf" + +# shellcheck source=/dev/null +[[ -r "${CONF_FOLDER}/${configfile}" ]] && source "${CONF_FOLDER}/${configfile}" + +# Initialize PID/LOG locations if they weren't provided by the config file +[[ -z "$PID_FOLDER" ]] && PID_FOLDER="/var/run" +[[ -z "$LOG_FOLDER" ]] && LOG_FOLDER="${pkg.unixLogFolder}" +! [[ "$PID_FOLDER" == /* ]] && PID_FOLDER="$(dirname "$mainfile")"/"$PID_FOLDER" +! [[ "$LOG_FOLDER" == /* ]] && LOG_FOLDER="$(dirname "$mainfile")"/"$LOG_FOLDER" +! [[ -x "$PID_FOLDER" ]] && PID_FOLDER="/tmp" +! [[ -x "$LOG_FOLDER" ]] && LOG_FOLDER="/tmp" + +# Set up defaults +[[ -z "$MODE" ]] && MODE="auto" # modes are "auto", "service" or "run" +[[ -z "$USE_START_STOP_DAEMON" ]] && USE_START_STOP_DAEMON="true" + +# Create an identity for log/pid files +if [[ -z "$identity" ]]; then + if [[ -n "$init_script" ]]; then + identity="${init_script}" + else + identity=$(basename "${initfile%.*}")_${initfolder//\//} + fi +fi + +# Initialize log file name if not provided by the config file +[[ -z "$LOG_FILENAME" ]] && LOG_FILENAME="${identity}.log" + +# ANSI Colors +echoRed() { echo $'\e[0;31m'"$1"$'\e[0m'; } +echoGreen() { echo $'\e[0;32m'"$1"$'\e[0m'; } +echoYellow() { echo $'\e[0;33m'"$1"$'\e[0m'; } + +# Utility functions +checkPermissions() { + touch "$pid_file" &> /dev/null || { echoRed "Operation not permitted (cannot access pid file)"; return 4; } + touch "$log_file" &> /dev/null || { echoRed "Operation not permitted (cannot access log file)"; return 4; } +} + +isRunning() { + ps -p "$1" &> /dev/null +} + +await_file() { + end=$(date +%s) + let "end+=10" + while [[ ! -s "$1" ]] + do + now=$(date +%s) + if [[ $now -ge $end ]]; then + break + fi + sleep 1 + done +} + +# Determine the script mode +action="run" +if [[ "$MODE" == "auto" && -n "$init_script" ]] || [[ "$MODE" == "service" ]]; then + action="$1" + shift +fi + +# Build the pid and log filenames +if [[ "$identity" == "$init_script" ]] || [[ "$identity" == "$APP_NAME" ]]; then + PID_FOLDER="$PID_FOLDER/${identity}" + pid_subfolder=$PID_FOLDER +fi +pid_file="$PID_FOLDER/${identity}.pid" +log_file="$LOG_FOLDER/$LOG_FILENAME" + +# Determine the user to run as if we are root +# shellcheck disable=SC2012 +[[ $(id -u) == "0" ]] && run_user=$(ls -ld "$mainfile" | awk '{print $3}') + +arguments=($RUN_ARGS "$@") + +# Action functions +start() { + if [[ -f "$pid_file" ]]; then + pid=$(cat "$pid_file") + isRunning "$pid" && { echoYellow "Already running [$pid]"; return 0; } + fi + do_start "$@" +} + +do_start() { + working_dir=$(dirname "$mainfile") + pushd "$working_dir" > /dev/null + mkdir -p "$PID_FOLDER" &> /dev/null + if [[ -n "$run_user" ]]; then + checkPermissions || return $? + if [[ -z "$pid_subfolder" ]]; then + chown "$run_user" "$pid_subfolder" + fi + chown "$run_user" "$pid_file" + chown "$run_user" "$log_file" + if [ $USE_START_STOP_DAEMON = true ] && type start-stop-daemon > /dev/null 2>&1; then + start-stop-daemon --start --quiet \ + --chuid "$run_user" \ + --name "$identity" \ + --make-pidfile --pidfile "$pid_file" \ + --background --no-close \ + --startas "$mainfile" \ + --chdir "$working_dir" \ + -- "${arguments[@]}" \ + >> "$log_file" 2>&1 + await_file "$pid_file" + else + su -s /bin/sh -c "$mainfile $(printf "\"%s\" " "${arguments[@]}") >> \"$log_file\" 2>&1 & echo \$!" "$run_user" > "$pid_file" + fi + pid=$(cat "$pid_file") + else + checkPermissions || return $? + "$mainfile" "${arguments[@]}" >> "$log_file" 2>&1 & + pid=$! + disown $pid + echo "$pid" > "$pid_file" + fi + [[ -z $pid ]] && { echoRed "Failed to start"; return 1; } + echoGreen "Started [$pid]" +} + +stop() { + working_dir=$(dirname "$mainfile") + pushd "$working_dir" > /dev/null + [[ -f $pid_file ]] || { echoYellow "Not running (pidfile not found)"; return 0; } + pid=$(cat "$pid_file") + isRunning "$pid" || { echoYellow "Not running (process ${pid}). Removing stale pid file."; rm -f "$pid_file"; return 0; } + do_stop "$pid" "$pid_file" +} + +do_stop() { + kill -2 "$1" &> /dev/null || { echoRed "Unable to kill process $1"; return 1; } + for i in $(seq 1 60); do + isRunning "$1" || { echoGreen "Stopped [$1]"; rm -f "$2"; return 0; } + [[ $i -eq 30 ]] && kill -9 "$1" &> /dev/null + sleep 1 + done + echoRed "Unable to kill process $1"; + return 1; +} + +restart() { + stop && start +} + +orce_reload() { + working_dir=$(dirname "$mainfile") + pushd "$working_dir" > /dev/null + [[ -f $pid_file ]] || { echoRed "Not running (pidfile not found)"; return 7; } + pid=$(cat "$pid_file") + rm -f "$pid_file" + isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 7; } + do_stop "$pid" "$pid_file" + do_start +} + +status() { + working_dir=$(dirname "$mainfile") + pushd "$working_dir" > /dev/null + [[ -f "$pid_file" ]] || { echoRed "Not running"; return 3; } + pid=$(cat "$pid_file") + isRunning "$pid" || { echoRed "Not running (process ${pid} not found)"; return 1; } + echoGreen "Running [$pid]" + return 0 +} + +run() { + pushd "$(dirname "$mainfile")" > /dev/null + "$mainfile" "${arguments[@]}" + result=$? + popd > /dev/null + return "$result" +} + +# Call the appropriate action function +case "$action" in +start) + start "$@"; exit $?;; +stop) + stop "$@"; exit $?;; +restart) + restart "$@"; exit $?;; +force-reload) + force_reload "$@"; exit $?;; +status) + status "$@"; exit $?;; +run) + run "$@"; exit $?;; +*) + echo "Usage: $0 {start|stop|restart|force-reload|status|run}"; exit 1; +esac + +exit 0 diff --git a/msa/js-executor/src/main/scripts/windows/install.bat b/msa/js-executor/src/main/scripts/windows/install.bat new file mode 100644 index 0000000000..4da55425bb --- /dev/null +++ b/msa/js-executor/src/main/scripts/windows/install.bat @@ -0,0 +1,31 @@ +@REM +@REM Copyright © 2016-2018 The Thingsboard Authors +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. +@REM + +@ECHO OFF + +setlocal ENABLEEXTENSIONS + +@ECHO Installing ${pkg.name} ... + +SET BASE=%~dp0 + +%BASE%${pkg.name}.exe install + +@ECHO ${pkg.name} installed successfully! + +GOTO END + +:END diff --git a/msa/js-executor/src/main/scripts/windows/service.xml b/msa/js-executor/src/main/scripts/windows/service.xml new file mode 100644 index 0000000000..bb91111586 --- /dev/null +++ b/msa/js-executor/src/main/scripts/windows/service.xml @@ -0,0 +1,29 @@ + + + ${pkg.name} + ${project.name} + ${project.description} + %BASE%\bin + ${pkg.winWrapperLogFolder} + rotate + + + + %BASE%\bin\${pkg.name}.exe + diff --git a/msa/js-executor/src/main/scripts/windows/uninstall.bat b/msa/js-executor/src/main/scripts/windows/uninstall.bat new file mode 100644 index 0000000000..7061d2a052 --- /dev/null +++ b/msa/js-executor/src/main/scripts/windows/uninstall.bat @@ -0,0 +1,25 @@ +@REM +@REM Copyright © 2016-2018 The Thingsboard Authors +@REM +@REM Licensed under the Apache License, Version 2.0 (the "License"); +@REM you may not use this file except in compliance with the License. +@REM You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, software +@REM distributed under the License is distributed on an "AS IS" BASIS, +@REM WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@REM See the License for the specific language governing permissions and +@REM limitations under the License. +@REM + +@ECHO OFF + +@ECHO Stopping ${pkg.name} ... +net stop ${pkg.name} + +@ECHO Uninstalling ${pkg.name} ... +%~dp0${pkg.name}.exe uninstall + +@ECHO DONE. \ No newline at end of file diff --git a/msa/pom.xml b/msa/pom.xml new file mode 100644 index 0000000000..85d45a1f76 --- /dev/null +++ b/msa/pom.xml @@ -0,0 +1,41 @@ + + + 4.0.0 + + org.thingsboard + 2.2.0-SNAPSHOT + thingsboard + + org.thingsboard + msa + pom + + ThingsBoard Microservices + https://thingsboard.io + + + ${basedir}/.. + + + + js-executor + + + diff --git a/pom.xml b/pom.xml index 57603eedd1..e824177ac7 100755 --- a/pom.xml +++ b/pom.xml @@ -93,6 +93,7 @@ ui tools application + msa