github.com/zoomfoo/nomad@v0.8.5-0.20180907175415-f28fd3a1a056/plugins/device/client.go (about)

     1  package device
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  
     7  	"github.com/hashicorp/nomad/plugins/base"
     8  	"github.com/hashicorp/nomad/plugins/device/proto"
     9  	netctx "golang.org/x/net/context"
    10  	"google.golang.org/grpc/codes"
    11  	"google.golang.org/grpc/status"
    12  )
    13  
    14  // devicePluginClient implements the client side of a remote device plugin, using
    15  // gRPC to communicate to the remote plugin.
    16  type devicePluginClient struct {
    17  	// basePluginClient is embedded to give access to the base plugin methods.
    18  	*base.BasePluginClient
    19  
    20  	client proto.DevicePluginClient
    21  }
    22  
    23  // Fingerprint is used to retrieve the set of devices and their health from the
    24  // device plugin. An error may be immediately returned if the fingerprint call
    25  // could not be made or as part of the streaming response. If the context is
    26  // cancelled, the error will be propogated.
    27  func (d *devicePluginClient) Fingerprint(ctx context.Context) (<-chan *FingerprintResponse, error) {
    28  	var req proto.FingerprintRequest
    29  	stream, err := d.client.Fingerprint(ctx, &req)
    30  	if err != nil {
    31  		return nil, err
    32  	}
    33  
    34  	out := make(chan *FingerprintResponse, 1)
    35  	go d.handleFingerprint(ctx, stream, out)
    36  	return out, nil
    37  }
    38  
    39  // handleFingerprint should be launched in a goroutine and handles converting
    40  // the gRPC stream to a channel. Exits either when context is cancelled or the
    41  // stream has an error.
    42  func (d *devicePluginClient) handleFingerprint(
    43  	ctx netctx.Context,
    44  	stream proto.DevicePlugin_FingerprintClient,
    45  	out chan *FingerprintResponse) {
    46  
    47  	for {
    48  		resp, err := stream.Recv()
    49  		if err != nil {
    50  			// Handle a non-graceful stream error
    51  			if err != io.EOF {
    52  				if errStatus := status.FromContextError(ctx.Err()); errStatus.Code() == codes.Canceled {
    53  					err = context.Canceled
    54  				}
    55  
    56  				out <- &FingerprintResponse{
    57  					Error: err,
    58  				}
    59  			}
    60  
    61  			// End the stream
    62  			close(out)
    63  			return
    64  		}
    65  
    66  		// Send the response
    67  		out <- &FingerprintResponse{
    68  			Devices: convertProtoDeviceGroups(resp.GetDeviceGroup()),
    69  		}
    70  	}
    71  }
    72  
    73  func (d *devicePluginClient) Reserve(deviceIDs []string) (*ContainerReservation, error) {
    74  	// Build the request
    75  	req := &proto.ReserveRequest{
    76  		DeviceIds: deviceIDs,
    77  	}
    78  
    79  	// Make the request
    80  	resp, err := d.client.Reserve(context.Background(), req)
    81  	if err != nil {
    82  		return nil, err
    83  	}
    84  
    85  	// Convert the response
    86  	out := convertProtoContainerReservation(resp.GetContainerRes())
    87  	return out, nil
    88  }
    89  
    90  // Stats is used to retrieve device statistics from the device plugin. An error
    91  // may be immediately returned if the stats call could not be made or as part of
    92  // the streaming response. If the context is cancelled, the error will be
    93  // propogated.
    94  func (d *devicePluginClient) Stats(ctx context.Context) (<-chan *StatsResponse, error) {
    95  	var req proto.StatsRequest
    96  	stream, err := d.client.Stats(ctx, &req)
    97  	if err != nil {
    98  		return nil, err
    99  	}
   100  
   101  	out := make(chan *StatsResponse, 1)
   102  	go d.handleStats(ctx, stream, out)
   103  	return out, nil
   104  }
   105  
   106  // handleStats should be launched in a goroutine and handles converting
   107  // the gRPC stream to a channel. Exits either when context is cancelled or the
   108  // stream has an error.
   109  func (d *devicePluginClient) handleStats(
   110  	ctx netctx.Context,
   111  	stream proto.DevicePlugin_StatsClient,
   112  	out chan *StatsResponse) {
   113  
   114  	for {
   115  		resp, err := stream.Recv()
   116  		if err != nil {
   117  			// Handle a non-graceful stream error
   118  			if err != io.EOF {
   119  				if errStatus := status.FromContextError(ctx.Err()); errStatus.Code() == codes.Canceled {
   120  					err = context.Canceled
   121  				}
   122  
   123  				out <- &StatsResponse{
   124  					Error: err,
   125  				}
   126  			}
   127  
   128  			// End the stream
   129  			close(out)
   130  			return
   131  		}
   132  
   133  		// Send the response
   134  		out <- &StatsResponse{
   135  			Groups: convertProtoDeviceGroupsStats(resp.GetGroups()),
   136  		}
   137  	}
   138  }