github.com/ranjib/nomad@v0.1.1-0.20160225204057-97751b02f70b/client/driver/raw_exec.go (about)

     1  package driver
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"os/exec"
     8  	"path/filepath"
     9  	"time"
    10  
    11  	"github.com/hashicorp/go-plugin"
    12  	"github.com/hashicorp/nomad/client/allocdir"
    13  	"github.com/hashicorp/nomad/client/config"
    14  	"github.com/hashicorp/nomad/client/driver/executor"
    15  	cstructs "github.com/hashicorp/nomad/client/driver/structs"
    16  	"github.com/hashicorp/nomad/client/fingerprint"
    17  	"github.com/hashicorp/nomad/client/getter"
    18  	"github.com/hashicorp/nomad/helper/discover"
    19  	"github.com/hashicorp/nomad/nomad/structs"
    20  	"github.com/mitchellh/mapstructure"
    21  )
    22  
    23  const (
    24  	// The option that enables this driver in the Config.Options map.
    25  	rawExecConfigOption = "driver.raw_exec.enable"
    26  )
    27  
    28  // The RawExecDriver is a privileged version of the exec driver. It provides no
    29  // resource isolation and just fork/execs. The Exec driver should be preferred
    30  // and this should only be used when explicitly needed.
    31  type RawExecDriver struct {
    32  	DriverContext
    33  	fingerprint.StaticFingerprinter
    34  }
    35  
    36  // rawExecHandle is returned from Start/Open as a handle to the PID
    37  type rawExecHandle struct {
    38  	version      string
    39  	pluginClient *plugin.Client
    40  	userPid      int
    41  	executor     executor.Executor
    42  	killTimeout  time.Duration
    43  	allocDir     *allocdir.AllocDir
    44  	logger       *log.Logger
    45  	waitCh       chan *cstructs.WaitResult
    46  	doneCh       chan struct{}
    47  }
    48  
    49  // NewRawExecDriver is used to create a new raw exec driver
    50  func NewRawExecDriver(ctx *DriverContext) Driver {
    51  	return &RawExecDriver{DriverContext: *ctx}
    52  }
    53  
    54  func (d *RawExecDriver) Fingerprint(cfg *config.Config, node *structs.Node) (bool, error) {
    55  	// Check that the user has explicitly enabled this executor.
    56  	enabled := cfg.ReadBoolDefault(rawExecConfigOption, false)
    57  
    58  	if enabled {
    59  		d.logger.Printf("[WARN] driver.raw_exec: raw exec is enabled. Only enable if needed")
    60  		node.Attributes["driver.raw_exec"] = "1"
    61  		return true, nil
    62  	}
    63  
    64  	return false, nil
    65  }
    66  
    67  func (d *RawExecDriver) Start(ctx *ExecContext, task *structs.Task) (DriverHandle, error) {
    68  	var driverConfig ExecDriverConfig
    69  	if err := mapstructure.WeakDecode(task.Config, &driverConfig); err != nil {
    70  		return nil, err
    71  	}
    72  	// Get the tasks local directory.
    73  	taskName := d.DriverContext.taskName
    74  	taskDir, ok := ctx.AllocDir.TaskDirs[taskName]
    75  	if !ok {
    76  		return nil, fmt.Errorf("Could not find task directory for task: %v", d.DriverContext.taskName)
    77  	}
    78  
    79  	// Get the command to be ran
    80  	command := driverConfig.Command
    81  	if err := validateCommand(command, "args"); err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	// Check if an artificat is specified and attempt to download it
    86  	source, ok := task.Config["artifact_source"]
    87  	if ok && source != "" {
    88  		// Proceed to download an artifact to be executed.
    89  		_, err := getter.GetArtifact(
    90  			taskDir,
    91  			driverConfig.ArtifactSource,
    92  			driverConfig.Checksum,
    93  			d.logger,
    94  		)
    95  		if err != nil {
    96  			return nil, err
    97  		}
    98  	}
    99  
   100  	bin, err := discover.NomadExecutable()
   101  	if err != nil {
   102  		return nil, fmt.Errorf("unable to find the nomad binary: %v", err)
   103  	}
   104  	pluginLogFile := filepath.Join(taskDir, fmt.Sprintf("%s-executor.out", task.Name))
   105  	pluginConfig := &plugin.ClientConfig{
   106  		Cmd: exec.Command(bin, "executor", pluginLogFile),
   107  	}
   108  
   109  	exec, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config)
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  	executorCtx := &executor.ExecutorContext{
   114  		TaskEnv:       d.taskEnv,
   115  		AllocDir:      ctx.AllocDir,
   116  		TaskName:      task.Name,
   117  		TaskResources: task.Resources,
   118  		LogConfig:     task.LogConfig,
   119  	}
   120  	ps, err := exec.LaunchCmd(&executor.ExecCommand{Cmd: command, Args: driverConfig.Args}, executorCtx)
   121  	if err != nil {
   122  		pluginClient.Kill()
   123  		return nil, fmt.Errorf("error starting process via the plugin: %v", err)
   124  	}
   125  	d.logger.Printf("[DEBUG] driver.raw_exec: started process with pid: %v", ps.Pid)
   126  
   127  	// Return a driver handle
   128  	h := &rawExecHandle{
   129  		pluginClient: pluginClient,
   130  		executor:     exec,
   131  		userPid:      ps.Pid,
   132  		killTimeout:  d.DriverContext.KillTimeout(task),
   133  		allocDir:     ctx.AllocDir,
   134  		version:      d.config.Version,
   135  		logger:       d.logger,
   136  		doneCh:       make(chan struct{}),
   137  		waitCh:       make(chan *cstructs.WaitResult, 1),
   138  	}
   139  	go h.run()
   140  	return h, nil
   141  }
   142  
   143  type rawExecId struct {
   144  	Version      string
   145  	KillTimeout  time.Duration
   146  	UserPid      int
   147  	PluginConfig *PluginReattachConfig
   148  	AllocDir     *allocdir.AllocDir
   149  }
   150  
   151  func (d *RawExecDriver) Open(ctx *ExecContext, handleID string) (DriverHandle, error) {
   152  	id := &rawExecId{}
   153  	if err := json.Unmarshal([]byte(handleID), id); err != nil {
   154  		return nil, fmt.Errorf("Failed to parse handle '%s': %v", handleID, err)
   155  	}
   156  
   157  	pluginConfig := &plugin.ClientConfig{
   158  		Reattach: id.PluginConfig.PluginConfig(),
   159  	}
   160  	executor, pluginClient, err := createExecutor(pluginConfig, d.config.LogOutput, d.config)
   161  	if err != nil {
   162  		d.logger.Println("[ERR] driver.raw_exec: error connecting to plugin so destroying plugin pid and user pid")
   163  		if e := destroyPlugin(id.PluginConfig.Pid, id.UserPid); e != nil {
   164  			d.logger.Printf("[ERR] driver.raw_exec: error destroying plugin and userpid: %v", e)
   165  		}
   166  		return nil, fmt.Errorf("error connecting to plugin: %v", err)
   167  	}
   168  
   169  	// Return a driver handle
   170  	h := &rawExecHandle{
   171  		pluginClient: pluginClient,
   172  		executor:     executor,
   173  		userPid:      id.UserPid,
   174  		logger:       d.logger,
   175  		killTimeout:  id.KillTimeout,
   176  		allocDir:     id.AllocDir,
   177  		version:      id.Version,
   178  		doneCh:       make(chan struct{}),
   179  		waitCh:       make(chan *cstructs.WaitResult, 1),
   180  	}
   181  	go h.run()
   182  	return h, nil
   183  }
   184  
   185  func (h *rawExecHandle) ID() string {
   186  	id := rawExecId{
   187  		Version:      h.version,
   188  		KillTimeout:  h.killTimeout,
   189  		PluginConfig: NewPluginReattachConfig(h.pluginClient.ReattachConfig()),
   190  		UserPid:      h.userPid,
   191  		AllocDir:     h.allocDir,
   192  	}
   193  
   194  	data, err := json.Marshal(id)
   195  	if err != nil {
   196  		h.logger.Printf("[ERR] driver.raw_exec: failed to marshal ID to JSON: %s", err)
   197  	}
   198  	return string(data)
   199  }
   200  
   201  func (h *rawExecHandle) WaitCh() chan *cstructs.WaitResult {
   202  	return h.waitCh
   203  }
   204  
   205  func (h *rawExecHandle) Update(task *structs.Task) error {
   206  	// Store the updated kill timeout.
   207  	h.killTimeout = task.KillTimeout
   208  	h.executor.UpdateLogConfig(task.LogConfig)
   209  
   210  	// Update is not possible
   211  	return nil
   212  }
   213  
   214  func (h *rawExecHandle) Kill() error {
   215  	if err := h.executor.ShutDown(); err != nil {
   216  		if h.pluginClient.Exited() {
   217  			return nil
   218  		}
   219  		return fmt.Errorf("executor Shutdown failed: %v", err)
   220  	}
   221  
   222  	select {
   223  	case <-h.doneCh:
   224  		return nil
   225  	case <-time.After(h.killTimeout):
   226  		if h.pluginClient.Exited() {
   227  			return nil
   228  		}
   229  		if err := h.executor.Exit(); err != nil {
   230  			return fmt.Errorf("executor Exit failed: %v", err)
   231  		}
   232  
   233  		return nil
   234  	}
   235  }
   236  
   237  func (h *rawExecHandle) run() {
   238  	ps, err := h.executor.Wait()
   239  	close(h.doneCh)
   240  	if ps.ExitCode == 0 && err != nil {
   241  		if e := killProcess(h.userPid); e != nil {
   242  			h.logger.Printf("[ERR] driver.raw_exec: error killing user process: %v", e)
   243  		}
   244  		if e := h.allocDir.UnmountAll(); e != nil {
   245  			h.logger.Printf("[ERR] driver.raw_exec: unmounting dev,proc and alloc dirs failed: %v", e)
   246  		}
   247  	}
   248  	h.waitCh <- &cstructs.WaitResult{ExitCode: ps.ExitCode, Signal: 0, Err: err}
   249  	close(h.waitCh)
   250  	h.pluginClient.Kill()
   251  }