Skip to content

Commit 0162ebf

Browse files
committed
Add spawnpoint health check before performing client operations
1 parent 1988c49 commit 0162ebf

2 files changed

Lines changed: 66 additions & 11 deletions

File tree

spawnclient/client.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@ import (
1616
"github.com/pkg/errors"
1717
)
1818

19-
type SpawnClient struct {
19+
type Client struct {
2020
bwClient *bw2.BW2Client
2121
}
2222

23-
func New(router, entityFile string) (*SpawnClient, error) {
23+
func New(router, entityFile string) (*Client, error) {
2424
bw2.SilenceLog()
2525
client, err := bw2.Connect(router)
2626
if err != nil {
@@ -30,10 +30,18 @@ func New(router, entityFile string) (*SpawnClient, error) {
3030
return nil, errors.Wrap(err, "Failed to set BW2 entity file")
3131
}
3232

33-
return &SpawnClient{bwClient: client}, nil
33+
return &Client{bwClient: client}, nil
3434
}
3535

36-
func (sc *SpawnClient) Scan(baseURI string) (map[string]daemon.Heartbeat, error) {
36+
func (sc *Client) Scan(baseURI string) (map[string]daemon.Heartbeat, error) {
37+
namespace := strings.Split(baseURI, "/")[0]
38+
vk, _, err := sc.bwClient.ResolveLongAlias(namespace)
39+
aliased := (err == nil)
40+
var encodedVk string
41+
if aliased {
42+
encodedVk = base64.URLEncoding.EncodeToString(vk)
43+
}
44+
3745
svcClient := sc.bwClient.NewServiceClient(baseURI, "s.spawnpoint")
3846
iFaceClient := svcClient.AddInterface("daemon", "i.spawnpoint")
3947
heartbeatMsgs, err := sc.bwClient.Query(&bw2.QueryParams{
@@ -52,16 +60,21 @@ func (sc *SpawnClient) Scan(baseURI string) (map[string]daemon.Heartbeat, error)
5260
// Ignore this query result
5361
continue
5462
}
55-
uri := msg.URI[:len(msg.URI)-len("/s.spawnpoint/daemon/i.spawnpoint/signal/heartbeat")]
56-
spawnpoints[uri] = hb
63+
rawURI := msg.URI[:len(msg.URI)-len("/s.spawnpoint/daemon/i.spawnpoint/signal/heartbeat")]
64+
if aliased {
65+
convertedURI := strings.Replace(rawURI, encodedVk, namespace, 1)
66+
spawnpoints[convertedURI] = hb
67+
} else {
68+
spawnpoints[rawURI] = hb
69+
}
5770
}
5871
}
5972
}
6073

6174
return spawnpoints, nil
6275
}
6376

64-
func (sc *SpawnClient) Inspect(uri string) (*daemon.Heartbeat, map[string]daemon.ServiceHeartbeat, error) {
77+
func (sc *Client) Inspect(uri string) (*daemon.Heartbeat, map[string]daemon.ServiceHeartbeat, error) {
6578
daemonHbs, err := sc.Scan(uri)
6679
if err != nil {
6780
return nil, nil, errors.Wrap(err, "Initial spawnpoint scan failed")
@@ -103,7 +116,7 @@ func (sc *SpawnClient) Inspect(uri string) (*daemon.Heartbeat, map[string]daemon
103116
return &daemonHb, svcHeartbeats, nil
104117
}
105118

106-
func (sc *SpawnClient) Deploy(config *service.Configuration, uri string) error {
119+
func (sc *Client) Deploy(config *service.Configuration, uri string) error {
107120
if err := validateConfig(config); err != nil {
108121
return errors.Wrap(err, "Invalid service configuration")
109122
}
@@ -135,7 +148,7 @@ func (sc *SpawnClient) Deploy(config *service.Configuration, uri string) error {
135148
return nil
136149
}
137150

138-
func (sc *SpawnClient) Stop(uri string, svcName string) error {
151+
func (sc *Client) Stop(uri string, svcName string) error {
139152
svcClient := sc.bwClient.NewServiceClient(uri, "s.spawnpoint")
140153
iFaceClient := svcClient.AddInterface(svcName, "i.spawnable")
141154
if err := iFaceClient.PublishSlot("stop"); err != nil {
@@ -144,7 +157,7 @@ func (sc *SpawnClient) Stop(uri string, svcName string) error {
144157
return nil
145158
}
146159

147-
func (sc *SpawnClient) Restart(uri string, svcName string) error {
160+
func (sc *Client) Restart(uri string, svcName string) error {
148161
svcClient := sc.bwClient.NewServiceClient(uri, "s.spawnpoint")
149162
iFaceClient := svcClient.AddInterface(svcName, "i.spawnable")
150163
if err := iFaceClient.PublishSlot("restart"); err != nil {
@@ -153,7 +166,7 @@ func (sc *SpawnClient) Restart(uri string, svcName string) error {
153166
return nil
154167
}
155168

156-
func (sc *SpawnClient) Tail(ctx context.Context, svcName string, uri string) (<-chan service.LogMessage, <-chan error) {
169+
func (sc *Client) Tail(ctx context.Context, svcName string, uri string) (<-chan service.LogMessage, <-chan error) {
157170
svcClient := sc.bwClient.NewServiceClient(uri, "s.spawnpoint")
158171
iFaceClient := svcClient.AddInterface(svcName, "i.spawnable")
159172
errChan := make(chan error, 1)

spawnctl/main.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
)
1919

2020
const deploymentHistVar = "SPAWNCTL_HISTORY_FILE"
21+
const healthHorizon = 5 * time.Minute
2122

2223
func main() {
2324
app := cli.NewApp()
@@ -226,6 +227,19 @@ func actionDeploy(c *cli.Context) error {
226227
spawnClient, err := spawnclient.New(c.GlobalString("router"), entity)
227228
if err != nil {
228229
fmt.Printf("Could not create spawnpoint client: %s\n", err)
230+
os.Exit(1)
231+
}
232+
age, err := checkSpawnpointHealth(spawnClient, spawnpointURI)
233+
if err != nil {
234+
fmt.Printf("Failed to check spawnpoint health: %s\n", err)
235+
os.Exit(1)
236+
} else if age == 0 {
237+
fmt.Printf("No spawnpoint exists at %s\n", spawnpointURI)
238+
os.Exit(1)
239+
} else if age > healthHorizon {
240+
fmt.Printf("Spawnpoint at %s appears to be down\n", spawnpointURI)
241+
fmt.Printf("Last seen %s ago\n", age.String())
242+
os.Exit(1)
229243
}
230244

231245
var ctx context.Context
@@ -316,6 +330,19 @@ func manipulateService(c *cli.Context, command string) error {
316330
spawnClient, err := spawnclient.New(c.GlobalString("router"), entity)
317331
if err != nil {
318332
fmt.Printf("Could not create spawnpoint client: %s\n", err)
333+
os.Exit(1)
334+
}
335+
age, err := checkSpawnpointHealth(spawnClient, spawnpointURI)
336+
if err != nil {
337+
fmt.Printf("Failed to check spawnpoint health: %s\n", err)
338+
os.Exit(1)
339+
} else if age == 0 {
340+
fmt.Printf("No spawnpoint exists at %s\n", spawnpointURI)
341+
os.Exit(1)
342+
} else if age > healthHorizon {
343+
fmt.Printf("Spawnpoint at %s appears to be down\n", spawnpointURI)
344+
fmt.Printf("Last seen %s ago\n", age.String())
345+
os.Exit(1)
319346
}
320347

321348
var ctx context.Context
@@ -448,3 +475,18 @@ func parseSvcConfig(configFile string) (*service.Configuration, error) {
448475

449476
return &svcConfig, nil
450477
}
478+
479+
func checkSpawnpointHealth(sc *spawnclient.Client, uri string) (time.Duration, error) {
480+
spawnpoints, err := sc.Scan(uri)
481+
if err != nil {
482+
return 0, errors.Wrap(err, "Failed to scan for spawnpoint at URI")
483+
}
484+
daemonHb, ok := spawnpoints[uri]
485+
if !ok {
486+
return 0, nil
487+
}
488+
489+
daemonTimestamp := time.Unix(0, daemonHb.Time)
490+
duration := time.Now().Sub(daemonTimestamp)
491+
return duration, nil
492+
}

0 commit comments

Comments
 (0)