mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
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
80 lines
1.9 KiB
Go
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)
|
|
}()
|
|
}
|