github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/uniter/runlistener.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // The run listener is a worker go-routine that listens on either a unix 5 // socket or a tcp connection for juju-run commands. 6 7 package uniter 8 9 import ( 10 "net" 11 "net/rpc" 12 "sync" 13 14 "github.com/juju/errors" 15 "github.com/juju/utils/exec" 16 "gopkg.in/juju/worker.v1" 17 "gopkg.in/tomb.v2" 18 19 "github.com/juju/juju/juju/sockets" 20 "github.com/juju/juju/worker/uniter/operation" 21 "github.com/juju/juju/worker/uniter/runcommands" 22 ) 23 24 const JujuRunEndpoint = "JujuRunServer.RunCommands" 25 26 var errCommandAborted = errors.New("command execution aborted") 27 28 // RunCommandsArgs stores the arguments for a RunCommands call. 29 type RunCommandsArgs struct { 30 // Commands is the arbitrary commands to execute on the unit 31 Commands string 32 // RelationId is the relation context to execute the commands in. 33 RelationId int 34 // RemoteUnitName is the remote unit for the relation context. 35 RemoteUnitName string 36 // ForceRemoteUnit skips relation membership and existence validation. 37 ForceRemoteUnit bool 38 } 39 40 // A CommandRunner is something that will actually execute the commands and 41 // return the results of that execution in the exec.ExecResponse (which 42 // contains stdout, stderr, and return code). 43 type CommandRunner interface { 44 RunCommands(RunCommandsArgs RunCommandsArgs) (results *exec.ExecResponse, err error) 45 } 46 47 // RunListenerConfig contains the configuration for a RunListener. 48 type RunListenerConfig struct { 49 // SocketPath is the path of the socket to listen on for run commands. 50 SocketPath string 51 52 // CommandRunner is the CommandRunner that will run commands. 53 CommandRunner CommandRunner 54 } 55 56 func (cfg *RunListenerConfig) Validate() error { 57 if cfg.SocketPath == "" { 58 return errors.NotValidf("SocketPath unspecified") 59 } 60 if cfg.CommandRunner == nil { 61 return errors.NotValidf("CommandRunner unspecified") 62 } 63 return nil 64 } 65 66 // RunListener is responsible for listening on the network connection and 67 // setting up the rpc server on that net connection. Also starts the go routine 68 // that listens and hands off the work. 69 type RunListener struct { 70 RunListenerConfig 71 listener net.Listener 72 server *rpc.Server 73 closed chan struct{} 74 closing chan struct{} 75 wg sync.WaitGroup 76 } 77 78 // NewRunListener returns a new RunListener that is listening on given 79 // socket or named pipe passed in. If a valid RunListener is returned, is 80 // has the go routine running, and should be closed by the creator 81 // when they are done with it. 82 func NewRunListener(cfg RunListenerConfig) (*RunListener, error) { 83 if err := cfg.Validate(); err != nil { 84 return nil, errors.Trace(err) 85 } 86 listener, err := sockets.Listen(cfg.SocketPath) 87 if err != nil { 88 return nil, errors.Trace(err) 89 } 90 runListener := &RunListener{ 91 RunListenerConfig: cfg, 92 listener: listener, 93 server: rpc.NewServer(), 94 closed: make(chan struct{}), 95 closing: make(chan struct{}), 96 } 97 if err := runListener.server.Register(&JujuRunServer{runListener}); err != nil { 98 return nil, errors.Trace(err) 99 } 100 go runListener.Run() 101 return runListener, nil 102 } 103 104 // Run accepts new connections until it encounters an error, or until Close is 105 // called, and then blocks until all existing connections have been closed. 106 func (s *RunListener) Run() (err error) { 107 logger.Debugf("juju-run listener running") 108 var conn net.Conn 109 for { 110 conn, err = s.listener.Accept() 111 if err != nil { 112 break 113 } 114 s.wg.Add(1) 115 go func(conn net.Conn) { 116 s.server.ServeConn(conn) 117 s.wg.Done() 118 }(conn) 119 } 120 logger.Debugf("juju-run listener stopping") 121 select { 122 case <-s.closing: 123 // Someone has called Close(), so it is overwhelmingly likely that 124 // the error from Accept is a direct result of the Listener being 125 // closed, and can therefore be safely ignored. 126 err = nil 127 default: 128 } 129 s.wg.Wait() 130 close(s.closed) 131 return 132 } 133 134 // Close immediately stops accepting connections, and blocks until all existing 135 // connections have been closed. 136 func (s *RunListener) Close() error { 137 defer func() { 138 <-s.closed 139 logger.Debugf("juju-run listener stopped") 140 }() 141 close(s.closing) 142 return s.listener.Close() 143 } 144 145 // RunCommands executes the supplied commands in a hook context. 146 func (r *RunListener) RunCommands(args RunCommandsArgs) (results *exec.ExecResponse, err error) { 147 logger.Tracef("run commands: %s", args.Commands) 148 return r.CommandRunner.RunCommands(args) 149 } 150 151 // newRunListenerWrapper returns a worker that will Close the supplied run 152 // listener when the worker is killed. The Wait() method will never return 153 // an error -- NewRunListener just drops the Run error on the floor and that's 154 // not what I'm fixing here. 155 func newRunListenerWrapper(rl *RunListener) worker.Worker { 156 rlw := &runListenerWrapper{rl: rl} 157 rlw.tomb.Go(func() error { 158 defer rlw.tearDown() 159 <-rlw.tomb.Dying() 160 return nil 161 }) 162 return rlw 163 } 164 165 type runListenerWrapper struct { 166 tomb tomb.Tomb 167 rl *RunListener 168 } 169 170 func (rlw *runListenerWrapper) tearDown() { 171 if err := rlw.rl.Close(); err != nil { 172 logger.Warningf("error closing runlistener: %v", err) 173 } 174 } 175 176 // Kill is part of the worker.Worker interface. 177 func (rlw *runListenerWrapper) Kill() { 178 rlw.tomb.Kill(nil) 179 } 180 181 // Wait is part of the worker.Worker interface. 182 func (rlw *runListenerWrapper) Wait() error { 183 return rlw.tomb.Wait() 184 } 185 186 // The JujuRunServer is the entity that has the methods that are called over 187 // the rpc connection. 188 type JujuRunServer struct { 189 runner CommandRunner 190 } 191 192 // RunCommands delegates the actual running to the runner and populates the 193 // response structure. 194 func (r *JujuRunServer) RunCommands(args RunCommandsArgs, result *exec.ExecResponse) error { 195 logger.Debugf("RunCommands: %+v", args) 196 runResult, err := r.runner.RunCommands(args) 197 if err != nil { 198 return errors.Annotate(err, "r.runner.RunCommands") 199 } 200 *result = *runResult 201 return err 202 } 203 204 // ChannelCommandRunnerConfig contains the configuration for a ChannelCommandRunner. 205 type ChannelCommandRunnerConfig struct { 206 // Abort is a channel that will be closed when the runner should abort 207 // the execution of run commands. 208 Abort <-chan struct{} 209 210 // Commands is used to add commands received from the listener. 211 Commands runcommands.Commands 212 213 // CommandChannel will be sent the IDs of commands added to Commands. 214 CommandChannel chan<- string 215 } 216 217 func (cfg ChannelCommandRunnerConfig) Validate() error { 218 if cfg.Abort == nil { 219 return errors.NotValidf("Abort unspecified") 220 } 221 if cfg.Commands == nil { 222 return errors.NotValidf("Commands unspecified") 223 } 224 if cfg.CommandChannel == nil { 225 return errors.NotValidf("CommandChannel unspecified") 226 } 227 return nil 228 } 229 230 // ChannelCommandRunner is a CommandRunner that registers command 231 // arguments in a runcommands.Commands, sends the returned IDs to 232 // a channel and waits for response callbacks. 233 type ChannelCommandRunner struct { 234 config ChannelCommandRunnerConfig 235 } 236 237 // NewChannelCommandRunner returns a new ChannelCommandRunner with the 238 // given configuration. 239 func NewChannelCommandRunner(cfg ChannelCommandRunnerConfig) (*ChannelCommandRunner, error) { 240 if err := cfg.Validate(); err != nil { 241 return nil, errors.Trace(err) 242 } 243 return &ChannelCommandRunner{cfg}, nil 244 } 245 246 // RunCommands executes the supplied run commands by registering the 247 // arguments in a runcommands.Commands, and then sending the returned 248 // ID to a channel and waiting for a response callback. 249 func (c *ChannelCommandRunner) RunCommands(args RunCommandsArgs) (results *exec.ExecResponse, err error) { 250 type responseInfo struct { 251 response *exec.ExecResponse 252 err error 253 } 254 255 // NOTE(axw) the response channel must be synchronous so that the 256 // response is received before the uniter resumes operation, and 257 // potentially aborts. This prevents a race when rebooting. 258 responseChan := make(chan responseInfo) 259 responseFunc := func(response *exec.ExecResponse, err error) { 260 select { 261 case <-c.config.Abort: 262 case responseChan <- responseInfo{response, err}: 263 } 264 } 265 266 id := c.config.Commands.AddCommand( 267 operation.CommandArgs{ 268 Commands: args.Commands, 269 RelationId: args.RelationId, 270 RemoteUnitName: args.RemoteUnitName, 271 ForceRemoteUnit: args.ForceRemoteUnit, 272 }, 273 responseFunc, 274 ) 275 select { 276 case <-c.config.Abort: 277 return nil, errCommandAborted 278 case c.config.CommandChannel <- id: 279 } 280 281 select { 282 case <-c.config.Abort: 283 return nil, errCommandAborted 284 case response := <-responseChan: 285 return response.response, response.err 286 } 287 }