2018-12-05 16:03:56 +00:00
|
|
|
package executor
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2018-12-15 03:20:01 +00:00
|
|
|
"fmt"
|
2018-12-11 20:27:50 +00:00
|
|
|
"io"
|
2018-12-05 16:03:56 +00:00
|
|
|
"os"
|
2018-12-15 03:20:01 +00:00
|
|
|
"syscall"
|
2018-12-05 16:03:56 +00:00
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/LK4D4/joincontext"
|
|
|
|
"github.com/golang/protobuf/ptypes"
|
2018-12-11 20:27:50 +00:00
|
|
|
hclog "github.com/hashicorp/go-hclog"
|
|
|
|
cstructs "github.com/hashicorp/nomad/client/structs"
|
2018-12-07 02:13:45 +00:00
|
|
|
"github.com/hashicorp/nomad/drivers/shared/executor/proto"
|
2018-12-05 16:03:56 +00:00
|
|
|
"github.com/hashicorp/nomad/plugins/drivers"
|
|
|
|
)
|
|
|
|
|
2018-12-07 02:13:45 +00:00
|
|
|
var _ Executor = (*grpcExecutorClient)(nil)
|
2018-12-05 16:03:56 +00:00
|
|
|
|
|
|
|
type grpcExecutorClient struct {
|
|
|
|
client proto.ExecutorClient
|
2018-12-11 20:27:50 +00:00
|
|
|
logger hclog.Logger
|
2018-12-05 16:03:56 +00:00
|
|
|
|
|
|
|
// doneCtx is close when the plugin exits
|
|
|
|
doneCtx context.Context
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:13:45 +00:00
|
|
|
func (c *grpcExecutorClient) Launch(cmd *ExecCommand) (*ProcessState, error) {
|
2018-12-05 16:03:56 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
req := &proto.LaunchRequest{
|
|
|
|
Cmd: cmd.Cmd,
|
|
|
|
Args: cmd.Args,
|
2018-12-07 02:22:02 +00:00
|
|
|
Resources: drivers.ResourcesToProto(cmd.Resources),
|
2018-12-05 16:03:56 +00:00
|
|
|
StdoutPath: cmd.StdoutPath,
|
|
|
|
StderrPath: cmd.StderrPath,
|
|
|
|
Env: cmd.Env,
|
|
|
|
User: cmd.User,
|
|
|
|
TaskDir: cmd.TaskDir,
|
|
|
|
ResourceLimits: cmd.ResourceLimits,
|
|
|
|
BasicProcessCgroup: cmd.BasicProcessCgroup,
|
2018-12-15 05:08:23 +00:00
|
|
|
Mounts: drivers.MountsToProto(cmd.Mounts),
|
|
|
|
Devices: drivers.DevicesToProto(cmd.Devices),
|
2018-12-05 16:03:56 +00:00
|
|
|
}
|
|
|
|
resp, err := c.client.Launch(ctx, req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ps, err := processStateFromProto(resp.Process)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return ps, nil
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:13:45 +00:00
|
|
|
func (c *grpcExecutorClient) Wait(ctx context.Context) (*ProcessState, error) {
|
2018-12-05 16:03:56 +00:00
|
|
|
// Join the passed context and the shutdown context
|
|
|
|
ctx, _ = joincontext.Join(ctx, c.doneCtx)
|
|
|
|
|
|
|
|
resp, err := c.client.Wait(ctx, &proto.WaitRequest{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
ps, err := processStateFromProto(resp.Process)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return ps, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *grpcExecutorClient) Shutdown(signal string, gracePeriod time.Duration) error {
|
|
|
|
ctx := context.Background()
|
|
|
|
req := &proto.ShutdownRequest{
|
|
|
|
Signal: signal,
|
|
|
|
GracePeriod: gracePeriod.Nanoseconds(),
|
|
|
|
}
|
|
|
|
if _, err := c.client.Shutdown(ctx, req); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:22:02 +00:00
|
|
|
func (c *grpcExecutorClient) UpdateResources(r *drivers.Resources) error {
|
2018-12-05 16:03:56 +00:00
|
|
|
ctx := context.Background()
|
2018-12-07 02:22:02 +00:00
|
|
|
req := &proto.UpdateResourcesRequest{Resources: drivers.ResourcesToProto(r)}
|
2018-12-05 16:03:56 +00:00
|
|
|
if _, err := c.client.UpdateResources(ctx, req); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2018-12-07 02:13:45 +00:00
|
|
|
func (c *grpcExecutorClient) Version() (*ExecutorVersion, error) {
|
2018-12-05 16:03:56 +00:00
|
|
|
ctx := context.Background()
|
|
|
|
resp, err := c.client.Version(ctx, &proto.VersionRequest{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2018-12-07 02:13:45 +00:00
|
|
|
return &ExecutorVersion{Version: resp.Version}, nil
|
2018-12-05 16:03:56 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 20:27:50 +00:00
|
|
|
func (c *grpcExecutorClient) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {
|
|
|
|
stream, err := c.client.Stats(ctx, &proto.StatsRequest{})
|
2018-12-05 16:03:56 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2018-12-11 20:27:50 +00:00
|
|
|
ch := make(chan *cstructs.TaskResourceUsage)
|
|
|
|
go c.handleStats(ctx, stream, ch)
|
|
|
|
return ch, nil
|
|
|
|
}
|
2018-12-05 16:03:56 +00:00
|
|
|
|
2018-12-11 20:27:50 +00:00
|
|
|
func (c *grpcExecutorClient) handleStats(ctx context.Context, stream proto.Executor_StatsClient, ch chan<- *cstructs.TaskResourceUsage) {
|
|
|
|
defer close(ch)
|
|
|
|
for {
|
|
|
|
resp, err := stream.Recv()
|
|
|
|
if err != nil {
|
|
|
|
if err != io.EOF {
|
2019-01-10 19:20:18 +00:00
|
|
|
c.logger.Error("error receiving stream from Stats executor RPC, closing stream", "error", err)
|
2018-12-11 20:27:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// End stream
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
stats, err := drivers.TaskStatsFromProto(resp.Stats)
|
|
|
|
if err != nil {
|
2019-01-10 19:20:18 +00:00
|
|
|
c.logger.Error("failed to decode stats from RPC", "error", err, "stats", resp.Stats)
|
|
|
|
continue
|
2018-12-11 20:27:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case ch <- stats:
|
|
|
|
case <-ctx.Done():
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2018-12-05 16:03:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *grpcExecutorClient) Signal(s os.Signal) error {
|
|
|
|
ctx := context.Background()
|
2018-12-15 03:20:01 +00:00
|
|
|
sig, ok := s.(syscall.Signal)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("unsupported signal type: %q", s.String())
|
|
|
|
}
|
2018-12-05 16:03:56 +00:00
|
|
|
req := &proto.SignalRequest{
|
2018-12-15 03:20:01 +00:00
|
|
|
Signal: int32(sig),
|
2018-12-05 16:03:56 +00:00
|
|
|
}
|
|
|
|
if _, err := c.client.Signal(ctx, req); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (c *grpcExecutorClient) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) {
|
|
|
|
ctx := context.Background()
|
|
|
|
pbDeadline, err := ptypes.TimestampProto(deadline)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
req := &proto.ExecRequest{
|
|
|
|
Deadline: pbDeadline,
|
|
|
|
Cmd: cmd,
|
|
|
|
Args: args,
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := c.client.Exec(ctx, req)
|
|
|
|
if err != nil {
|
|
|
|
return nil, 0, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return resp.Output, int(resp.ExitCode), nil
|
|
|
|
}
|