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