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 }