github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/drivers/shared/executor/legacy_executor_wrapper.go (about)

     1  package executor
     2  
     3  import (
     4  	"encoding/gob"
     5  	"fmt"
     6  	"net/rpc"
     7  	"os"
     8  	"syscall"
     9  	"time"
    10  
    11  	hclog "github.com/hashicorp/go-hclog"
    12  	plugin "github.com/hashicorp/go-plugin"
    13  	cstructs "github.com/hashicorp/nomad/client/structs"
    14  	"github.com/hashicorp/nomad/plugins/drivers"
    15  	"golang.org/x/net/context"
    16  )
    17  
    18  const (
    19  	// pre09DockerSignal is used in executor.Shutdown to know if it should
    20  	// call the ShutDown RPC on the pre09 executor
    21  	pre09DockerSignal = "docker"
    22  )
    23  
    24  // Registering these types since we have to serialize and de-serialize the Task
    25  // structs over the wire between drivers and the executor.
    26  func init() {
    27  	gob.Register([]interface{}{})
    28  	gob.Register(map[string]interface{}{})
    29  	gob.Register([]map[string]string{})
    30  	gob.Register([]map[string]int{})
    31  	gob.Register(syscall.Signal(0x1))
    32  }
    33  
    34  type legacyExecutorWrapper struct {
    35  	client *pre09ExecutorRPC
    36  	logger hclog.Logger
    37  }
    38  
    39  func (l *legacyExecutorWrapper) Launch(launchCmd *ExecCommand) (*ProcessState, error) {
    40  	return nil, fmt.Errorf("operation not supported for legacy exec wrapper")
    41  }
    42  
    43  func (l *legacyExecutorWrapper) Wait(ctx context.Context) (*ProcessState, error) {
    44  	ps, err := l.client.Wait()
    45  	if err != nil {
    46  		return nil, err
    47  	}
    48  
    49  	return &ProcessState{
    50  		Pid:      ps.Pid,
    51  		ExitCode: ps.ExitCode,
    52  		Signal:   ps.Signal,
    53  		Time:     ps.Time,
    54  	}, nil
    55  }
    56  
    57  func (l *legacyExecutorWrapper) Shutdown(signal string, gracePeriod time.Duration) error {
    58  	// The legacy docker driver only used the executor to start a syslog server
    59  	// for logging. Thus calling ShutDown for docker will always return an error
    60  	// because it never started a process through the executor. If signal is set
    61  	// to 'docker' then we'll skip the ShutDown RPC and just call Exit.
    62  	//
    63  	// This is painful to look at but will only be around a few releases
    64  	if signal != pre09DockerSignal {
    65  		if err := l.client.ShutDown(); err != nil {
    66  			return err
    67  		}
    68  	}
    69  
    70  	if err := l.client.Exit(); err != nil {
    71  		return err
    72  	}
    73  	return nil
    74  }
    75  
    76  func (l *legacyExecutorWrapper) UpdateResources(*drivers.Resources) error {
    77  	return fmt.Errorf("operation not supported for legacy exec wrapper")
    78  }
    79  
    80  func (l *legacyExecutorWrapper) Version() (*ExecutorVersion, error) {
    81  	v, err := l.client.Version()
    82  	if err != nil {
    83  		return nil, err
    84  	}
    85  
    86  	return &ExecutorVersion{
    87  		Version: v.Version,
    88  	}, nil
    89  }
    90  
    91  func (l *legacyExecutorWrapper) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {
    92  	ch := make(chan *cstructs.TaskResourceUsage, 1)
    93  	stats, err := l.client.Stats()
    94  	if err != nil {
    95  		close(ch)
    96  		return nil, err
    97  	}
    98  	select {
    99  	case ch <- stats:
   100  	default:
   101  	}
   102  	go l.handleStats(ctx, interval, ch)
   103  	return ch, nil
   104  }
   105  
   106  func (l *legacyExecutorWrapper) handleStats(ctx context.Context, interval time.Duration, ch chan *cstructs.TaskResourceUsage) {
   107  	defer close(ch)
   108  	ticker := time.NewTicker(interval)
   109  	for range ticker.C {
   110  		stats, err := l.client.Stats()
   111  		if err != nil {
   112  			if err == rpc.ErrShutdown {
   113  				return
   114  			}
   115  			l.logger.Warn("stats collection from legacy executor failed, waiting for next interval", "error", err)
   116  			continue
   117  		}
   118  		if stats != nil {
   119  			select {
   120  			case ch <- stats:
   121  			default:
   122  			}
   123  		}
   124  
   125  	}
   126  }
   127  
   128  func (l *legacyExecutorWrapper) Signal(s os.Signal) error {
   129  	return l.client.Signal(s)
   130  }
   131  
   132  func (l *legacyExecutorWrapper) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) {
   133  	return l.client.Exec(deadline, cmd, args)
   134  }
   135  
   136  type pre09ExecutorRPC struct {
   137  	client *rpc.Client
   138  	logger hclog.Logger
   139  }
   140  
   141  type pre09ExecCmdArgs struct {
   142  	Deadline time.Time
   143  	Name     string
   144  	Args     []string
   145  }
   146  
   147  type pre09ExecCmdReturn struct {
   148  	Output []byte
   149  	Code   int
   150  }
   151  
   152  func (e *pre09ExecutorRPC) Wait() (*ProcessState, error) {
   153  	var ps ProcessState
   154  	err := e.client.Call("Plugin.Wait", new(interface{}), &ps)
   155  	return &ps, err
   156  }
   157  
   158  func (e *pre09ExecutorRPC) ShutDown() error {
   159  	return e.client.Call("Plugin.ShutDown", new(interface{}), new(interface{}))
   160  }
   161  
   162  func (e *pre09ExecutorRPC) Exit() error {
   163  	return e.client.Call("Plugin.Exit", new(interface{}), new(interface{}))
   164  }
   165  
   166  func (e *pre09ExecutorRPC) Version() (*ExecutorVersion, error) {
   167  	var version ExecutorVersion
   168  	err := e.client.Call("Plugin.Version", new(interface{}), &version)
   169  	return &version, err
   170  }
   171  
   172  func (e *pre09ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) {
   173  	var resourceUsage cstructs.TaskResourceUsage
   174  	err := e.client.Call("Plugin.Stats", new(interface{}), &resourceUsage)
   175  	return &resourceUsage, err
   176  }
   177  
   178  func (e *pre09ExecutorRPC) Signal(s os.Signal) error {
   179  	return e.client.Call("Plugin.Signal", &s, new(interface{}))
   180  }
   181  
   182  func (e *pre09ExecutorRPC) Exec(deadline time.Time, name string, args []string) ([]byte, int, error) {
   183  	req := pre09ExecCmdArgs{
   184  		Deadline: deadline,
   185  		Name:     name,
   186  		Args:     args,
   187  	}
   188  	var resp *pre09ExecCmdReturn
   189  	err := e.client.Call("Plugin.Exec", req, &resp)
   190  	if resp == nil {
   191  		return nil, 0, err
   192  	}
   193  	return resp.Output, resp.Code, err
   194  }
   195  
   196  type pre09ExecutorPlugin struct {
   197  	logger hclog.Logger
   198  }
   199  
   200  func newPre09ExecutorPlugin(logger hclog.Logger) plugin.Plugin {
   201  	return &pre09ExecutorPlugin{logger: logger}
   202  }
   203  
   204  func (p *pre09ExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
   205  	return &legacyExecutorWrapper{
   206  		client: &pre09ExecutorRPC{client: c, logger: p.logger},
   207  		logger: p.logger,
   208  	}, nil
   209  }
   210  
   211  func (p *pre09ExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
   212  	return nil, fmt.Errorf("client only supported")
   213  }