2020-04-13 18:02:27 +00:00
package kubernetes
import (
"encoding/json"
"fmt"
2021-03-11 14:41:09 +00:00
"io"
2020-04-23 08:34:04 +00:00
2021-10-19 10:19:18 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
2020-04-23 08:34:04 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
2022-11-30 05:22:12 +00:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promutils"
2020-04-13 18:02:27 +00:00
)
2021-04-02 11:45:08 +00:00
func ( eps * Endpoints ) key ( ) string {
return eps . Metadata . key ( )
2021-02-26 14:54:03 +00:00
}
2021-03-11 14:41:09 +00:00
func parseEndpointsList ( r io . Reader ) ( map [ string ] object , ListMeta , error ) {
2021-02-26 14:54:03 +00:00
var epsl EndpointsList
2021-03-11 14:41:09 +00:00
d := json . NewDecoder ( r )
if err := d . Decode ( & epsl ) ; err != nil {
return nil , epsl . Metadata , fmt . Errorf ( "cannot unmarshal EndpointsList: %w" , err )
2021-02-26 14:54:03 +00:00
}
2021-04-02 11:45:08 +00:00
objectsByKey := make ( map [ string ] object )
2021-02-26 14:54:03 +00:00
for _ , eps := range epsl . Items {
2021-04-02 11:45:08 +00:00
objectsByKey [ eps . key ( ) ] = eps
2021-02-26 14:54:03 +00:00
}
2021-04-02 11:45:08 +00:00
return objectsByKey , epsl . Metadata , nil
2021-02-26 14:54:03 +00:00
}
func parseEndpoints ( data [ ] byte ) ( object , error ) {
var eps Endpoints
if err := json . Unmarshal ( data , & eps ) ; err != nil {
return nil , err
}
return & eps , nil
}
2020-04-13 18:02:27 +00:00
// EndpointsList implements k8s endpoints list.
//
2024-10-24 15:04:12 +00:00
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpointslist-v1-core
2020-04-13 18:02:27 +00:00
type EndpointsList struct {
2021-02-26 14:54:03 +00:00
Metadata ListMeta
Items [ ] * Endpoints
2020-04-13 18:02:27 +00:00
}
// Endpoints implements k8s endpoints.
//
2024-10-24 15:04:12 +00:00
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpoints-v1-core
2020-04-13 18:02:27 +00:00
type Endpoints struct {
Metadata ObjectMeta
Subsets [ ] EndpointSubset
}
// EndpointSubset implements k8s endpoint subset.
//
2024-10-24 15:04:12 +00:00
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpointsubset-v1-core
2020-04-13 18:02:27 +00:00
type EndpointSubset struct {
Addresses [ ] EndpointAddress
NotReadyAddresses [ ] EndpointAddress
Ports [ ] EndpointPort
}
// EndpointAddress implements k8s endpoint address.
//
2024-10-24 15:04:12 +00:00
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpointaddress-v1-core
2020-04-13 18:02:27 +00:00
type EndpointAddress struct {
Hostname string
IP string
NodeName string
TargetRef ObjectReference
}
// ObjectReference implements k8s object reference.
//
2024-10-24 15:04:12 +00:00
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#objectreference-v1-core
2020-04-13 18:02:27 +00:00
type ObjectReference struct {
Kind string
Name string
Namespace string
}
// EndpointPort implements k8s endpoint port.
//
2024-10-24 15:04:12 +00:00
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.31/#endpointport-v1-discovery-k8s-io
2020-04-13 18:02:27 +00:00
type EndpointPort struct {
AppProtocol string
Name string
Port int
Protocol string
}
2021-02-26 18:21:27 +00:00
// getTargetLabels returns labels for each endpoint in eps.
2020-04-13 18:02:27 +00:00
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#endpoints
2022-11-30 05:22:12 +00:00
func ( eps * Endpoints ) getTargetLabels ( gw * groupWatcher ) [ ] * promutils . Labels {
2021-02-26 14:46:13 +00:00
var svc * Service
2021-04-29 07:14:24 +00:00
if o := gw . getObjectByRoleLocked ( "service" , eps . Metadata . Namespace , eps . Metadata . Name ) ; o != nil {
2021-02-26 14:54:03 +00:00
svc = o . ( * Service )
2021-02-26 14:46:13 +00:00
}
2020-04-13 18:02:27 +00:00
podPortsSeen := make ( map [ * Pod ] [ ] int )
2022-11-30 05:22:12 +00:00
var ms [ ] * promutils . Labels
2020-04-13 18:02:27 +00:00
for _ , ess := range eps . Subsets {
for _ , epp := range ess . Ports {
2021-03-14 19:10:35 +00:00
ms = appendEndpointLabelsForAddresses ( ms , gw , podPortsSeen , eps , ess . Addresses , epp , svc , "true" )
ms = appendEndpointLabelsForAddresses ( ms , gw , podPortsSeen , eps , ess . NotReadyAddresses , epp , svc , "false" )
2020-04-13 18:02:27 +00:00
}
}
2021-10-19 10:19:18 +00:00
// See https://kubernetes.io/docs/reference/labels-annotations-taints/#endpoints-kubernetes-io-over-capacity
// and https://github.com/kubernetes/kubernetes/pull/99975
2022-11-30 05:22:12 +00:00
switch eps . Metadata . Annotations . Get ( "endpoints.kubernetes.io/over-capacity" ) {
2021-10-19 10:19:18 +00:00
case "truncated" :
logger . Warnf ( ` the number of targets for "role: endpoints" %q exceeds 1000 and has been truncated; please use "role: endpointslice" instead ` , eps . Metadata . key ( ) )
case "warning" :
logger . Warnf ( ` the number of targets for "role: endpoints" %q exceeds 1000 and will be truncated in the next k8s releases; please use "role: endpointslice" instead ` , eps . Metadata . key ( ) )
}
2020-04-13 18:02:27 +00:00
// Append labels for skipped ports on seen pods.
portSeen := func ( port int , ports [ ] int ) bool {
for _ , p := range ports {
if p == port {
return true
}
}
return false
}
2024-10-24 15:04:12 +00:00
appendPodMetadata := func ( p * Pod , c * Container , seen [ ] int , isInit bool ) {
for _ , cp := range c . Ports {
if portSeen ( cp . ContainerPort , seen ) {
continue
}
addr := discoveryutils . JoinHostPort ( p . Status . PodIP , cp . ContainerPort )
m := promutils . GetLabels ( )
m . Add ( "__address__" , addr )
p . appendCommonLabels ( m , gw )
p . appendContainerLabels ( m , c , & cp , isInit )
// Prometheus sets endpoints_name and namespace labels for all endpoints
// Even if port is not matching service port.
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4154
p . appendEndpointLabels ( m , eps )
if svc != nil {
svc . appendCommonLabels ( m )
}
// Remove possible duplicate labels, which can appear after appendCommonLabels() call
m . RemoveDuplicates ( )
ms = append ( ms , m )
}
}
2020-04-13 18:02:27 +00:00
for p , ports := range podPortsSeen {
for _ , c := range p . Spec . Containers {
2024-10-24 15:04:12 +00:00
appendPodMetadata ( p , & c , ports , false )
}
for _ , c := range p . Spec . InitContainers {
// Defines native sidecar https://kubernetes.io/blog/2023/08/25/native-sidecar-containers/#what-are-sidecar-containers-in-1-28
if c . RestartPolicy != "Always" {
continue
2020-04-13 18:02:27 +00:00
}
2024-10-24 15:04:12 +00:00
appendPodMetadata ( p , & c , ports , true )
2020-04-13 18:02:27 +00:00
}
}
return ms
}
2022-11-30 05:22:12 +00:00
func appendEndpointLabelsForAddresses ( ms [ ] * promutils . Labels , gw * groupWatcher , podPortsSeen map [ * Pod ] [ ] int , eps * Endpoints ,
eas [ ] EndpointAddress , epp EndpointPort , svc * Service , ready string ) [ ] * promutils . Labels {
2020-04-13 18:02:27 +00:00
for _ , ea := range eas {
2021-02-26 14:46:13 +00:00
var p * Pod
2021-04-05 17:27:23 +00:00
if ea . TargetRef . Name != "" {
2021-04-29 07:14:24 +00:00
if o := gw . getObjectByRoleLocked ( "pod" , ea . TargetRef . Namespace , ea . TargetRef . Name ) ; o != nil {
2021-04-05 17:27:23 +00:00
p = o . ( * Pod )
}
2021-02-26 14:46:13 +00:00
}
2022-04-22 16:39:34 +00:00
m := getEndpointLabelsForAddressAndPort ( gw , podPortsSeen , eps , ea , epp , p , svc , ready )
2023-02-23 01:01:01 +00:00
// Remove possible duplicate labels, which can appear inside getEndpointLabelsForAddressAndPort()
2022-11-30 05:22:12 +00:00
m . RemoveDuplicates ( )
2022-04-26 12:25:58 +00:00
ms = append ( ms , m )
2020-04-13 18:02:27 +00:00
}
return ms
}
2022-04-22 16:39:34 +00:00
func getEndpointLabelsForAddressAndPort ( gw * groupWatcher , podPortsSeen map [ * Pod ] [ ] int , eps * Endpoints , ea EndpointAddress , epp EndpointPort ,
2022-11-30 05:22:12 +00:00
p * Pod , svc * Service , ready string ) * promutils . Labels {
2020-04-13 18:02:27 +00:00
m := getEndpointLabels ( eps . Metadata , ea , epp , ready )
if svc != nil {
svc . appendCommonLabels ( m )
}
2022-02-11 12:54:47 +00:00
// See https://github.com/prometheus/prometheus/issues/10284
2021-02-15 00:46:14 +00:00
eps . Metadata . registerLabelsAndAnnotations ( "__meta_kubernetes_endpoints" , m )
2020-04-13 18:02:27 +00:00
if ea . TargetRef . Kind != "Pod" || p == nil {
return m
}
2022-04-26 12:25:58 +00:00
p . appendCommonLabels ( m , gw )
2022-02-11 11:34:22 +00:00
// always add pod targetRef, even if epp port doesn't match container port
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/2134
if _ , ok := podPortsSeen [ p ] ; ! ok {
podPortsSeen [ p ] = [ ] int { }
}
2020-04-13 18:02:27 +00:00
for _ , c := range p . Spec . Containers {
for _ , cp := range c . Ports {
if cp . ContainerPort == epp . Port {
podPortsSeen [ p ] = append ( podPortsSeen [ p ] , cp . ContainerPort )
2024-10-24 15:04:12 +00:00
p . appendContainerLabels ( m , & c , & cp , false )
break
}
}
}
for _ , c := range p . Spec . InitContainers {
// Defines native sidecar https://kubernetes.io/blog/2023/08/25/native-sidecar-containers/#what-are-sidecar-containers-in-1-28
if c . RestartPolicy != "Always" {
continue
}
for _ , cp := range c . Ports {
if cp . ContainerPort == epp . Port {
podPortsSeen [ p ] = append ( podPortsSeen [ p ] , cp . ContainerPort )
p . appendContainerLabels ( m , & c , & cp , true )
2020-04-13 18:02:27 +00:00
break
}
}
}
return m
}
2022-11-30 05:22:12 +00:00
func getEndpointLabels ( om ObjectMeta , ea EndpointAddress , epp EndpointPort , ready string ) * promutils . Labels {
2020-04-23 08:34:04 +00:00
addr := discoveryutils . JoinHostPort ( ea . IP , epp . Port )
2022-11-30 05:22:12 +00:00
m := promutils . GetLabels ( )
m . Add ( "__address__" , addr )
m . Add ( "__meta_kubernetes_namespace" , om . Namespace )
m . Add ( "__meta_kubernetes_endpoints_name" , om . Name )
m . Add ( "__meta_kubernetes_endpoint_ready" , ready )
m . Add ( "__meta_kubernetes_endpoint_port_name" , epp . Name )
m . Add ( "__meta_kubernetes_endpoint_port_protocol" , epp . Protocol )
2020-04-13 18:02:27 +00:00
if ea . TargetRef . Kind != "" {
2022-11-30 05:22:12 +00:00
m . Add ( "__meta_kubernetes_endpoint_address_target_kind" , ea . TargetRef . Kind )
m . Add ( "__meta_kubernetes_endpoint_address_target_name" , ea . TargetRef . Name )
2020-04-13 18:02:27 +00:00
}
if ea . NodeName != "" {
2022-11-30 05:22:12 +00:00
m . Add ( "__meta_kubernetes_endpoint_node_name" , ea . NodeName )
2020-04-13 18:02:27 +00:00
}
if ea . Hostname != "" {
2022-11-30 05:22:12 +00:00
m . Add ( "__meta_kubernetes_endpoint_hostname" , ea . Hostname )
2020-04-13 18:02:27 +00:00
}
return m
}