2020-04-13 20:02:27 +02:00
package kubernetes
import (
"encoding/json"
"fmt"
2021-03-11 15:41:09 +01:00
"io"
2020-04-23 10:34:04 +02:00
2021-10-19 12:19:18 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/logger"
2020-04-23 10:34:04 +02:00
"github.com/VictoriaMetrics/VictoriaMetrics/lib/promscrape/discoveryutils"
2020-04-13 20:02:27 +02:00
)
2021-04-02 13:45:08 +02:00
func ( eps * Endpoints ) key ( ) string {
return eps . Metadata . key ( )
2021-02-26 15:54:03 +01:00
}
2021-03-11 15:41:09 +01:00
func parseEndpointsList ( r io . Reader ) ( map [ string ] object , ListMeta , error ) {
2021-02-26 15:54:03 +01:00
var epsl EndpointsList
2021-03-11 15:41:09 +01: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 15:54:03 +01:00
}
2021-04-02 13:45:08 +02:00
objectsByKey := make ( map [ string ] object )
2021-02-26 15:54:03 +01:00
for _ , eps := range epsl . Items {
2021-04-02 13:45:08 +02:00
objectsByKey [ eps . key ( ) ] = eps
2021-02-26 15:54:03 +01:00
}
2021-04-02 13:45:08 +02:00
return objectsByKey , epsl . Metadata , nil
2021-02-26 15:54:03 +01: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 20:02:27 +02:00
// EndpointsList implements k8s endpoints list.
//
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#endpointslist-v1-core
type EndpointsList struct {
2021-02-26 15:54:03 +01:00
Metadata ListMeta
Items [ ] * Endpoints
2020-04-13 20:02:27 +02:00
}
// Endpoints implements k8s endpoints.
//
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#endpoints-v1-core
type Endpoints struct {
Metadata ObjectMeta
Subsets [ ] EndpointSubset
}
// EndpointSubset implements k8s endpoint subset.
//
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#endpointsubset-v1-core
type EndpointSubset struct {
Addresses [ ] EndpointAddress
NotReadyAddresses [ ] EndpointAddress
Ports [ ] EndpointPort
}
// EndpointAddress implements k8s endpoint address.
//
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#endpointaddress-v1-core
type EndpointAddress struct {
Hostname string
IP string
NodeName string
TargetRef ObjectReference
}
// ObjectReference implements k8s object reference.
//
// See https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.17/#objectreference-v1-core
type ObjectReference struct {
Kind string
Name string
Namespace string
}
// EndpointPort implements k8s endpoint port.
//
2021-08-29 10:16:40 +02:00
// See https://v1-21.docs.kubernetes.io/docs/reference/generated/kubernetes-api/v1.21/#endpointport-v1-discovery-k8s-io
2020-04-13 20:02:27 +02:00
type EndpointPort struct {
AppProtocol string
Name string
Port int
Protocol string
}
2021-02-26 19:21:27 +01:00
// getTargetLabels returns labels for each endpoint in eps.
2020-04-13 20:02:27 +02:00
//
// See https://prometheus.io/docs/prometheus/latest/configuration/configuration/#endpoints
2021-03-14 20:10:35 +01:00
func ( eps * Endpoints ) getTargetLabels ( gw * groupWatcher ) [ ] map [ string ] string {
2021-02-26 15:46:13 +01:00
var svc * Service
2021-04-29 09:14:24 +02:00
if o := gw . getObjectByRoleLocked ( "service" , eps . Metadata . Namespace , eps . Metadata . Name ) ; o != nil {
2021-02-26 15:54:03 +01:00
svc = o . ( * Service )
2021-02-26 15:46:13 +01:00
}
2020-04-13 20:02:27 +02:00
podPortsSeen := make ( map [ * Pod ] [ ] int )
2021-02-26 19:21:27 +01:00
var ms [ ] map [ string ] string
2020-04-13 20:02:27 +02:00
for _ , ess := range eps . Subsets {
for _ , epp := range ess . Ports {
2021-03-14 20:10:35 +01: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 20:02:27 +02:00
}
}
2021-10-19 12:19:18 +02:00
// See https://kubernetes.io/docs/reference/labels-annotations-taints/#endpoints-kubernetes-io-over-capacity
// and https://github.com/kubernetes/kubernetes/pull/99975
switch eps . Metadata . Annotations . GetByName ( "endpoints.kubernetes.io/over-capacity" ) {
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 20:02:27 +02: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
}
for p , ports := range podPortsSeen {
for _ , c := range p . Spec . Containers {
for _ , cp := range c . Ports {
if portSeen ( cp . ContainerPort , ports ) {
continue
}
2020-04-23 10:34:04 +02:00
addr := discoveryutils . JoinHostPort ( p . Status . PodIP , cp . ContainerPort )
2020-04-13 20:02:27 +02:00
m := map [ string ] string {
"__address__" : addr ,
}
p . appendCommonLabels ( m )
p . appendContainerLabels ( m , c , & cp )
2020-12-24 10:26:14 +01:00
if svc != nil {
svc . appendCommonLabels ( m )
}
2020-04-13 20:02:27 +02:00
ms = append ( ms , m )
}
}
}
return ms
}
2021-03-14 20:10:35 +01:00
func appendEndpointLabelsForAddresses ( ms [ ] map [ string ] string , gw * groupWatcher , podPortsSeen map [ * Pod ] [ ] int , eps * Endpoints ,
2021-02-26 15:54:03 +01:00
eas [ ] EndpointAddress , epp EndpointPort , svc * Service , ready string ) [ ] map [ string ] string {
2020-04-13 20:02:27 +02:00
for _ , ea := range eas {
2021-02-26 15:46:13 +01:00
var p * Pod
2021-04-05 19:27:23 +02:00
if ea . TargetRef . Name != "" {
2021-04-29 09:14:24 +02:00
if o := gw . getObjectByRoleLocked ( "pod" , ea . TargetRef . Namespace , ea . TargetRef . Name ) ; o != nil {
2021-04-05 19:27:23 +02:00
p = o . ( * Pod )
}
2021-02-26 15:46:13 +01:00
}
2020-04-13 20:02:27 +02:00
m := getEndpointLabelsForAddressAndPort ( podPortsSeen , eps , ea , epp , p , svc , ready )
ms = append ( ms , m )
}
return ms
}
func getEndpointLabelsForAddressAndPort ( podPortsSeen map [ * Pod ] [ ] int , eps * Endpoints , ea EndpointAddress , epp EndpointPort , p * Pod , svc * Service , ready string ) map [ string ] string {
m := getEndpointLabels ( eps . Metadata , ea , epp , ready )
if svc != nil {
svc . appendCommonLabels ( m )
}
2021-02-15 01:46:14 +01:00
eps . Metadata . registerLabelsAndAnnotations ( "__meta_kubernetes_endpoints" , m )
2020-04-13 20:02:27 +02:00
if ea . TargetRef . Kind != "Pod" || p == nil {
return m
}
p . appendCommonLabels ( m )
2022-02-11 12:34:22 +01: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 20:02:27 +02:00
for _ , c := range p . Spec . Containers {
for _ , cp := range c . Ports {
if cp . ContainerPort == epp . Port {
podPortsSeen [ p ] = append ( podPortsSeen [ p ] , cp . ContainerPort )
2022-02-11 12:34:22 +01:00
p . appendContainerLabels ( m , c , & cp )
2020-04-13 20:02:27 +02:00
break
}
}
}
return m
}
func getEndpointLabels ( om ObjectMeta , ea EndpointAddress , epp EndpointPort , ready string ) map [ string ] string {
2020-04-23 10:34:04 +02:00
addr := discoveryutils . JoinHostPort ( ea . IP , epp . Port )
2020-04-13 20:02:27 +02:00
m := map [ string ] string {
"__address__" : addr ,
"__meta_kubernetes_namespace" : om . Namespace ,
"__meta_kubernetes_endpoints_name" : om . Name ,
"__meta_kubernetes_endpoint_ready" : ready ,
"__meta_kubernetes_endpoint_port_name" : epp . Name ,
"__meta_kubernetes_endpoint_port_protocol" : epp . Protocol ,
}
if ea . TargetRef . Kind != "" {
m [ "__meta_kubernetes_endpoint_address_target_kind" ] = ea . TargetRef . Kind
m [ "__meta_kubernetes_endpoint_address_target_name" ] = ea . TargetRef . Name
}
if ea . NodeName != "" {
m [ "__meta_kubernetes_endpoint_node_name" ] = ea . NodeName
}
if ea . Hostname != "" {
m [ "__meta_kubernetes_endpoint_hostname" ] = ea . Hostname
}
return m
}