Skip to content

Commit

Permalink
feat: add advanced healthcheck
Browse files Browse the repository at this point in the history
  • Loading branch information
mgjules committed Apr 11, 2022
1 parent 2a28d15 commit 73c85a9
Show file tree
Hide file tree
Showing 8 changed files with 204 additions and 8 deletions.
41 changes: 40 additions & 1 deletion docs/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,13 @@ const docTemplate = `{
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.Success"
"$ref": "#/definitions/health.CheckerResult"
}
},
"503": {
"description": "Service Unavailable",
"schema": {
"$ref": "#/definitions/health.CheckerResult"
}
}
}
Expand Down Expand Up @@ -233,6 +239,39 @@ const docTemplate = `{
}
}
},
"health.CheckResult": {
"type": "object",
"properties": {
"error": {
"description": "Error contains the check error message, if the check failed.",
"type": "string"
},
"status": {
"description": "Status is the availability status of a component.",
"type": "string"
},
"timestamp": {
"description": "Timestamp holds the time when the check was executed.",
"type": "string"
}
}
},
"health.CheckerResult": {
"type": "object",
"properties": {
"details": {
"description": "Details contains health information for all checked components.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/health.CheckResult"
}
},
"status": {
"description": "Status is the aggregated system availability status.",
"type": "string"
}
}
},
"http.Error": {
"type": "object",
"properties": {
Expand Down
41 changes: 40 additions & 1 deletion docs/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,13 @@
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/http.Success"
"$ref": "#/definitions/health.CheckerResult"
}
},
"503": {
"description": "Service Unavailable",
"schema": {
"$ref": "#/definitions/health.CheckerResult"
}
}
}
Expand Down Expand Up @@ -224,6 +230,39 @@
}
}
},
"health.CheckResult": {
"type": "object",
"properties": {
"error": {
"description": "Error contains the check error message, if the check failed.",
"type": "string"
},
"status": {
"description": "Status is the availability status of a component.",
"type": "string"
},
"timestamp": {
"description": "Timestamp holds the time when the check was executed.",
"type": "string"
}
}
},
"health.CheckerResult": {
"type": "object",
"properties": {
"details": {
"description": "Details contains health information for all checked components.",
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/health.CheckResult"
}
},
"status": {
"description": "Status is the aggregated system availability status.",
"type": "string"
}
}
},
"http.Error": {
"type": "object",
"properties": {
Expand Down
29 changes: 28 additions & 1 deletion docs/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@ definitions:
r:
type: integer
type: object
health.CheckResult:
properties:
error:
description: Error contains the check error message, if the check failed.
type: string
status:
description: Status is the availability status of a component.
type: string
timestamp:
description: Timestamp holds the time when the check was executed.
type: string
type: object
health.CheckerResult:
properties:
details:
additionalProperties:
$ref: '#/definitions/health.CheckResult'
description: Details contains health information for all checked components.
type: object
status:
description: Status is the aggregated system availability status.
type: string
type: object
http.Error:
properties:
error:
Expand Down Expand Up @@ -254,7 +277,11 @@ paths:
"200":
description: OK
schema:
$ref: '#/definitions/http.Success'
$ref: '#/definitions/health.CheckerResult'
"503":
description: Service Unavailable
schema:
$ref: '#/definitions/health.CheckerResult'
summary: Health Check
tags:
- core
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/JulesMike/spoty
go 1.18

require (
github.com/alexliesenfeld/health v0.6.0
github.com/cenkalti/dominantcolor v0.0.0-20171020061837-df772e8dd39e
github.com/dgraph-io/ristretto v0.1.0
github.com/gin-contrib/zap v0.0.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
github.com/agiledragon/gomonkey/v2 v2.3.1 h1:k+UnUY0EMNYUFUAQVETGY9uUTxjMdnUkP0ARyJS1zzs=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alexliesenfeld/health v0.6.0 h1:HRBTCgybNSe4lqGEk7nU82c3bjwh9W+3b46W6UvD4CQ=
github.com/alexliesenfeld/health v0.6.0/go.mod h1:N4NDIeQtlWumG+6z1ne1v62eQxktz5ylEgGgH9emdMw=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/cenkalti/dominantcolor v0.0.0-20171020061837-df772e8dd39e h1:5KS7rBZBuj4CrssHHg40+e15Myj2aA+V3tg7vfZBvE8=
Expand Down
60 changes: 60 additions & 0 deletions health/check.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package health

import (
"context"
"time"

"github.com/alexliesenfeld/health"
)

// Check represents a single health check.
type Check struct {
// The Name must be unique among all checks. Name is a required attribute.
Name string // Required

// Check is the check function that will be executed to check availability.
// This function must return an error if the checked service is considered
// not available. Check is a required attribute.
Check func(ctx context.Context) error // Required

// Timeout will override the global timeout value, if it is smaller than
// the global timeout (see WithTimeout).
Timeout time.Duration // Optional

// MaxTimeInError will set a duration for how long a service must be
// in an error state until it is considered down/unavailable.
MaxTimeInError time.Duration // Optional

// MaxContiguousFails will set a maximum number of contiguous
// check fails until the service is considered down/unavailable.
MaxContiguousFails uint // Optional

// StatusListener allows to set a listener that will be called
// whenever the AvailabilityStatus (e.g. from "up" to "down").
StatusListener func(ctx context.Context, name string, state health.CheckState) // Optional

RefreshPeriod time.Duration
InitialDelay time.Duration
}

// CompileHealthCheckerOption takes a list of Check and returns health.CheckerOption.
func CompileHealthCheckerOption(checks ...Check) []health.CheckerOption {
var opts []health.CheckerOption
for i := range checks {
c := &checks[i]

if c.Name == "" {
continue
}
opts = append(opts, health.WithPeriodicCheck(c.RefreshPeriod, c.InitialDelay, health.Check{
Name: c.Name,
Timeout: c.Timeout,
MaxTimeInError: c.MaxTimeInError,
MaxContiguousFails: c.MaxContiguousFails,
StatusListener: c.StatusListener,
Check: c.Check,
}))
}

return opts
}
18 changes: 13 additions & 5 deletions http/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import (
"net/http"

"github.com/JulesMike/spoty/docs"
"github.com/JulesMike/spoty/health"
ahealth "github.com/alexliesenfeld/health"
"github.com/gin-gonic/gin"
ginSwagger "github.com/swaggo/gin-swagger"
"github.com/swaggo/gin-swagger/swaggerFiles"
Expand All @@ -24,12 +26,18 @@ type Error struct {
// @Description checks if server is running
// @Tags core
// @Produce json
// @Success 200 {object} http.Success
// @Success 200 {object} ahealth.CheckerResult
// @Success 503 {object} ahealth.CheckerResult
// @Router / [get]
func (Server) handleHealthCheck() gin.HandlerFunc {
return func(c *gin.Context) {
c.JSON(http.StatusOK, Success{Success: "i'm alright!"})
}
func (s *Server) handleHealthCheck() gin.HandlerFunc {
opts := health.CompileHealthCheckerOption(s.spoty.Check())
checker := ahealth.NewChecker(opts...)

return gin.WrapF(
ahealth.NewHandler(
checker,
),
)
}

// handleVersion godoc
Expand Down
20 changes: 20 additions & 0 deletions spoty/spoty.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package spoty

import (
"context"
"errors"
"fmt"
"image"
Expand All @@ -13,6 +14,7 @@ import (

"github.com/JulesMike/spoty/cache"
"github.com/JulesMike/spoty/config"
"github.com/JulesMike/spoty/health"
"github.com/cenkalti/dominantcolor"
"github.com/google/uuid"
"github.com/iancoleman/strcase"
Expand Down Expand Up @@ -200,3 +202,21 @@ func (s *Spoty) TrackImages(track *spotify.FullTrack) ([]Image, error) {

return images, nil
}

// Check checks if the spoty service is authenticated.
func (s *Spoty) Check() health.Check {
//nolint:revive
return health.Check{
Name: "spoty",
RefreshPeriod: 10 * time.Second,
InitialDelay: 10 * time.Second,
Timeout: 5 * time.Second,
Check: func(ctx context.Context) error {
if s.IsAuth() {
return nil
}

return errors.New("spoty not authenticated")
},
}
}

0 comments on commit 73c85a9

Please sign in to comment.