github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/drivers/mock/driver.go (about)

     1  package mock
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/rand"
     9  	"strconv"
    10  	"strings"
    11  	"sync"
    12  	"time"
    13  
    14  	hclog "github.com/hashicorp/go-hclog"
    15  	"github.com/hashicorp/nomad/drivers/shared/eventer"
    16  	"github.com/hashicorp/nomad/helper/pluginutils/loader"
    17  	"github.com/hashicorp/nomad/nomad/structs"
    18  	"github.com/hashicorp/nomad/plugins/base"
    19  	"github.com/hashicorp/nomad/plugins/drivers"
    20  	"github.com/hashicorp/nomad/plugins/shared/hclspec"
    21  	pstructs "github.com/hashicorp/nomad/plugins/shared/structs"
    22  )
    23  
    24  const (
    25  	// pluginName is the name of the plugin
    26  	pluginName = "mock_driver"
    27  
    28  	// fingerprintPeriod is the interval at which the driver will send fingerprint responses
    29  	fingerprintPeriod = 500 * time.Millisecond
    30  
    31  	// taskHandleVersion is the version of task handle which this driver sets
    32  	// and understands how to decode driver state
    33  	taskHandleVersion = 1
    34  )
    35  
    36  var (
    37  	// PluginID is the mock driver plugin metadata registered in the plugin
    38  	// catalog.
    39  	PluginID = loader.PluginID{
    40  		Name:       pluginName,
    41  		PluginType: base.PluginTypeDriver,
    42  	}
    43  
    44  	// PluginConfig is the mock driver factory function registered in the
    45  	// plugin catalog.
    46  	PluginConfig = &loader.InternalPluginConfig{
    47  		Config:  map[string]interface{}{},
    48  		Factory: func(l hclog.Logger) interface{} { return NewMockDriver(l) },
    49  	}
    50  
    51  	// pluginInfo is the response returned for the PluginInfo RPC
    52  	pluginInfo = &base.PluginInfoResponse{
    53  		Type:              base.PluginTypeDriver,
    54  		PluginApiVersions: []string{drivers.ApiVersion010},
    55  		PluginVersion:     "0.1.0",
    56  		Name:              pluginName,
    57  	}
    58  
    59  	// configSpec is the hcl specification returned by the ConfigSchema RPC
    60  	configSpec = hclspec.NewObject(map[string]*hclspec.Spec{
    61  		"fs_isolation": hclspec.NewDefault(
    62  			hclspec.NewAttr("fs_isolation", "string", false),
    63  			hclspec.NewLiteral(fmt.Sprintf("%q", drivers.FSIsolationNone)),
    64  		),
    65  		"shutdown_periodic_after": hclspec.NewDefault(
    66  			hclspec.NewAttr("shutdown_periodic_after", "bool", false),
    67  			hclspec.NewLiteral("false"),
    68  		),
    69  		"shutdown_periodic_duration": hclspec.NewAttr("shutdown_periodic_duration", "number", false),
    70  	})
    71  
    72  	// taskConfigSpec is the hcl specification for the driver config section of
    73  	// a task within a job. It is returned in the TaskConfigSchema RPC
    74  	taskConfigSpec = hclspec.NewObject(map[string]*hclspec.Spec{
    75  		"start_error":             hclspec.NewAttr("start_error", "string", false),
    76  		"start_error_recoverable": hclspec.NewAttr("start_error_recoverable", "bool", false),
    77  		"start_block_for":         hclspec.NewAttr("start_block_for", "string", false),
    78  		"kill_after":              hclspec.NewAttr("kill_after", "string", false),
    79  		"plugin_exit_after":       hclspec.NewAttr("plugin_exit_after", "string", false),
    80  		"driver_ip":               hclspec.NewAttr("driver_ip", "string", false),
    81  		"driver_advertise":        hclspec.NewAttr("driver_advertise", "bool", false),
    82  		"driver_port_map":         hclspec.NewAttr("driver_port_map", "string", false),
    83  
    84  		"run_for":                hclspec.NewAttr("run_for", "string", false),
    85  		"exit_code":              hclspec.NewAttr("exit_code", "number", false),
    86  		"exit_signal":            hclspec.NewAttr("exit_signal", "number", false),
    87  		"exit_err_msg":           hclspec.NewAttr("exit_err_msg", "string", false),
    88  		"signal_error":           hclspec.NewAttr("signal_error", "string", false),
    89  		"stdout_string":          hclspec.NewAttr("stdout_string", "string", false),
    90  		"stdout_repeat":          hclspec.NewAttr("stdout_repeat", "number", false),
    91  		"stdout_repeat_duration": hclspec.NewAttr("stdout_repeat_duration", "string", false),
    92  		"stderr_string":          hclspec.NewAttr("stderr_string", "string", false),
    93  		"stderr_repeat":          hclspec.NewAttr("stderr_repeat", "number", false),
    94  		"stderr_repeat_duration": hclspec.NewAttr("stderr_repeat_duration", "string", false),
    95  
    96  		"exec_command": hclspec.NewBlock("exec_command", false, hclspec.NewObject(map[string]*hclspec.Spec{
    97  			"run_for":                hclspec.NewAttr("run_for", "string", false),
    98  			"exit_code":              hclspec.NewAttr("exit_code", "number", false),
    99  			"exit_signal":            hclspec.NewAttr("exit_signal", "number", false),
   100  			"exit_err_msg":           hclspec.NewAttr("exit_err_msg", "string", false),
   101  			"signal_error":           hclspec.NewAttr("signal_error", "string", false),
   102  			"stdout_string":          hclspec.NewAttr("stdout_string", "string", false),
   103  			"stdout_repeat":          hclspec.NewAttr("stdout_repeat", "number", false),
   104  			"stdout_repeat_duration": hclspec.NewAttr("stdout_repeat_duration", "string", false),
   105  			"stderr_string":          hclspec.NewAttr("stderr_string", "string", false),
   106  			"stderr_repeat":          hclspec.NewAttr("stderr_repeat", "number", false),
   107  			"stderr_repeat_duration": hclspec.NewAttr("stderr_repeat_duration", "string", false),
   108  		})),
   109  	})
   110  )
   111  
   112  // Driver is a mock DriverPlugin implementation
   113  type Driver struct {
   114  	// eventer is used to handle multiplexing of TaskEvents calls such that an
   115  	// event can be broadcast to all callers
   116  	eventer *eventer.Eventer
   117  
   118  	// capabilities is returned by the Capabilities RPC and indicates what
   119  	// optional features this driver supports
   120  	capabilities *drivers.Capabilities
   121  
   122  	// config is the driver configuration set by the SetConfig RPC
   123  	config *Config
   124  
   125  	// tasks is the in memory datastore mapping taskIDs to mockDriverHandles
   126  	tasks *taskStore
   127  
   128  	// ctx is the context for the driver. It is passed to other subsystems to
   129  	// coordinate shutdown
   130  	ctx context.Context
   131  
   132  	// signalShutdown is called when the driver is shutting down and cancels the
   133  	// ctx passed to any subsystems
   134  	signalShutdown context.CancelFunc
   135  
   136  	shutdownFingerprintTime time.Time
   137  
   138  	// lastDriverTaskConfig is the last *drivers.TaskConfig passed to StartTask
   139  	lastDriverTaskConfig *drivers.TaskConfig
   140  
   141  	// lastTaskConfig is the last decoded *TaskConfig created by StartTask
   142  	lastTaskConfig *TaskConfig
   143  
   144  	// lastMu guards access to last[Driver]TaskConfig
   145  	lastMu sync.Mutex
   146  
   147  	// logger will log to the Nomad agent
   148  	logger hclog.Logger
   149  }
   150  
   151  // NewMockDriver returns a new DriverPlugin implementation
   152  func NewMockDriver(logger hclog.Logger) drivers.DriverPlugin {
   153  	ctx, cancel := context.WithCancel(context.Background())
   154  	logger = logger.Named(pluginName)
   155  
   156  	capabilities := &drivers.Capabilities{
   157  		SendSignals: true,
   158  		Exec:        true,
   159  		FSIsolation: drivers.FSIsolationNone,
   160  	}
   161  
   162  	return &Driver{
   163  		eventer:        eventer.NewEventer(ctx, logger),
   164  		capabilities:   capabilities,
   165  		config:         &Config{},
   166  		tasks:          newTaskStore(),
   167  		ctx:            ctx,
   168  		signalShutdown: cancel,
   169  		logger:         logger,
   170  	}
   171  }
   172  
   173  // Config is the configuration for the driver that applies to all tasks
   174  type Config struct {
   175  	FSIsolation string `codec:"fs_isolation"`
   176  
   177  	// ShutdownPeriodicAfter is a toggle that can be used during tests to
   178  	// "stop" a previously-functioning driver, allowing for testing of periodic
   179  	// drivers and fingerprinters
   180  	ShutdownPeriodicAfter bool `codec:"shutdown_periodic_after"`
   181  
   182  	// ShutdownPeriodicDuration is a option that can be used during tests
   183  	// to "stop" a previously functioning driver after the specified duration
   184  	// for testing of periodic drivers and fingerprinters.
   185  	ShutdownPeriodicDuration time.Duration `codec:"shutdown_periodic_duration"`
   186  }
   187  
   188  type Command struct {
   189  	// RunFor is the duration for which the fake task runs for. After this
   190  	// period the MockDriver responds to the task running indicating that the
   191  	// task has terminated
   192  	RunFor         string `codec:"run_for"`
   193  	runForDuration time.Duration
   194  
   195  	// ExitCode is the exit code with which the MockDriver indicates the task
   196  	// has exited
   197  	ExitCode int `codec:"exit_code"`
   198  
   199  	// ExitSignal is the signal with which the MockDriver indicates the task has
   200  	// been killed
   201  	ExitSignal int `codec:"exit_signal"`
   202  
   203  	// ExitErrMsg is the error message that the task returns while exiting
   204  	ExitErrMsg string `codec:"exit_err_msg"`
   205  
   206  	// SignalErr is the error message that the task returns if signalled
   207  	SignalErr string `codec:"signal_error"`
   208  
   209  	// StdoutString is the string that should be sent to stdout
   210  	StdoutString string `codec:"stdout_string"`
   211  
   212  	// StdoutRepeat is the number of times the output should be sent.
   213  	StdoutRepeat int `codec:"stdout_repeat"`
   214  
   215  	// StdoutRepeatDur is the duration between repeated outputs.
   216  	StdoutRepeatDur      string `codec:"stdout_repeat_duration"`
   217  	stdoutRepeatDuration time.Duration
   218  
   219  	// StderrString is the string that should be sent to stderr
   220  	StderrString string `codec:"stderr_string"`
   221  
   222  	// StderrRepeat is the number of times the errput should be sent.
   223  	StderrRepeat int `codec:"stderr_repeat"`
   224  
   225  	// StderrRepeatDur is the duration between repeated errputs.
   226  	StderrRepeatDur      string `codec:"stderr_repeat_duration"`
   227  	stderrRepeatDuration time.Duration
   228  }
   229  
   230  // TaskConfig is the driver configuration of a task within a job
   231  type TaskConfig struct {
   232  	Command
   233  
   234  	ExecCommand *Command `codec:"exec_command"`
   235  
   236  	// PluginExitAfter is the duration after which the mock driver indicates the
   237  	// plugin has exited via the WaitTask call.
   238  	PluginExitAfter         string `codec:"plugin_exit_after"`
   239  	pluginExitAfterDuration time.Duration
   240  
   241  	// StartErr specifies the error that should be returned when starting the
   242  	// mock driver.
   243  	StartErr string `codec:"start_error"`
   244  
   245  	// StartErrRecoverable marks the error returned is recoverable
   246  	StartErrRecoverable bool `codec:"start_error_recoverable"`
   247  
   248  	// StartBlockFor specifies a duration in which to block before returning
   249  	StartBlockFor         string `codec:"start_block_for"`
   250  	startBlockForDuration time.Duration
   251  
   252  	// KillAfter is the duration after which the mock driver indicates the task
   253  	// has exited after getting the initial SIGINT signal
   254  	KillAfter         string `codec:"kill_after"`
   255  	killAfterDuration time.Duration
   256  
   257  	// DriverIP will be returned as the DriverNetwork.IP from Start()
   258  	DriverIP string `codec:"driver_ip"`
   259  
   260  	// DriverAdvertise will be returned as DriverNetwork.AutoAdvertise from
   261  	// Start().
   262  	DriverAdvertise bool `codec:"driver_advertise"`
   263  
   264  	// DriverPortMap will parse a label:number pair and return it in
   265  	// DriverNetwork.PortMap from Start().
   266  	DriverPortMap string `codec:"driver_port_map"`
   267  }
   268  
   269  type MockTaskState struct {
   270  	StartedAt time.Time
   271  }
   272  
   273  func (d *Driver) PluginInfo() (*base.PluginInfoResponse, error) {
   274  	return pluginInfo, nil
   275  }
   276  
   277  func (d *Driver) ConfigSchema() (*hclspec.Spec, error) {
   278  	return configSpec, nil
   279  }
   280  
   281  func (d *Driver) SetConfig(cfg *base.Config) error {
   282  	var config Config
   283  	if len(cfg.PluginConfig) != 0 {
   284  		if err := base.MsgPackDecode(cfg.PluginConfig, &config); err != nil {
   285  			return err
   286  		}
   287  	}
   288  
   289  	d.config = &config
   290  	if d.config.ShutdownPeriodicAfter {
   291  		d.shutdownFingerprintTime = time.Now().Add(d.config.ShutdownPeriodicDuration)
   292  	}
   293  
   294  	isolation := config.FSIsolation
   295  	if isolation != "" {
   296  		d.capabilities.FSIsolation = drivers.FSIsolation(isolation)
   297  	}
   298  
   299  	return nil
   300  }
   301  
   302  func (d *Driver) TaskConfigSchema() (*hclspec.Spec, error) {
   303  	return taskConfigSpec, nil
   304  }
   305  
   306  func (d *Driver) Capabilities() (*drivers.Capabilities, error) {
   307  	return d.capabilities, nil
   308  }
   309  
   310  func (d *Driver) Fingerprint(ctx context.Context) (<-chan *drivers.Fingerprint, error) {
   311  	ch := make(chan *drivers.Fingerprint)
   312  	go d.handleFingerprint(ctx, ch)
   313  	return ch, nil
   314  }
   315  
   316  func (d *Driver) handleFingerprint(ctx context.Context, ch chan *drivers.Fingerprint) {
   317  	ticker := time.NewTimer(0)
   318  	for {
   319  		select {
   320  		case <-ctx.Done():
   321  			return
   322  		case <-d.ctx.Done():
   323  			return
   324  		case <-ticker.C:
   325  			ticker.Reset(fingerprintPeriod)
   326  			ch <- d.buildFingerprint()
   327  		}
   328  	}
   329  }
   330  
   331  func (d *Driver) buildFingerprint() *drivers.Fingerprint {
   332  	var health drivers.HealthState
   333  	var desc string
   334  	attrs := map[string]*pstructs.Attribute{}
   335  	if !d.shutdownFingerprintTime.IsZero() && time.Now().After(d.shutdownFingerprintTime) {
   336  		health = drivers.HealthStateUndetected
   337  		desc = "disabled"
   338  	} else {
   339  		health = drivers.HealthStateHealthy
   340  		attrs["driver.mock"] = pstructs.NewBoolAttribute(true)
   341  		desc = drivers.DriverHealthy
   342  	}
   343  
   344  	return &drivers.Fingerprint{
   345  		Attributes:        attrs,
   346  		Health:            health,
   347  		HealthDescription: desc,
   348  	}
   349  }
   350  
   351  func (d *Driver) RecoverTask(handle *drivers.TaskHandle) error {
   352  	if handle == nil {
   353  		return fmt.Errorf("handle cannot be nil")
   354  	}
   355  
   356  	// Unmarshall the driver state and create a new handle
   357  	var taskState MockTaskState
   358  	if err := handle.GetDriverState(&taskState); err != nil {
   359  		d.logger.Error("failed to decode task state from handle", "error", err, "task_id", handle.Config.ID)
   360  		return fmt.Errorf("failed to decode task state from handle: %v", err)
   361  	}
   362  
   363  	driverCfg, err := parseDriverConfig(handle.Config)
   364  	if err != nil {
   365  		d.logger.Error("failed to parse driver config from handle", "error", err, "task_id", handle.Config.ID, "config", hclog.Fmt("%+v", handle.Config))
   366  		return fmt.Errorf("failed to parse driver config from handle: %v", err)
   367  	}
   368  
   369  	// Remove the plugin exit time if set
   370  	driverCfg.pluginExitAfterDuration = 0
   371  
   372  	// Correct the run_for time based on how long it has already been running
   373  	now := time.Now()
   374  	driverCfg.runForDuration = driverCfg.runForDuration - now.Sub(taskState.StartedAt)
   375  
   376  	h := newTaskHandle(handle.Config, driverCfg, d.logger)
   377  	h.Recovered = true
   378  	d.tasks.Set(handle.Config.ID, h)
   379  	go h.run()
   380  	return nil
   381  }
   382  
   383  func (c *Command) parseDurations() error {
   384  	var err error
   385  	if c.runForDuration, err = parseDuration(c.RunFor); err != nil {
   386  		return fmt.Errorf("run_for %v not a valid duration: %v", c.RunFor, err)
   387  	}
   388  
   389  	if c.stdoutRepeatDuration, err = parseDuration(c.StdoutRepeatDur); err != nil {
   390  		return fmt.Errorf("stdout_repeat_duration %v not a valid duration: %v", c.stdoutRepeatDuration, err)
   391  	}
   392  
   393  	if c.stderrRepeatDuration, err = parseDuration(c.StderrRepeatDur); err != nil {
   394  		return fmt.Errorf("stderr_repeat_duration %v not a valid duration: %v", c.stderrRepeatDuration, err)
   395  	}
   396  
   397  	return nil
   398  }
   399  
   400  func parseDriverConfig(cfg *drivers.TaskConfig) (*TaskConfig, error) {
   401  	var driverConfig TaskConfig
   402  	if err := cfg.DecodeDriverConfig(&driverConfig); err != nil {
   403  		return nil, err
   404  	}
   405  
   406  	var err error
   407  	if driverConfig.startBlockForDuration, err = parseDuration(driverConfig.StartBlockFor); err != nil {
   408  		return nil, fmt.Errorf("start_block_for %v not a valid duration: %v", driverConfig.StartBlockFor, err)
   409  	}
   410  
   411  	if driverConfig.pluginExitAfterDuration, err = parseDuration(driverConfig.PluginExitAfter); err != nil {
   412  		return nil, fmt.Errorf("plugin_exit_after %v not a valid duration: %v", driverConfig.PluginExitAfter, err)
   413  	}
   414  
   415  	if err = driverConfig.parseDurations(); err != nil {
   416  		return nil, err
   417  	}
   418  
   419  	if driverConfig.ExecCommand != nil {
   420  		if err = driverConfig.ExecCommand.parseDurations(); err != nil {
   421  			return nil, err
   422  		}
   423  	}
   424  
   425  	return &driverConfig, nil
   426  }
   427  
   428  func newTaskHandle(cfg *drivers.TaskConfig, driverConfig *TaskConfig, logger hclog.Logger) *taskHandle {
   429  	killCtx, killCancel := context.WithCancel(context.Background())
   430  	h := &taskHandle{
   431  		taskConfig:      cfg,
   432  		command:         driverConfig.Command,
   433  		execCommand:     driverConfig.ExecCommand,
   434  		pluginExitAfter: driverConfig.pluginExitAfterDuration,
   435  		killAfter:       driverConfig.killAfterDuration,
   436  		logger:          logger.With("task_name", cfg.Name),
   437  		waitCh:          make(chan interface{}),
   438  		killCh:          killCtx.Done(),
   439  		kill:            killCancel,
   440  		startedAt:       time.Now(),
   441  	}
   442  	return h
   443  }
   444  
   445  func (d *Driver) StartTask(cfg *drivers.TaskConfig) (*drivers.TaskHandle, *drivers.DriverNetwork, error) {
   446  	driverConfig, err := parseDriverConfig(cfg)
   447  	if err != nil {
   448  		return nil, nil, err
   449  	}
   450  
   451  	if driverConfig.startBlockForDuration != 0 {
   452  		time.Sleep(driverConfig.startBlockForDuration)
   453  	}
   454  
   455  	// Store last configs
   456  	d.lastMu.Lock()
   457  	d.lastDriverTaskConfig = cfg
   458  	d.lastTaskConfig = driverConfig
   459  	d.lastMu.Unlock()
   460  
   461  	if driverConfig.StartErr != "" {
   462  		return nil, nil, structs.NewRecoverableError(errors.New(driverConfig.StartErr), driverConfig.StartErrRecoverable)
   463  	}
   464  
   465  	// Create the driver network
   466  	net := &drivers.DriverNetwork{
   467  		IP:            driverConfig.DriverIP,
   468  		AutoAdvertise: driverConfig.DriverAdvertise,
   469  	}
   470  	if raw := driverConfig.DriverPortMap; len(raw) > 0 {
   471  		parts := strings.Split(raw, ":")
   472  		if len(parts) != 2 {
   473  			return nil, nil, fmt.Errorf("malformed port map: %q", raw)
   474  		}
   475  		port, err := strconv.Atoi(parts[1])
   476  		if err != nil {
   477  			return nil, nil, fmt.Errorf("malformed port map: %q -- error: %v", raw, err)
   478  		}
   479  		net.PortMap = map[string]int{parts[0]: port}
   480  	}
   481  
   482  	h := newTaskHandle(cfg, driverConfig, d.logger)
   483  	driverState := MockTaskState{
   484  		StartedAt: h.startedAt,
   485  	}
   486  	handle := drivers.NewTaskHandle(taskHandleVersion)
   487  	handle.Config = cfg
   488  	if err := handle.SetDriverState(&driverState); err != nil {
   489  		d.logger.Error("failed to start task, error setting driver state", "error", err, "task_name", cfg.Name)
   490  		return nil, nil, fmt.Errorf("failed to set driver state: %v", err)
   491  	}
   492  
   493  	d.tasks.Set(cfg.ID, h)
   494  
   495  	d.logger.Debug("starting task", "task_name", cfg.Name)
   496  	go h.run()
   497  	return handle, net, nil
   498  
   499  }
   500  
   501  func (d *Driver) WaitTask(ctx context.Context, taskID string) (<-chan *drivers.ExitResult, error) {
   502  	handle, ok := d.tasks.Get(taskID)
   503  	if !ok {
   504  		return nil, drivers.ErrTaskNotFound
   505  	}
   506  
   507  	ch := make(chan *drivers.ExitResult)
   508  	go d.handleWait(ctx, handle, ch)
   509  
   510  	return ch, nil
   511  
   512  }
   513  func (d *Driver) handleWait(ctx context.Context, handle *taskHandle, ch chan *drivers.ExitResult) {
   514  	defer close(ch)
   515  
   516  	select {
   517  	case <-ctx.Done():
   518  		return
   519  	case <-d.ctx.Done():
   520  		return
   521  	case <-handle.waitCh:
   522  		ch <- handle.exitResult
   523  	}
   524  }
   525  func (d *Driver) StopTask(taskID string, timeout time.Duration, signal string) error {
   526  	h, ok := d.tasks.Get(taskID)
   527  	if !ok {
   528  		return drivers.ErrTaskNotFound
   529  	}
   530  
   531  	d.logger.Debug("killing task", "task_name", h.taskConfig.Name, "kill_after", h.killAfter)
   532  
   533  	select {
   534  	case <-h.waitCh:
   535  		d.logger.Debug("not killing task: already exited", "task_name", h.taskConfig.Name)
   536  	case <-time.After(h.killAfter):
   537  		d.logger.Debug("killing task due to kill_after", "task_name", h.taskConfig.Name)
   538  		h.kill()
   539  	}
   540  	return nil
   541  }
   542  
   543  func (d *Driver) DestroyTask(taskID string, force bool) error {
   544  	handle, ok := d.tasks.Get(taskID)
   545  	if !ok {
   546  		return drivers.ErrTaskNotFound
   547  	}
   548  
   549  	if handle.IsRunning() && !force {
   550  		return fmt.Errorf("cannot destroy running task")
   551  	}
   552  
   553  	d.tasks.Delete(taskID)
   554  	return nil
   555  }
   556  
   557  func (d *Driver) InspectTask(taskID string) (*drivers.TaskStatus, error) {
   558  	h, ok := d.tasks.Get(taskID)
   559  	if !ok {
   560  		return nil, drivers.ErrTaskNotFound
   561  	}
   562  
   563  	return h.TaskStatus(), nil
   564  
   565  }
   566  
   567  func (d *Driver) TaskStats(ctx context.Context, taskID string, interval time.Duration) (<-chan *drivers.TaskResourceUsage, error) {
   568  	ch := make(chan *drivers.TaskResourceUsage)
   569  	go d.handleStats(ctx, ch)
   570  	return ch, nil
   571  }
   572  
   573  func (d *Driver) handleStats(ctx context.Context, ch chan<- *drivers.TaskResourceUsage) {
   574  	timer := time.NewTimer(0)
   575  	for {
   576  		select {
   577  		case <-timer.C:
   578  			// Generate random value for the memory usage
   579  			s := &drivers.TaskResourceUsage{
   580  				ResourceUsage: &drivers.ResourceUsage{
   581  					MemoryStats: &drivers.MemoryStats{
   582  						RSS:      rand.Uint64(),
   583  						Measured: []string{"RSS"},
   584  					},
   585  				},
   586  				Timestamp: time.Now().UTC().UnixNano(),
   587  			}
   588  			select {
   589  			case <-ctx.Done():
   590  				return
   591  			case ch <- s:
   592  			default:
   593  			}
   594  		case <-ctx.Done():
   595  			return
   596  		}
   597  	}
   598  }
   599  
   600  func (d *Driver) TaskEvents(ctx context.Context) (<-chan *drivers.TaskEvent, error) {
   601  	return d.eventer.TaskEvents(ctx)
   602  }
   603  
   604  func (d *Driver) SignalTask(taskID string, signal string) error {
   605  	h, ok := d.tasks.Get(taskID)
   606  	if !ok {
   607  		return drivers.ErrTaskNotFound
   608  	}
   609  
   610  	if h.command.SignalErr == "" {
   611  		return nil
   612  	}
   613  
   614  	return errors.New(h.command.SignalErr)
   615  }
   616  
   617  func (d *Driver) ExecTask(taskID string, cmd []string, timeout time.Duration) (*drivers.ExecTaskResult, error) {
   618  	h, ok := d.tasks.Get(taskID)
   619  	if !ok {
   620  		return nil, drivers.ErrTaskNotFound
   621  	}
   622  
   623  	res := drivers.ExecTaskResult{
   624  		Stdout:     []byte(fmt.Sprintf("Exec(%q, %q)", h.taskConfig.Name, cmd)),
   625  		ExitResult: &drivers.ExitResult{},
   626  	}
   627  	return &res, nil
   628  }
   629  
   630  var _ drivers.ExecTaskStreamingDriver = (*Driver)(nil)
   631  
   632  func (d *Driver) ExecTaskStreaming(ctx context.Context, taskID string, execOpts *drivers.ExecOptions) (*drivers.ExitResult, error) {
   633  	h, ok := d.tasks.Get(taskID)
   634  	if !ok {
   635  		return nil, drivers.ErrTaskNotFound
   636  	}
   637  
   638  	d.logger.Info("executing task", "command", h.execCommand, "task_id", taskID)
   639  
   640  	if h.execCommand == nil {
   641  		return nil, errors.New("no exec command is configured")
   642  	}
   643  
   644  	cancelCh := make(chan struct{})
   645  	exitTimer := make(chan time.Time)
   646  
   647  	cmd := *h.execCommand
   648  	if len(execOpts.Command) == 1 && execOpts.Command[0] == "showinput" {
   649  		stdin, _ := ioutil.ReadAll(execOpts.Stdin)
   650  		cmd = Command{
   651  			RunFor: "1ms",
   652  			StdoutString: fmt.Sprintf("TTY: %v\nStdin:\n%s\n",
   653  				execOpts.Tty,
   654  				stdin,
   655  			),
   656  		}
   657  	}
   658  
   659  	return runCommand(cmd, execOpts.Stdout, execOpts.Stderr, cancelCh, exitTimer, d.logger), nil
   660  }
   661  
   662  // GetTaskConfig is unique to the mock driver and for testing purposes only. It
   663  // returns the *drivers.TaskConfig passed to StartTask and the decoded
   664  // *mock.TaskConfig created by the last StartTask call.
   665  func (d *Driver) GetTaskConfig() (*drivers.TaskConfig, *TaskConfig) {
   666  	d.lastMu.Lock()
   667  	defer d.lastMu.Unlock()
   668  	return d.lastDriverTaskConfig, d.lastTaskConfig
   669  }
   670  
   671  // GetHandle is unique to the mock driver and for testing purposes only. It
   672  // returns the handle of the given task ID
   673  func (d *Driver) GetHandle(taskID string) *taskHandle {
   674  	h, _ := d.tasks.Get(taskID)
   675  	return h
   676  }
   677  
   678  func (d *Driver) Shutdown() {
   679  	d.signalShutdown()
   680  }
   681  
   682  func (d *Driver) CreateNetwork(allocID string) (*drivers.NetworkIsolationSpec, error) {
   683  	return nil, nil
   684  }
   685  
   686  func (d *Driver) DestroyNetwork(allocID string, spec *drivers.NetworkIsolationSpec) error {
   687  	return nil
   688  }