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