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