2018-07-07 17:46:18 +00:00
/ *
Copyright 2016 The Kubernetes Authors .
Licensed under the Apache License , Version 2.0 ( the "License" ) ;
you may not use this file except in compliance with the License .
You may obtain a copy of the License at
http : //www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing , software
distributed under the License is distributed on an "AS IS" BASIS ,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
See the License for the specific language governing permissions and
limitations under the License .
* /
package collectors
import (
"fmt"
"io"
"net"
2018-08-03 13:50:53 +00:00
"os"
2023-04-11 08:01:18 +00:00
"strings"
2020-01-25 17:52:51 +00:00
"syscall"
2018-07-07 17:46:18 +00:00
2018-09-22 17:25:57 +00:00
jsoniter "github.com/json-iterator/go"
2018-07-07 17:46:18 +00:00
"github.com/prometheus/client_golang/prometheus"
"k8s.io/apimachinery/pkg/util/sets"
2020-08-08 23:31:02 +00:00
"k8s.io/klog/v2"
2018-07-07 17:46:18 +00:00
)
type socketData struct {
Host string ` json:"host" `
Status string ` json:"status" `
ResponseLength float64 ` json:"responseLength" `
Method string ` json:"method" `
RequestLength float64 ` json:"requestLength" `
RequestTime float64 ` json:"requestTime" `
2022-09-30 15:00:36 +00:00
Latency float64 ` json:"upstreamLatency" `
HeaderTime float64 ` json:"upstreamHeaderTime" `
ResponseTime float64 ` json:"upstreamResponseTime" `
2023-08-31 07:36:48 +00:00
Namespace string ` json:"namespace" `
Ingress string ` json:"ingress" `
Service string ` json:"service" `
Canary string ` json:"canary" `
Path string ` json:"path" `
2018-07-07 17:46:18 +00:00
}
2022-01-15 01:27:41 +00:00
// HistogramBuckets allow customizing prometheus histogram buckets values
type HistogramBuckets struct {
TimeBuckets [ ] float64
LengthBuckets [ ] float64
SizeBuckets [ ] float64
}
2023-04-11 08:01:18 +00:00
type metricMapping map [ string ] prometheus . Collector
2018-07-07 17:46:18 +00:00
// SocketCollector stores prometheus metrics and ingress meta-data
type SocketCollector struct {
prometheus . Collector
2024-08-16 08:53:12 +00:00
connectTime * prometheus . HistogramVec
headerTime * prometheus . HistogramVec
requestTime * prometheus . HistogramVec
responseTime * prometheus . HistogramVec
2018-07-07 17:46:18 +00:00
2022-09-30 15:00:36 +00:00
requestLength * prometheus . HistogramVec
2018-07-07 17:46:18 +00:00
responseLength * prometheus . HistogramVec
2022-09-30 15:00:36 +00:00
bytesSent * prometheus . HistogramVec // TODO: DEPRECATED, remove
2018-07-07 17:46:18 +00:00
requests * prometheus . CounterVec
listener net . Listener
2023-04-11 08:01:18 +00:00
metricMapping metricMapping
2018-09-22 17:25:57 +00:00
2023-02-16 14:05:48 +00:00
hosts sets . Set [ string ]
2018-12-21 17:10:28 +00:00
2024-08-26 19:09:11 +00:00
metricsPerHost bool
metricsPerUndefinedHost bool
reportStatusClasses bool
2018-07-07 17:46:18 +00:00
}
2023-08-31 07:36:48 +00:00
var requestTags = [ ] string {
"status" ,
2018-07-07 17:46:18 +00:00
2023-08-31 07:36:48 +00:00
"method" ,
"path" ,
2018-07-07 17:46:18 +00:00
2023-08-31 07:36:48 +00:00
"namespace" ,
"ingress" ,
"service" ,
"canary" ,
}
2018-07-07 17:46:18 +00:00
// NewSocketCollector creates a new SocketCollector instance using
2018-11-05 14:10:11 +00:00
// the ingress watch namespace and class used by the controller
2024-08-26 19:09:11 +00:00
func NewSocketCollector ( pod , namespace , class string , metricsPerHost , metricsPerUndefinedHost , reportStatusClasses bool , buckets HistogramBuckets , bucketFactor float64 , maxBuckets uint32 , excludeMetrics [ ] string ) ( * SocketCollector , error ) {
2022-04-09 04:48:04 +00:00
socket := "/tmp/nginx/prometheus-nginx.socket"
2020-01-25 17:52:51 +00:00
// unix sockets must be unlink()ed before being used
2023-08-31 07:36:48 +00:00
//nolint:errcheck // Ignore unlink error
2020-02-06 12:50:13 +00:00
_ = syscall . Unlink ( socket )
2020-01-25 17:52:51 +00:00
2018-08-03 13:50:53 +00:00
listener , err := net . Listen ( "unix" , socket )
if err != nil {
return nil , err
}
2023-08-31 07:36:48 +00:00
err = os . Chmod ( socket , 0 o777 ) // #nosec
2018-07-07 17:46:18 +00:00
if err != nil {
return nil , err
}
constLabels := prometheus . Labels {
"controller_namespace" : namespace ,
"controller_class" : class ,
"controller_pod" : pod ,
}
2018-12-21 17:10:28 +00:00
requestTags := requestTags
if metricsPerHost {
requestTags = append ( requestTags , "host" )
}
2023-04-11 08:01:18 +00:00
em := make ( map [ string ] struct { } , len ( excludeMetrics ) )
for _ , m := range excludeMetrics {
// remove potential nginx_ingress_controller prefix from the metric name
// TBD: how to handle fully qualified histogram metrics e.g. _buckets and _sum. Should we just remove the suffix and remove the histogram metric or ignore it?
em [ strings . TrimPrefix ( m , "nginx_ingress_controller_" ) ] = struct { } { }
}
// create metric mapping with only the metrics that are not excluded
mm := make ( metricMapping )
2018-07-07 17:46:18 +00:00
sc := & SocketCollector {
listener : listener ,
2024-08-26 19:09:11 +00:00
metricsPerHost : metricsPerHost ,
metricsPerUndefinedHost : metricsPerUndefinedHost ,
reportStatusClasses : reportStatusClasses ,
2018-12-21 17:10:28 +00:00
2023-04-11 08:01:18 +00:00
connectTime : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2024-08-23 16:32:48 +00:00
Name : "connect_duration_seconds" ,
Help : "The time spent on establishing a connection with the upstream server" ,
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
Buckets : buckets . TimeBuckets ,
NativeHistogramBucketFactor : bucketFactor ,
NativeHistogramMaxBucketNumber : maxBuckets ,
2018-07-07 17:46:18 +00:00
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2018-07-07 17:46:18 +00:00
) ,
2023-04-11 08:01:18 +00:00
headerTime : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2024-08-23 16:32:48 +00:00
Name : "header_duration_seconds" ,
Help : "The time spent on receiving first header from the upstream server" ,
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
Buckets : buckets . TimeBuckets ,
NativeHistogramBucketFactor : bucketFactor ,
NativeHistogramMaxBucketNumber : maxBuckets ,
2022-09-30 15:00:36 +00:00
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2022-09-30 15:00:36 +00:00
) ,
2023-04-11 08:01:18 +00:00
responseTime : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2024-08-23 16:32:48 +00:00
Name : "response_duration_seconds" ,
Help : "The time spent on receiving the response from the upstream server" ,
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
Buckets : buckets . TimeBuckets ,
NativeHistogramBucketFactor : bucketFactor ,
NativeHistogramMaxBucketNumber : maxBuckets ,
2018-07-07 17:46:18 +00:00
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2018-07-07 17:46:18 +00:00
) ,
2023-04-11 08:01:18 +00:00
requestTime : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2024-08-23 16:32:48 +00:00
Name : "request_duration_seconds" ,
Help : "The request processing time in milliseconds" ,
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
Buckets : buckets . TimeBuckets ,
NativeHistogramBucketFactor : bucketFactor ,
NativeHistogramMaxBucketNumber : maxBuckets ,
2018-07-07 17:46:18 +00:00
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2018-07-07 17:46:18 +00:00
) ,
2022-09-30 15:00:36 +00:00
2023-04-11 08:01:18 +00:00
responseLength : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2024-08-23 16:32:48 +00:00
Name : "response_size" ,
Help : "The response length (including request line, header, and request body)" ,
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
Buckets : buckets . LengthBuckets ,
NativeHistogramBucketFactor : bucketFactor ,
NativeHistogramMaxBucketNumber : maxBuckets ,
2022-09-30 15:00:36 +00:00
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2022-09-30 15:00:36 +00:00
) ,
2023-04-11 08:01:18 +00:00
requestLength : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2024-08-23 16:32:48 +00:00
Name : "request_size" ,
Help : "The request length (including request line, header, and request body)" ,
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
Buckets : buckets . LengthBuckets ,
NativeHistogramBucketFactor : bucketFactor ,
NativeHistogramMaxBucketNumber : maxBuckets ,
2018-07-07 17:46:18 +00:00
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2018-07-07 17:46:18 +00:00
) ,
2023-04-11 08:01:18 +00:00
requests : counterMetric (
2023-08-31 07:36:48 +00:00
& prometheus . CounterOpts {
2018-07-07 17:46:18 +00:00
Name : "requests" ,
2022-09-30 15:00:36 +00:00
Help : "The total number of client requests" ,
2018-07-07 17:46:18 +00:00
Namespace : PrometheusNamespace ,
ConstLabels : constLabels ,
} ,
2022-01-30 11:52:23 +00:00
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2018-07-07 17:46:18 +00:00
) ,
2023-04-11 08:01:18 +00:00
bytesSent : histogramMetric (
2023-08-31 07:36:48 +00:00
& prometheus . HistogramOpts {
2018-07-07 17:46:18 +00:00
Name : "bytes_sent" ,
2022-09-30 15:00:36 +00:00
Help : "DEPRECATED The number of bytes sent to a client" ,
2018-07-07 17:46:18 +00:00
Namespace : PrometheusNamespace ,
2022-01-15 01:27:41 +00:00
Buckets : buckets . SizeBuckets ,
2018-07-07 17:46:18 +00:00
ConstLabels : constLabels ,
} ,
requestTags ,
2023-04-11 08:01:18 +00:00
em ,
mm ,
2018-07-07 17:46:18 +00:00
) ,
}
2023-04-11 08:01:18 +00:00
sc . metricMapping = mm
return sc , nil
}
2022-09-30 15:00:36 +00:00
2023-04-11 08:01:18 +00:00
func containsMetric ( excludeMetrics map [ string ] struct { } , name string ) bool {
if _ , ok := excludeMetrics [ name ] ; ok {
klog . V ( 3 ) . InfoS ( "Skipping metric" , "metric" , name )
return true
}
return false
}
2018-07-07 17:46:18 +00:00
2023-08-31 07:36:48 +00:00
func counterMetric ( opts * prometheus . CounterOpts , requestTags [ ] string , excludeMetrics map [ string ] struct { } , metricMapping metricMapping ) * prometheus . CounterVec {
2023-04-11 08:01:18 +00:00
if containsMetric ( excludeMetrics , opts . Name ) {
return nil
2018-07-07 17:46:18 +00:00
}
2023-04-11 08:01:18 +00:00
m := prometheus . NewCounterVec (
2023-08-31 07:36:48 +00:00
* opts ,
2023-04-11 08:01:18 +00:00
requestTags ,
)
metricMapping [ prometheus . BuildFQName ( PrometheusNamespace , "" , opts . Name ) ] = m
return m
}
2018-07-07 17:46:18 +00:00
2023-08-31 07:36:48 +00:00
func histogramMetric ( opts * prometheus . HistogramOpts , requestTags [ ] string , excludeMetrics map [ string ] struct { } , metricMapping metricMapping ) * prometheus . HistogramVec {
2023-04-11 08:01:18 +00:00
if containsMetric ( excludeMetrics , opts . Name ) {
return nil
}
m := prometheus . NewHistogramVec (
2023-08-31 07:36:48 +00:00
* opts ,
2023-04-11 08:01:18 +00:00
requestTags ,
)
metricMapping [ prometheus . BuildFQName ( PrometheusNamespace , "" , opts . Name ) ] = m
return m
2018-07-07 17:46:18 +00:00
}
func ( sc * SocketCollector ) handleMessage ( msg [ ] byte ) {
2020-09-27 20:32:40 +00:00
klog . V ( 5 ) . InfoS ( "Metric" , "message" , string ( msg ) )
2018-07-07 17:46:18 +00:00
2018-11-07 09:34:24 +00:00
// Unmarshal bytes
2018-08-18 00:01:50 +00:00
var statsBatch [ ] socketData
2018-09-22 17:25:57 +00:00
err := jsoniter . ConfigCompatibleWithStandardLibrary . Unmarshal ( msg , & statsBatch )
2018-07-07 17:46:18 +00:00
if err != nil {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( err , "Unexpected error deserializing JSON" , "payload" , string ( msg ) )
2018-07-07 17:46:18 +00:00
return
}
2023-08-31 07:36:48 +00:00
for i := range statsBatch {
stats := & statsBatch [ i ]
2024-08-26 19:09:11 +00:00
if sc . metricsPerHost && ! sc . hosts . Has ( stats . Host ) && ! sc . metricsPerUndefinedHost {
klog . V ( 3 ) . InfoS ( "Skipping metric for host not explicitly defined in an ingress" , "host" , stats . Host )
2018-09-22 17:25:57 +00:00
continue
}
2024-03-07 10:39:53 +00:00
if sc . reportStatusClasses && stats . Status != "" {
2022-05-21 18:18:00 +00:00
stats . Status = fmt . Sprintf ( "%cxx" , stats . Status [ 0 ] )
}
2018-12-21 17:10:28 +00:00
// Note these must match the order in requestTags at the top
2018-08-18 00:01:50 +00:00
requestLabels := prometheus . Labels {
2018-11-21 16:05:44 +00:00
"status" : stats . Status ,
"method" : stats . Method ,
"path" : stats . Path ,
2018-08-18 00:01:50 +00:00
"namespace" : stats . Namespace ,
"ingress" : stats . Ingress ,
"service" : stats . Service ,
2021-09-26 17:54:22 +00:00
"canary" : stats . Canary ,
2018-08-18 00:01:50 +00:00
}
2018-07-07 17:46:18 +00:00
2018-08-18 00:01:50 +00:00
collectorLabels := prometheus . Labels {
"namespace" : stats . Namespace ,
"ingress" : stats . Ingress ,
"status" : stats . Status ,
2019-05-24 21:23:57 +00:00
"service" : stats . Service ,
2021-09-26 17:54:22 +00:00
"canary" : stats . Canary ,
2022-02-13 18:33:47 +00:00
"method" : stats . Method ,
"path" : stats . Path ,
}
if sc . metricsPerHost {
requestLabels [ "host" ] = stats . Host
collectorLabels [ "host" ] = stats . Host
2018-08-18 00:01:50 +00:00
}
2018-07-07 17:46:18 +00:00
2023-04-11 08:01:18 +00:00
if sc . requests != nil {
requestsMetric , err := sc . requests . GetMetricWith ( collectorLabels )
if err != nil {
klog . ErrorS ( err , "Error fetching requests metric" )
} else {
requestsMetric . Inc ( )
}
2018-07-24 14:53:46 +00:00
}
2018-07-07 17:46:18 +00:00
2018-08-18 00:01:50 +00:00
if stats . Latency != - 1 {
2023-04-11 08:01:18 +00:00
if sc . connectTime != nil {
connectTimeMetric , err := sc . connectTime . GetMetricWith ( requestLabels )
if err != nil {
klog . ErrorS ( err , "Error fetching connect time metric" )
} else {
connectTimeMetric . Observe ( stats . Latency )
}
2022-09-30 15:00:36 +00:00
}
2018-07-07 17:46:18 +00:00
}
2023-04-11 08:01:18 +00:00
if stats . HeaderTime != - 1 && sc . headerTime != nil {
2022-09-30 15:00:36 +00:00
headerTimeMetric , err := sc . headerTime . GetMetricWith ( requestLabels )
2022-06-22 19:59:43 +00:00
if err != nil {
klog . ErrorS ( err , "Error fetching header time metric" )
} else {
headerTimeMetric . Observe ( stats . HeaderTime )
}
}
2023-04-11 08:01:18 +00:00
if stats . RequestTime != - 1 && sc . requestTime != nil {
2018-08-18 00:01:50 +00:00
requestTimeMetric , err := sc . requestTime . GetMetricWith ( requestLabels )
if err != nil {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( err , "Error fetching request duration metric" )
2018-08-18 00:01:50 +00:00
} else {
requestTimeMetric . Observe ( stats . RequestTime )
}
2018-07-07 17:46:18 +00:00
}
2023-04-11 08:01:18 +00:00
if stats . RequestLength != - 1 && sc . requestLength != nil {
2018-08-18 00:01:50 +00:00
requestLengthMetric , err := sc . requestLength . GetMetricWith ( requestLabels )
if err != nil {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( err , "Error fetching request length metric" )
2018-08-18 00:01:50 +00:00
} else {
requestLengthMetric . Observe ( stats . RequestLength )
}
2018-07-07 17:46:18 +00:00
}
2023-04-11 08:01:18 +00:00
if stats . ResponseTime != - 1 && sc . responseTime != nil {
2018-08-18 00:01:50 +00:00
responseTimeMetric , err := sc . responseTime . GetMetricWith ( requestLabels )
if err != nil {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( err , "Error fetching upstream response time metric" )
2018-08-18 00:01:50 +00:00
} else {
responseTimeMetric . Observe ( stats . ResponseTime )
}
2018-07-07 17:46:18 +00:00
}
2018-08-18 00:01:50 +00:00
if stats . ResponseLength != - 1 {
2023-04-11 08:01:18 +00:00
if sc . bytesSent != nil {
bytesSentMetric , err := sc . bytesSent . GetMetricWith ( requestLabels )
if err != nil {
klog . ErrorS ( err , "Error fetching bytes sent metric" )
} else {
bytesSentMetric . Observe ( stats . ResponseLength )
}
2018-08-18 00:01:50 +00:00
}
2023-04-11 08:01:18 +00:00
if sc . responseLength != nil {
responseSizeMetric , err := sc . responseLength . GetMetricWith ( requestLabels )
if err != nil {
klog . ErrorS ( err , "Error fetching bytes sent metric" )
} else {
responseSizeMetric . Observe ( stats . ResponseLength )
}
2018-08-18 00:01:50 +00:00
}
2018-07-07 17:46:18 +00:00
}
}
}
// Start listen for connections in the unix socket and spawns a goroutine to process the content
func ( sc * SocketCollector ) Start ( ) {
for {
conn , err := sc . listener . Accept ( )
if err != nil {
continue
}
go handleMessages ( conn , sc . handleMessage )
}
}
// Stop stops unix listener
func ( sc * SocketCollector ) Stop ( ) {
sc . listener . Close ( )
}
// RemoveMetrics deletes prometheus metrics from prometheus for ingresses and
// host that are not available anymore.
// Ref: https://godoc.org/github.com/prometheus/client_golang/prometheus#CounterVec.Delete
func ( sc * SocketCollector ) RemoveMetrics ( ingresses [ ] string , registry prometheus . Gatherer ) {
mfs , err := registry . Gather ( )
if err != nil {
2020-09-27 20:32:40 +00:00
klog . ErrorS ( err , "Error gathering metrics: %v" )
2018-07-07 17:46:18 +00:00
return
}
// 1. remove metrics of removed ingresses
2020-09-27 20:32:40 +00:00
klog . V ( 2 ) . InfoS ( "removing metrics" , "ingresses" , ingresses )
2018-07-07 17:46:18 +00:00
for _ , mf := range mfs {
metricName := mf . GetName ( )
metric , ok := sc . metricMapping [ metricName ]
if ! ok {
continue
}
toRemove := sets . NewString ( ingresses ... )
for _ , m := range mf . GetMetric ( ) {
labels := make ( map [ string ] string , len ( m . GetLabel ( ) ) )
for _ , labelPair := range m . GetLabel ( ) {
labels [ * labelPair . Name ] = * labelPair . Value
}
// remove labels that are constant
deleteConstants ( labels )
ns , ok := labels [ "namespace" ]
if ! ok {
continue
}
ing , ok := labels [ "ingress" ]
if ! ok {
continue
}
ingKey := fmt . Sprintf ( "%v/%v" , ns , ing )
if ! toRemove . Has ( ingKey ) {
continue
}
2018-12-05 16:27:55 +00:00
klog . V ( 2 ) . Infof ( "Removing prometheus metric from histogram %v for ingress %v" , metricName , ingKey )
2018-07-07 17:46:18 +00:00
h , ok := metric . ( * prometheus . HistogramVec )
if ok {
removed := h . Delete ( labels )
if ! removed {
2020-09-27 20:32:40 +00:00
klog . V ( 2 ) . InfoS ( "metric not removed" , "name" , metricName , "ingress" , ingKey , "labels" , labels )
2018-07-07 17:46:18 +00:00
}
}
s , ok := metric . ( * prometheus . SummaryVec )
if ok {
removed := s . Delete ( labels )
if ! removed {
2020-09-27 20:32:40 +00:00
klog . V ( 2 ) . InfoS ( "metric not removed" , "name" , metricName , "ingress" , ingKey , "labels" , labels )
2018-07-07 17:46:18 +00:00
}
}
2022-02-13 18:33:47 +00:00
if c , ok := metric . ( * prometheus . CounterVec ) ; ok {
if removed := c . Delete ( labels ) ; ! removed {
klog . V ( 2 ) . InfoS ( "metric not removed" , "name" , metricName , "ingress" , ingKey , "labels" , labels )
}
}
2018-07-07 17:46:18 +00:00
}
}
}
// Describe implements prometheus.Collector
2023-08-31 07:36:48 +00:00
func ( sc * SocketCollector ) Describe ( ch chan <- * prometheus . Desc ) {
2023-04-11 08:01:18 +00:00
for _ , metric := range sc . metricMapping {
metric . Describe ( ch )
}
2018-07-07 17:46:18 +00:00
}
// Collect implements the prometheus.Collector interface.
2023-08-31 07:36:48 +00:00
func ( sc * SocketCollector ) Collect ( ch chan <- prometheus . Metric ) {
2023-04-11 08:01:18 +00:00
for _ , metric := range sc . metricMapping {
metric . Collect ( ch )
}
2018-07-07 17:46:18 +00:00
}
2018-09-22 17:25:57 +00:00
// SetHosts sets the hostnames that are being served by the ingress controller
// This set of hostnames is used to filter the metrics to be exposed
2023-02-16 14:05:48 +00:00
func ( sc * SocketCollector ) SetHosts ( hosts sets . Set [ string ] ) {
2018-09-22 17:25:57 +00:00
sc . hosts = hosts
}
2018-07-07 17:46:18 +00:00
// handleMessages process the content received in a network connection
func handleMessages ( conn io . ReadCloser , fn func ( [ ] byte ) ) {
defer conn . Close ( )
2021-08-06 14:18:17 +00:00
data , err := io . ReadAll ( conn )
2018-07-07 17:46:18 +00:00
if err != nil {
return
}
2018-08-18 00:01:50 +00:00
fn ( data )
2018-07-07 17:46:18 +00:00
}
func deleteConstants ( labels prometheus . Labels ) {
delete ( labels , "controller_namespace" )
delete ( labels , "controller_class" )
delete ( labels , "controller_pod" )
}