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