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