Files
nomad/helper/subproc/subproc.go
Seth Hoenig 77889a16fb exec2: more tweaks to driver harness (#20221)
Also add an explicit exit code to subproc package for when a child
process is instructed to run an unrunnable command (i.e. cannot be
found or is not executable) - with the 127 return code folks using bash
are familiar with
2024-03-26 08:02:41 -05:00

80 lines
1.9 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package subproc
import (
"bufio"
"context"
"fmt"
"io"
"os"
"strings"
"time"
)
const (
// ExitSuccess indicates the subprocess completed successfully.
ExitSuccess = 0
// ExitFailure indicates the subprocess terminated unsuccessfully.
ExitFailure = 1
// ExitTimeout indicates the subprocess timed out before completion.
ExitTimeout = 2
// ExitNotRunnable indicates a command cannot be run.
ExitNotRunnable = 127 // bash-ism
)
// MainFunc is the function that runs for this sub-process.
//
// The return value is a process exit code.
type MainFunc func() int
// Do f if nomad was launched as, "nomad [name]". This process will exit without
// running any other part of Nomad.
func Do(name string, f MainFunc) {
if len(os.Args) > 1 && os.Args[1] == name {
os.Exit(f())
}
}
// Print the given message to standard error.
func Print(format string, args ...any) {
_, _ = fmt.Fprintf(os.Stderr, format+"\n", args...)
}
// Log the given output to the logger.
//
// r should be a buffer containing output (typically combined stdin + stdout)
// f should be an HCLogger Print method (e.g. log.Debug)
func Log(r io.Reader, f func(msg string, args ...any)) string {
scanner := bufio.NewScanner(r)
lines := ""
for scanner.Scan() {
line := scanner.Text()
lines += line + "\n"
f("sub-process", "OUTPUT", line)
}
return strings.TrimSpace(lines)
}
// Context creates a context setup with the given timeout.
func Context(timeout time.Duration) (context.Context, context.CancelFunc) {
ctx := context.Background()
return context.WithTimeout(ctx, timeout)
}
// SetExpiration is used to ensure the process terminates, once ctx
// is complete. A short grace period is added to allow any cleanup
// to happen first.
func SetExpiration(ctx context.Context) {
const graceful = 5 * time.Second
go func() {
<-ctx.Done()
time.Sleep(graceful)
os.Exit(ExitTimeout)
}()
}