Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async Healthcheck #301

Merged
merged 19 commits into from
Aug 23, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 14 additions & 14 deletions api/healthcheck/healthcheck_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ import (

// controller platform controller
type controller struct {
health h.IHealth
tresholds map[string]int64
health h.IHealth
thresholds map[string]int64
}

// NewController returns a new healthcheck controller with the given health and tresholds
func NewController(health h.IHealth, tresholds map[string]int64) web.Controller {
// NewController returns a new healthcheck controller with the given health and thresholds
func NewController(health h.IHealth, thresholds map[string]int64) web.Controller {
return &controller{
health: health,
tresholds: tresholds,
health: health,
thresholds: thresholds,
}
}

Expand All @@ -56,21 +56,21 @@ func (c *controller) healthCheck(r *web.Request) (*web.Response, error) {
return util.NewJSONResponse(status, healthResult)
}

func (c *controller) aggregate(state map[string]h.State) *health.Health {
if len(state) == 0 {
return health.New().WithDetail("error", "no health indicators registered")
func (c *controller) aggregate(overallState map[string]h.State) *health.Health {
if len(overallState) == 0 {
return health.New().WithStatus(health.StatusUp)
}
overallStatus := health.StatusUp
for i, v := range state {
if v.Fatal && v.ContiguousFailures >= c.tresholds[i] {
for name, state := range overallState {
if state.Fatal && state.ContiguousFailures >= c.thresholds[name] {
overallStatus = health.StatusDown
break
}
}
details := make(map[string]interface{})
for k, v := range state {
v.Status = convertStatus(v.Status)
details[k] = v
for name, state := range overallState {
state.Status = convertStatus(state.Status)
details[name] = state
}
return health.New().WithStatus(overallStatus).WithDetails(details)
}
Expand Down
38 changes: 12 additions & 26 deletions api/healthcheck/healthcheck_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,6 @@ var _ = Describe("Healthcheck controller", func() {
})
})

When("health returns unknown", func() {
It("should respond with 503", func() {
assertResponse(health.StatusUnknown, http.StatusServiceUnavailable)
})
})

When("health returns up", func() {
It("should respond with 200", func() {
assertResponse(health.StatusUp, http.StatusOK)
Expand All @@ -68,53 +62,51 @@ var _ = Describe("Healthcheck controller", func() {
Describe("aggregation", func() {
var c *controller
var healths map[string]h.State
var tresholds map[string]int64
var thresholds map[string]int64

BeforeEach(func() {
healths = map[string]h.State{
"test1": {Status: "ok"},
"test2": {Status: "ok"},
}
tresholds = map[string]int64{
thresholds = map[string]int64{
"test1": 3,
"test2": 3,
}
c = &controller{
health: HealthFake{},
tresholds: tresholds,
health: HealthFake{},
thresholds: thresholds,
}
})

When("No healths are provided", func() {
It("Returns UNKNOWN and an error detail", func() {
It("Returns UP", func() {
aggregatedHealth := c.aggregate(nil)
Expect(aggregatedHealth.Status).To(Equal(health.StatusUnknown))
Expect(aggregatedHealth.Details["error"]).ToNot(BeNil())
Expect(aggregatedHealth.Status).To(Equal(health.StatusUp))
})
})

When("At least one health is DOWN more than treshold times and is Fatal", func() {
When("At least one health is DOWN more than threshold times and is Fatal", func() {
It("Returns DOWN", func() {
healths["test3"] = h.State{Status: "failed", Fatal: true, ContiguousFailures: 4}
c.tresholds["test3"] = 3
c.thresholds["test3"] = 3
aggregatedHealth := c.aggregate(healths)
Expect(aggregatedHealth.Status).To(Equal(health.StatusDown))
})
})

When("At least one health is DOWN more than treshold times and is not Fatal", func() {
When("At least one health is DOWN and is not Fatal", func() {
It("Returns UP", func() {
healths["test3"] = h.State{Status: "failed", Fatal: false, ContiguousFailures: 4}
c.tresholds["test3"] = 3
aggregatedHealth := c.aggregate(healths)
Expect(aggregatedHealth.Status).To(Equal(health.StatusUp))
})
})

When("There is DOWN healths but not more than treshold times in a row", func() {
When("There is DOWN healths but not more than threshold times in a row", func() {
It("Returns UP", func() {
healths["test3"] = h.State{Status: "failed"}
c.tresholds["test3"] = 3
c.thresholds["test3"] = 3
aggregatedHealth := c.aggregate(healths)
Expect(aggregatedHealth.Status).To(Equal(health.StatusUp))
})
Expand All @@ -140,12 +132,6 @@ var _ = Describe("Healthcheck controller", func() {
})

func createController(status health.Status) *controller {
if status == health.StatusUnknown {
return &controller{
health: HealthFake{},
}
}

stringStatus := "ok"
var contiguousFailures int64 = 0
if status == health.StatusDown {
Expand All @@ -159,7 +145,7 @@ func createController(status health.Status) *controller {
"test1": {Status: stringStatus, Fatal: true, ContiguousFailures: contiguousFailures},
},
},
tresholds: map[string]int64{
thresholds: map[string]int64{
"test1": 1,
},
}
Expand Down
47 changes: 33 additions & 14 deletions config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ var _ = Describe("config", func() {

Describe("Validate", func() {
var fatal bool
var failuresTreshold int64
var failuresThreshold int64
var interval time.Duration

assertErrorDuringValidate := func() {
Expand All @@ -56,12 +56,12 @@ var _ = Describe("config", func() {

registerIndicatorSettings := func() {
indicatorSettings := &health.IndicatorSettings{
Fatal: fatal,
FailuresTreshold: failuresTreshold,
Interval: interval,
Fatal: fatal,
FailuresThreshold: failuresThreshold,
Interval: interval,
}

config.Health.IndicatorsSettings["test"] = indicatorSettings
config.Health.Indicators["test"] = indicatorSettings
}

BeforeEach(func() {
Expand All @@ -72,22 +72,41 @@ var _ = Describe("config", func() {
config.API.SkipSSLValidation = true
config.Storage.EncryptionKey = "ejHjRNHbS0NaqARSRvnweVV9zcmhQEa8"

fatal = false
failuresTreshold = 1
fatal = true
failuresThreshold = 1
interval = 30 * time.Second
})

Context("health indicator with negative treshold", func() {
Context("health indicator with negative threshold", func() {
It("should be considered invalid", func() {
failuresTreshold = -1
failuresThreshold = -1
registerIndicatorSettings()
assertErrorDuringValidate()
})
})

Context("health indicator with 0 treshold", func() {
It("should be considered invalid", func() {
failuresTreshold = 0
Context("health indicator with 0 threshold", func() {
It("should be considered invalid if it is fatal", func() {
failuresThreshold = 0
registerIndicatorSettings()
assertErrorDuringValidate()
})
})

Context("health indicator with 0 threshold", func() {
It("should be considered valid if it is not fatal", func() {
fatal = false
failuresThreshold = 0
registerIndicatorSettings()
err := config.Validate()
Expect(err).ShouldNot(HaveOccurred())
})
})

Context("health indicator with positive threshold", func() {
It("should be considered invalid if it is not fatal", func() {
fatal = false
failuresThreshold = 3
registerIndicatorSettings()
assertErrorDuringValidate()
})
Expand All @@ -101,10 +120,10 @@ var _ = Describe("config", func() {
})
})

Context("health indicator with positive treshold and interval >= 30", func() {
Context("health indicator with positive threshold and interval >= 30", func() {
It("should be considered valid", func() {
interval = 30 * time.Second
failuresTreshold = 3
failuresThreshold = 3
registerIndicatorSettings()

err := config.Validate()
Expand Down
Loading