Skip to content

Commit 91813e2

Browse files
authored
Merge pull request #10 from machinebox/promote-boxutil
Move boxutil out of x package
2 parents db06e07 + 104e026 commit 91813e2

File tree

5 files changed

+177
-0
lines changed

5 files changed

+177
-0
lines changed

boxutil/doc.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Package boxutil provides some helpful utilities for consuming Machine Box services.
2+
package boxutil

boxutil/info.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package boxutil
2+
3+
// Box represents a box client capable of returning
4+
// Info.
5+
type Box interface {
6+
Info() (*Info, error)
7+
}
8+
9+
// Info describes box information.
10+
type Info struct {
11+
Name string
12+
Version int
13+
Build string
14+
Status string
15+
}

boxutil/status.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package boxutil
2+
3+
import (
4+
"context"
5+
"time"
6+
)
7+
8+
// readyCheckInterval is the interval to wait between checking
9+
// the status in StatusChan.
10+
// Unexported because 1 second is sensible, but configurable to make
11+
// tests run quicker.
12+
var readyCheckInterval = 1 * time.Second
13+
14+
// StatusChan gets a channel that periodically gets the box info
15+
// and sends a message whenever the status changes.
16+
func StatusChan(ctx context.Context, i Box) <-chan string {
17+
statusChan := make(chan string)
18+
go func() {
19+
var lastStatus string
20+
for {
21+
select {
22+
case <-ctx.Done():
23+
return
24+
default:
25+
time.Sleep(readyCheckInterval)
26+
status := "unavailable"
27+
info, err := i.Info()
28+
if err == nil {
29+
status = info.Status
30+
}
31+
if status != lastStatus {
32+
lastStatus = status
33+
statusChan <- status
34+
}
35+
}
36+
}
37+
}()
38+
return statusChan
39+
}
40+
41+
// WaitForReady blocks until the Box is ready.
42+
func WaitForReady(ctx context.Context, i Box) error {
43+
ctx, cancel := context.WithCancel(ctx)
44+
defer cancel()
45+
statusChan := StatusChan(ctx, i)
46+
for {
47+
select {
48+
case <-ctx.Done():
49+
return ctx.Err()
50+
case status := <-statusChan:
51+
if IsReady(status) {
52+
return nil
53+
}
54+
}
55+
}
56+
}
57+
58+
// IsReady gets whether the box info status is ready or not.
59+
func IsReady(status string) bool {
60+
return status == "ready"
61+
}

boxutil/status_test.go

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
package boxutil
2+
3+
import (
4+
"context"
5+
"errors"
6+
"sync"
7+
"testing"
8+
"time"
9+
10+
"github.com/matryer/is"
11+
)
12+
13+
func init() {
14+
// quicker for testing
15+
readyCheckInterval = 100 * time.Millisecond
16+
}
17+
18+
func TestStatusChan(t *testing.T) {
19+
is := is.New(t)
20+
21+
i := &testBox{}
22+
ctx, cancel := context.WithCancel(context.Background())
23+
defer cancel()
24+
25+
status := StatusChan(ctx, i)
26+
is.Equal(<-status, "starting...")
27+
i.setReady()
28+
is.Equal(<-status, "ready")
29+
i.setError()
30+
is.Equal(<-status, "unavailable")
31+
i.clearError()
32+
is.Equal(<-status, "ready")
33+
34+
}
35+
36+
func TestWaitForReady(t *testing.T) {
37+
is := is.New(t)
38+
i := &testBox{}
39+
ctx, cancel := context.WithCancel(context.Background())
40+
time.AfterFunc(300*time.Millisecond, cancel)
41+
go func() {
42+
i.setReady()
43+
}()
44+
err := WaitForReady(ctx, i)
45+
is.NoErr(err)
46+
}
47+
48+
func TestWaitForReadyTimeout(t *testing.T) {
49+
is := is.New(t)
50+
i := &testBox{}
51+
ctx, cancel := context.WithCancel(context.Background())
52+
time.AfterFunc(100*time.Millisecond, cancel)
53+
go func() {
54+
time.Sleep(200 * time.Millisecond)
55+
i.setReady()
56+
}()
57+
err := WaitForReady(ctx, i)
58+
is.Equal(err, context.Canceled)
59+
}
60+
61+
type testBox struct {
62+
lock sync.Mutex
63+
ready bool
64+
err error
65+
}
66+
67+
func (i *testBox) setReady() {
68+
i.lock.Lock()
69+
defer i.lock.Unlock()
70+
i.ready = true
71+
}
72+
73+
func (i *testBox) setError() {
74+
i.lock.Lock()
75+
defer i.lock.Unlock()
76+
i.err = errors.New("cannot reach server")
77+
}
78+
79+
func (i *testBox) clearError() {
80+
i.lock.Lock()
81+
defer i.lock.Unlock()
82+
i.err = nil
83+
}
84+
85+
func (i *testBox) Info() (*Info, error) {
86+
i.lock.Lock()
87+
defer i.lock.Unlock()
88+
if i.err != nil {
89+
return nil, i.err
90+
}
91+
if i.ready {
92+
return &Info{Status: "ready"}, nil
93+
}
94+
return &Info{Status: "starting..."}, nil
95+
}

x/boxutil/status.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package boxutil
33
import (
44
"context"
55
"errors"
6+
"log"
67
"time"
78
)
89

@@ -19,6 +20,7 @@ var readyCheckInterval = 1 * time.Second
1920
// StatusChan gets a channel that periodically gets the box info
2021
// and sends a message whenever the status changes.
2122
func StatusChan(ctx context.Context, i Box) <-chan string {
23+
log.Println("boxutil: DEPRECATED use github.com/machinebox/sdk-go/boxutil instead")
2224
statusChan := make(chan string)
2325
go func() {
2426
var lastStatus string
@@ -45,6 +47,7 @@ func StatusChan(ctx context.Context, i Box) <-chan string {
4547

4648
// WaitForReady blocks until the Box is ready.
4749
func WaitForReady(ctx context.Context, i Box) error {
50+
log.Println("boxutil: DEPRECATED use github.com/machinebox/sdk-go/boxutil instead")
4851
ctx, cancel := context.WithCancel(ctx)
4952
defer cancel()
5053
statusChan := StatusChan(ctx, i)
@@ -62,5 +65,6 @@ func WaitForReady(ctx context.Context, i Box) error {
6265

6366
// IsReady gets whether the box info status is ready or not.
6467
func IsReady(status string) bool {
68+
log.Println("boxutil: DEPRECATED use github.com/machinebox/sdk-go/boxutil instead")
6569
return status == "ready"
6670
}

0 commit comments

Comments
 (0)