I'm using PM2 in windows and would like to stop an app gracefully. In my js code, i have this:
const pmx = require("pmx"); //
pmx.action("sigterm",(reply) => {
stop(
() => reply({success:true }),
() => reply({success:false}))
})
and if I start my script like this:
pm2 start MyScript.js --name MyScript
then when I call this, the current running instance shuts down quite gracefully:
pm2 trigger MyScript sigterm
Of course, PM2 dutifully starts up a new instance which have to shut down quite ungracefully like so:
pm2 stop MyScript
[insert sound of un-closed resources crashing here.]
Is there a way to have PM2 sent a trigger when calling pm2 stop?
BTW, I do know about --no-autorestart but that's not what I want because I do want PM2 to restart the processes when it crashes. I just want a way to gracefully stop my app in windows.
I've settled on the cludgy:
// PM2 initiated shutdown
pmx.action("stop",(my_name,reply) => {
graceful_shutdown().then{ () => {
reply({success:true });
fs.execSync("pm2 stop " +my_name);
process.exit();
}).catch(() => reply({success:false}))
})
combined with
> pm2 trigger MyScript stop "MyScript"
but I'm eager to hear a better solution.
Related
I have clear cache in my nuxt3js application with npx nuxi clean, this command removes .nuxt ,
.output , node_modules/.vite and node_modules/.cache. So inorder to run the app again I need to run npm install and then start the serve with npm run dev.
But I receive the below message without ending. Error: Reloding server...
How do I stop this.
I would like to answer my own question after a day of research. I found out that by adding the below code in nuxt.config.ts solved the issue
export default defineNuxtConfig({
nitro: {
esbuild: {
options: {
target: 'esnext'
}
}
}
})
thereafter every thing works back to normally again.
What is happening:
user starts local react server via any variation of npm [run] start[:mod]
my prestart script runs and kills the local webserver if found
Once pkill node is run, that kills the npm start script as well so I want to run the starting command again.
My current solution is to do
history 1 | awk '/some-regex/
to get the name of the last command which I can run with
exec('bash -c 'sleep 1 ; pkill node && ${previousCommand}' &')
This is starting to get pretty hacky so I'm thinking there has to be a better way to do this.
My node script so far:
const execSync = require("child_process").execSync;
const exec = require("child_process").exec;
const netcat = execSync('netcat -z 127.0.0.1 3000; echo $?') == 1 ? true : false; // true when :3000 is available #jkr
if(netcat == false) {
exec(`bash -c 'sleep 1 ; pkill node' &`);
console.warn('\x1b[32m%s\x1b[0m', `\nKilling all local webservers, please run 'npm start' again.\n`);
}
There seems to be an npm package which does this: kill-port
const kill = require('kill-port')
kill(port, 'tcp').then(console.log).catch(console.log)
Source: https://www.npmjs.com/package/kill-port
I understand this might not answer the question of running last command but should solve OP's problem.
my e2e test task sends some http requests to the server. i want to start that server (Play framework based) on a separate jvm, then start the test which hits the server and let it finish, then stop the server.
i looked through many SO threads so far found these options:
use sbt-sequential
use sbt-revolver
use alias
but in my experiments setting fork doesn't work, i.e. it still blocks execution when server is started
fork := true
fork in run := true
fork in Test := true
fork in IntegrationTest := true
The startServer/stopServer examples in sbt docs are also blocking it seems
I also tried just starting the server in background from shell but server is quickly shut down, similar to this question
nohup sbt -Djline.terminal=jline.UnsupportedTerminal web/run < /dev/null > /tmp/sbt.log 2>&1 &
related questions:
scala sbt test run setup and cleanup command once on multi project
How do I start a server before running a test suite in SBT?
fork doesn't run task in parallel - it just makes sure that tests are run in a separate JVM which helps with things like shutdown webhooks or disconnecting from services that doesn't handle resource release properly (e.g. DB connection that never calls disconnect).
If you want to use the same sbt to start server AND run test against that instance (which sounds like easily breakable antipattern BTW) you can use somethings like:
reStart
it:test
reStop
However that would be tricky because reStart yields immediately so tests would start when the server setup started but not necessarily completed. Race condition, failing tests, or blocking all tests until server finishes starting.
This is why nobody does it. Much easier to handle solution is to:
start the server in test in some beforeAll method and make this method complete only after server is responding to queries
shutdown it in some afterAll method (or somehow handle both of these using something like cats.effect.Resource or similar)
depending on situation:
running tests sequentially to avoid starting two instances at the same time or
generating config for each test so that they could be run in parallel without clashing on ports allocations
Anything else is just a hack that is going to fail sooner rather than later.
answering my own question, what we ended up doing is
use "sbt stage" to create standalone server jar & run script for the Play web app (in web/target/universal/stage/bin/)
create run_integration_tests.sh shell script that starts the server, waits 30 sec and starts test
add runIntegrationTests task in build.sbt which calls run_integration_tests.sh, and add it to it:test
run_integration_tests.sh:
#! /bin/bash
CURDIR=$(pwd)
echo "Starting integration/e2e test runner"
date >runner.log
export JAVA_OPTS="-Dplay.server.http.port=9195 -Dconfig.file=$CURDIR/web/conf/application_test.conf -Xmx2G"
rm -f "$CURDIR/web/target/universal/stage/RUNNING_PID"
echo "Starting server"
nohup web/target/universal/stage/bin/myapp >>runner.log 2>&1 &
echo "Webserver PID is $pid"
echo "Waiting for server start"
sleep 30
echo "Running the tests"
sbt "service/test:run-main com.blah.myapp.E2ETest"
ERR="$?"
echo "Tests Done at $(date), killing server"
kill $pid
echo "Waiting for server exit"
wait $pid
echo "All done"
if [ $ERR -ne 0 ]; then
cat runner.log
exit "$ERR"
fi
build.sbt:
lazy val runIntegrationTests = taskKey[Unit]("Run integration tests")
runIntegrationTests := {
val s: TaskStreams = streams.value
s.log.info("Running integration tests...")
val shell: Seq[String] = Seq("bash", "-c")
val runTests: Seq[String] = shell :+ "./run_integration_tests.sh"
if ((runTests !) == 0) {
s.log.success("Integration tests successful!")
} else {
s.log.error("Integration tests failed!")
throw new IllegalStateException("Integration tests failed!")
}
}
lazy val root = project.in(file("."))
.aggregate(service, web, tools)
.configs(IntegrationTest)
.settings(Defaults.itSettings)
.settings(
publishLocal := {},
publish := {},
(test in IntegrationTest) := (runIntegrationTests dependsOn (test in IntegrationTest)).value
)
calling sbt in CI/jenkins:
sh 'sbt clean coverage test stage it:test'
I am trying to create a simple VSCode extension to run a set of commands when I open a folder. Basically these commands will set up our development environment. I have started off creating the boilerplace and ran through the example that VSCode provided but I am not clear how to run system commands. Appreciate any help or point me to some documentation about this topic.
Your extension environment has access to node.js libraries, so you can just use child_process or any helper libraries to execute commands:
const cp = require('child_process')
cp.exec('pwd', (err, stdout, stderr) => {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (err) {
console.log('error: ' + err);
}
});
One alternative could be to use the Terminal API which is the best option if you have the need for the process to be fully observable and controllable to/by the user.
Biggest downside: The Terminal API does not yet offer a way to introspect the processes that run inside of it.
If you actually want to run the process in the terminal, the only way to do this safely for now, would be to use a two-layer approach, where you start a wrapper process that in turn launches and observes the actual process (taken in via command line args).
Our self-made TerminalWrapper
We tried this ourselves.
In our first approach, the wrapper used a socket.io connection that allows for communication with and control by the extension.
In our second approach, we simplified and instead created the terminal using bash -c (non-interactive shell), and used a file watcher to get the results instead. Easier this way but after the process is done, the user won't be able to use the Terminal window (because its non-interactive). A lot less error-prone and does not require fulfilling socket.io dependency.
Implementation Details
In our extension, we use a TerminalWrapper which runs the command inside a wrapper process, and waits for a file to contain the results.
The wrapper process is here. It writes the result to a file.
Usage example here:
const cwd = '.';
const command = `node -e "console.log('hi!');"`;
const { code } = await TerminalWrapper.execInTerminal(cwd, command, {}).waitForResult();
if (code) {
const processExecMsg = `${cwd}$ ${command}`;
throw new Error(`Process failed with exit code ${code} (${processExecMsg})`);
}
Biggest downside with the second approach is that we now need bash to be present, however (i) we do have a dependency checker that will warn you if you don't and explain how to get it, and (ii) using a unified shell, makes running commands a lot easier, as we now have a pretty strong unified feature set, we know we can rely on, rather than only being able to use common command execution syntax, and (iii) we can even run *.sh files without having to worry.
Introducing: VSCode Terminal API
VSCode API Documentation: Terminal
Official Terminal API Samples
All of the following imagery and excerpts are just straight up copy-and-paste'd from their official sample repository:
Create a terminal and run a command in it
context.subscriptions.push(vscode.commands.registerCommand('terminalTest.createAndSend', () => {
const terminal = vscode.window.createTerminal(`Ext Terminal #${NEXT_TERM_ID++}`);
terminal.sendText("echo 'Sent text immediately after creating'");
}));
Terminal activation event
vscode.window.onDidChangeActiveTerminal(e => {
console.log(`Active terminal changed, name=${e ? e.name : 'undefined'}`);
});
TerminalQuickPickItem
function selectTerminal(): Thenable<vscode.Terminal | undefined> {
interface TerminalQuickPickItem extends vscode.QuickPickItem {
terminal: vscode.Terminal;
}
const terminals = <vscode.Terminal[]>(<any>vscode.window).terminals;
const items: TerminalQuickPickItem[] = terminals.map(t => {
return {
label: `name: ${t.name}`,
terminal: t
};
});
return vscode.window.showQuickPick(items).then(item => {
return item ? item.terminal : undefined;
});
}
...and a lot more!...
(<3 for the VSCode team and their hard work.)
What I did was to create a promise based utility function to run all shell command with child_process
import * as cp from "child_process";
const execShell = (cmd: string) =>
new Promise<string>((resolve, reject) => {
cp.exec(cmd, (err, out) => {
if (err) {
return reject(err);
}
return resolve(out);
});
});
To get current directory
const currentDir = await execShell('pwd');
To get current git branch name
const branchName = await execShell('git rev-parse --abbrev-ref HEAD');
I am trying to get logstash 2.3.3 websocket input working.
Logstash: https://download.elastic.co/logstash/logstash/logstash-2.3.3.tar.gz
Websocket Input Plugin for Logstash: https://www.elastic.co/guide/en/logstash/current/plugins-inputs-websocket.html
Websocket server: https://github.com/joewalnes/websocketd/releases/download/v0.2.11/websocketd-0.2.11-linux_amd64.zip
Websocket Client: Chrome Plugin "Simple Web Socket Client"
I am aware of a bug filed last year logstash 1.5.0 and the websocket input plugin. https://github.com/logstash-plugins/logstash-input-websocket/issues/3 I have also received those same error messages, although I can't reproduce them anymore. The following is my current procedure and result. I am hoping that bug has since been fixed and I just can't find the correct config.
First I installed the plugin and confirmed it is listed as installed.
/app/bin/logstash-plugin list | grep "websocket"
Next, I checked that logstash was working with the following config
input {
stdin { }
}
output {
file {
path => "/app/logstash-2.3.3/logstash-log.txt"
}
}
Logstash worked.
/app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
Hello World
The file logstash-log.txt contained:
{"message":"Hello World","#version":"1","#timestamp":"2016-07-05T20:04:14.850Z","host":"server-name.domain.com"}
Next I opened port 9300
I wrote a simple bash script to return some numbers
#!/bin/bash
case $1 in
-t|--to)
COUNTTO=$2
shift
;;
esac
shift
printf 'Count to %i\n' $COUNTTO
for COUNT in $(seq 1 $COUNTTO); do
echo $COUNT
sleep 0.1
done
I started up websocketd pointing to my bash script
/app/websocketd --port=9300 /app/count.sh --to 7
I opened Simple Web Socket Client in Chrome and connected
ws://server-name.domain.com:9300
Success! It returned the following.
Count to 7
1
2
3
4
5
6
7
At this point I know websocketd works and logstash works. Now is when the trouble starts.
Logstash websocket input configuration file
input {
websocket {
codec => "plain"
url => "ws://127.0.0.1:9300/"
}
}
output {
file {
path => "/app/logstash-2.3.3/logstash-log.txt"
}
}
Run configtest
/app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf --configtest
Receive "Configuration OK"
Start up websocketd
/app/websocketd --port=9300 /app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
Back in Simple Web Socket Client, I connect to ws://server-name.domain.com:9300. I see a message pop up that I started a session.
Tue, 05 Jul 2016 20:07:13 -0400 | ACCESS | session | url:'http://server-name.domain.com:9300/' id:'1467732248361139010' remote:'192.168.0.1' command:'/app/logstash-2.3.3/bin/logstash' origin:'chrome-extension://pfdhoblngbopfeibdeiidpjgfnlcodoo' | CONNECT
I try to send "hello world". Nothing apparent happens on the server. After about 15 seconds I see a disconnect message in my console window. logstash-log.txt is never created.
Any ideas for what to try? Thank you!
UPDATE 1:
I tried putting the following in a bash script called "launch_logstash.sh":
#!/bin/bash
exec /app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
Then I started websocketd like so:
/app/websocketd --port=9300 /app/logstash-2.3.3/bin/launch_logstash.sh
Same result; no success.
Upon reading the websocketd documentation more closely, it sends the data received on the socket to a program's stdin. I was trying to listen to a socket in my logstash config, but the data is actually going to that app's stdin. I changed my config to this:
input {
stdin { }
}
output {
file {
path => "/app/logstash-2.3.3/logstash-log.txt"
}
}
Then launched websocketd like this:
/app/websocketd --port=9300 /app/logstash-2.3.3/bin/logstash agent --config /app/logstash-2.3.3/logstash.conf
So in short, until logstash-websocket-input implements their server option, stdin{} and stdout{} are the input and output if using websocketd as the web server.