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  }