github.com/ryanslade/nomad@v0.2.4-0.20160128061903-fc95782f2089/client/driver/executor/exec.go (about) 1 // Package executor is used to invoke child processes across various operating 2 // systems in a way that provides the following features: 3 // 4 // - Least privilege 5 // - Resource constraints 6 // - Process isolation 7 // 8 // An operating system may be something like "windows" or "linux with systemd". 9 // Executors allow drivers like `exec` and `java` to share an implementation 10 // for isolation capabilities on a particular operating system. 11 // 12 // For example: 13 // 14 // - `exec` and `java` on Linux use a cgroups executor 15 // - `exec` and `java` on FreeBSD use a jails executor 16 // 17 // However, drivers that provide their own isolation should not use executors. 18 // For example, using an executor to start QEMU means that the QEMU call is 19 // run inside a chroot+cgroup, even though the VM already provides isolation for 20 // the task running inside it. This is an extraneous level of indirection. 21 package executor 22 23 import ( 24 "fmt" 25 "os/exec" 26 "path/filepath" 27 28 "github.com/hashicorp/nomad/client/allocdir" 29 "github.com/hashicorp/nomad/nomad/structs" 30 31 "github.com/hashicorp/nomad/client/driver/env" 32 cstructs "github.com/hashicorp/nomad/client/driver/structs" 33 ) 34 35 var errNoResources = fmt.Errorf("No resources are associated with this task") 36 37 // Executor is an interface that any platform- or capability-specific exec 38 // wrapper must implement. You should not need to implement a Java executor. 39 // Rather, you would implement a cgroups executor that the Java driver will use. 40 type Executor interface { 41 // Limit must be called before Start and restricts the amount of resources 42 // the process can use. Note that an error may be returned ONLY IF the 43 // executor implements resource limiting. Otherwise Limit is ignored. 44 Limit(*structs.Resources) error 45 46 // ConfigureTaskDir must be called before Start and ensures that the tasks 47 // directory is properly configured. 48 ConfigureTaskDir(taskName string, alloc *allocdir.AllocDir) error 49 50 // Start the process. This may wrap the actual process in another command, 51 // depending on the capabilities in this environment. Errors that arise from 52 // Limits or Runas may bubble through Start() 53 Start() error 54 55 // Open should be called to restore a previous execution. This might be needed if 56 // nomad is restarted. 57 Open(string) error 58 59 // Wait waits till the user's command is completed. 60 Wait() *cstructs.WaitResult 61 62 // Returns a handle that is executor specific for use in reopening. 63 ID() (string, error) 64 65 // Shutdown should use a graceful stop mechanism so the application can 66 // perform checkpointing or cleanup, if such a mechanism is available. 67 // If such a mechanism is not available, Shutdown() should call ForceStop(). 68 Shutdown() error 69 70 // ForceStop will terminate the process without waiting for cleanup. Every 71 // implementations must provide this. 72 ForceStop() error 73 74 // Command provides access the underlying Cmd struct in case the Executor 75 // interface doesn't expose the functionality you need. 76 Command() *exec.Cmd 77 } 78 79 // ExecutorContext is a means to inject dependencies such as loggers, configs, and 80 // node attributes into a Driver without having to change the Driver interface 81 // each time we do it. Used in conjection with Factory, above. 82 type ExecutorContext struct { 83 taskEnv *env.TaskEnvironment 84 } 85 86 // NewExecutorContext initializes a new DriverContext with the specified fields. 87 func NewExecutorContext(taskEnv *env.TaskEnvironment) *ExecutorContext { 88 return &ExecutorContext{ 89 taskEnv: taskEnv, 90 } 91 } 92 93 // Command returns a platform-specific Executor 94 func Command(ctx *ExecutorContext, name string, args ...string) Executor { 95 executor := NewExecutor(ctx) 96 SetCommand(executor, name, args) 97 return executor 98 } 99 100 func SetCommand(e Executor, name string, args []string) { 101 cmd := e.Command() 102 cmd.Path = name 103 cmd.Args = append([]string{name}, args...) 104 105 if filepath.Base(name) == name { 106 if lp, err := exec.LookPath(name); err != nil { 107 // cmd.lookPathErr = err 108 } else { 109 cmd.Path = lp 110 } 111 } 112 } 113 114 // OpenId is similar to executor.Command but will attempt to reopen with the 115 // passed ID. 116 func OpenId(ctx *ExecutorContext, id string) (Executor, error) { 117 executor := NewExecutor(ctx) 118 err := executor.Open(id) 119 if err != nil { 120 return nil, err 121 } 122 return executor, nil 123 }