Skip to content

Commit ab150cf

Browse files
committed
More precise waiting for socket
Signed-off-by: Maksym Pavlenko <[email protected]>
1 parent 3bb2ef9 commit ab150cf

File tree

5 files changed

+222
-26
lines changed

5 files changed

+222
-26
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/go-openapi/strfmt v0.17.1
77
github.com/go-openapi/swag v0.17.1
88
github.com/go-openapi/validate v0.17.1
9+
github.com/golang/mock v1.1.1
910
github.com/jessevdk/go-flags v1.4.0
1011
github.com/sirupsen/logrus v1.1.1
1112
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ github.com/go-openapi/swag v0.17.1/go.mod h1:AByQ+nYG6gQg71GINrmuDXCPWdL640yX49/
3535
github.com/go-openapi/validate v0.17.0/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
3636
github.com/go-openapi/validate v0.17.1 h1:RfQTLHm/gEu0oSUmbTOy0PMufjkE5/pPfnqYpor3WLc=
3737
github.com/go-openapi/validate v0.17.1/go.mod h1:Uh4HdOzKt19xGIGm1qHf/ofbX1YQ4Y+MYsct2VUrAJ4=
38+
github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8=
39+
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
3840
github.com/google/uuid v1.0.0 h1:b4Gk+7WdP/d3HZH8EJsZpvV7EtDOgaZLtnaNGIu1adA=
3941
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
4042
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=

machine.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ func (m *Machine) startVMM(ctx context.Context) (chan error, error) {
299299
}()
300300

301301
// Wait for firecracker to initialize:
302-
err = waitForSocket(m.cfg.SocketPath, 3*time.Second, exitchannel)
302+
err = m.waitForSocket(3*time.Second, exitchannel)
303303
if err != nil {
304304
msg := fmt.Sprintf("Firecracker did not create API socket %s: %s", m.cfg.SocketPath, err)
305305
err = errors.New(msg)
@@ -514,28 +514,38 @@ func (m *Machine) refreshMachineConfig() error {
514514
return nil
515515
}
516516

517-
// waitFile waits for the given file to exist
518-
func waitForSocket(path string, timeout time.Duration, exitchan chan error) error {
517+
// waitForSocket waits for the given file to exist
518+
func (m *Machine) waitForSocket(timeout time.Duration, exitchan chan error) error {
519519
ctx, cancel := context.WithTimeout(context.Background(), timeout)
520520
defer cancel()
521-
exists := make(chan bool)
522-
var err error
521+
522+
done := make(chan error)
523+
ticker := time.NewTicker(10 * time.Millisecond)
524+
523525
go func() {
524526
for {
525-
_, err = os.Stat(path)
526-
if err == nil {
527-
exists <- true
527+
select {
528+
case <-ctx.Done():
529+
done <- ctx.Err()
530+
return
531+
case err := <-exitchan:
532+
done <- err
533+
return
534+
case <-ticker.C:
535+
if _, err := os.Stat(m.cfg.SocketPath); err != nil {
536+
continue
537+
}
538+
539+
// Send test HTTP request to make sure socket is available
540+
if _, err := m.client.GetMachineConfig(); err != nil {
541+
continue
542+
}
543+
544+
done <- nil
528545
return
529546
}
530-
time.Sleep(10 * time.Millisecond)
531547
}
532548
}()
533-
select {
534-
case <-ctx.Done():
535-
return ctx.Err()
536-
case <-exists:
537-
return nil
538-
case <-exitchan:
539-
return errors.New("Firecracker exited unexpectedly")
540-
}
549+
550+
return <-done
541551
}

machine_mock_test.go

Lines changed: 154 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

machine_test.go

Lines changed: 38 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
// express or implied. See the License for the specific language governing
1212
// permissions and limitations under the License.
1313

14+
//go:generate mockgen -source=machine.go -destination=machine_mock_test.go -package=firecracker
15+
1416
package firecracker
1517

1618
import (
@@ -21,6 +23,8 @@ import (
2123
"path/filepath"
2224
"testing"
2325
"time"
26+
27+
"github.com/golang/mock/gomock"
2428
)
2529

2630
const (
@@ -216,38 +220,63 @@ func testStopVMM(ctx context.Context, t *testing.T, m *Machine) {
216220
}
217221

218222
func TestWaitForSocket(t *testing.T) {
223+
ctrl := gomock.NewController(t)
224+
defer ctrl.Finish()
225+
226+
okClient := NewMockFirecracker(ctrl)
227+
okClient.EXPECT().GetMachineConfig().AnyTimes().Return(nil, nil)
228+
229+
errClient := NewMockFirecracker(ctrl)
230+
errClient.EXPECT().GetMachineConfig().AnyTimes().Return(nil, errors.New("http error"))
231+
219232
// testWaitForSocket has three conditions that need testing:
220-
// 1. The expected file is created within the deadline
233+
// 1. The expected file is created within the deadline and
234+
// socket HTTP request succeeded
221235
// 2. The expected file is not created within the deadline
222236
// 3. The process responsible for creating the file exits
223-
// (indicated by an error published to exitchan)
237+
// (indicated by an error published to exitchan)
224238
filename := "./test-create-file"
225239
errchan := make(chan error)
226240

241+
m := Machine{
242+
cfg: Config{SocketPath: filename},
243+
}
244+
227245
go func() {
228246
time.Sleep(50 * time.Millisecond)
229247
_, err := os.Create(filename)
230248
if err != nil {
231249
t.Fatalf("Unable to create test file %s: %s", filename, err)
232250
}
233251
}()
234-
err := waitForSocket(filename, 500*time.Millisecond, errchan)
235-
if err != nil {
252+
253+
// Socket file created, HTTP request succeeded
254+
m.client = okClient
255+
if err := m.waitForSocket(500*time.Millisecond, errchan); err != nil {
236256
t.Errorf("waitForSocket returned unexpected error %s", err)
237257
}
238258

259+
// Socket file exists, HTTP request failed
260+
m.client = errClient
261+
if err := m.waitForSocket(500*time.Millisecond, errchan); err != context.DeadlineExceeded {
262+
t.Error("waitforSocket did not return an expected timeout error")
263+
}
264+
239265
os.Remove(filename)
240-
err = waitForSocket(filename, 100*time.Millisecond, errchan)
241-
if err == nil {
266+
267+
// No socket file
268+
if err := m.waitForSocket(100*time.Millisecond, errchan); err != context.DeadlineExceeded {
242269
t.Error("waitforSocket did not return an expected timeout error")
243270
}
244271

272+
chanErr := errors.New("this is an expected error")
245273
go func() {
246274
time.Sleep(50 * time.Millisecond)
247-
errchan <- errors.New("This is an expected error")
275+
errchan <- chanErr
248276
}()
249-
err = waitForSocket(filename, 100*time.Millisecond, errchan)
250-
if err == nil {
277+
278+
// Unexpected process exit
279+
if err := m.waitForSocket(100*time.Millisecond, errchan); err != chanErr {
251280
t.Error("waitForSocket did not properly detect program exit")
252281
}
253282
}

0 commit comments

Comments
 (0)