Skip to content

Commit

Permalink
feat(load-balancer): Add health status to list output (#542)
Browse files Browse the repository at this point in the history
This commit adds the health status of a loadbalancer to the list command
to display the loadbalancer health in a similar style as in the browser
  • Loading branch information
cedi authored Sep 20, 2023
1 parent 340dce3 commit 272cc63
Show file tree
Hide file tree
Showing 2 changed files with 242 additions and 1 deletion.
41 changes: 40 additions & 1 deletion internal/cmd/loadbalancer/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
var ListCmd = base.ListCmd{
ResourceNamePlural: "Load Balancer",

DefaultColumns: []string{"id", "name", "ipv4", "ipv6", "type", "location", "network_zone", "age"},
DefaultColumns: []string{"id", "name", "health", "ipv4", "ipv6", "type", "location", "network_zone", "age"},
Fetch: func(ctx context.Context, client hcapi2.Client, cmd *cobra.Command, listOpts hcloud.ListOpts, sorts []string) ([]interface{}, error) {
opts := hcloud.LoadBalancerListOpts{ListOpts: listOpts}
if len(sorts) > 0 {
Expand Down Expand Up @@ -75,6 +75,10 @@ var ListCmd = base.ListCmd{
AddFieldFn("age", output.FieldFn(func(obj interface{}) string {
loadBalancer := obj.(*hcloud.LoadBalancer)
return util.Age(loadBalancer.Created, time.Now())
})).
AddFieldFn("health", output.FieldFn(func(obj interface{}) string {
loadBalancer := obj.(*hcloud.LoadBalancer)
return loadBalancerHealth(loadBalancer)
}))
},

Expand Down Expand Up @@ -167,3 +171,38 @@ var ListCmd = base.ListCmd{
return loadBalancerSchemas
},
}

func loadBalancerHealth(l *hcloud.LoadBalancer) string {
healthyCount := 0
unhealthyCount := 0
unknownCount := 0

for _, lbTarget := range l.Targets {
for _, svcHealth := range lbTarget.HealthStatus {
switch svcHealth.Status {
case hcloud.LoadBalancerTargetHealthStatusStatusHealthy:
healthyCount++

case hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy:
unhealthyCount++

default:
unknownCount++
}
}
}

switch len(l.Targets) * len(l.Services) {
case healthyCount:
return string(hcloud.LoadBalancerTargetHealthStatusStatusHealthy)

case unhealthyCount:
return string(hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy)

case unknownCount:
return string(hcloud.LoadBalancerTargetHealthStatusStatusUnknown)

default:
return "mixed"
}
}
202 changes: 202 additions & 0 deletions internal/cmd/loadbalancer/list_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package loadbalancer

import (
"testing"

"github.com/hetznercloud/hcloud-go/v2/hcloud"
"github.com/stretchr/testify/assert"
)

func TestLoadBalancerHealth(t *testing.T) {
tests := []struct {
name string
lb *hcloud.LoadBalancer
expected string
}{
{
name: "healthy",
lb: &hcloud.LoadBalancer{
Name: "foobar",
Services: make([]hcloud.LoadBalancerService, 1),
Targets: []hcloud.LoadBalancerTarget{
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
},
},
},
},
expected: string(hcloud.LoadBalancerTargetHealthStatusStatusHealthy),
},
{
name: "unhealthy",
lb: &hcloud.LoadBalancer{
Name: "foobar",
Services: make([]hcloud.LoadBalancerService, 1),
Targets: []hcloud.LoadBalancerTarget{
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
},
},
},
},
expected: string(hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy),
},
{
name: "unknown",
lb: &hcloud.LoadBalancer{
Name: "foobar",
Services: make([]hcloud.LoadBalancerService, 1),
Targets: []hcloud.LoadBalancerTarget{
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
},
},
},
},
expected: string(hcloud.LoadBalancerTargetHealthStatusStatusUnknown),
},
{
name: "mixed",
lb: &hcloud.LoadBalancer{
Name: "foobar",
Services: make([]hcloud.LoadBalancerService, 1),
Targets: []hcloud.LoadBalancerTarget{
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
},
},
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
},
},
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
},
},
},
},
expected: "mixed",
},
{
name: "mixed_many_services_grouped_by_target",
lb: &hcloud.LoadBalancer{
Name: "foobar",
Services: make([]hcloud.LoadBalancerService, 3),
Targets: []hcloud.LoadBalancerTarget{
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
},
},
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
},
},
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
},
},
},
},
expected: "mixed",
},
{
name: "mixed_many_services_mixed",
lb: &hcloud.LoadBalancer{
Name: "foobar",
Services: make([]hcloud.LoadBalancerService, 3),
Targets: []hcloud.LoadBalancerTarget{
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
},
},
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
},
},
{
HealthStatus: []hcloud.LoadBalancerTargetHealthStatus{
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusHealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnhealthy,
},
{
Status: hcloud.LoadBalancerTargetHealthStatusStatusUnknown,
},
},
},
},
},
expected: "mixed",
},
}

for _, test := range tests {
res := loadBalancerHealth(test.lb)
assert.Equal(t, test.expected, res, test.name)
}
}

0 comments on commit 272cc63

Please sign in to comment.