@@ -25,6 +25,7 @@ import (
2525 "regexp"
2626 "strconv"
2727 "strings"
28+ "time"
2829
2930 "github.com/facebookincubator/contest/pkg/cerrors"
3031 "github.com/facebookincubator/contest/pkg/event"
@@ -48,6 +49,7 @@ var log = logging.GetLogger("teststeps/" + strings.ToLower(Name))
4849var Events = []event.Name {}
4950
5051const defaultSSHPort = 22
52+ const defaultTimeoutParameter = "10m"
5153
5254// SSHCmd is used to run arbitrary commands as test steps.
5355type SSHCmd struct {
@@ -59,6 +61,7 @@ type SSHCmd struct {
5961 Executable * test.Param
6062 Args []test.Param
6163 Expect * test.Param
64+ Timeout * test.Param
6265}
6366
6467// Name returns the plugin name.
@@ -101,6 +104,18 @@ func (ts *SSHCmd) Run(cancel, pause <-chan struct{}, ch test.TestStepChannels, p
101104 return fmt .Errorf ("failed to convert port parameter to integer: %v" , err )
102105 }
103106
107+ timeoutStr , err := ts .Timeout .Expand (target )
108+ if err != nil {
109+ return fmt .Errorf ("cannot expand timeout parameter %s: %v" , timeoutStr , err )
110+ }
111+
112+ timeout , err := time .ParseDuration (timeoutStr )
113+ if err != nil {
114+ return fmt .Errorf ("cannot parse timeout paramter: %v" , err )
115+ }
116+
117+ timeTimeout := time .Now ().Add (timeout )
118+
104119 // apply functions to the private key, if any
105120 var signer ssh.Signer
106121 privKeyFile , err := ts .PrivateKeyFile .Expand (target )
@@ -186,31 +201,58 @@ func (ts *SSHCmd) Run(cancel, pause <-chan struct{}, ch test.TestStepChannels, p
186201 errCh <- innerErr
187202 }()
188203
189- select {
190- case err := <- errCh :
191- log .Infof ("Stdout of command '%s' is '%s'" , cmd , stdout .Bytes ())
192- if err == nil {
193- // Execute expectations
194- expect := ts .Expect .String ()
195- if expect == "" {
196- log .Warningf ("no expectations specified" )
204+ expect := ts .Expect .String ()
205+ re , err := regexp .Compile (expect )
206+ keepAliveCnt := 0
207+
208+ if err != nil {
209+ return fmt .Errorf ("malformed expect parameter: Can not compile %s with %v" , expect , err )
210+ }
211+
212+ for {
213+ select {
214+ case err := <- errCh :
215+ log .Infof ("Stdout of command '%s' is '%s'" , cmd , stdout .Bytes ())
216+ if err == nil {
217+ // Execute expectations
218+ if expect == "" {
219+ log .Warningf ("no expectations specified" )
220+ } else {
221+ matches := re .FindAll (stdout .Bytes (), - 1 )
222+ if len (matches ) > 0 {
223+ log .Infof ("match for regex '%s' found" , expect )
224+ } else {
225+ return fmt .Errorf ("match for %s not found for target %v" , expect , target )
226+ }
227+ }
197228 } else {
198- re := regexp .MustCompile (expect )
229+ log .Warningf ("Stderr of command '%s' is '%s'" , cmd , stderr .Bytes ())
230+ }
231+ return err
232+ case <- cancel :
233+ return session .Signal (ssh .SIGKILL )
234+ case <- pause :
235+ return session .Signal (ssh .SIGKILL )
236+ case <- time .After (250 * time .Millisecond ):
237+ keepAliveCnt ++
238+ if expect != "" {
199239 matches := re .FindAll (stdout .Bytes (), - 1 )
200240 if len (matches ) > 0 {
201- log .Infof ("match for regex \" %s\" found" , expect )
202- } else {
203- return fmt .Errorf ("match for %s not found for target %v" , expect , target )
241+ log .Infof ("match for regex '%s' found" , expect )
242+ return nil
243+ }
244+ }
245+ if time .Now ().After (timeTimeout ) {
246+ return fmt .Errorf ("timed out after %s" , timeout )
247+ }
248+ // This is needed to keep the connection to the server alive
249+ if keepAliveCnt % 20 == 0 {
250+ err = session .Signal (ssh .Signal ("CONT" ))
251+ if err != nil {
252+ log .Warnf ("Unable to send CONT to ssh server: %v" , err )
204253 }
205254 }
206- } else {
207- log .Warningf ("Stderr of command '%s' is '%s'" , cmd , stderr .Bytes ())
208255 }
209- return err
210- case <- cancel :
211- return session .Signal (ssh .SIGKILL )
212- case <- pause :
213- return session .Signal (ssh .SIGKILL )
214256 }
215257 }
216258 return teststeps .ForEachTarget (Name , cancel , pause , ch , f )
@@ -253,6 +295,12 @@ func (ts *SSHCmd) validateAndPopulate(params test.TestStepParameters) error {
253295 }
254296 ts .Args = params .Get ("args" )
255297 ts .Expect = params .GetOne ("expect" )
298+
299+ if params .GetOne ("timeout" ).IsEmpty () {
300+ ts .Timeout = test .NewParam (defaultTimeoutParameter )
301+ } else {
302+ ts .Timeout = params .GetOne ("timeout" )
303+ }
256304 return nil
257305}
258306
0 commit comments