mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 18:35:44 +03:00
Moved packages around
This commit is contained in:
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/nomad/client/allocdir"
|
||||
"github.com/hashicorp/nomad/client/config"
|
||||
"github.com/hashicorp/nomad/client/driver/plugins"
|
||||
"github.com/hashicorp/nomad/client/driver/executor"
|
||||
cstructs "github.com/hashicorp/nomad/client/driver/structs"
|
||||
"github.com/hashicorp/nomad/client/getter"
|
||||
"github.com/hashicorp/nomad/helper/discover"
|
||||
@@ -36,7 +36,7 @@ type ExecDriverConfig struct {
|
||||
// execHandle is returned from Start/Open as a handle to the PID
|
||||
type execHandle struct {
|
||||
pluginClient *plugin.Client
|
||||
executor plugins.Executor
|
||||
executor executor.Executor
|
||||
userPid int
|
||||
killTimeout time.Duration
|
||||
logger *log.Logger
|
||||
@@ -107,11 +107,11 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
Cmd: exec.Command(bin, "executor"),
|
||||
}
|
||||
|
||||
executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
executorCtx := &plugins.ExecutorContext{
|
||||
executorCtx := &executor.ExecutorContext{
|
||||
TaskEnv: d.taskEnv,
|
||||
AllocDir: ctx.AllocDir,
|
||||
TaskName: task.Name,
|
||||
@@ -120,7 +120,7 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
FSIsolation: true,
|
||||
UnprivilegedUser: false,
|
||||
}
|
||||
ps, err := executor.LaunchCmd(&plugins.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx)
|
||||
ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx)
|
||||
if err != nil {
|
||||
pluginClient.Kill()
|
||||
return nil, fmt.Errorf("error starting process via the plugin: %v", err)
|
||||
@@ -131,7 +131,7 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
h := &execHandle{
|
||||
pluginClient: pluginClient,
|
||||
userPid: ps.Pid,
|
||||
executor: executor,
|
||||
executor: exec,
|
||||
killTimeout: d.DriverContext.KillTimeout(task),
|
||||
logger: d.logger,
|
||||
doneCh: make(chan struct{}),
|
||||
@@ -144,7 +144,7 @@ func (d *ExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
type execId struct {
|
||||
KillTimeout time.Duration
|
||||
UserPid int
|
||||
PluginConfig *plugins.ExecutorReattachConfig
|
||||
PluginConfig *ExecutorReattachConfig
|
||||
}
|
||||
|
||||
func (d *ExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
|
||||
@@ -183,7 +183,7 @@ func (d *ExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, erro
|
||||
func (h *execHandle) ID() string {
|
||||
id := execId{
|
||||
KillTimeout: h.killTimeout,
|
||||
PluginConfig: plugins.NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
PluginConfig: NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
UserPid: h.userPid,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package plugins
|
||||
package executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,6 +1,6 @@
|
||||
// +build !linux
|
||||
|
||||
package plugins
|
||||
package executor
|
||||
|
||||
func (e *UniversalExecutor) configureChroot() error {
|
||||
return nil
|
||||
@@ -1,4 +1,4 @@
|
||||
package plugins
|
||||
package executor
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -1,4 +1,4 @@
|
||||
package plugins
|
||||
package executor
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
@@ -1,4 +1,4 @@
|
||||
package plugins
|
||||
package driver
|
||||
|
||||
import (
|
||||
"log"
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/nomad/client/driver/executor"
|
||||
)
|
||||
|
||||
var HandshakeConfig = plugin.HandshakeConfig{
|
||||
@@ -45,39 +46,39 @@ type ExecutorRPC struct {
|
||||
}
|
||||
|
||||
type LaunchCmdArgs struct {
|
||||
Cmd *ExecCommand
|
||||
Ctx *ExecutorContext
|
||||
Cmd *executor.ExecCommand
|
||||
Ctx *executor.ExecutorContext
|
||||
}
|
||||
|
||||
func (e *ExecutorRPC) LaunchCmd(cmd *ExecCommand, ctx *ExecutorContext) (*ProcessState, error) {
|
||||
var ps *ProcessState
|
||||
func (e *ExecutorRPC) LaunchCmd(cmd *executor.ExecCommand, ctx *executor.ExecutorContext) (*executor.ProcessState, error) {
|
||||
var ps *executor.ProcessState
|
||||
err := e.client.Call("Plugin.LaunchCmd", LaunchCmdArgs{Cmd: cmd, Ctx: ctx}, &ps)
|
||||
return ps, err
|
||||
}
|
||||
|
||||
func (e *ExecutorRPC) Wait() (*ProcessState, error) {
|
||||
var ps ProcessState
|
||||
func (e *ExecutorRPC) Wait() (*executor.ProcessState, error) {
|
||||
var ps executor.ProcessState
|
||||
err := e.client.Call("Plugin.Wait", new(interface{}), &ps)
|
||||
return &ps, err
|
||||
}
|
||||
|
||||
func (e *ExecutorRPC) ShutDown() error {
|
||||
var ps ProcessState
|
||||
var ps executor.ProcessState
|
||||
err := e.client.Call("Plugin.ShutDown", new(interface{}), &ps)
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ExecutorRPC) Exit() error {
|
||||
var ps ProcessState
|
||||
var ps executor.ProcessState
|
||||
err := e.client.Call("Plugin.Exit", new(interface{}), &ps)
|
||||
return err
|
||||
}
|
||||
|
||||
type ExecutorRPCServer struct {
|
||||
Impl Executor
|
||||
Impl executor.Executor
|
||||
}
|
||||
|
||||
func (e *ExecutorRPCServer) LaunchCmd(args LaunchCmdArgs, ps *ProcessState) error {
|
||||
func (e *ExecutorRPCServer) LaunchCmd(args LaunchCmdArgs, ps *executor.ProcessState) error {
|
||||
state, err := e.Impl.LaunchCmd(args.Cmd, args.Ctx)
|
||||
if state != nil {
|
||||
*ps = *state
|
||||
@@ -85,7 +86,7 @@ func (e *ExecutorRPCServer) LaunchCmd(args LaunchCmdArgs, ps *ProcessState) erro
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ExecutorRPCServer) Wait(args interface{}, ps *ProcessState) error {
|
||||
func (e *ExecutorRPCServer) Wait(args interface{}, ps *executor.ProcessState) error {
|
||||
state, err := e.Impl.Wait()
|
||||
if state != nil {
|
||||
*ps = *state
|
||||
@@ -93,13 +94,13 @@ func (e *ExecutorRPCServer) Wait(args interface{}, ps *ProcessState) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ExecutorRPCServer) ShutDown(args interface{}, ps *ProcessState) error {
|
||||
func (e *ExecutorRPCServer) ShutDown(args interface{}, ps *executor.ProcessState) error {
|
||||
var err error
|
||||
err = e.Impl.ShutDown()
|
||||
return err
|
||||
}
|
||||
|
||||
func (e *ExecutorRPCServer) Exit(args interface{}, ps *ProcessState) error {
|
||||
func (e *ExecutorRPCServer) Exit(args interface{}, ps *executor.ProcessState) error {
|
||||
var err error
|
||||
err = e.Impl.Exit()
|
||||
return err
|
||||
@@ -111,7 +112,7 @@ type ExecutorPlugin struct {
|
||||
|
||||
func (p *ExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
|
||||
p.logger = log.New(os.Stdout, "executor-plugin-server:", log.LstdFlags)
|
||||
return &ExecutorRPCServer{Impl: NewExecutor(p.logger)}, nil
|
||||
return &ExecutorRPCServer{Impl: executor.NewExecutor(p.logger)}, nil
|
||||
}
|
||||
|
||||
func (p *ExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/nomad/client/allocdir"
|
||||
"github.com/hashicorp/nomad/client/config"
|
||||
"github.com/hashicorp/nomad/client/driver/plugins"
|
||||
"github.com/hashicorp/nomad/client/driver/executor"
|
||||
cstructs "github.com/hashicorp/nomad/client/driver/structs"
|
||||
"github.com/hashicorp/nomad/client/fingerprint"
|
||||
"github.com/hashicorp/nomad/client/getter"
|
||||
@@ -43,7 +43,7 @@ type JavaDriverConfig struct {
|
||||
type javaHandle struct {
|
||||
pluginClient *plugin.Client
|
||||
userPid int
|
||||
executor plugins.Executor
|
||||
executor executor.Executor
|
||||
|
||||
killTimeout time.Duration
|
||||
logger *log.Logger
|
||||
@@ -152,17 +152,17 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
Cmd: exec.Command(bin, "executor"),
|
||||
}
|
||||
|
||||
executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
executorCtx := &plugins.ExecutorContext{
|
||||
executorCtx := &executor.ExecutorContext{
|
||||
TaskEnv: d.taskEnv,
|
||||
AllocDir: ctx.AllocDir,
|
||||
TaskName: task.Name,
|
||||
TaskResources: task.Resources,
|
||||
}
|
||||
ps, err := executor.LaunchCmd(&plugins.ExecCommand{Cmd: "java", Args: args}, executorCtx)
|
||||
ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: "java", Args: args}, executorCtx)
|
||||
if err != nil {
|
||||
pluginClient.Kill()
|
||||
return nil, fmt.Errorf("error starting process via the plugin: %v", err)
|
||||
@@ -172,7 +172,7 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
// Return a driver handle
|
||||
h := &javaHandle{
|
||||
pluginClient: pluginClient,
|
||||
executor: executor,
|
||||
executor: exec,
|
||||
userPid: ps.Pid,
|
||||
killTimeout: d.DriverContext.KillTimeout(task),
|
||||
logger: d.logger,
|
||||
@@ -186,7 +186,7 @@ func (d *JavaDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
|
||||
type javaId struct {
|
||||
KillTimeout time.Duration
|
||||
PluginConfig *plugins.ExecutorReattachConfig
|
||||
PluginConfig *ExecutorReattachConfig
|
||||
UserPid int
|
||||
}
|
||||
|
||||
@@ -227,7 +227,7 @@ func (d *JavaDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, erro
|
||||
func (h *javaHandle) ID() string {
|
||||
id := javaId{
|
||||
KillTimeout: h.killTimeout,
|
||||
PluginConfig: plugins.NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
PluginConfig: NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
UserPid: h.userPid,
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/nomad/client/allocdir"
|
||||
"github.com/hashicorp/nomad/client/config"
|
||||
"github.com/hashicorp/nomad/client/driver/plugins"
|
||||
"github.com/hashicorp/nomad/client/driver/executor"
|
||||
cstructs "github.com/hashicorp/nomad/client/driver/structs"
|
||||
"github.com/hashicorp/nomad/client/fingerprint"
|
||||
"github.com/hashicorp/nomad/client/getter"
|
||||
@@ -46,7 +46,7 @@ type QemuDriverConfig struct {
|
||||
type qemuHandle struct {
|
||||
pluginClient *plugin.Client
|
||||
userPid int
|
||||
executor plugins.Executor
|
||||
executor executor.Executor
|
||||
killTimeout time.Duration
|
||||
logger *log.Logger
|
||||
waitCh chan *cstructs.WaitResult
|
||||
@@ -196,17 +196,17 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
Cmd: exec.Command(bin, "executor"),
|
||||
}
|
||||
|
||||
executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
executorCtx := &plugins.ExecutorContext{
|
||||
executorCtx := &executor.ExecutorContext{
|
||||
TaskEnv: d.taskEnv,
|
||||
AllocDir: ctx.AllocDir,
|
||||
TaskName: task.Name,
|
||||
TaskResources: task.Resources,
|
||||
}
|
||||
ps, err := executor.LaunchCmd(&plugins.ExecCommand{Cmd: args[0], Args: args[1:]}, executorCtx)
|
||||
ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: args[0], Args: args[1:]}, executorCtx)
|
||||
if err != nil {
|
||||
pluginClient.Kill()
|
||||
return nil, fmt.Errorf("error starting process via the plugin: %v", err)
|
||||
@@ -217,7 +217,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
// Create and Return Handle
|
||||
h := &qemuHandle{
|
||||
pluginClient: pluginClient,
|
||||
executor: executor,
|
||||
executor: exec,
|
||||
userPid: ps.Pid,
|
||||
killTimeout: d.DriverContext.KillTimeout(task),
|
||||
logger: d.logger,
|
||||
@@ -232,7 +232,7 @@ func (d *QemuDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle,
|
||||
type qemuId struct {
|
||||
KillTimeout time.Duration
|
||||
UserPid int
|
||||
PluginConfig *plugins.ExecutorReattachConfig
|
||||
PluginConfig *ExecutorReattachConfig
|
||||
}
|
||||
|
||||
func (d *QemuDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
|
||||
@@ -272,7 +272,7 @@ func (d *QemuDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, erro
|
||||
func (h *qemuHandle) ID() string {
|
||||
id := qemuId{
|
||||
KillTimeout: h.killTimeout,
|
||||
PluginConfig: plugins.NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
PluginConfig: NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
UserPid: h.userPid,
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/nomad/client/allocdir"
|
||||
"github.com/hashicorp/nomad/client/config"
|
||||
"github.com/hashicorp/nomad/client/driver/plugins"
|
||||
"github.com/hashicorp/nomad/client/driver/executor"
|
||||
cstructs "github.com/hashicorp/nomad/client/driver/structs"
|
||||
"github.com/hashicorp/nomad/client/fingerprint"
|
||||
"github.com/hashicorp/nomad/client/getter"
|
||||
@@ -37,7 +37,7 @@ type RawExecDriver struct {
|
||||
type rawExecHandle struct {
|
||||
pluginClient *plugin.Client
|
||||
userPid int
|
||||
executor plugins.Executor
|
||||
executor executor.Executor
|
||||
killTimeout time.Duration
|
||||
logger *log.Logger
|
||||
waitCh chan *cstructs.WaitResult
|
||||
@@ -103,17 +103,17 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl
|
||||
Cmd: exec.Command(bin, "executor"),
|
||||
}
|
||||
|
||||
executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
executorCtx := &plugins.ExecutorContext{
|
||||
executorCtx := &executor.ExecutorContext{
|
||||
TaskEnv: d.taskEnv,
|
||||
AllocDir: ctx.AllocDir,
|
||||
TaskName: task.Name,
|
||||
TaskResources: task.Resources,
|
||||
}
|
||||
ps, err := executor.LaunchCmd(&plugins.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx)
|
||||
ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx)
|
||||
if err != nil {
|
||||
pluginClient.Kill()
|
||||
return nil, fmt.Errorf("error starting process via the plugin: %v", err)
|
||||
@@ -123,7 +123,7 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl
|
||||
// Return a driver handle
|
||||
h := &rawExecHandle{
|
||||
pluginClient: pluginClient,
|
||||
executor: executor,
|
||||
executor: exec,
|
||||
userPid: ps.Pid,
|
||||
killTimeout: d.DriverContext.KillTimeout(task),
|
||||
logger: d.logger,
|
||||
@@ -137,7 +137,7 @@ func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandl
|
||||
type rawExecId struct {
|
||||
KillTimeout time.Duration
|
||||
UserPid int
|
||||
PluginConfig *plugins.ExecutorReattachConfig
|
||||
PluginConfig *ExecutorReattachConfig
|
||||
}
|
||||
|
||||
func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
|
||||
@@ -175,7 +175,7 @@ func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, e
|
||||
func (h *rawExecHandle) ID() string {
|
||||
id := rawExecId{
|
||||
KillTimeout: h.killTimeout,
|
||||
PluginConfig: plugins.NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
PluginConfig: NewExecutorReattachConfig(h.pluginClient.ReattachConfig()),
|
||||
UserPid: h.userPid,
|
||||
}
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ import (
|
||||
|
||||
"github.com/hashicorp/go-multierror"
|
||||
"github.com/hashicorp/go-plugin"
|
||||
"github.com/hashicorp/nomad/client/driver/plugins"
|
||||
"github.com/hashicorp/nomad/client/driver/executor"
|
||||
)
|
||||
|
||||
func createExecutor(config *plugin.ClientConfig, w io.Writer) (plugins.Executor, *plugin.Client, error) {
|
||||
config.HandshakeConfig = plugins.HandshakeConfig
|
||||
config.Plugins = plugins.PluginMap
|
||||
func createExecutor(config *plugin.ClientConfig, w io.Writer) (executor.Executor, *plugin.Client, error) {
|
||||
config.HandshakeConfig = HandshakeConfig
|
||||
config.Plugins = PluginMap
|
||||
config.SyncStdout = w
|
||||
config.SyncStderr = w
|
||||
executorClient := plugin.NewClient(config)
|
||||
@@ -25,7 +25,7 @@ func createExecutor(config *plugin.ClientConfig, w io.Writer) (plugins.Executor,
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to dispense the executor plugin: %v", err)
|
||||
}
|
||||
executorPlugin := raw.(plugins.Executor)
|
||||
executorPlugin := raw.(executor.Executor)
|
||||
return executorPlugin, executorClient, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
"github.com/hashicorp/go-plugin"
|
||||
|
||||
"github.com/hashicorp/nomad/client/driver/plugins"
|
||||
"github.com/hashicorp/nomad/client/driver"
|
||||
)
|
||||
|
||||
type ExecutorPluginCommand struct {
|
||||
@@ -25,8 +25,8 @@ func (e *ExecutorPluginCommand) Synopsis() string {
|
||||
|
||||
func (e *ExecutorPluginCommand) Run(args []string) int {
|
||||
plugin.Serve(&plugin.ServeConfig{
|
||||
HandshakeConfig: plugins.HandshakeConfig,
|
||||
Plugins: plugins.PluginMap,
|
||||
HandshakeConfig: driver.HandshakeConfig,
|
||||
Plugins: driver.PluginMap,
|
||||
})
|
||||
return 0
|
||||
}
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type SpawnDaemonCommand struct {
|
||||
Meta
|
||||
config *DaemonConfig
|
||||
exitFile io.WriteCloser
|
||||
}
|
||||
|
||||
func (c *SpawnDaemonCommand) Help() string {
|
||||
helpText := `
|
||||
Usage: nomad spawn-daemon [options] <daemon_config>
|
||||
|
||||
INTERNAL ONLY
|
||||
|
||||
Spawns a daemon process by double forking. The required daemon_config is a
|
||||
json encoding of the DaemonConfig struct containing the isolation
|
||||
configuration and command to run. SpawnStartStatus is json serialized to
|
||||
stdout upon running the user command or if any error prevents its execution.
|
||||
If there is no error, the process waits on the users command. Once the user
|
||||
command exits, the exit code is written to a file specified in the
|
||||
daemon_config and this process exits with the same exit status as the user
|
||||
command.
|
||||
`
|
||||
|
||||
return strings.TrimSpace(helpText)
|
||||
}
|
||||
|
||||
func (c *SpawnDaemonCommand) Synopsis() string {
|
||||
return "Spawn a daemon command with configurable isolation."
|
||||
}
|
||||
|
||||
// Status of executing the user's command.
|
||||
type SpawnStartStatus struct {
|
||||
// The PID of the user's command.
|
||||
UserPID int
|
||||
|
||||
// ErrorMsg will be empty if the user command was started successfully.
|
||||
// Otherwise it will have an error message.
|
||||
ErrorMsg string
|
||||
}
|
||||
|
||||
// Exit status of the user's command.
|
||||
type SpawnExitStatus struct {
|
||||
// The exit code of the user's command.
|
||||
ExitCode int
|
||||
}
|
||||
|
||||
// Configuration for the command to start as a daemon.
|
||||
type DaemonConfig struct {
|
||||
exec.Cmd
|
||||
|
||||
// The filepath to write the exit status to.
|
||||
ExitStatusFile string
|
||||
|
||||
// The paths, if not /dev/null, must be either in the tasks root directory
|
||||
// or in the shared alloc directory.
|
||||
StdoutFile string
|
||||
StdinFile string
|
||||
StderrFile string
|
||||
|
||||
// An optional path specifying the directory to chroot the process in.
|
||||
Chroot string
|
||||
}
|
||||
|
||||
// Whether to start the user command or abort.
|
||||
type TaskStart bool
|
||||
|
||||
// parseConfig reads the DaemonConfig from the passed arguments. If not
|
||||
// successful, an error is returned.
|
||||
func (c *SpawnDaemonCommand) parseConfig(args []string) (*DaemonConfig, error) {
|
||||
flags := c.Meta.FlagSet("spawn-daemon", FlagSetClient)
|
||||
flags.Usage = func() { c.Ui.Output(c.Help()) }
|
||||
if err := flags.Parse(args); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse args: %v", err)
|
||||
}
|
||||
|
||||
// Check that we got json input.
|
||||
args = flags.Args()
|
||||
if len(args) != 1 {
|
||||
return nil, fmt.Errorf("incorrect number of args; got %v; want 1", len(args))
|
||||
}
|
||||
jsonInput, err := strconv.Unquote(args[0])
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Failed to unquote json input: %v", err)
|
||||
}
|
||||
|
||||
// De-serialize the passed command.
|
||||
var config DaemonConfig
|
||||
dec := json.NewDecoder(strings.NewReader(jsonInput))
|
||||
if err := dec.Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
// configureLogs creates the log files and redirects the process
|
||||
// stdin/stderr/stdout to them. If unsuccessful, an error is returned.
|
||||
func (c *SpawnDaemonCommand) configureLogs() error {
|
||||
if len(c.config.StdoutFile) != 0 {
|
||||
stdo, err := os.OpenFile(c.config.StdoutFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file to redirect stdout: %v", err)
|
||||
}
|
||||
|
||||
c.config.Cmd.Stdout = stdo
|
||||
}
|
||||
|
||||
if len(c.config.StderrFile) != 0 {
|
||||
stde, err := os.OpenFile(c.config.StderrFile, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file to redirect stderr: %v", err)
|
||||
}
|
||||
c.config.Cmd.Stderr = stde
|
||||
}
|
||||
|
||||
if len(c.config.StdinFile) != 0 {
|
||||
stdi, err := os.OpenFile(c.config.StdinFile, os.O_CREATE|os.O_RDONLY, 0666)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error opening file to redirect stdin: %v", err)
|
||||
}
|
||||
c.config.Cmd.Stdin = stdi
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *SpawnDaemonCommand) Run(args []string) int {
|
||||
var err error
|
||||
c.config, err = c.parseConfig(args)
|
||||
if err != nil {
|
||||
return c.outputStartStatus(err, 1)
|
||||
}
|
||||
|
||||
// Open the file we will be using to write exit codes to. We do this early
|
||||
// to ensure that we don't start the user process when we can't capture its
|
||||
// exit status.
|
||||
c.exitFile, err = os.OpenFile(c.config.ExitStatusFile, os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Error opening file to store exit status: %v", err), 1)
|
||||
}
|
||||
|
||||
// Isolate the user process.
|
||||
if err := c.isolateCmd(); err != nil {
|
||||
return c.outputStartStatus(err, 1)
|
||||
}
|
||||
|
||||
// Redirect logs.
|
||||
if err := c.configureLogs(); err != nil {
|
||||
return c.outputStartStatus(err, 1)
|
||||
}
|
||||
|
||||
// Chroot jail the process and set its working directory.
|
||||
c.configureChroot()
|
||||
|
||||
// Wait to get the start command.
|
||||
var start TaskStart
|
||||
dec := json.NewDecoder(os.Stdin)
|
||||
if err := dec.Decode(&start); err != nil {
|
||||
return c.outputStartStatus(err, 1)
|
||||
}
|
||||
|
||||
// Aborted by Nomad process.
|
||||
if !start {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Spawn the user process.
|
||||
if err := c.config.Cmd.Start(); err != nil {
|
||||
return c.outputStartStatus(fmt.Errorf("Error starting user command: %v", err), 1)
|
||||
}
|
||||
|
||||
// Indicate that the command was started successfully.
|
||||
c.outputStartStatus(nil, 0)
|
||||
|
||||
// Wait and then output the exit status.
|
||||
return c.writeExitStatus(c.config.Cmd.Wait())
|
||||
}
|
||||
|
||||
// outputStartStatus is a helper function that outputs a SpawnStartStatus to
|
||||
// Stdout with the passed error, which may be nil to indicate no error. It
|
||||
// returns the passed status.
|
||||
func (c *SpawnDaemonCommand) outputStartStatus(err error, status int) int {
|
||||
startStatus := &SpawnStartStatus{}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
|
||||
if err != nil {
|
||||
startStatus.ErrorMsg = err.Error()
|
||||
}
|
||||
|
||||
if c.config != nil && c.config.Cmd.Process != nil {
|
||||
startStatus.UserPID = c.config.Process.Pid
|
||||
}
|
||||
|
||||
enc.Encode(startStatus)
|
||||
return status
|
||||
}
|
||||
|
||||
// writeExitStatus takes in the error result from calling wait and writes out
|
||||
// the exit status to a file. It returns the same exit status as the user
|
||||
// command.
|
||||
func (c *SpawnDaemonCommand) writeExitStatus(exit error) int {
|
||||
// Parse the exit code.
|
||||
exitStatus := &SpawnExitStatus{}
|
||||
if exit != nil {
|
||||
// Default to exit code 1 if we can not get the actual exit code.
|
||||
exitStatus.ExitCode = 1
|
||||
|
||||
if exiterr, ok := exit.(*exec.ExitError); ok {
|
||||
if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
|
||||
exitStatus.ExitCode = status.ExitStatus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if c.exitFile != nil {
|
||||
enc := json.NewEncoder(c.exitFile)
|
||||
enc.Encode(exitStatus)
|
||||
c.exitFile.Close()
|
||||
}
|
||||
|
||||
return exitStatus.ExitCode
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
package command
|
||||
|
||||
// No chroot on darwin.
|
||||
func (c *SpawnDaemonCommand) configureChroot() {}
|
||||
@@ -1,16 +0,0 @@
|
||||
package command
|
||||
|
||||
import "syscall"
|
||||
|
||||
// configureChroot enters the user command into a chroot if specified in the
|
||||
// config and on an OS that supports Chroots.
|
||||
func (c *SpawnDaemonCommand) configureChroot() {
|
||||
if len(c.config.Chroot) != 0 {
|
||||
if c.config.Cmd.SysProcAttr == nil {
|
||||
c.config.Cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
|
||||
c.config.Cmd.SysProcAttr.Chroot = c.config.Chroot
|
||||
c.config.Cmd.Dir = "/"
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type nopCloser struct {
|
||||
io.ReadWriter
|
||||
}
|
||||
|
||||
func (n *nopCloser) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestSpawnDaemon_WriteExitStatus(t *testing.T) {
|
||||
// Check if there is python.
|
||||
path, err := exec.LookPath("python")
|
||||
if err != nil {
|
||||
t.Skip("python not detected")
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
daemon := &SpawnDaemonCommand{exitFile: &nopCloser{&b}}
|
||||
|
||||
code := 3
|
||||
cmd := exec.Command(path, "./test-resources/exiter.py", fmt.Sprintf("%d", code))
|
||||
err = cmd.Run()
|
||||
actual := daemon.writeExitStatus(err)
|
||||
if actual != code {
|
||||
t.Fatalf("writeExitStatus(%v) returned %v; want %v", err, actual, code)
|
||||
}
|
||||
|
||||
// De-serialize the passed command.
|
||||
var exitStatus SpawnExitStatus
|
||||
dec := json.NewDecoder(&b)
|
||||
if err := dec.Decode(&exitStatus); err != nil {
|
||||
t.Fatalf("failed to decode exit status: %v", err)
|
||||
}
|
||||
|
||||
if exitStatus.ExitCode != code {
|
||||
t.Fatalf("writeExitStatus(%v) wrote exit status %v; want %v", err, exitStatus.ExitCode, code)
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
// +build !windows
|
||||
|
||||
package command
|
||||
|
||||
import "syscall"
|
||||
|
||||
// isolateCmd sets the session id for the process and the umask.
|
||||
func (c *SpawnDaemonCommand) isolateCmd() error {
|
||||
if c.config.Cmd.SysProcAttr == nil {
|
||||
c.config.Cmd.SysProcAttr = &syscall.SysProcAttr{}
|
||||
}
|
||||
|
||||
c.config.Cmd.SysProcAttr.Setsid = true
|
||||
syscall.Umask(0)
|
||||
return nil
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
// build !linux !darwin
|
||||
|
||||
package command
|
||||
|
||||
// No isolation on Windows.
|
||||
func (c *SpawnDaemonCommand) isolateCmd() error { return nil }
|
||||
func (c *SpawnDaemonCommand) configureChroot() {}
|
||||
@@ -118,13 +118,6 @@ func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"spawn-daemon": func() (cli.Command, error) {
|
||||
return &command.SpawnDaemonCommand{
|
||||
Meta: meta,
|
||||
}, nil
|
||||
},
|
||||
|
||||
"status": func() (cli.Command, error) {
|
||||
return &command.StatusCommand{
|
||||
Meta: meta,
|
||||
|
||||
Reference in New Issue
Block a user