Skip to content

Commit 0db266c

Browse files
committed
Move boxutil out of x package
The old package now prints a warning if used.
1 parent a32688d commit 0db266c

File tree

5 files changed

+182
-0
lines changed

5 files changed

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

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, ErrCanceled)
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)