mirror of
https://github.com/kemko/nomad.git
synced 2026-01-06 02:15:43 +03:00
Update the interface, add UniversalExecutor, add stub for LinuxExecutor
This commit is contained in:
@@ -1,47 +1,147 @@
|
||||
// Package exec is used to invoke child processes across various operating
|
||||
// systems and child processes and provide the following features:
|
||||
// Package exec is used to invoke child processes across various platforms to
|
||||
// provide the following features:
|
||||
//
|
||||
// - Least privilege
|
||||
// - Resource constraints
|
||||
// - Process isolation
|
||||
//
|
||||
// The semantics and implementation for these differ between operating systems,
|
||||
// operating system versions, and types of child processes. For example, running
|
||||
// Docker on Linux has different semantics than running Java on Windows. Also,
|
||||
// versions of an OS may provide different capabilities for resource isolation,
|
||||
// such as ulimits, cgroups, containers, jails, etc. Please refer to the
|
||||
// relevant implementation for specific details.
|
||||
// A "platform" may be defined as coarsely as "Windows" or as specifically as
|
||||
// "linux 3.20 with systemd". This allows Nomad to use best-effort, best-
|
||||
// available capabilities of each platform to provide resource constraints,
|
||||
// process isolation, and security features, or otherwise take advantage of
|
||||
// features that are unique to that platform.
|
||||
//
|
||||
// The semantics of any particular instance are left up to the implementation.
|
||||
// However, these should be completely transparent to the calling context. In
|
||||
// other words, the Java driver should be able to call exec for any platform and
|
||||
// just work.
|
||||
package exec
|
||||
|
||||
import "github.com/hashicorp/nomad/nomad/structs"
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
|
||||
type Command struct {
|
||||
// This may be a username or Uid. The implementation will decide how to use it.
|
||||
UserID string
|
||||
}
|
||||
"github.com/hashicorp/nomad/nomad/structs"
|
||||
)
|
||||
|
||||
// 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.
|
||||
type Executor interface {
|
||||
// Limit must be called before Start and restricts the amount of resources
|
||||
// the process can use
|
||||
Limit(structs.Resources)
|
||||
|
||||
// RunAs sets the user we should use to run this command. This may be set as
|
||||
// a username, uid, or other identifier. The implementation will decide what
|
||||
// to do with it, if anything.
|
||||
RunAs(string)
|
||||
|
||||
// Start the process. This may wrap the actual process in another command,
|
||||
// depending on the capabilities in this environment.
|
||||
// depending on the capabilities in this environment. Errors that arise from
|
||||
// Limits or Runas will bubble through Start()
|
||||
Start() error
|
||||
|
||||
// Open should be called to restore a previous pid. This might be needed if
|
||||
// nomad is restarted. This sets os.Process internally.
|
||||
Open(int) error
|
||||
|
||||
// Shutdown should use a graceful stop mechanism so the application can
|
||||
// perform checkpointing or cleanup, if such a mechanism is available.
|
||||
// If such a mechanism is not available, Showdown() should call ForceStop().
|
||||
// If such a mechanism is not available, Shutdown() should call ForceStop().
|
||||
Shutdown() error
|
||||
|
||||
// ForceStop will terminate the process without waiting for cleanup. Every
|
||||
// implementations must provide this.
|
||||
ForceStop() error
|
||||
|
||||
// Access the underlying Cmd struct. This should never be nil. Also, this is
|
||||
// not intended to be access outside the exec package, so YMMV.
|
||||
Command() *cmd
|
||||
}
|
||||
|
||||
// DefaultExecutor uses capability testing to give you the best available
|
||||
// Cmd is an extension of exec.Cmd that incorporates functionality for
|
||||
// re-attaching to processes, dropping priviledges, etc., based on platform-
|
||||
// specific implementations.
|
||||
type cmd struct {
|
||||
exec.Cmd
|
||||
|
||||
// Resources is used to limit CPU and RAM used by the process, by way of
|
||||
// cgroups or a similar mechanism.
|
||||
Resources structs.Resources
|
||||
|
||||
// RunAs may be a username or Uid. The implementation will decide how to use it.
|
||||
RunAs string
|
||||
}
|
||||
|
||||
// Command is a mirror of exec.Command that returns a platform-specific Executor
|
||||
func Command(name string, arg ...string) Executor {
|
||||
executor := AutoselectExecutor()
|
||||
cmd := executor.Command()
|
||||
cmd.Path = name
|
||||
cmd.Args = append([]string{name}, arg...)
|
||||
|
||||
if filepath.Base(name) == name {
|
||||
if lp, err := exec.LookPath(name); err != nil {
|
||||
// cmd.lookPathErr = err
|
||||
} else {
|
||||
cmd.Path = lp
|
||||
}
|
||||
}
|
||||
return executor
|
||||
}
|
||||
|
||||
func OpenPid(int) Executor {
|
||||
executor := AutoselectExecutor()
|
||||
return executor
|
||||
}
|
||||
|
||||
// AutoselectExecutor uses capability testing to give you the best available
|
||||
// executor based on your platform and execution environment. If you need a
|
||||
// specific executor, call it directly.
|
||||
func DefaultExecutor() Executor {
|
||||
// TODO Implement this
|
||||
func AutoselectExecutor() Executor {
|
||||
// TODO platform switching
|
||||
return &UniversalExecutor{}
|
||||
}
|
||||
|
||||
// UniversalExecutor should work everywhere, and as a result does not include
|
||||
// any resource restrictions or runas capabilities.
|
||||
type UniversalExecutor struct {
|
||||
cmd
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) Limit(resources structs.Resources) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) RunAs(userid string) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) Start() error {
|
||||
// We don't want to call ourself. We want to call Start on our embedded Cmd
|
||||
return e.cmd.Start()
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) Open(pid int) error {
|
||||
process, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Failed to reopen pid %d: %s", pid, err)
|
||||
}
|
||||
e.Process = process
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) Shutdown() error {
|
||||
return e.ForceStop()
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) ForceStop() error {
|
||||
return e.Process.Kill()
|
||||
}
|
||||
|
||||
func (e *UniversalExecutor) Command() *cmd {
|
||||
return &e.cmd
|
||||
}
|
||||
|
||||
@@ -1,3 +1,35 @@
|
||||
package exec
|
||||
|
||||
// TODO Implement this!
|
||||
import "github.com/hashicorp/nomad/nomad/structs"
|
||||
|
||||
type LinuxExecutor struct {
|
||||
cmd
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) Limit(resources structs.Resources) {
|
||||
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) RunAs(userid string) {
|
||||
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) Start() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) Open(pid int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) Shutdown() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) ForceStop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *LinuxExecutor) Command() *cmd {
|
||||
return &e.cmd
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user