diff --git a/internal/cmd/loadbalancer/list.go b/internal/cmd/loadbalancer/list.go index 9edf9e49..47b9a371 100644 --- a/internal/cmd/loadbalancer/list.go +++ b/internal/cmd/loadbalancer/list.go @@ -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 { @@ -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) })) }, @@ -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" + } +} diff --git a/internal/cmd/loadbalancer/list_test.go b/internal/cmd/loadbalancer/list_test.go new file mode 100644 index 00000000..54a14931 --- /dev/null +++ b/internal/cmd/loadbalancer/list_test.go @@ -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) + } +}