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

     1  package executor
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"syscall"
     9  	"time"
    10  
    11  	"github.com/LK4D4/joincontext"
    12  	"github.com/golang/protobuf/ptypes"
    13  	hclog "github.com/hashicorp/go-hclog"
    14  	cstructs "github.com/hashicorp/nomad/client/structs"
    15  	"github.com/hashicorp/nomad/drivers/shared/executor/proto"
    16  	"github.com/hashicorp/nomad/helper/pluginutils/grpcutils"
    17  	"github.com/hashicorp/nomad/plugins/drivers"
    18  	dproto "github.com/hashicorp/nomad/plugins/drivers/proto"
    19  )
    20  
    21  var _ Executor = (*grpcExecutorClient)(nil)
    22  
    23  type grpcExecutorClient struct {
    24  	client proto.ExecutorClient
    25  	logger hclog.Logger
    26  
    27  	// doneCtx is close when the plugin exits
    28  	doneCtx context.Context
    29  }
    30  
    31  func (c *grpcExecutorClient) Launch(cmd *ExecCommand) (*ProcessState, error) {
    32  	ctx := context.Background()
    33  	req := &proto.LaunchRequest{
    34  		Cmd:                cmd.Cmd,
    35  		Args:               cmd.Args,
    36  		Resources:          drivers.ResourcesToProto(cmd.Resources),
    37  		StdoutPath:         cmd.StdoutPath,
    38  		StderrPath:         cmd.StderrPath,
    39  		Env:                cmd.Env,
    40  		User:               cmd.User,
    41  		TaskDir:            cmd.TaskDir,
    42  		ResourceLimits:     cmd.ResourceLimits,
    43  		BasicProcessCgroup: cmd.BasicProcessCgroup,
    44  		NoPivotRoot:        cmd.NoPivotRoot,
    45  		Mounts:             drivers.MountsToProto(cmd.Mounts),
    46  		Devices:            drivers.DevicesToProto(cmd.Devices),
    47  		NetworkIsolation:   drivers.NetworkIsolationSpecToProto(cmd.NetworkIsolation),
    48  	}
    49  	resp, err := c.client.Launch(ctx, req)
    50  	if err != nil {
    51  		return nil, err
    52  	}
    53  
    54  	ps, err := processStateFromProto(resp.Process)
    55  	if err != nil {
    56  		return nil, err
    57  	}
    58  	return ps, nil
    59  }
    60  
    61  func (c *grpcExecutorClient) Wait(ctx context.Context) (*ProcessState, error) {
    62  	// Join the passed context and the shutdown context
    63  	ctx, _ = joincontext.Join(ctx, c.doneCtx)
    64  
    65  	resp, err := c.client.Wait(ctx, &proto.WaitRequest{})
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  
    70  	ps, err := processStateFromProto(resp.Process)
    71  	if err != nil {
    72  		return nil, err
    73  	}
    74  
    75  	return ps, nil
    76  }
    77  
    78  func (c *grpcExecutorClient) Shutdown(signal string, gracePeriod time.Duration) error {
    79  	ctx := context.Background()
    80  	req := &proto.ShutdownRequest{
    81  		Signal:      signal,
    82  		GracePeriod: gracePeriod.Nanoseconds(),
    83  	}
    84  	if _, err := c.client.Shutdown(ctx, req); err != nil {
    85  		return err
    86  	}
    87  
    88  	return nil
    89  }
    90  
    91  func (c *grpcExecutorClient) UpdateResources(r *drivers.Resources) error {
    92  	ctx := context.Background()
    93  	req := &proto.UpdateResourcesRequest{Resources: drivers.ResourcesToProto(r)}
    94  	if _, err := c.client.UpdateResources(ctx, req); err != nil {
    95  		return err
    96  	}
    97  
    98  	return nil
    99  }
   100  
   101  func (c *grpcExecutorClient) Version() (*ExecutorVersion, error) {
   102  	ctx := context.Background()
   103  	resp, err := c.client.Version(ctx, &proto.VersionRequest{})
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	return &ExecutorVersion{Version: resp.Version}, nil
   108  }
   109  
   110  func (c *grpcExecutorClient) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {
   111  	stream, err := c.client.Stats(ctx, &proto.StatsRequest{
   112  		Interval: int64(interval),
   113  	})
   114  	if err != nil {
   115  		return nil, err
   116  	}
   117  
   118  	ch := make(chan *cstructs.TaskResourceUsage)
   119  	go c.handleStats(ctx, stream, ch)
   120  	return ch, nil
   121  }
   122  
   123  func (c *grpcExecutorClient) handleStats(ctx context.Context, stream proto.Executor_StatsClient, ch chan<- *cstructs.TaskResourceUsage) {
   124  	defer close(ch)
   125  	for {
   126  		resp, err := stream.Recv()
   127  		if ctx.Err() != nil {
   128  			// Context canceled; exit gracefully
   129  			return
   130  		}
   131  
   132  		if err != nil {
   133  			if err != io.EOF {
   134  				c.logger.Error("error receiving stream from Stats executor RPC, closing stream", "error", err)
   135  			}
   136  
   137  			// End stream
   138  			return
   139  		}
   140  
   141  		stats, err := drivers.TaskStatsFromProto(resp.Stats)
   142  		if err != nil {
   143  			c.logger.Error("failed to decode stats from RPC", "error", err, "stats", resp.Stats)
   144  			continue
   145  		}
   146  
   147  		select {
   148  		case ch <- stats:
   149  		case <-ctx.Done():
   150  			return
   151  		}
   152  	}
   153  }
   154  
   155  func (c *grpcExecutorClient) Signal(s os.Signal) error {
   156  	ctx := context.Background()
   157  	sig, ok := s.(syscall.Signal)
   158  	if !ok {
   159  		return fmt.Errorf("unsupported signal type: %q", s.String())
   160  	}
   161  	req := &proto.SignalRequest{
   162  		Signal: int32(sig),
   163  	}
   164  	if _, err := c.client.Signal(ctx, req); err != nil {
   165  		return err
   166  	}
   167  
   168  	return nil
   169  }
   170  
   171  func (c *grpcExecutorClient) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) {
   172  	ctx := context.Background()
   173  	pbDeadline, err := ptypes.TimestampProto(deadline)
   174  	if err != nil {
   175  		return nil, 0, err
   176  	}
   177  	req := &proto.ExecRequest{
   178  		Deadline: pbDeadline,
   179  		Cmd:      cmd,
   180  		Args:     args,
   181  	}
   182  
   183  	resp, err := c.client.Exec(ctx, req)
   184  	if err != nil {
   185  		return nil, 0, err
   186  	}
   187  
   188  	return resp.Output, int(resp.ExitCode), nil
   189  }
   190  
   191  func (d *grpcExecutorClient) ExecStreaming(ctx context.Context,
   192  	command []string,
   193  	tty bool,
   194  	execStream drivers.ExecTaskStream) error {
   195  
   196  	err := d.execStreaming(ctx, command, tty, execStream)
   197  	if err != nil {
   198  		return grpcutils.HandleGrpcErr(err, d.doneCtx)
   199  	}
   200  	return nil
   201  }
   202  
   203  func (d *grpcExecutorClient) execStreaming(ctx context.Context,
   204  	command []string,
   205  	tty bool,
   206  	execStream drivers.ExecTaskStream) error {
   207  
   208  	stream, err := d.client.ExecStreaming(ctx)
   209  	if err != nil {
   210  		return err
   211  	}
   212  
   213  	err = stream.Send(&dproto.ExecTaskStreamingRequest{
   214  		Setup: &dproto.ExecTaskStreamingRequest_Setup{
   215  			Command: command,
   216  			Tty:     tty,
   217  		},
   218  	})
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	errCh := make(chan error, 1)
   224  	go func() {
   225  		for {
   226  			m, err := execStream.Recv()
   227  			if err == io.EOF {
   228  				return
   229  			} else if err != nil {
   230  				errCh <- err
   231  				return
   232  			}
   233  
   234  			if err := stream.Send(m); err != nil {
   235  				errCh <- err
   236  				return
   237  			}
   238  
   239  		}
   240  	}()
   241  
   242  	for {
   243  		select {
   244  		case err := <-errCh:
   245  			return err
   246  		default:
   247  		}
   248  
   249  		m, err := stream.Recv()
   250  		if err == io.EOF {
   251  			return nil
   252  		} else if err != nil {
   253  			return err
   254  		}
   255  
   256  		if err := execStream.Send(m); err != nil {
   257  			return err
   258  		}
   259  	}
   260  }