github.com/MetalBlockchain/metalgo@v1.11.9/api/health/client.go (about) 1 // Copyright (C) 2019-2024, Ava Labs, Inc. All rights reserved. 2 // See the file LICENSE for licensing terms. 3 4 package health 5 6 import ( 7 "context" 8 "time" 9 10 "github.com/MetalBlockchain/metalgo/utils/rpc" 11 ) 12 13 var _ Client = (*client)(nil) 14 15 // Client interface for Avalanche Health API Endpoint 16 // For helpers to wait for Readiness, Health, or Liveness, see AwaitReady, 17 // AwaitHealthy, and AwaitAlive. 18 type Client interface { 19 // Readiness returns if the node has finished initialization 20 Readiness(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error) 21 // Health returns a summation of the health of the node 22 Health(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error) 23 // Liveness returns if the node is in need of a restart 24 Liveness(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error) 25 } 26 27 // Client implementation for Avalanche Health API Endpoint 28 type client struct { 29 requester rpc.EndpointRequester 30 } 31 32 // NewClient returns a client to interact with Health API endpoint 33 func NewClient(uri string) Client { 34 return &client{requester: rpc.NewEndpointRequester( 35 uri + "/ext/health", 36 )} 37 } 38 39 func (c *client) Readiness(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error) { 40 res := &APIReply{} 41 err := c.requester.SendRequest(ctx, "health.readiness", &APIArgs{Tags: tags}, res, options...) 42 return res, err 43 } 44 45 func (c *client) Health(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error) { 46 res := &APIReply{} 47 err := c.requester.SendRequest(ctx, "health.health", &APIArgs{Tags: tags}, res, options...) 48 return res, err 49 } 50 51 func (c *client) Liveness(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error) { 52 res := &APIReply{} 53 err := c.requester.SendRequest(ctx, "health.liveness", &APIArgs{Tags: tags}, res, options...) 54 return res, err 55 } 56 57 // AwaitReady polls the node every [freq] until the node reports ready. 58 // Only returns an error if [ctx] returns an error. 59 func AwaitReady(ctx context.Context, c Client, freq time.Duration, tags []string, options ...rpc.Option) (bool, error) { 60 return await(ctx, freq, c.Readiness, tags, options...) 61 } 62 63 // AwaitHealthy polls the node every [freq] until the node reports healthy. 64 // Only returns an error if [ctx] returns an error. 65 func AwaitHealthy(ctx context.Context, c Client, freq time.Duration, tags []string, options ...rpc.Option) (bool, error) { 66 return await(ctx, freq, c.Health, tags, options...) 67 } 68 69 // AwaitAlive polls the node every [freq] until the node reports liveness. 70 // Only returns an error if [ctx] returns an error. 71 func AwaitAlive(ctx context.Context, c Client, freq time.Duration, tags []string, options ...rpc.Option) (bool, error) { 72 return await(ctx, freq, c.Liveness, tags, options...) 73 } 74 75 func await( 76 ctx context.Context, 77 freq time.Duration, 78 check func(ctx context.Context, tags []string, options ...rpc.Option) (*APIReply, error), 79 tags []string, 80 options ...rpc.Option, 81 ) (bool, error) { 82 ticker := time.NewTicker(freq) 83 defer ticker.Stop() 84 85 for { 86 res, err := check(ctx, tags, options...) 87 if err == nil && res.Healthy { 88 return true, nil 89 } 90 91 select { 92 case <-ticker.C: 93 case <-ctx.Done(): 94 return false, ctx.Err() 95 } 96 } 97 }