69 lines
1.8 KiB
Go
69 lines
1.8 KiB
Go
package launch
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"os/exec"
|
|
|
|
"github.com/BurntSushi/toml"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// ExecDRunner is responsible for running ExecD binaries.
|
|
type ExecDRunner struct {
|
|
Out, Err io.Writer // Out and Err can be used to configure Stdout and Stderr processes run by ExecDRunner.
|
|
}
|
|
|
|
// NewExecDRunner creates an ExecDRunner with Out and Err set to stdout and stderr
|
|
func NewExecDRunner() *ExecDRunner {
|
|
return &ExecDRunner{
|
|
Out: os.Stdout,
|
|
Err: os.Stderr,
|
|
}
|
|
}
|
|
|
|
// ExecD executes the executable file at path and sets the returned variables in env. The executable at path
|
|
// should implement the ExecD interface in the buildpack specification https://github.com/buildpacks/spec/blob/main/buildpack.md#execd
|
|
func (e *ExecDRunner) ExecD(path string, env Env) error {
|
|
pr, pw, err := os.Pipe()
|
|
if err != nil {
|
|
return errors.Wrap(err, "failed to create pipe")
|
|
}
|
|
errChan := make(chan error, 1)
|
|
go func() {
|
|
defer pw.Close()
|
|
cmd := exec.Command(path)
|
|
cmd.Stdout = e.Out
|
|
cmd.Stderr = e.Err
|
|
cmd.Env = env.List()
|
|
if err := setHandle(cmd, pw); err != nil {
|
|
errChan <- err
|
|
} else {
|
|
errChan <- cmd.Run()
|
|
}
|
|
}()
|
|
|
|
out, err := io.ReadAll(pr)
|
|
if cmdErr := <-errChan; cmdErr != nil {
|
|
// prefer the error from the command
|
|
return errors.Wrapf(cmdErr, "failed to execute exec.d file at path '%s'", path)
|
|
} else if err != nil {
|
|
// return the read error only if the command succeeded
|
|
return errors.Wrapf(err, "failed to read output from exec.d file at path '%s'", path)
|
|
}
|
|
|
|
envVars := map[string]string{}
|
|
if _, err := toml.Decode(string(out), &envVars); err != nil {
|
|
return errors.Wrapf(err, "failed to decode output from exec.d file at path '%s'", path)
|
|
}
|
|
for k, v := range envVars {
|
|
env.Set(k, v)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func setHandle(cmd *exec.Cmd, f *os.File) error {
|
|
cmd.ExtraFiles = []*os.File{f}
|
|
return nil
|
|
}
|