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 }