mirror of https://github.com/containers/podman.git
117 lines
3.1 KiB
Go
117 lines
3.1 KiB
Go
package netavark
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type netavarkError struct {
|
|
exitCode int
|
|
// Set the json key to "error" so we can directly unmarshal into this struct
|
|
Msg string `json:"error"`
|
|
err error
|
|
}
|
|
|
|
func (e *netavarkError) Error() string {
|
|
ec := ""
|
|
// only add the exit code the the error message if we have at least info log level
|
|
// the normal user does not need to care about the number
|
|
if e.exitCode > 0 && logrus.IsLevelEnabled(logrus.InfoLevel) {
|
|
ec = " (exit code " + strconv.Itoa(e.exitCode) + ")"
|
|
}
|
|
msg := "netavark" + ec
|
|
if len(msg) > 0 {
|
|
msg += ": " + e.Msg
|
|
}
|
|
if e.err != nil {
|
|
msg += ": " + e.err.Error()
|
|
}
|
|
return msg
|
|
}
|
|
|
|
func (e *netavarkError) Unwrap() error {
|
|
return e.err
|
|
}
|
|
|
|
func newNetavarkError(msg string, err error) error {
|
|
return &netavarkError{
|
|
Msg: msg,
|
|
err: err,
|
|
}
|
|
}
|
|
|
|
// execNetavark will execute netavark with the following arguments
|
|
// It takes the path to the binary, the list of args and an interface which is
|
|
// marshaled to json and send via stdin to netavark. The result interface is
|
|
// used to marshal the netavark output into it. This can be nil.
|
|
// All errors return by this function should be of the type netavarkError
|
|
// to provide a helpful error message.
|
|
func execNetavark(binary string, args []string, stdin, result interface{}) error {
|
|
stdinR, stdinW, err := os.Pipe()
|
|
if err != nil {
|
|
return newNetavarkError("failed to create stdin pipe", err)
|
|
}
|
|
defer stdinR.Close()
|
|
|
|
stdoutR, stdoutW, err := os.Pipe()
|
|
if err != nil {
|
|
return newNetavarkError("failed to create stdout pipe", err)
|
|
}
|
|
defer stdoutR.Close()
|
|
defer stdoutW.Close()
|
|
|
|
cmd := exec.Command(binary, args...)
|
|
// connect the pipes to stdin and stdout
|
|
cmd.Stdin = stdinR
|
|
cmd.Stdout = stdoutW
|
|
// connect stderr to the podman stderr for logging
|
|
cmd.Stderr = os.Stderr
|
|
// set the netavark log level to the same as the podman
|
|
cmd.Env = append(os.Environ(), "RUST_LOG="+logrus.GetLevel().String())
|
|
// if we run with debug log level lets also set RUST_BACKTRACE=1 so we can get the full stack trace in case of panics
|
|
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
|
cmd.Env = append(cmd.Env, "RUST_BACKTRACE=1")
|
|
}
|
|
|
|
err = cmd.Start()
|
|
if err != nil {
|
|
return newNetavarkError("failed to start process", err)
|
|
}
|
|
err = json.NewEncoder(stdinW).Encode(stdin)
|
|
stdinW.Close()
|
|
if err != nil {
|
|
return newNetavarkError("failed to encode stdin data", err)
|
|
}
|
|
|
|
dec := json.NewDecoder(stdoutR)
|
|
|
|
err = cmd.Wait()
|
|
stdoutW.Close()
|
|
if err != nil {
|
|
exitError := &exec.ExitError{}
|
|
if errors.As(err, &exitError) {
|
|
ne := &netavarkError{}
|
|
// lets disallow unknown fields to make sure we do not get some unexpected stuff
|
|
dec.DisallowUnknownFields()
|
|
// this will unmarshal the error message into the error struct
|
|
ne.err = dec.Decode(ne)
|
|
ne.exitCode = exitError.ExitCode()
|
|
return ne
|
|
}
|
|
return newNetavarkError("unexpected failure during execution", err)
|
|
}
|
|
|
|
if result != nil {
|
|
err = dec.Decode(result)
|
|
if err != nil {
|
|
return newNetavarkError("failed to decode result", err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|