2019-01-13 21:05:07 +00:00
|
|
|
package executor
|
|
|
|
|
|
|
|
import (
|
2019-01-14 17:25:59 +00:00
|
|
|
"encoding/gob"
|
2019-01-17 02:52:31 +00:00
|
|
|
"fmt"
|
2019-01-14 17:25:59 +00:00
|
|
|
"net/rpc"
|
2019-01-13 21:05:07 +00:00
|
|
|
"os"
|
2019-01-14 17:25:59 +00:00
|
|
|
"syscall"
|
2019-01-13 21:05:07 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
hclog "github.com/hashicorp/go-hclog"
|
2019-01-14 17:25:59 +00:00
|
|
|
plugin "github.com/hashicorp/go-plugin"
|
2019-01-13 21:05:07 +00:00
|
|
|
cstructs "github.com/hashicorp/nomad/client/structs"
|
|
|
|
"github.com/hashicorp/nomad/plugins/drivers"
|
|
|
|
"golang.org/x/net/context"
|
|
|
|
)
|
|
|
|
|
2019-01-24 19:21:59 +00:00
|
|
|
const (
|
|
|
|
// pre09DockerSignal is used in executor.Shutdown to know if it should
|
|
|
|
// call the ShutDown RPC on the pre09 executor
|
|
|
|
pre09DockerSignal = "docker"
|
|
|
|
)
|
|
|
|
|
2019-01-14 17:25:59 +00:00
|
|
|
// Registering these types since we have to serialize and de-serialize the Task
|
|
|
|
// structs over the wire between drivers and the executor.
|
|
|
|
func init() {
|
|
|
|
gob.Register([]interface{}{})
|
|
|
|
gob.Register(map[string]interface{}{})
|
|
|
|
gob.Register([]map[string]string{})
|
|
|
|
gob.Register([]map[string]int{})
|
|
|
|
gob.Register(syscall.Signal(0x1))
|
|
|
|
}
|
|
|
|
|
2019-01-13 21:05:07 +00:00
|
|
|
type legacyExecutorWrapper struct {
|
2019-01-14 17:25:59 +00:00
|
|
|
client *pre09ExecutorRPC
|
2019-01-13 21:05:07 +00:00
|
|
|
logger hclog.Logger
|
|
|
|
}
|
|
|
|
|
2020-11-10 15:20:07 +00:00
|
|
|
// validate that legacyExecutorWrapper is an executor
|
|
|
|
var _ Executor = (*legacyExecutorWrapper)(nil)
|
|
|
|
|
2019-01-13 21:05:07 +00:00
|
|
|
func (l *legacyExecutorWrapper) Launch(launchCmd *ExecCommand) (*ProcessState, error) {
|
2019-01-17 02:52:31 +00:00
|
|
|
return nil, fmt.Errorf("operation not supported for legacy exec wrapper")
|
2019-01-13 21:05:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) Wait(ctx context.Context) (*ProcessState, error) {
|
|
|
|
ps, err := l.client.Wait()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ProcessState{
|
|
|
|
Pid: ps.Pid,
|
|
|
|
ExitCode: ps.ExitCode,
|
|
|
|
Signal: ps.Signal,
|
|
|
|
Time: ps.Time,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) Shutdown(signal string, gracePeriod time.Duration) error {
|
2019-01-24 19:21:59 +00:00
|
|
|
// The legacy docker driver only used the executor to start a syslog server
|
|
|
|
// for logging. Thus calling ShutDown for docker will always return an error
|
|
|
|
// because it never started a process through the executor. If signal is set
|
|
|
|
// to 'docker' then we'll skip the ShutDown RPC and just call Exit.
|
|
|
|
//
|
|
|
|
// This is painful to look at but will only be around a few releases
|
|
|
|
if signal != pre09DockerSignal {
|
|
|
|
if err := l.client.ShutDown(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2019-01-13 21:05:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := l.client.Exit(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) UpdateResources(*drivers.Resources) error {
|
2019-01-17 02:52:31 +00:00
|
|
|
return fmt.Errorf("operation not supported for legacy exec wrapper")
|
2019-01-13 21:05:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) Version() (*ExecutorVersion, error) {
|
|
|
|
v, err := l.client.Version()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ExecutorVersion{
|
|
|
|
Version: v.Version,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {
|
|
|
|
ch := make(chan *cstructs.TaskResourceUsage, 1)
|
|
|
|
stats, err := l.client.Stats()
|
|
|
|
if err != nil {
|
|
|
|
close(ch)
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
select {
|
|
|
|
case ch <- stats:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
go l.handleStats(ctx, interval, ch)
|
|
|
|
return ch, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) handleStats(ctx context.Context, interval time.Duration, ch chan *cstructs.TaskResourceUsage) {
|
|
|
|
defer close(ch)
|
|
|
|
ticker := time.NewTicker(interval)
|
|
|
|
for range ticker.C {
|
|
|
|
stats, err := l.client.Stats()
|
|
|
|
if err != nil {
|
2019-01-14 17:25:59 +00:00
|
|
|
if err == rpc.ErrShutdown {
|
|
|
|
return
|
|
|
|
}
|
2019-01-13 21:05:07 +00:00
|
|
|
l.logger.Warn("stats collection from legacy executor failed, waiting for next interval", "error", err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if stats != nil {
|
|
|
|
select {
|
|
|
|
case ch <- stats:
|
|
|
|
default:
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) Signal(s os.Signal) error {
|
|
|
|
return l.client.Signal(s)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *legacyExecutorWrapper) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) {
|
|
|
|
return l.client.Exec(deadline, cmd, args)
|
|
|
|
}
|
2019-01-14 17:25:59 +00:00
|
|
|
|
2020-11-10 15:20:07 +00:00
|
|
|
func (l *legacyExecutorWrapper) ExecStreaming(ctx context.Context, cmd []string, tty bool,
|
|
|
|
stream drivers.ExecTaskStream) error {
|
|
|
|
return fmt.Errorf("operation not supported for legacy exec wrapper")
|
|
|
|
}
|
|
|
|
|
2019-01-14 17:25:59 +00:00
|
|
|
type pre09ExecutorRPC struct {
|
|
|
|
client *rpc.Client
|
|
|
|
logger hclog.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
type pre09ExecCmdArgs struct {
|
|
|
|
Deadline time.Time
|
|
|
|
Name string
|
|
|
|
Args []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type pre09ExecCmdReturn struct {
|
|
|
|
Output []byte
|
|
|
|
Code int
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) Wait() (*ProcessState, error) {
|
|
|
|
var ps ProcessState
|
|
|
|
err := e.client.Call("Plugin.Wait", new(interface{}), &ps)
|
|
|
|
return &ps, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) ShutDown() error {
|
|
|
|
return e.client.Call("Plugin.ShutDown", new(interface{}), new(interface{}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) Exit() error {
|
|
|
|
return e.client.Call("Plugin.Exit", new(interface{}), new(interface{}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) Version() (*ExecutorVersion, error) {
|
|
|
|
var version ExecutorVersion
|
|
|
|
err := e.client.Call("Plugin.Version", new(interface{}), &version)
|
|
|
|
return &version, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) {
|
|
|
|
var resourceUsage cstructs.TaskResourceUsage
|
|
|
|
err := e.client.Call("Plugin.Stats", new(interface{}), &resourceUsage)
|
|
|
|
return &resourceUsage, err
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) Signal(s os.Signal) error {
|
|
|
|
return e.client.Call("Plugin.Signal", &s, new(interface{}))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *pre09ExecutorRPC) Exec(deadline time.Time, name string, args []string) ([]byte, int, error) {
|
|
|
|
req := pre09ExecCmdArgs{
|
|
|
|
Deadline: deadline,
|
|
|
|
Name: name,
|
|
|
|
Args: args,
|
|
|
|
}
|
|
|
|
var resp *pre09ExecCmdReturn
|
|
|
|
err := e.client.Call("Plugin.Exec", req, &resp)
|
|
|
|
if resp == nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
return resp.Output, resp.Code, err
|
|
|
|
}
|
|
|
|
|
|
|
|
type pre09ExecutorPlugin struct {
|
|
|
|
logger hclog.Logger
|
|
|
|
}
|
|
|
|
|
|
|
|
func newPre09ExecutorPlugin(logger hclog.Logger) plugin.Plugin {
|
|
|
|
return &pre09ExecutorPlugin{logger: logger}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pre09ExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) {
|
|
|
|
return &legacyExecutorWrapper{
|
|
|
|
client: &pre09ExecutorRPC{client: c, logger: p.logger},
|
|
|
|
logger: p.logger,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *pre09ExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) {
|
2019-01-17 02:52:31 +00:00
|
|
|
return nil, fmt.Errorf("client only supported")
|
2019-01-14 17:25:59 +00:00
|
|
|
}
|