launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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 "launchpad.net/juju-core/utils/exec" 15 ) 16 17 const JujuRunEndpoint = "JujuRunServer.RunCommands" 18 19 // A CommandRunner is something that will actually execute the commands and 20 // return the results of that execution in the exec.ExecResponse (which 21 // contains stdout, stderr, and return code). 22 type CommandRunner interface { 23 RunCommands(commands string) (results *exec.ExecResponse, err error) 24 } 25 26 // RunListener is responsible for listening on the network connection and 27 // seting up the rpc server on that net connection. Also starts the go routine 28 // that listens and hands off the work. 29 type RunListener struct { 30 listener net.Listener 31 server *rpc.Server 32 closed chan struct{} 33 closing chan struct{} 34 wg sync.WaitGroup 35 } 36 37 // The JujuRunServer is the entity that has the methods that are called over 38 // the rpc connection. 39 type JujuRunServer struct { 40 runner CommandRunner 41 } 42 43 // RunCommands delegates the actual running to the runner and populates the 44 // response structure. 45 func (r *JujuRunServer) RunCommands(commands string, result *exec.ExecResponse) error { 46 logger.Debugf("RunCommands: %q", commands) 47 runResult, err := r.runner.RunCommands(commands) 48 *result = *runResult 49 return err 50 } 51 52 // NewRunListener returns a new RunListener that is listening on the network 53 // type and address passed in. If a valid RunListener is returned, is has the 54 // go routine running, and should be closed by the creator when they are done 55 // with it. 56 func NewRunListener(runner CommandRunner, netType, localAddr string) (*RunListener, error) { 57 server := rpc.NewServer() 58 if err := server.Register(&JujuRunServer{runner}); err != nil { 59 return nil, mask(err) 60 } 61 listener, err := net.Listen(netType, localAddr) 62 if err != nil { 63 logger.Errorf("failed to listen on %s %s: %v", netType, localAddr, err) 64 return nil, err 65 } 66 runListener := &RunListener{ 67 listener: listener, 68 server: server, 69 closed: make(chan struct{}), 70 closing: make(chan struct{}), 71 } 72 go runListener.Run() 73 return runListener, nil 74 } 75 76 // Run accepts new connections until it encounters an error, or until Close is 77 // called, and then blocks until all existing connections have been closed. 78 func (s *RunListener) Run() (err error) { 79 logger.Debugf("juju-run listener running") 80 var conn net.Conn 81 for { 82 conn, err = s.listener.Accept() 83 if err != nil { 84 break 85 } 86 s.wg.Add(1) 87 go func(conn net.Conn) { 88 s.server.ServeConn(conn) 89 s.wg.Done() 90 }(conn) 91 } 92 logger.Debugf("juju-run listener stopping") 93 select { 94 case <-s.closing: 95 // Someone has called Close(), so it is overwhelmingly likely that 96 // the error from Accept is a direct result of the Listener being 97 // closed, and can therefore be safely ignored. 98 err = nil 99 default: 100 } 101 s.wg.Wait() 102 close(s.closed) 103 return 104 } 105 106 // Close immediately stops accepting connections, and blocks until all existing 107 // connections have been closed. 108 func (s *RunListener) Close() { 109 close(s.closing) 110 s.listener.Close() 111 <-s.closed 112 logger.Debugf("juju-run listener stopped") 113 }