github.com/aminovpavel/nomad@v0.11.8/drivers/shared/executor/legacy_executor_wrapper.go (about) 1 package executor 2 3 import ( 4 "encoding/gob" 5 "fmt" 6 "net/rpc" 7 "os" 8 "syscall" 9 "time" 10 11 hclog "github.com/hashicorp/go-hclog" 12 plugin "github.com/hashicorp/go-plugin" 13 cstructs "github.com/hashicorp/nomad/client/structs" 14 "github.com/hashicorp/nomad/plugins/drivers" 15 "golang.org/x/net/context" 16 ) 17 18 const ( 19 // pre09DockerSignal is used in executor.Shutdown to know if it should 20 // call the ShutDown RPC on the pre09 executor 21 pre09DockerSignal = "docker" 22 ) 23 24 // Registering these types since we have to serialize and de-serialize the Task 25 // structs over the wire between drivers and the executor. 26 func init() { 27 gob.Register([]interface{}{}) 28 gob.Register(map[string]interface{}{}) 29 gob.Register([]map[string]string{}) 30 gob.Register([]map[string]int{}) 31 gob.Register(syscall.Signal(0x1)) 32 } 33 34 type legacyExecutorWrapper struct { 35 client *pre09ExecutorRPC 36 logger hclog.Logger 37 } 38 39 // validate that legacyExecutorWrapper is an executor 40 var _ Executor = (*legacyExecutorWrapper)(nil) 41 42 func (l *legacyExecutorWrapper) Launch(launchCmd *ExecCommand) (*ProcessState, error) { 43 return nil, fmt.Errorf("operation not supported for legacy exec wrapper") 44 } 45 46 func (l *legacyExecutorWrapper) Wait(ctx context.Context) (*ProcessState, error) { 47 ps, err := l.client.Wait() 48 if err != nil { 49 return nil, err 50 } 51 52 return &ProcessState{ 53 Pid: ps.Pid, 54 ExitCode: ps.ExitCode, 55 Signal: ps.Signal, 56 Time: ps.Time, 57 }, nil 58 } 59 60 func (l *legacyExecutorWrapper) Shutdown(signal string, gracePeriod time.Duration) error { 61 // The legacy docker driver only used the executor to start a syslog server 62 // for logging. Thus calling ShutDown for docker will always return an error 63 // because it never started a process through the executor. If signal is set 64 // to 'docker' then we'll skip the ShutDown RPC and just call Exit. 65 // 66 // This is painful to look at but will only be around a few releases 67 if signal != pre09DockerSignal { 68 if err := l.client.ShutDown(); err != nil { 69 return err 70 } 71 } 72 73 if err := l.client.Exit(); err != nil { 74 return err 75 } 76 return nil 77 } 78 79 func (l *legacyExecutorWrapper) UpdateResources(*drivers.Resources) error { 80 return fmt.Errorf("operation not supported for legacy exec wrapper") 81 } 82 83 func (l *legacyExecutorWrapper) Version() (*ExecutorVersion, error) { 84 v, err := l.client.Version() 85 if err != nil { 86 return nil, err 87 } 88 89 return &ExecutorVersion{ 90 Version: v.Version, 91 }, nil 92 } 93 94 func (l *legacyExecutorWrapper) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) { 95 ch := make(chan *cstructs.TaskResourceUsage, 1) 96 stats, err := l.client.Stats() 97 if err != nil { 98 close(ch) 99 return nil, err 100 } 101 select { 102 case ch <- stats: 103 default: 104 } 105 go l.handleStats(ctx, interval, ch) 106 return ch, nil 107 } 108 109 func (l *legacyExecutorWrapper) handleStats(ctx context.Context, interval time.Duration, ch chan *cstructs.TaskResourceUsage) { 110 defer close(ch) 111 ticker := time.NewTicker(interval) 112 for range ticker.C { 113 stats, err := l.client.Stats() 114 if err != nil { 115 if err == rpc.ErrShutdown { 116 return 117 } 118 l.logger.Warn("stats collection from legacy executor failed, waiting for next interval", "error", err) 119 continue 120 } 121 if stats != nil { 122 select { 123 case ch <- stats: 124 default: 125 } 126 } 127 128 } 129 } 130 131 func (l *legacyExecutorWrapper) Signal(s os.Signal) error { 132 return l.client.Signal(s) 133 } 134 135 func (l *legacyExecutorWrapper) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) { 136 return l.client.Exec(deadline, cmd, args) 137 } 138 139 func (l *legacyExecutorWrapper) ExecStreaming(ctx context.Context, cmd []string, tty bool, 140 stream drivers.ExecTaskStream) error { 141 return fmt.Errorf("operation not supported for legacy exec wrapper") 142 } 143 144 type pre09ExecutorRPC struct { 145 client *rpc.Client 146 logger hclog.Logger 147 } 148 149 type pre09ExecCmdArgs struct { 150 Deadline time.Time 151 Name string 152 Args []string 153 } 154 155 type pre09ExecCmdReturn struct { 156 Output []byte 157 Code int 158 } 159 160 func (e *pre09ExecutorRPC) Wait() (*ProcessState, error) { 161 var ps ProcessState 162 err := e.client.Call("Plugin.Wait", new(interface{}), &ps) 163 return &ps, err 164 } 165 166 func (e *pre09ExecutorRPC) ShutDown() error { 167 return e.client.Call("Plugin.ShutDown", new(interface{}), new(interface{})) 168 } 169 170 func (e *pre09ExecutorRPC) Exit() error { 171 return e.client.Call("Plugin.Exit", new(interface{}), new(interface{})) 172 } 173 174 func (e *pre09ExecutorRPC) Version() (*ExecutorVersion, error) { 175 var version ExecutorVersion 176 err := e.client.Call("Plugin.Version", new(interface{}), &version) 177 return &version, err 178 } 179 180 func (e *pre09ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) { 181 var resourceUsage cstructs.TaskResourceUsage 182 err := e.client.Call("Plugin.Stats", new(interface{}), &resourceUsage) 183 return &resourceUsage, err 184 } 185 186 func (e *pre09ExecutorRPC) Signal(s os.Signal) error { 187 return e.client.Call("Plugin.Signal", &s, new(interface{})) 188 } 189 190 func (e *pre09ExecutorRPC) Exec(deadline time.Time, name string, args []string) ([]byte, int, error) { 191 req := pre09ExecCmdArgs{ 192 Deadline: deadline, 193 Name: name, 194 Args: args, 195 } 196 var resp *pre09ExecCmdReturn 197 err := e.client.Call("Plugin.Exec", req, &resp) 198 if resp == nil { 199 return nil, 0, err 200 } 201 return resp.Output, resp.Code, err 202 } 203 204 type pre09ExecutorPlugin struct { 205 logger hclog.Logger 206 } 207 208 func newPre09ExecutorPlugin(logger hclog.Logger) plugin.Plugin { 209 return &pre09ExecutorPlugin{logger: logger} 210 } 211 212 func (p *pre09ExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { 213 return &legacyExecutorWrapper{ 214 client: &pre09ExecutorRPC{client: c, logger: p.logger}, 215 logger: p.logger, 216 }, nil 217 } 218 219 func (p *pre09ExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) { 220 return nil, fmt.Errorf("client only supported") 221 }