github.com/mirantis/virtlet@v1.5.2-0.20191204181327-1659b8a48e9b/tests/e2e/framework/common.go (about) 1 /* 2 Copyright 2017 Mirantis 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package framework 18 19 import ( 20 "bytes" 21 "fmt" 22 "io" 23 "strings" 24 "time" 25 ) 26 27 const ( 28 NginxImage = "docker.io/nginx:1.14.2" 29 BusyboxImage = "docker.io/busybox:1.30.0" 30 ) 31 32 // ErrTimeout is the timeout error returned from functions wrapped by WithTimeout 33 var ErrTimeout = fmt.Errorf("timeout") 34 35 // CommandError holds an exit code for commands finished without any Executor error 36 type CommandError struct { 37 ExitCode int 38 } 39 40 func (e CommandError) Error() string { 41 return fmt.Sprintf("command finished with %d exit code", e.ExitCode) 42 } 43 44 var _ error = CommandError{} 45 46 // Command is the interface to control the command started with an Executor 47 type Command interface { 48 Kill() error 49 Wait() error 50 } 51 52 // Executor is the interface to run shell commands in arbitrary places 53 type Executor interface { 54 io.Closer 55 Run(stdin io.Reader, stdout, stderr io.Writer, command ...string) error 56 Start(stdin io.Reader, stdout, stderr io.Writer, command ...string) (Command, error) 57 Logs() (string, error) 58 } 59 60 // Run executes command with the given executor, returns stdout/stderr as strings 61 // and exit code in CommandError 62 func Run(executor Executor, input string, command ...string) (string, string, error) { 63 outBuf := &bytes.Buffer{} 64 errBuf := &bytes.Buffer{} 65 inBuf := bytes.NewBufferString(input) 66 err := executor.Run(inBuf, outBuf, errBuf, command...) 67 return outBuf.String(), errBuf.String(), err 68 } 69 70 // RunSimple is a simplified version of Run that verifies exit code/stderr internally and returns stdout only 71 func RunSimple(executor Executor, command ...string) (string, error) { 72 stdout, stderr, err := Run(executor, "", command...) 73 if err != nil { 74 if ce, ok := err.(CommandError); ok { 75 if ce.ExitCode != 0 { 76 return "", fmt.Errorf("command exited with code %d, stderr: %s", ce.ExitCode, strings.TrimSpace(stderr)+strings.TrimSpace(stdout)) 77 } 78 return strings.TrimSpace(stdout), nil 79 } 80 return "", err 81 } 82 return strings.TrimSpace(stdout), nil 83 } 84 85 func trimBlock(s string) string { 86 lines := strings.Split(s, "\n") 87 for i, line := range lines { 88 lines[i] = strings.TrimSpace(line) 89 } 90 return strings.Join(lines, "\n") 91 } 92 93 func waitFor(f func() error, wait, poll time.Duration, waitFailure bool) error { 94 if poll <= 0 || wait <= 0 { 95 wait = time.Duration(time.Hour) 96 poll = 0 97 } 98 timeout := time.After(wait) 99 err := f() 100 if err == nil && !waitFailure || err != nil && waitFailure { 101 return err 102 } 103 result := err 104 for { 105 select { 106 case <-time.After(poll): 107 err := f() 108 if err == nil && !waitFailure || err != nil && waitFailure { 109 return err 110 } 111 result = err 112 if poll == 0 { 113 return result 114 } 115 case <-timeout: 116 return result 117 } 118 } 119 } 120 121 func waitForConsistentState(f func() error, timing ...time.Duration) error { 122 if len(timing) == 0 { 123 panic("timing is not provided") 124 } 125 var pollPeriod time.Duration 126 if len(timing) == 1 || timing[1] <= 0 { 127 pollPeriod = time.Duration(timing[0].Nanoseconds() / 10) 128 } else { 129 pollPeriod = timing[1] 130 } 131 var err error 132 for timing[0] > 0 { 133 now := time.Now() 134 if err = waitFor(f, timing[0], pollPeriod, false); err != nil { 135 timing[0] -= time.Now().Sub(now) 136 continue 137 } 138 139 if len(timing) >= 2 { 140 now := time.Now() 141 if err = waitFor(f, timing[2], pollPeriod, true); err != nil { 142 timing[0] -= time.Now().Sub(now) 143 continue 144 } 145 } 146 break 147 } 148 return err 149 } 150 151 // WithTimeout adds timeout to synchronous function 152 func WithTimeout(timeout time.Duration, fn func() error) func() error { 153 return func() error { 154 res := make(chan error, 1) 155 go func() { 156 res <- fn() 157 }() 158 timer := time.After(timeout) 159 select { 160 case e := <-res: 161 return e 162 case <-timer: 163 return ErrTimeout 164 } 165 } 166 }