// closer provides an embeddable implementation of Close which awaits a main loop acknowledging it has stopped.
typecloserstruct{
stopchanstruct{}
stoppedchanstruct{}
}
// newCloser returns a closer with the channels initialised.
funcnewCloser()closer{
returncloser{
stop:make(chanstruct{}),
stopped:make(chanstruct{}),
}
}
// Close stops the main loop, waiting for the main loop to stop until it stops or the context is cancelled, whichever happens first.
func(c*closer)Close(ctxcontext.Context)error{
select{
case<-c.stopped:
returnnil
case<-c.stop:
returnnil
case<-ctx.Done():
returnctx.Err()
default:
}
close(c.stop)
select{
case<-c.stopped:
returnnil
case<-ctx.Done():
returnctx.Err()
}
}
// lineWriter is an io.Writer which splits on \n and outputs each line (with no trailing newline) to its output channel.
typelineWriterstruct{
bufstring
outchanstring
}
// Write accepts a slice of bytes containing zero or more new lines.
// If the contained channel is non-buffering or is full, this will block.
func(w*lineWriter)Write(p[]byte)(nint,errerror){
w.buf+=string(p)
pieces:=strings.Split(w.buf,"\n")
w.buf=pieces[len(pieces)-1]
forn:=0;n<len(pieces)-1;n++{
w.out<-pieces[n]
}
returnlen(p),nil
}
// restartingClient is a simple SSH client that repeatedly connects to an SSH server, runs a command, and outputs the lines output by it on stdout onto a channel.
typerestartingClientstruct{
closer
networkstring
addrstring
cfg*ssh.ClientConfig
execstring
outputchanstring
shutdownfunc()
}
var(
errStopConnect=errors.New("gerrit: told to stop reconnecting by remote server")
)
func(c*restartingClient)runOnce()error{
netConn,err:=net.Dial(c.network,c.addr)
iferr!=nil{
returnfmt.Errorf("connecting to %v/%v: %w",c.network,c.addr,err)