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

Bug/bz1507664 suppress health checks #17155

Merged
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 2 additions & 4 deletions images/router/haproxy/conf/haproxy-config.template
Original file line number Diff line number Diff line change
Expand Up @@ -410,10 +410,8 @@ backend be_secure:{{$cfgIdx}}
{{- else if or (eq $cfg.TLSTermination "") (eq $cfg.TLSTermination "edge") }}
{{- end }}{{/* end type specific options*/}}

{{- if not $endpoint.NoHealthCheck }} check inter {{firstMatch $timeSpecPattern (index $cfg.Annotations "router.openshift.io/haproxy.health.check.interval") (env "ROUTER_BACKEND_CHECK_INTERVAL") "5000ms"}}
{{- if and (not $endpoint.NoHealthCheck) (gt $cfg.ActiveEndpoints 1) }} check inter {{firstMatch $timeSpecPattern (index $cfg.Annotations "router.openshift.io/haproxy.health.check.interval") (env "ROUTER_BACKEND_CHECK_INTERVAL") "5000ms"}}
{{- end }}{{/* end else no health check */}}


{{- end }}{{/* end if cg.TLSTermination */}}
{{- end }}{{/* end range processEndpointsForAlias */}}
{{- end }}{{/* end get serviceUnit from its name */}}
Expand Down Expand Up @@ -464,7 +462,7 @@ backend be_tcp:{{$cfgIdx}}
{{- with $serviceUnit := index $.ServiceUnits $serviceUnitName }}
{{- range $idx, $endpoint := processEndpointsForAlias $cfg $serviceUnit (env "ROUTER_BACKEND_PROCESS_ENDPOINTS" "") }}
server {{$endpoint.ID}} {{$endpoint.IP}}:{{$endpoint.Port}} weight {{$weight}}
{{- if not $endpoint.NoHealthCheck }} check inter {{firstMatch $timeSpecPattern (index $cfg.Annotations "router.openshift.io/haproxy.health.check.interval") (env "ROUTER_BACKEND_CHECK_INTERVAL") "5000ms"}}
{{- if and (not $endpoint.NoHealthCheck) (gt $cfg.ActiveEndpoints 1) }} check inter {{firstMatch $timeSpecPattern (index $cfg.Annotations "router.openshift.io/haproxy.health.check.interval") (env "ROUTER_BACKEND_CHECK_INTERVAL") "5000ms"}}
{{- end }}{{/* end else no health check */}}
{{- end }}{{/* end range processEndpointsForAlias */}}
{{- end }}{{/* end get ServiceUnit from serviceUnitName */}}
Expand Down
52 changes: 38 additions & 14 deletions pkg/router/template/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,9 @@ func (r *templateRouter) writeConfig() error {
// called here to make sure we have the actual number of endpoints.
cfg.ServiceUnitNames = r.calculateServiceWeights(cfg.ServiceUnits)

// Calculate the number of active endpoints for the route.
cfg.ActiveEndpoints = r.getActiveEndpoints(cfg.ServiceUnits)

cfg.Status = ServiceAliasConfigStatusSaved
r.state[k] = cfg
}
Expand Down Expand Up @@ -877,28 +880,49 @@ func getServiceUnits(route *routeapi.Route) map[string]int32 {

// get the weight and number of endpoints for each service
key := endpointsKeyFromParts(route.Namespace, route.Spec.To.Name)
serviceUnits[key] = 0
if route.Spec.To.Weight != nil {
serviceUnits[key] = int32(*route.Spec.To.Weight)
}
if serviceUnits[key] < 0 || serviceUnits[key] > 256 {
serviceUnits[key] = 0
}
serviceUnits[key] = getServiceUnitWeight(route.Spec.To.Weight)

for _, svc := range route.Spec.AlternateBackends {
key = endpointsKeyFromParts(route.Namespace, svc.Name)
serviceUnits[key] = 0
if svc.Weight != nil {
serviceUnits[key] = int32(*svc.Weight)
}
if serviceUnits[key] < 0 || serviceUnits[key] > 256 {
serviceUnits[key] = 0
}
serviceUnits[key] = getServiceUnitWeight(svc.Weight)
}

return serviceUnits
}

// getServiceUnitWeight takes a reference to a weight and returns its value or the default.
// It also checks that it is in the correct range.
func getServiceUnitWeight(weightRef *int32) int32 {
// Default to 1 if there is no weight
var weight int32 = 1
if weightRef != nil {
weight = *weightRef
}

// Do a bounds check
if weight < 0 {
weight = 0
} else if weight > 256 {
weight = 256

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When weight >= 256, don't we want to set it to 0?
Otherwise in Line 918, we count this serviceUnit toward active endpoints which doesn't seem right.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assuming we want to ignore the service unit if the corresponding weight is invalid.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weight=0 clearly means user want to ignore the endpoint but weight > 256 means mostly some typo/calculation error and may not mean ignore the endpoint.

It may be useful to log a warning when we are defaulting invalid weights to 0 or 256?

}

return weight
}

