github.com/iqoqo/nomad@v0.11.3-0.20200911112621-d7021c74d101/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 func (l *legacyExecutorWrapper) Launch(launchCmd *ExecCommand) (*ProcessState, error) { 40 return nil, fmt.Errorf("operation not supported for legacy exec wrapper") 41 } 42 43 func (l *legacyExecutorWrapper) Wait(ctx context.Context) (*ProcessState, error) { 44 ps, err := l.client.Wait() 45 if err != nil { 46 return nil, err 47 } 48 49 return &ProcessState{ 50 Pid: ps.Pid, 51 ExitCode: ps.ExitCode, 52 Signal: ps.Signal, 53 Time: ps.Time, 54 }, nil 55 } 56 57 func (l *legacyExecutorWrapper) Shutdown(signal string, gracePeriod time.Duration) error { 58 // The legacy docker driver only used the executor to start a syslog server 59 // for logging. Thus calling ShutDown for docker will always return an error 60 // because it never started a process through the executor. If signal is set 61 // to 'docker' then we'll skip the ShutDown RPC and just call Exit. 62 // 63 // This is painful to look at but will only be around a few releases 64 if signal != pre09DockerSignal { 65 if err := l.client.ShutDown(); err != nil { 66 return err 67 } 68 } 69 70 if err := l.client.Exit(); err != nil { 71 return err 72 } 73 return nil 74 } 75 76 func (l *legacyExecutorWrapper) UpdateResources(*drivers.Resources) error { 77 return fmt.Errorf("operation not supported for legacy exec wrapper") 78 } 79 80 func (l *legacyExecutorWrapper) Version() (*ExecutorVersion, error) { 81 v, err := l.client.Version() 82 if err != nil { 83 return nil, err 84 } 85 86 return &ExecutorVersion{ 87 Version: v.Version, 88 }, nil 89 } 90 91 func (l *legacyExecutorWrapper) Stats(ctx context.Context, interval time.Duration) (<-chan *cstructs.TaskResourceUsage, error) { 92 ch := make(chan *cstructs.TaskResourceUsage, 1) 93 stats, err := l.client.Stats() 94 if err != nil { 95 close(ch) 96 return nil, err 97 } 98 select { 99 case ch <- stats: 100 default: 101 } 102 go l.handleStats(ctx, interval, ch) 103 return ch, nil 104 } 105 106 func (l *legacyExecutorWrapper) handleStats(ctx context.Context, interval time.Duration, ch chan *cstructs.TaskResourceUsage) { 107 defer close(ch) 108 ticker := time.NewTicker(interval) 109 for range ticker.C { 110 stats, err := l.client.Stats() 111 if err != nil { 112 if err == rpc.ErrShutdown { 113 return 114 } 115 l.logger.Warn("stats collection from legacy executor failed, waiting for next interval", "error", err) 116 continue 117 } 118 if stats != nil { 119 select { 120 case ch <- stats: 121 default: 122 } 123 } 124 125 } 126 } 127 128 func (l *legacyExecutorWrapper) Signal(s os.Signal) error { 129 return l.client.Signal(s) 130 } 131 132 func (l *legacyExecutorWrapper) Exec(deadline time.Time, cmd string, args []string) ([]byte, int, error) { 133 return l.client.Exec(deadline, cmd, args) 134 } 135 136 type pre09ExecutorRPC struct { 137 client *rpc.Client 138 logger hclog.Logger 139 } 140 141 type pre09ExecCmdArgs struct { 142 Deadline time.Time 143 Name string 144 Args []string 145 } 146 147 type pre09ExecCmdReturn struct { 148 Output []byte 149 Code int 150 } 151 152 func (e *pre09ExecutorRPC) Wait() (*ProcessState, error) { 153 var ps ProcessState 154 err := e.client.Call("Plugin.Wait", new(interface{}), &ps) 155 return &ps, err 156 } 157 158 func (e *pre09ExecutorRPC) ShutDown() error { 159 return e.client.Call("Plugin.ShutDown", new(interface{}), new(interface{})) 160 } 161 162 func (e *pre09ExecutorRPC) Exit() error { 163 return e.client.Call("Plugin.Exit", new(interface{}), new(interface{})) 164 } 165 166 func (e *pre09ExecutorRPC) Version() (*ExecutorVersion, error) { 167 var version ExecutorVersion 168 err := e.client.Call("Plugin.Version", new(interface{}), &version) 169 return &version, err 170 } 171 172 func (e *pre09ExecutorRPC) Stats() (*cstructs.TaskResourceUsage, error) { 173 var resourceUsage cstructs.TaskResourceUsage 174 err := e.client.Call("Plugin.Stats", new(interface{}), &resourceUsage) 175 return &resourceUsage, err 176 } 177 178 func (e *pre09ExecutorRPC) Signal(s os.Signal) error { 179 return e.client.Call("Plugin.Signal", &s, new(interface{})) 180 } 181 182 func (e *pre09ExecutorRPC) Exec(deadline time.Time, name string, args []string) ([]byte, int, error) { 183 req := pre09ExecCmdArgs{ 184 Deadline: deadline, 185 Name: name, 186 Args: args, 187 } 188 var resp *pre09ExecCmdReturn 189 err := e.client.Call("Plugin.Exec", req, &resp) 190 if resp == nil { 191 return nil, 0, err 192 } 193 return resp.Output, resp.Code, err 194 } 195 196 type pre09ExecutorPlugin struct { 197 logger hclog.Logger 198 } 199 200 func newPre09ExecutorPlugin(logger hclog.Logger) plugin.Plugin { 201 return &pre09ExecutorPlugin{logger: logger} 202 } 203 204 func (p *pre09ExecutorPlugin) Client(b *plugin.MuxBroker, c *rpc.Client) (interface{}, error) { 205 return &legacyExecutorWrapper{ 206 client: &pre09ExecutorRPC{client: c, logger: p.logger}, 207 logger: p.logger, 208 }, nil 209 } 210 211 func (p *pre09ExecutorPlugin) Server(*plugin.MuxBroker) (interface{}, error) { 212 return nil, fmt.Errorf("client only supported") 213 }