140 lines
3.2 KiB
Go
140 lines
3.2 KiB
Go
package healthchecks
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"strconv"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// Client for healthchecks
|
|
// if client initialized without any options, it will be disabled by default,
|
|
// but you can override it by calling SetEnabled(true).
|
|
type Client struct {
|
|
wg sync.WaitGroup
|
|
enabled bool
|
|
http *http.Client
|
|
log func(string, error)
|
|
userAgent string
|
|
baseURL string
|
|
uuid string
|
|
rid string
|
|
create bool
|
|
done chan bool
|
|
}
|
|
|
|
// init client
|
|
func (c *Client) init(options ...Option) {
|
|
c.enabled = true
|
|
c.log = DefaultErrLog
|
|
c.baseURL = DefaultAPI
|
|
c.userAgent = DefaultUserAgent
|
|
c.http = &http.Client{Timeout: 10 * time.Second}
|
|
c.done = make(chan bool, 1)
|
|
c.uuid = ""
|
|
|
|
if len(options) == 0 {
|
|
c.enabled = false
|
|
}
|
|
|
|
for _, option := range options {
|
|
option(c)
|
|
}
|
|
|
|
if c.uuid == "" {
|
|
randomUUID, _ := uuid.NewRandom()
|
|
c.uuid = randomUUID.String()
|
|
c.create = true
|
|
c.log("uuid", fmt.Errorf("check UUID is not provided, using random %q with auto provision", c.uuid))
|
|
}
|
|
}
|
|
|
|
// call API
|
|
func (c *Client) call(operation, endpoint string, body ...io.Reader) {
|
|
if !c.enabled {
|
|
return
|
|
}
|
|
|
|
c.wg.Add(1)
|
|
defer c.wg.Done()
|
|
|
|
targetURL := fmt.Sprintf("%s/%s%s?rid=%s", c.baseURL, c.uuid, endpoint, c.rid)
|
|
if c.create {
|
|
targetURL += "&create=1"
|
|
}
|
|
|
|
var req *http.Request
|
|
var err error
|
|
if len(body) > 0 {
|
|
req, err = http.NewRequest(http.MethodPost, targetURL, body[0])
|
|
} else {
|
|
req, err = http.NewRequest(http.MethodHead, targetURL, http.NoBody)
|
|
}
|
|
if err != nil {
|
|
c.log(operation, err)
|
|
return
|
|
}
|
|
req.Header.Set("User-Agent", c.userAgent)
|
|
req.Header.Set("Content-Type", "text/plain; charset=utf-8")
|
|
|
|
resp, err := c.http.Do(req)
|
|
if err != nil {
|
|
c.log(operation, err)
|
|
return
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
|
|
respb, rerr := io.ReadAll(resp.Body)
|
|
if rerr != nil {
|
|
c.log(operation+":response", rerr)
|
|
return
|
|
}
|
|
rerr = fmt.Errorf(string(respb))
|
|
c.log(operation+":response", rerr)
|
|
return
|
|
}
|
|
}
|
|
|
|
// SetEnabled sets the enabled flag, ignoring the options
|
|
// if client initialized without any options, it will be disabled by default,
|
|
// but you can override it by calling SetEnabled(true).
|
|
func (c *Client) SetEnabled(enabled bool) {
|
|
c.enabled = enabled
|
|
}
|
|
|
|
// Start signal means the job started
|
|
func (c *Client) Start(optionalBody ...io.Reader) {
|
|
go c.call("start", "/start", optionalBody...)
|
|
}
|
|
|
|
// Success signal means the job has completed successfully (or, a continuously running process is still running and healthy).
|
|
func (c *Client) Success(optionalBody ...io.Reader) {
|
|
go c.call("success", "", optionalBody...)
|
|
}
|
|
|
|
// Fail signal means the job failed
|
|
func (c *Client) Fail(optionalBody ...io.Reader) {
|
|
go c.call("fail", "/fail", optionalBody...)
|
|
}
|
|
|
|
// Log signal just adds an event to the job log, without changing job status
|
|
func (c *Client) Log(optionalBody ...io.Reader) {
|
|
go c.call("log", "/log", optionalBody...)
|
|
}
|
|
|
|
// ExitStatus signal sends job's exit code (0-255)
|
|
func (c *Client) ExitStatus(exitCode int, optionalBody ...io.Reader) {
|
|
go c.call("exit status", "/"+strconv.Itoa(exitCode), optionalBody...)
|
|
}
|
|
|
|
// Shutdown the client
|
|
func (c *Client) Shutdown() {
|
|
c.done <- true
|
|
c.wg.Wait()
|
|
}
|