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

feat: Add support for endpoint topology routing hints #2090

Merged
merged 14 commits into from
Aug 31, 2023
2 changes: 1 addition & 1 deletion docs/endpointslice-metrics.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
| kube_endpointslice_annotations | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `annotation_ENDPOINTSLICE_ANNOTATION`=&lt;ENDPOINTSLICE_ANNOTATION&gt; | EXPERIMENTAL |
| kube_endpointslice_info | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; | EXPERIMENTAL |
| kube_endpointslice_ports | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `port_name`=&lt;endpointslice-port-name&gt; <br> `port_protocol`=&lt;endpointslice-port-protocol&gt; <br> `port_number`=&lt;endpointslice-port-number&gt; | EXPERIMENTAL |
| kube_endpointslice_endpoints | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `ready`=&lt;endpointslice-ready&gt; <br> `serving`=&lt;endpointslice-serving&gt; <br> `terminating`=&lt;endpointslice-terminating&gt; <br> `hostname`=&lt;endpointslice-hostname&gt; <br> `targetref_kind`=&lt;endpointslice-targetref-kind&gt; <br> `targetref_name`=&lt;endpointslice-targetref-name&gt; <br> `targetref_namespace`=&lt;endpointslice-targetref-namespace&gt; <br> `nodename`=&lt;endpointslice-nodename&gt; <br> `endpoint_zone`=&lt;endpointslice-zone&gt; | EXPERIMENTAL |
| kube_endpointslice_endpoints | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `ready`=&lt;endpointslice-ready&gt; <br> `serving`=&lt;endpointslice-serving&gt; <br> `terminating`=&lt;endpointslice-terminating&gt; <br> `hostname`=&lt;endpointslice-hostname&gt; <br> `targetref_kind`=&lt;endpointslice-targetref-kind&gt; <br> `targetref_name`=&lt;endpointslice-targetref-name&gt; <br> `targetref_namespace`=&lt;endpointslice-targetref-namespace&gt; <br> `nodename`=&lt;endpointslice-nodename&gt; <br> `endpoint_zone`=&lt;endpointslice-zone&gt; <br> `hint`=&lt;topology-aware-routing-hint&gt; | EXPERIMENTAL |
| kube_endpointslice_labels | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; <br> `label_ENDPOINTSLICE_LABEL`=&lt;ENDPOINTSLICE_LABEL&gt; | EXPERIMENTAL |
| kube_endpointslice_created | Gauge | `endpointslice`=&lt;endpointslice-name&gt; <br> `namespace`=&lt;endpointslice-namespace&gt; | EXPERIMENTAL |
29 changes: 24 additions & 5 deletions internal/store/endpointslice.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,30 @@ func endpointSliceMetricFamilies(allowAnnotationsList, allowLabelsList []string)
for _, address := range ep.Addresses {
newlabelValues := make([]string, len(labelValues))
copy(newlabelValues, labelValues)
m = append(m, &metric.Metric{
LabelKeys: labelKeys,
LabelValues: append(newlabelValues, address),
Value: 1,
})
newlabelValues = append(newlabelValues, address)

// Hint is populated when the endpoint is configured to be zone aware and preferentially route requests to its local zone.
if ep.Hints != nil && len(ep.Hints.ForZones) > 0 {

// Because each endpoint can have multiple zones, we need to create a metric for each zone.
// and we need to make sure we aren't adding the hint label repeatedly, we need to copy the array
for _, zone := range ep.Hints.ForZones {
zoneLabelValues := make([]string, len(newlabelValues))
copy(zoneLabelValues, newlabelValues)
zoneLabelValues = append(zoneLabelValues, zone.Name)
m = append(m, &metric.Metric{
LabelKeys: append(labelKeys, "hint"),
LabelValues: zoneLabelValues,
Value: 1,
})
}
} else {
m = append(m, &metric.Metric{
LabelKeys: labelKeys,
LabelValues: newlabelValues,
Value: 1,
})
}
Copy link
Member

Choose a reason for hiding this comment

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

This shouldn't need to be in the loop iterating over addresses

Copy link
Member

Choose a reason for hiding this comment

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

@MarkSRobinson this is the comment I was mentioning 🙂

}
}
return &metric.Family{
Expand Down
35 changes: 35 additions & 0 deletions internal/store/endpointslice_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,41 @@ func TestEndpointSliceStore(t *testing.T) {
"kube_endpointslice_endpoints",
},
},
{
Obj: &discoveryv1.EndpointSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test_endpointslice-endpoints",
},
AddressType: "IPv4",
Endpoints: []discoveryv1.Endpoint{
{
NodeName: &nodename,
Conditions: discoveryv1.EndpointConditions{
Ready: &ready,
Terminating: &terminating,
},
Hostname: &hostname,
Zone: &zone,
Addresses: addresses,
Hints: &discoveryv1.EndpointHints{
ForZones: []discoveryv1.ForZone{
{Name: "zone1"},
},
},
},
},
},
Want: `
# HELP kube_endpointslice_endpoints Endpoints attached to the endpointslice.
# TYPE kube_endpointslice_endpoints gauge
kube_endpointslice_endpoints{address="10.0.0.1",endpoint_nodename="node",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hostname="host",ready="true",terminating="false",hint="zone1"} 1
kube_endpointslice_endpoints{address="192.168.1.10",endpoint_nodename="node",endpoint_zone="west",endpointslice="test_endpointslice-endpoints",hostname="host",ready="true",terminating="false",hint="zone1"} 1
`,

MetricNames: []string{
"kube_endpointslice_endpoints",
},
},
{
AllowAnnotationsList: []string{
"foo",
Expand Down