github.com/Ilhicas/nomad@v1.0.4-0.20210304152020-e86851182bc3/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  		DefaultPidMode:     cmd.ModePID,
    49  		DefaultIpcMode:     cmd.ModeIPC,
    50  	}
    51  	resp, err := c.client.Launch(ctx, req)
    52  	if err != nil {
    53  		return nil, err
    54  	}
    55  
    56  	ps, err := processStateFromProto(resp.Process)
    57  	if err != nil {
    58  		return nil, err
    59  	}
    60  	return ps, nil
    61  }
    62  
    63  func (c *grpcExecutorClient) Wait(ctx context.Context) (*ProcessState, error) {
    64  	// Join the passed context and the shutdown context
    65  	ctx, _ = joincontext.Join(ctx, c.doneCtx)
    66  
    67  	resp, err := c.client.Wait(ctx, &proto.WaitRequest{})
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  
    72  	ps, err := processStateFromProto(resp.Process)
    73  	if err != nil {
    74  		return nil, err
    75  	}
    76  
    77  	return ps, nil
    78  }
    79  
    80  func (c *grpcExecutorClient) Shutdown(signal string, gracePeriod time.Duration) error {
    81  	ctx := context.Background()
    82  	req := &proto.ShutdownRequest{
    83  		Signal:      signal,
    84  		GracePeriod: gracePeriod.Nanoseconds(),
    85  	}
    86  	if _, err := c.client.Shutdown(ctx, req); err != nil {
    87  		return err
    88  	}
    89  
    90  	return nil
    91  }
    92  
    93  func (c *grpcExecutorClient) UpdateResources(r *drivers.Resources) error {
    94  	ctx := context.Background()
    95  	req := &proto.UpdateResourcesRequest{Resources: drivers.ResourcesToProto(r)}
    96  	if _, err := c.client.UpdateResources(ctx, req); err != nil {
    97  		return err
    98  	}
    99  
   100  	return nil
   101  }
   102  
   103  func (c *grpcExecutorClient) Version() (*ExecutorVersion, error) {
   104  	ctx := context.Background()
   105  	resp, err := c.client.Version(ctx, &proto.VersionRequest{})
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  	return &ExecutorVersion{Version: resp.Version}, nil
   110  }
   111  
   112  func (c *grpcExecutorClient) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) {
   113  	stream, err := c.client.Stats(ctx, &proto.StatsRequest{
   114  		Interval: int64(interval),
   115  	})
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	ch := make(chan *cstructs.TaskResourceUsage)
   121  	go c.handleStats(ctx, stream, ch)
   122  	return ch, nil
   123  }
   124  
   125  func (c *grpcExecutorClient) handleStats(ctx context.Context, stream proto.Executor_StatsClient, ch chan<- *cstructs.TaskResourceUsage) {
   126  	defer close(ch)
   127  	for {
   128  		resp, err := stream.Recv()
   129  		if ctx.Err() != nil {
   130  			// Context canceled; exit gracefully
   131  			return
   132  		}
   133  
   134  		if err != nil {
   135  			if err != io.EOF {
   136  				c.logger.Error("error receiving stream from Stats executor RPC, closing stream", "error", err)
   137  			}
   138  
   139  			// End stream
   140  			return
   141  		}
   142  
   143  		stats, err := drivers.TaskStatsFromProto(resp.Stats)
   144  		if err != nil {
   145  			c.logger.Error("failed to decode stats from RPC", "error", err, "stats", resp.Stats)
   146  			continue
   147  		}
   148  
   149  		select {
   150  		case ch <- stats:
   151  		case <-ctx.Done():
   152  			return
   153  		}
   154  	}
   155  }
   156  
   157  func (c *grpcExecutorClient) Signal(s os.Signal) error {
   158  	ctx := context.Background()
   159  	sig, ok := s.(syscall.Signal)
   160  	if !ok {
   161  		return fmt.Errorf("unsupported signal type: %q", s.String())
   162  	}
   163  	req := &proto.SignalRequest{
   164  		Signal: int32(sig),
   165  	}
   166  	if _, err := c.client.Signal(ctx, req); err != nil {
   167  		return err
   168  	}
   169  
   170  	return nil
   171  }
   172  
   173  func (c *grpcExecutorClient) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) {
   174  	ctx := context.Background()
   175  	pbDeadline, err := ptypes.TimestampProto(deadline)
   176  	if err != nil {
   177  		return nil, 0, err
   178  	}
   179  	req := &proto.ExecRequest{
   180  		Deadline: pbDeadline,
   181  		Cmd:      cmd,
   182  		Args:     args,
   183  	}
   184  
   185  	resp, err := c.client.Exec(ctx, req)
   186  	if err != nil {
   187  		return nil, 0, err
   188  	}
   189  
   190  	return resp.Output, int(resp.ExitCode), nil
   191  }
   192  
   193  func (d *grpcExecutorClient) ExecStreaming(ctx context.Context,
   194  	command []string,
   195  	tty bool,
   196  	execStream drivers.ExecTaskStream) error {
   197  
   198  	err := d.execStreaming(ctx, command, tty, execStream)
   199  	if err != nil {
   200  		return grpcutils.HandleGrpcErr(err, d.doneCtx)
   201  	}
   202  	return nil
   203  }
   204  
   205  func (d *grpcExecutorClient) execStreaming(ctx context.Context,
   206  	command []string,
   207  	tty bool,
   208  	execStream drivers.ExecTaskStream) error {
   209  
   210  	stream, err := d.client.ExecStreaming(ctx)
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	err = stream.Send(&dproto.ExecTaskStreamingRequest{
   216  		Setup: &dproto.ExecTaskStreamingRequest_Setup{
   217  			Command: command,
   218  			Tty:     tty,
   219  		},
   220  	})
   221  	if err != nil {
   222  		return err
   223  	}
   224  
   225  	errCh := make(chan error, 1)
   226  	go func() {
   227  		for {
   228  			m, err := execStream.Recv()
   229  			if err == io.EOF {
   230  				return
   231  			} else if err != nil {
   232  				errCh <- err
   233  				return
   234  			}
   235  
   236  			if err := stream.Send(m); err != nil {
   237  				errCh <- err
   238  				return
   239  			}
   240  
   241  		}
   242  	}()
   243  
   244  	for {
   245  		select {
   246  		case err := <-errCh:
   247  			return err
   248  		default:
   249  		}
   250  
   251  		m, err := stream.Recv()
   252  		if err == io.EOF {
   253  			return nil
   254  		} else if err != nil {
   255  			return err
   256  		}
   257  
   258  		if err := execStream.Send(m); err != nil {
   259  			return err
   260  		}
   261  	}
   262  }