github.com/kardianos/nomad@v0.1.3-0.20151022182107-b13df73ee850/client/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  
    32  var errNoResources = fmt.Errorf("No resources are associated with this task")
    33  
    34  // Executor is an interface that any platform- or capability-specific exec
    35  // wrapper must implement. You should not need to implement a Java executor.
    36  // Rather, you would implement a cgroups executor that the Java driver will use.
    37  type Executor interface {
    38  	// Limit must be called before Start and restricts the amount of resources
    39  	// the process can use. Note that an error may be returned ONLY IF the
    40  	// executor implements resource limiting. Otherwise Limit is ignored.
    41  	Limit(*structs.Resources) error
    42  
    43  	// ConfigureTaskDir must be called before Start and ensures that the tasks
    44  	// directory is properly configured.
    45  	ConfigureTaskDir(taskName string, alloc *allocdir.AllocDir) error
    46  
    47  	// Start the process. This may wrap the actual process in another command,
    48  	// depending on the capabilities in this environment. Errors that arise from
    49  	// Limits or Runas may bubble through Start()
    50  	Start() error
    51  
    52  	// Open should be called to restore a previous execution. This might be needed if
    53  	// nomad is restarted.
    54  	Open(string) error
    55  
    56  	// Wait waits till the user's command is completed.
    57  	Wait() error
    58  
    59  	// Returns a handle that is executor specific for use in reopening.
    60  	ID() (string, error)
    61  
    62  	// Shutdown should use a graceful stop mechanism so the application can
    63  	// perform checkpointing or cleanup, if such a mechanism is available.
    64  	// If such a mechanism is not available, Shutdown() should call ForceStop().
    65  	Shutdown() error
    66  
    67  	// ForceStop will terminate the process without waiting for cleanup. Every
    68  	// implementations must provide this.
    69  	ForceStop() error
    70  
    71  	// Command provides access the underlying Cmd struct in case the Executor
    72  	// interface doesn't expose the functionality you need.
    73  	Command() *cmd
    74  }
    75  
    76  // Command is a mirror of exec.Command that returns a platform-specific Executor
    77  func Command(name string, arg ...string) Executor {
    78  	executor := NewExecutor()
    79  	cmd := executor.Command()
    80  	cmd.Path = name
    81  	cmd.Args = append([]string{name}, arg...)
    82  
    83  	if filepath.Base(name) == name {
    84  		if lp, err := exec.LookPath(name); err != nil {
    85  			// cmd.lookPathErr = err
    86  		} else {
    87  			cmd.Path = lp
    88  		}
    89  	}
    90  	return executor
    91  }
    92  
    93  // OpenId is similar to executor.Command but will attempt to reopen with the
    94  // passed ID.
    95  func OpenId(id string) (Executor, error) {
    96  	executor := NewExecutor()
    97  	err := executor.Open(id)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return executor, nil
   102  }
   103  
   104  // Cmd is an extension of exec.Cmd that incorporates functionality for
   105  // re-attaching to processes, dropping priviledges, etc., based on platform-
   106  // specific implementations.
   107  type cmd struct {
   108  	exec.Cmd
   109  
   110  	// Resources is used to limit CPU and RAM used by the process, by way of
   111  	// cgroups or a similar mechanism.
   112  	Resources structs.Resources
   113  
   114  	// RunAs may be a username or Uid. The implementation will decide how to use it.
   115  	RunAs string
   116  }