Skip to content
This repository has been archived by the owner on Oct 22, 2021. It is now read-only.

Create pidfile and "bpm pid" command #11

Merged
merged 4 commits into from
May 20, 2021
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Create pidfile and "bpm pid" command
Some drain scripts send signals directly to the process via the
PID from the pidfile (because bpm doesn't have a mechanism to
send e.g. a USR1 signal). This obviously only works when the drain
runs inside the same container as the process itself.

This commit maintains a pid file at the /var/vcap/sys/run/bpm/$JOB/$PORC.pid
location and also implements the `bpm pid` command to access it.

Signed-off-by: Jan Dubois <jan.dubois@suse.com>
  • Loading branch information
jandubois committed May 18, 2021
commit 56b96dbb1a0b35ccf42242a6d8fadf49138e0d2c
2 changes: 2 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package main

import (
log "github.com/sirupsen/logrus"
"os"

cmd "code.cloudfoundry.org/quarks-container-run/cmd/containerrun"
@@ -10,6 +11,7 @@ import (
func main() {
pkg.WriteBPMscript()
if err := cmd.NewDefaultContainerRunCmd().Execute(); err != nil {
log.Errorf("container-run failed: %v", err)
os.Exit(1)
}
}
41 changes: 25 additions & 16 deletions pkg/containerrun/bpm.go
Original file line number Diff line number Diff line change
@@ -13,15 +13,15 @@ func WriteBPMscript() error {
script := `#!/bin/bash

function usage {
echo "usage: $0 [start|stop|quit|term|running] JOBNAME [-p PROCESSNAME]"
echo "usage: $0 [start|stop|quit|term|pid|running] JOBNAME [-p PROCESSNAME]"
exit 1
}

if [ $# != 2 -a $# != 4 ]; then
usage
fi
if [ "$1" != "start" -a "$1" != "stop" -a "$1" != "running" -a \
"$1" != "quit" -a "$1" != "term" ]
"$1" != "pid" -a "$1" != "quit" -a "$1" != "term" ]
then
usage
fi
@@ -37,6 +37,17 @@ if [ $# == 4 ]; then
PROCESS="$4"
fi

if [ "$CMD" == "pid" ]; then
PIDFILE="/var/vcap/sys/run/bpm/${JOB}/${PROCESS}.pid"
if [ -f "${PIDFILE}" ]; then
cat "${PIDFILE}"; echo
exit 0
else
echo "Process is not running"
exit 1
fi
fi

CONTAINER_RUN="/var/vcap/data/${JOB}/${PROCESS}_containerrun"
if [ "$CMD" == "running" ]; then
# Print yes/no if stdout is a tty
@@ -47,20 +58,18 @@ if [ "$CMD" == "running" ]; then
test -t 1 && echo "no"
exit 1
fi
else
# "term" is the same as "stop", except we won't wait
ACTION="${CMD/term/stop}"
# Send "START", "STOP", or "QUIT" over UDP to the unix socket
# with a 1 seconds timeout to establish the connection.
echo "${ACTION^^}" | nc -w 1 -uU "${CONTAINER_RUN}.sock"
if [ "${CMD}" == "stop" ]; then
for i in $(seq 30); do
test ! -f "${CONTAINER_RUN}.running" && exit 0
sleep 1
done
echo Process did not stop within 30 seconds
exit 1
fi
fi

# "term" is the same as "stop", except we won't wait
ACTION="${CMD/term/stop}"
echo "${ACTION^^}" | nc -w 1 -uU "${CONTAINER_RUN}.sock"
if [ "${CMD}" == "stop" ]; then
for i in $(seq 30); do
test ! -f "${CONTAINER_RUN}.running" && exit 0
sleep 1
done
echo Process did not stop within 30 seconds
exit 1
fi
`
if _, err := os.Stat(fileName); !os.IsNotExist(err) {
34 changes: 28 additions & 6 deletions pkg/containerrun/containerrun.go
Original file line number Diff line number Diff line change
@@ -12,6 +12,8 @@ import (
"os"
"os/exec"
"os/signal"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
@@ -347,20 +349,33 @@ func startMainProcess(
processRegistry.Register(process)

sentinel := fmt.Sprintf("/var/vcap/data/%s/%s_containerrun.running", jobName, processName)
file, _ := os.Create(sentinel)
_ = file.Close()
err = ioutil.WriteFile(sentinel, nil, 0755)
if err != nil {
return err
}
pidfile := fmt.Sprintf("/var/vcap/sys/run/bpm/%s/%s.pid", jobName, processName)
err = os.MkdirAll(filepath.Dir(pidfile), 0755)
if err != nil {
return err
}
err = ioutil.WriteFile(pidfile, []byte(strconv.Itoa(process.Pid())), 0755)
if err != nil {
return err
}

go func() {
if err := process.Wait(); err != nil {
log.Debugf("Process has failed with error: %s\n", err)
processRegistry.Unregister(process)
os.Remove(sentinel)
_ = os.Remove(sentinel)
_ = os.Remove(pidfile)
errors <- &runErr{err}
return
}
log.Debugln("Process has ended normally")
processRegistry.Unregister(process)
os.Remove(sentinel)
_ = os.Remove(sentinel)
_ = os.Remove(pidfile)
done <- struct{}{}
}()

@@ -468,7 +483,7 @@ func (cr *ContainerRunner) run(
if err := cmd.Start(); err != nil {
return nil, fmt.Errorf("failed to run command: %v", err)
}
return NewContainerProcess(cmd.Process), nil
return NewContainerProcess(cmd.Process, cmd.Process.Pid), nil
}

// ConditionRunner satisfies the Runner interface. It represents a runner for a post-start
@@ -521,6 +536,7 @@ func (cr *ConditionRunner) RunContext(

// Process is the interface that wraps the Signal and Wait methods of a process.
type Process interface {
Pid() int
Signal(os.Signal) error
Wait() error
}
@@ -534,12 +550,14 @@ type OSProcess interface {
// ContainerProcess satisfies the Process interface.
type ContainerProcess struct {
process OSProcess
pid int
}

// NewContainerProcess constructs a new ContainerProcess.
func NewContainerProcess(process OSProcess) *ContainerProcess {
func NewContainerProcess(process OSProcess, pid int) *ContainerProcess {
return &ContainerProcess{
process: process,
pid: pid,
}
}

@@ -567,6 +585,10 @@ func (p *ContainerProcess) Wait() error {
return nil
}

func (p *ContainerProcess) Pid() int {
return p.pid
}

// Stdio represents the STDOUT and STDERR to be used by a process.
type Stdio struct {
Out io.Writer