// getActiveEndpoints calculates the number of endpoints that are not associated
// with service units with a zero weight and returns the count.
func (r *templateRouter) getActiveEndpoints(serviceUnits map[string]int32) int {
var activeEndpoints int32 = 0

for key, weight := range serviceUnits {
if weight > 0 {
activeEndpoints += r.numberOfEndpoints(key)
}
}

return int(activeEndpoints)
}

// calculateServiceWeights returns a map of service keys to their weights.
// Each service gets (weight/sum_of_weights) fraction of the requests.
// For each service, the requests are distributed among the endpoints.
Expand Down
20 changes: 4 additions & 16 deletions pkg/router/template/template_helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,28 +157,16 @@ func processEndpointsForAlias(alias ServiceAliasConfig, svc ServiceUnit, action
}

func endpointsForAlias(alias ServiceAliasConfig, svc ServiceUnit) []Endpoint {
if len(alias.PreferPort) == 0 && len(svc.EndpointTable) != 1 {
// We can skip the work. They are selecting everything, and since they don't have one endpoint,
// we can't disable the health checks. So we can just use their list.
if len(alias.PreferPort) == 0 {
return svc.EndpointTable
}

endpoints := make([]Endpoint, 0, len(svc.EndpointTable))
for _, endpoint := range svc.EndpointTable {
// Filter the list:
// - If PreferPort length is 0, match everything
// - Otherwise, if the PortName or Port matches PreferPort, then we have a match
if len(alias.PreferPort) == 0 || endpoint.PortName == alias.PreferPort || endpoint.Port == alias.PreferPort {
for i := range svc.EndpointTable {
endpoint := svc.EndpointTable[i]
if endpoint.PortName == alias.PreferPort || endpoint.Port == alias.PreferPort {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need the len(alias.PreferPort) == 0 to avoid a segfault?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pecameron
No, len(alias.PreferPort) == 0 implies user did not specific any preferred port (http/https), then we don't need to filter any endpoints. That case is already handled in Line 160

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are two commits in this PR. The first simply reverts my previous bad PR restoring the previous behavior. It probably makes sense to look at the second commit only.

endpoints = append(endpoints, endpoint)
}
}

// We want to disable endpoint checks if there is only one endpoint since there's no point in
// testing and removing it from the set ahead of time since there are no other eps to replace it.
if len(endpoints) == 1 {
endpoints[0].NoHealthCheck = true
}

return endpoints
}

Expand Down
166 changes: 0 additions & 166 deletions pkg/router/template/template_helper_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package templaterouter

import (
"reflect"
"regexp"
"testing"
)
Expand Down Expand Up @@ -298,168 +297,3 @@ func TestMatchPattern(t *testing.T) {
}
}
}

func TestEndpointsForAlias(t *testing.T) {
testEndpoints := []struct {
name string
preferPort string
eps []Endpoint
expected []Endpoint
}{
{
name: "Test no preference with a single endpoint",
preferPort: "",
eps: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
},
},
expected: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
NoHealthCheck: true,
},
},
},
{
name: "Test no preference with multiple",
preferPort: "",
eps: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
},
{
ID: "ep1",
Port: "443",
PortName: "https",
},
{
ID: "ep2",
Port: "80",
PortName: "http",
},
{
ID: "ep3",
Port: "80",
PortName: "http",
},
},
expected: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
},
{
ID: "ep1",
Port: "443",
PortName: "https",
},
{
ID: "ep2",
Port: "80",
PortName: "http",
},
{
ID: "ep3",
Port: "80",
PortName: "http",
},
},
},
{
name: "Test a preference resulting in multiple",
preferPort: "http",
eps: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
},
{
ID: "ep1",
Port: "443",
PortName: "https",
},
{
ID: "ep2",
Port: "80",
PortName: "http",
},
{
ID: "ep3",
Port: "80",
PortName: "http",
},
},
expected: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
},
{
ID: "ep2",
Port: "80",
PortName: "http",
},
{
ID: "ep3",
Port: "80",
PortName: "http",
},
},
},
{
name: "Test a preference resulting in one",
preferPort: "https",
eps: []Endpoint{
{
ID: "ep1",
Port: "80",
PortName: "http",
},
{
ID: "ep1",
Port: "443",
PortName: "https",
},
{
ID: "ep2",
Port: "80",
PortName: "http",
},
{
ID: "ep3",
Port: "80",
PortName: "http",
},
},
expected: []Endpoint{
{
ID: "ep1",
Port: "443",
PortName: "https",
NoHealthCheck: true,
},
},
},
}

for _, te := range testEndpoints {
eps := endpointsForAlias(
ServiceAliasConfig{PreferPort: te.preferPort},
ServiceUnit{EndpointTable: te.eps},
)

if !reflect.DeepEqual(te.expected, eps) {
t.Errorf("%s: expected result: %#v, got: %#v", te.name, te.expected, eps)
}
}
}
3 changes: 3 additions & 0 deletions pkg/router/template/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ type ServiceAliasConfig struct {

// ActiveServiceUnits is a count of the service units with a non-zero weight
ActiveServiceUnits int

// ActiveEndpoints is a count of the route endpoints that are part of a service unit with a non-zero weight
ActiveEndpoints int
}

type ServiceAliasConfigStatus string
Expand Down