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