github.com/aminovpavel/nomad@v0.11.8/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  // validate that legacyExecutorWrapper is an executor
    40  var _ Executor = (*legacyExecutorWrapper)(nil)
    41  
    42  func (l *legacyExecutorWrapper) Launch(launchCmd *ExecCommand) (*ProcessState, error) {
    43  	return nil, fmt.Errorf("operation not supported for legacy exec wrapper")
    44  }
    45  
    46  func (l *legacyExecutorWrapper) Wait(ctx context.Context) (*ProcessState, error) {
    47  	ps, err := l.client.Wait()
    48  	if err != nil {
    49  		return nil, err
    50  	}
    51  
    52  	return &ProcessState{
    53  		Pid:      ps.Pid,
    54  		ExitCode: ps.ExitCode,
    55  		Signal:   ps.Signal,
    56  		Time:     ps.Time,
    57  	}, nil
    58  }
    59  
    60  func (l *legacyExecutorWrapper) Shutdown(signal string, gracePeriod time.Duration) error {
    61  	// The legacy docker driver only used the executor to start a syslog server
    62  	// for logging. Thus calling ShutDown for docker will always return an error
    63  	// because it never started a process through the executor. If signal is set
    64  	// to 'docker' then we'll skip the ShutDown RPC and just call Exit.
    65  	//
    66  	// This is painful to look at but will only be around a few releases
    67  	if signal != pre09DockerSignal {
    68  		if err := l.client.ShutDown(); err != nil {
    69  			return err
    70  		}
    71  	}
    72  
    73  	if err := l.client.Exit(); err != nil {
    74  		return err
    75  	}
    76  	return nil
    77  }
    78  
    79  func (l *legacyExecutorWrapper) UpdateResources(*drivers.Resources) error {
    80  	return fmt.Errorf("operation not supported for legacy exec wrapper")
    81  }
    82  
    83  func (l *legacyExecutorWrapper) Version() (*ExecutorVersion, error) {
    84  	v, err := l.client.Version()
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	return &ExecutorVersion{
    90  		Version: v.Version,
    91  	}, nil
    92  }
    93  
    94  func (l *legacyExecutorWrapper) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {
    95  	ch := make(chan *cstructs.TaskResourceUsage, 1)
    96  	stats, err := l.client.Stats()
    97  	if err != nil {
    98  		close(ch)
    99  		return nil, err
   100  	}
   101  	select {
   102  	case ch <- stats:
   103  	default:
   104  	}
   105  	go l.handleStats(ctx, interval, ch)
   106  	return ch, nil
   107  }
   108  
   109  func (l *legacyExecutorWrapper) handleStats(ctx context.Context, interval time.Duration, ch chan *cstructs.TaskResourceUsage) {
   110  	defer close(ch)
   111  	ticker := time.NewTicker(interval)
   112  	for range ticker.C {
   113  		stats, err := l.client.Stats()
   114  		if err != nil {
   115  			if err == rpc.ErrShutdown {
   116  				return
   117  			}
   118  			l.logger.Warn("stats collection from legacy executor failed, waiting for next interval", "error", err)
   119  			continue
   120  		}
   121  		if stats != nil {
   122  			select {
   123  			case ch <- stats:
   124  			default:
   125  			}
   126  		}
   127  
   128  	}
   129  }
   130  
   131  func (l *legacyExecutorWrapper) Signal(s os.Signal) error {
   132  	return l.client.Signal(s)
   133  }
   134  
   135  func (l *legacyExecutorWrapper) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) {
   136  	return l.client.Exec(deadline, cmd, args)
   137  }
   138  
   139  func (l *legacyExecutorWrapper) ExecStreaming(ctx context.Context, cmd []string, tty bool,
   140  	stream drivers.ExecTaskStream) error {
   141  	return fmt.Errorf("operation not supported for legacy exec wrapper")
   142  }
   143  
   144  type pre09ExecutorRPC struct {
   145  	client *rpc.Client
   146  	logger hclog.Logger
   147  }
   148  
   149  type pre09ExecCmdArgs struct {
   150  	Deadline time.Time
   151  	Name     string
   152  	Args     []string
   153  }
   154  
   155  type pre09ExecCmdReturn struct {
   156  	Output []byte
   157  	Code   int
   158  }
   159  
   160  func (e *pre09ExecutorRPC) Wait() (*ProcessState, error) {
   161  	var ps ProcessState
   162  	err := e.client.Call("Plugin.Wait", new(interface{}), &ps)
   163  	return &ps, err
   164  }
   165  
   166  func (e *pre09ExecutorRPC) ShutDown() error {
   167  	return e.client.Call("Plugin.ShutDown", new(interface{}), new(interface{}))
   168  }
   169  
   170  func (e *pre09ExecutorRPC) Exit() error {
   171  	return e.client.Call("Plugin.Exit", new(interface{}), new(interface{}))
   172  }
   173  
   174  func (e *pre09ExecutorRPC) Version() (*ExecutorVersion, error) {
   175  	var version ExecutorVersion
   176  	err := e.client.Call("Plugin.Version", new(interface{}), &version)
   177  	return &version, err
   178  }
   179  
   180  func (e *pre09ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) {
   181  	var resourceUsage cstructs.TaskResourceUsage
   182  	err := e.client.Call("Plugin.Stats", new(interface{}), &resourceUsage)
   183  	return &resourceUsage, err
   184  }
   185  
   186  func (e *pre09ExecutorRPC) Signal(s os.Signal) error {
   187  	return e.client.Call("Plugin.Signal", &s, new(interface{}))
   188  }
   189  
   190  func (e *pre09ExecutorRPC) Exec(deadline time.Time, name string, args []string) ([]byte, int, error) {
   191  	req := pre09ExecCmdArgs{
   192  		Deadline: deadline,
   193  		Name:     name,
   194  		Args:     args,
   195  	}
   196  	var resp *pre09ExecCmdReturn
   197  	err := e.client.Call("Plugin.Exec", req, &resp)
   198  	if resp == nil {
   199  		return nil, 0, err
   200  	}
   201  	return resp.Output, resp.Code, err
   202  }
   203  
   204  type pre09ExecutorPlugin struct {
   205  	logger hclog.Logger
   206  }
   207  
   208  func newPre09ExecutorPlugin(logger hclog.Logger) plugin.Plugin {
   209  	return &pre09ExecutorPlugin{logger: logger}
   210  }
   211  
   212  func (p *pre09ExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
   213  	return &legacyExecutorWrapper{
   214  		client: &pre09ExecutorRPC{client: c, logger: p.logger},
   215  		logger: p.logger,
   216  	}, nil
   217  }
   218  
   219  func (p *pre09ExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
   220  	return nil, fmt.Errorf("client only supported")
   221  }