2015-10-08 08:38:39 +00:00
|
|
|
// Package executor is used to invoke child processes across various operating
|
|
|
|
// systems in a way that provides the following features:
|
2015-09-11 19:35:03 +00:00
|
|
|
//
|
|
|
|
// - Least privilege
|
|
|
|
// - Resource constraints
|
|
|
|
// - Process isolation
|
|
|
|
//
|
2015-10-08 08:38:39 +00:00
|
|
|
// An operating system may be something like "windows" or "linux with systemd".
|
|
|
|
// Executors allow drivers like `exec` and `java` to share an implementation
|
|
|
|
// for isolation capabilities on a particular operating system.
|
2015-09-12 00:12:48 +00:00
|
|
|
//
|
2015-10-08 07:02:46 +00:00
|
|
|
// For example:
|
|
|
|
//
|
2015-10-08 08:38:39 +00:00
|
|
|
// - `exec` and `java` on Linux use a cgroups executor
|
|
|
|
// - `exec` and `java` on FreeBSD use a jails executor
|
2015-10-08 07:02:46 +00:00
|
|
|
//
|
|
|
|
// However, drivers that provide their own isolation should not use executors.
|
|
|
|
// For example, using an executor to start QEMU means that the QEMU call is
|
|
|
|
// run inside a chroot+cgroup, even though the VM already provides isolation for
|
2015-10-08 08:38:39 +00:00
|
|
|
// the task running inside it. This is an extraneous level of indirection.
|
2015-09-15 21:03:03 +00:00
|
|
|
package executor
|
2015-09-11 19:35:03 +00:00
|
|
|
|
2015-09-12 00:12:48 +00:00
|
|
|
import (
|
2015-09-24 08:11:52 +00:00
|
|
|
"fmt"
|
2015-09-12 00:12:48 +00:00
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
2015-09-11 19:35:03 +00:00
|
|
|
|
2015-09-25 23:49:14 +00:00
|
|
|
"github.com/hashicorp/nomad/client/allocdir"
|
2015-09-12 00:12:48 +00:00
|
|
|
"github.com/hashicorp/nomad/nomad/structs"
|
2015-11-14 06:07:13 +00:00
|
|
|
|
2016-01-11 17:58:26 +00:00
|
|
|
"github.com/hashicorp/nomad/client/driver/env"
|
2015-11-14 06:07:13 +00:00
|
|
|
cstructs "github.com/hashicorp/nomad/client/driver/structs"
|
2015-09-12 00:12:48 +00:00
|
|
|
)
|
2015-09-11 19:35:03 +00:00
|
|
|
|
2015-09-24 08:11:52 +00:00
|
|
|
var errNoResources = fmt.Errorf("No resources are associated with this task")
|
|
|
|
|
2015-09-12 00:12:48 +00:00
|
|
|
// Executor is an interface that any platform- or capability-specific exec
|
|
|
|
// wrapper must implement. You should not need to implement a Java executor.
|
|
|
|
// Rather, you would implement a cgroups executor that the Java driver will use.
|
2015-09-11 19:35:03 +00:00
|
|
|
type Executor interface {
|
|
|
|
// Limit must be called before Start and restricts the amount of resources
|
2015-09-14 22:57:21 +00:00
|
|
|
// the process can use. Note that an error may be returned ONLY IF the
|
|
|
|
// executor implements resource limiting. Otherwise Limit is ignored.
|
2015-09-15 20:11:56 +00:00
|
|
|
Limit(*structs.Resources) error
|
2015-09-11 19:35:03 +00:00
|
|
|
|
2015-09-25 23:49:14 +00:00
|
|
|
// ConfigureTaskDir must be called before Start and ensures that the tasks
|
|
|
|
// directory is properly configured.
|
|
|
|
ConfigureTaskDir(taskName string, alloc *allocdir.AllocDir) error
|
|
|
|
|
2015-09-11 19:35:03 +00:00
|
|
|
// Start the process. This may wrap the actual process in another command,
|
2015-09-12 00:12:48 +00:00
|
|
|
// depending on the capabilities in this environment. Errors that arise from
|
2015-09-16 01:54:55 +00:00
|
|
|
// Limits or Runas may bubble through Start()
|
2015-09-11 19:35:03 +00:00
|
|
|
Start() error
|
|
|
|
|
2015-09-20 01:47:04 +00:00
|
|
|
// Open should be called to restore a previous execution. This might be needed if
|
|
|
|
// nomad is restarted.
|
|
|
|
Open(string) error
|
2015-09-12 00:12:48 +00:00
|
|
|
|
2015-09-20 01:47:04 +00:00
|
|
|
// Wait waits till the user's command is completed.
|
2015-11-14 06:07:13 +00:00
|
|
|
Wait() *cstructs.WaitResult
|
2015-09-15 20:11:56 +00:00
|
|
|
|
2015-09-20 01:47:04 +00:00
|
|
|
// Returns a handle that is executor specific for use in reopening.
|
|
|
|
ID() (string, error)
|
2015-09-15 20:11:56 +00:00
|
|
|
|
2015-09-11 19:35:03 +00:00
|
|
|
// Shutdown should use a graceful stop mechanism so the application can
|
|
|
|
// perform checkpointing or cleanup, if such a mechanism is available.
|
2015-09-12 00:12:48 +00:00
|
|
|
// If such a mechanism is not available, Shutdown() should call ForceStop().
|
2015-09-11 19:35:03 +00:00
|
|
|
Shutdown() error
|
|
|
|
|
|
|
|
// ForceStop will terminate the process without waiting for cleanup. Every
|
|
|
|
// implementations must provide this.
|
|
|
|
ForceStop() error
|
2015-09-12 00:12:48 +00:00
|
|
|
|
2015-09-16 01:54:55 +00:00
|
|
|
// Command provides access the underlying Cmd struct in case the Executor
|
|
|
|
// interface doesn't expose the functionality you need.
|
2015-11-05 01:20:52 +00:00
|
|
|
Command() *exec.Cmd
|
2015-09-11 19:35:03 +00:00
|
|
|
}
|
|
|
|
|
2016-01-11 17:58:26 +00:00
|
|
|
// ExecutorContext is a means to inject dependencies such as loggers, configs, and
|
|
|
|
// node attributes into a Driver without having to change the Driver interface
|
|
|
|
// each time we do it. Used in conjection with Factory, above.
|
|
|
|
type ExecutorContext struct {
|
|
|
|
taskEnv *env.TaskEnvironment
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewExecutorContext initializes a new DriverContext with the specified fields.
|
|
|
|
func NewExecutorContext(taskEnv *env.TaskEnvironment) *ExecutorContext {
|
|
|
|
return &ExecutorContext{
|
|
|
|
taskEnv: taskEnv,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Command returns a platform-specific Executor
|
|
|
|
func Command(ctx *ExecutorContext, name string, args ...string) Executor {
|
|
|
|
executor := NewExecutor(ctx)
|
2015-11-05 17:58:57 +00:00
|
|
|
SetCommand(executor, name, args)
|
|
|
|
return executor
|
|
|
|
}
|
|
|
|
|
|
|
|
func SetCommand(e Executor, name string, args []string) {
|
|
|
|
cmd := e.Command()
|
2015-09-12 00:12:48 +00:00
|
|
|
cmd.Path = name
|
2015-11-05 17:58:57 +00:00
|
|
|
cmd.Args = append([]string{name}, args...)
|
2015-09-12 00:12:48 +00:00
|
|
|
|
|
|
|
if filepath.Base(name) == name {
|
|
|
|
if lp, err := exec.LookPath(name); err != nil {
|
|
|
|
// cmd.lookPathErr = err
|
|
|
|
} else {
|
|
|
|
cmd.Path = lp
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-20 01:47:04 +00:00
|
|
|
// OpenId is similar to executor.Command but will attempt to reopen with the
|
|
|
|
// passed ID.
|
2016-01-11 17:58:26 +00:00
|
|
|
func OpenId(ctx *ExecutorContext, id string) (Executor, error) {
|
|
|
|
executor := NewExecutor(ctx)
|
2015-09-20 01:47:04 +00:00
|
|
|
err := executor.Open(id)
|
2015-09-15 20:11:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return executor, nil
|
2015-09-12 00:12:48 +00:00
|
|
|
}
|