diff --git a/README.md b/README.md index 5b403dc..cb3d236 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ this project. ## Acknowledgments -The [Spegel] project has greatly inspired this work, and a big THANK YOU to Philip Laine and Simon Gottschlag at Xenit +The [Spegel] project has inspired this work; thanks to Philip Laine and Simon Gottschlag at Xenit for generously sharing their insights with us. A hat tip also to the [DADI P2P Proxy] project for demonstrating the integration with [Overlaybd]. diff --git a/build/ci/scripts/azure.sh b/build/ci/scripts/azure.sh index 7d80eb9..b938b40 100755 --- a/build/ci/scripts/azure.sh +++ b/build/ci/scripts/azure.sh @@ -164,6 +164,7 @@ print_peerd_metrics() { for pod in $( echo "$p" | tr -s " " "\012" ); do echo "checking pod '$pod' for metrics" kubectl -n peerd-ns exec -i $pod -- bash -c "cat /var/log/peerdmetrics" + kubectl --context=$KIND_CLUSTER_CONTEXT -n peerd-ns exec -i $pod -- bash -c "curl http://localhost:5004/metrics/prometheus" done } diff --git a/build/ci/scripts/kind.sh b/build/ci/scripts/kind.sh index ff84130..b0b2a64 100755 --- a/build/ci/scripts/kind.sh +++ b/build/ci/scripts/kind.sh @@ -124,6 +124,7 @@ print_p2p_metrics() { for pod in $( echo "$p" | tr -s " " "\012" ); do echo "checking pod '$pod' for metrics" kubectl --context=$KIND_CLUSTER_CONTEXT -n peerd-ns exec -i $pod -- bash -c "cat /var/log/peerdmetrics" + kubectl --context=$KIND_CLUSTER_CONTEXT -n peerd-ns exec -i $pod -- bash -c "curl http://localhost:5004/metrics/prometheus" done } diff --git a/build/package/peerd-helm/Chart.yaml b/build/package/peerd-helm/Chart.yaml index ee5929c..65d7426 100644 --- a/build/package/peerd-helm/Chart.yaml +++ b/build/package/peerd-helm/Chart.yaml @@ -15,10 +15,10 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.0.1-alpha +version: 0.0.2-alpha # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. -appVersion: "0.0.1-alpha" +appVersion: "0.0.4-alpha" diff --git a/build/package/peerd-helm/templates/_helpers.tpl b/build/package/peerd-helm/templates/_helpers.tpl new file mode 100644 index 0000000..61d3ad8 --- /dev/null +++ b/build/package/peerd-helm/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "peerd.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{- define "peerd.namespace" -}} +{{ include "peerd.name" . }}-ns +{{- end }} + +{{- define "peerd.serviceAccountName" -}} +{{ include "peerd.name" . }}-sa +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "peerd.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "peerd.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "peerd.labels" -}} +helm.sh/chart: {{ include "peerd.chart" . }} +{{ include "peerd.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Common selector labels +*/}} +{{- define "peerd.selectorLabels" -}} +app: {{ include "peerd.name" . }} +app.kubernetes.io/name: {{ include "peerd.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} diff --git a/build/package/peerd-helm/templates/app.yml b/build/package/peerd-helm/templates/app.yml index 5a4abf4..59662c1 100644 --- a/build/package/peerd-helm/templates/app.yml +++ b/build/package/peerd-helm/templates/app.yml @@ -1,15 +1,45 @@ apiVersion: v1 kind: Namespace metadata: - name: peerd-ns + name: {{ include "peerd.namespace" . }} + labels: + {{- include "peerd.labels" . | nindent 4 }} +--- +kind: ConfigMap +apiVersion: v1 +data: + prometheus-config: |- + global: + scrape_interval: 15s + scrape_configs: + - job_name: peerd + kubernetes_sd_configs: + - role: pod + relabel_configs: + - source_labels: [__meta_kubernetes_pod_label_app] + action: keep + regex: peerd + - source_labels: [__meta_kubernetes_pod_container_port_number] + action: keep + regex: "5004" + metrics_path: /metrics/prometheus +metadata: + name: ama-metrics-prometheus-config + namespace: kube-system + labels: + {{- include "peerd.labels" . | nindent 4 }} --- apiVersion: apps/v1 kind: DaemonSet metadata: - name: &name peerd - namespace: peerd-ns + name: &name {{ include "peerd.name" . }} + namespace: {{ include "peerd.namespace" . }} labels: - app: *name + {{- include "peerd.labels" . | nindent 4 }} + annotations: + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics/prometheus' + prometheus.io/port: '5004' spec: selector: matchLabels: @@ -19,16 +49,7 @@ spec: labels: app: *name spec: - # affinity: - # nodeAffinity: - # requiredDuringSchedulingIgnoredDuringExecution: - # nodeSelectorTerms: - # - matchExpressions: - # - key: p2p-nodepool - # operator: In - # values: - # - "true" - serviceAccountName: peerd-sa + serviceAccountName: {{ include "peerd.serviceAccountName" . }} containers: - image: "{{ .Values.peerd.image.ref }}" imagePullPolicy: "{{ .Values.peerd.image.pullPolicy }}" @@ -50,6 +71,10 @@ spec: name: http - containerPort: 5001 name: https + - containerPort: 5003 + name: router + - containerPort: 5004 + name: metrics volumeMounts: - name: metricsmount mountPath: "/var/log/peerdmetrics" @@ -74,10 +99,14 @@ spec: apiVersion: v1 kind: Service metadata: - name: &name peerd - namespace: peerd-ns + name: &name {{ include "peerd.name" . }} + namespace: {{ include "peerd.namespace" . }} labels: - app: *name + {{- include "peerd.labels" . | nindent 4 }} + annotations: + prometheus.io/scrape: 'true' + prometheus.io/path: '/metrics/prometheus' + prometheus.io/port: '30004' spec: type: NodePort selector: @@ -93,3 +122,8 @@ spec: port: 5001 nodePort: 30001 targetPort: https + - name: metrics + protocol: TCP + port: 5004 + nodePort: 30004 + targetPort: metrics diff --git a/build/package/peerd-helm/templates/rbac.yml b/build/package/peerd-helm/templates/rbac.yml index 090914f..6c82f3f 100644 --- a/build/package/peerd-helm/templates/rbac.yml +++ b/build/package/peerd-helm/templates/rbac.yml @@ -1,29 +1,29 @@ apiVersion: v1 kind: ServiceAccount metadata: - name: peerd-sa - namespace: peerd-ns + name: {{ include "peerd.serviceAccountName" . }} + namespace: {{ include "peerd.namespace" . }} labels: - app: peerd + {{- include "peerd.labels" . | nindent 4 }} --- apiVersion: v1 kind: Secret metadata: - name: peerd-sa-secret - namespace: peerd-ns + name: {{ include "peerd.serviceAccountName" . }}-secret + namespace: {{ include "peerd.namespace" . }} labels: - app: peerd + {{- include "peerd.labels" . | nindent 4 }} annotations: - kubernetes.io/service-account.name: peerd-sa + kubernetes.io/service-account.name: {{ include "peerd.serviceAccountName" . }} type: kubernetes.io/service-account-token --- apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: - name: peerd-manager-role - namespace: peerd-ns + name: {{ include "peerd.name" . }}-manager-role + namespace: {{ include "peerd.namespace" . }} labels: - app: peerd + {{- include "peerd.labels" . | nindent 4 }} rules: - apiGroups: ["coordination.k8s.io"] resources: ["configmaps"] @@ -38,23 +38,25 @@ rules: apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: - name: peerd-manager-role-binding - namespace: peerd-ns + name: {{ include "peerd.name" . }}-manager-role-binding + namespace: {{ include "peerd.namespace" . }} labels: - app: peerd + {{- include "peerd.labels" . | nindent 4 }} roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: peerd-manager-role subjects: - kind: ServiceAccount - name: peerd-sa - namespace: peerd-ns + name: {{ include "peerd.serviceAccountName" . }} + namespace: {{ include "peerd.namespace" . }} --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: node-reader-and-event-creator-clusterrole + labels: + {{- include "peerd.labels" . | nindent 4 }} rules: - apiGroups: [""] resources: ["nodes"] @@ -67,10 +69,12 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: node-reader-and-event-creator-clusterrole-binding + labels: + {{- include "peerd.labels" . | nindent 4 }} subjects: - kind: ServiceAccount - name: peerd-sa - namespace: peerd-ns + name: {{ include "peerd.serviceAccountName" . }} + namespace: {{ include "peerd.namespace" . }} roleRef: kind: ClusterRole name: node-reader-and-event-creator-clusterrole diff --git a/cmd/proxy/cmd.go b/cmd/proxy/cmd.go index 7890291..c0e93b6 100644 --- a/cmd/proxy/cmd.go +++ b/cmd/proxy/cmd.go @@ -6,6 +6,7 @@ type ServerCmd struct { HttpAddr string `arg:"--http-addr" help:"address of the server" default:"127.0.0.1:5000"` HttpsAddr string `arg:"--https-addr" help:"address of the server" default:"0.0.0.0:5001"` RouterAddr string `arg:"--router-addr" help:"address of the router (p2p)" default:"0.0.0.0:5003"` + PromAddr string `arg:"--prom-addr" help:"address of prometheus metrics endpoint" default:"0.0.0.0:5004"` PrefetchWorkers int `arg:"--prefetch-workers" help:"number of workers to prefetch content" default:"50"` // Mirror configuration. diff --git a/cmd/proxy/main.go b/cmd/proxy/main.go index 0fa74d5..f23df17 100644 --- a/cmd/proxy/main.go +++ b/cmd/proxy/main.go @@ -24,6 +24,7 @@ import ( "github.com/azure/peerd/internal/state" "github.com/azure/peerd/pkg/containerd" "github.com/azure/peerd/pkg/k8s" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog" "github.com/spf13/afero" "golang.org/x/sync/errgroup" @@ -164,41 +165,45 @@ func serverCommand(ctx context.Context, args *ServerCmd) (err error) { Handler: handler, TLSConfig: r.Net().DefaultTLSConfig(), } - g.Go(func() error { if err := httpsSrv.ListenAndServeTLS("", ""); err != nil && !errors.Is(err, http.ErrServerClosed) { return err } return nil }) + g.Go(func() error { + <-ctx.Done() + shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + defer cancel() + return httpsSrv.Shutdown(shutdownCtx) + }) httpSrv := &http.Server{ Addr: args.HttpAddr, Handler: handler, } - g.Go(func() error { if err := httpSrv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { return err } return nil }) - g.Go(func() error { <-ctx.Done() shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() - return httpsSrv.Shutdown(shutdownCtx) + return httpSrv.Shutdown(shutdownCtx) }) g.Go(func() error { - <-ctx.Done() - shutdownCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - return httpSrv.Shutdown(shutdownCtx) + http.Handle("/metrics/prometheus", promhttp.Handler()) + if err = http.ListenAndServe(args.PromAddr, nil); err != nil && !errors.Is(err, http.ErrServerClosed) { + return err + } + return nil }) - l.Info().Str("https", args.HttpsAddr).Str("http", args.HttpAddr).Msg("server start") + l.Info().Str("https", args.HttpsAddr).Str("http", args.HttpAddr).Str("router", args.RouterAddr).Str("prom", args.PromAddr).Msg("server start") err = g.Wait() if err != nil { return err diff --git a/go.mod b/go.mod index 6671f7f..80bdcaa 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/opencontainers/go-digest v1.0.0 github.com/opencontainers/image-spec v1.1.0 github.com/pelletier/go-toml/v2 v2.1.1 + github.com/prometheus/client_golang v1.18.0 github.com/rs/zerolog v1.32.0 github.com/schollz/progressbar/v3 v3.14.2 github.com/swaggo/swag v1.16.3 @@ -130,7 +131,6 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.89.0 // indirect - github.com/prometheus/client_golang v1.18.0 // indirect github.com/prometheus/client_model v0.6.0 // indirect github.com/prometheus/common v0.47.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect diff --git a/go.sum b/go.sum index 9beec51..746d2d0 100644 --- a/go.sum +++ b/go.sum @@ -205,8 +205,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= @@ -848,16 +846,10 @@ honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= -k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= -k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= -k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= -k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= -k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= diff --git a/internal/metrics/interface.go b/internal/metrics/interface.go index fb8cb3a..682517e 100644 --- a/internal/metrics/interface.go +++ b/internal/metrics/interface.go @@ -2,6 +2,8 @@ // Licensed under the MIT License. package metrics +import "github.com/prometheus/client_golang/prometheus" + // Metrics defines an interface to collect p2p metrics. type Metrics interface { // RecordRequest records the time it takes to process a request. @@ -18,4 +20,4 @@ type Metrics interface { } // Global is the global metrics collector. -var Global Metrics = NewMemoryMetrics() +var Global Metrics = NewPromMetrics(prometheus.DefaultRegisterer) diff --git a/internal/metrics/prometheus.go b/internal/metrics/prometheus.go new file mode 100644 index 0000000..5d1e69e --- /dev/null +++ b/internal/metrics/prometheus.go @@ -0,0 +1,81 @@ +// Package metrics provides a metrics collector that stores metrics in Prometheus. +package metrics + +import ( + "github.com/azure/peerd/internal/context" + "github.com/prometheus/client_golang/prometheus" +) + +// promMetrics is a metrics collector that stores metrics in Prometheus. +type promMetrics struct { + requestDuration *prometheus.HistogramVec + peerDiscoveryDuration *prometheus.HistogramVec + peerResponseSpeed *prometheus.HistogramVec + upstreamResponseSpeed *prometheus.HistogramVec +} + +var _ Metrics = &promMetrics{} + +// RecordPeerDiscovery records the duration of peer discovery for a given IP address. +func (m *promMetrics) RecordPeerDiscovery(ip string, duration float64) { + m.peerDiscoveryDuration.WithLabelValues(context.NodeName, ip).Observe(duration) +} + +// RecordPeerResponse records the response time and count of a peer's operation. +// It calculates the speed (count/duration) and updates the Prometheus metric. +func (m *promMetrics) RecordPeerResponse(ip string, key string, op string, duration float64, count int64) { + bps := float64(count) / duration + m.peerResponseSpeed.WithLabelValues(context.NodeName, ip, op).Observe(bps / float64(1024*1024)) +} + +// RecordRequest records the duration of a request for a specific method and handler. +// It updates the Prometheus metric for request duration. +func (m *promMetrics) RecordRequest(method string, handler string, duration float64) { + m.requestDuration.WithLabelValues(context.NodeName, method, handler).Observe(duration) +} + +// RecordUpstreamResponse records the duration and count of an upstream response. +// It calculates the speed of the response and updates the corresponding Prometheus metric. +func (m *promMetrics) RecordUpstreamResponse(hostname string, key string, op string, duration float64, count int64) { + bps := float64(count) / duration + m.upstreamResponseSpeed.WithLabelValues(context.NodeName, hostname, op).Observe(bps / float64(1024*1024)) +} + +// NewPromMetrics creates a new instance of promMetrics. +func NewPromMetrics(reg prometheus.Registerer) *promMetrics { + + requestDurationHist := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "peerd_request_duration_seconds", + Help: "Duration of requests in seconds.", + Buckets: prometheus.LinearBuckets(0.005, 0.025, 200), + }, []string{"self", "method", "handler"}) + reg.MustRegister(requestDurationHist) + + peerDiscoveryDurationHist := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "peerd_peer_discovery_duration_seconds", + Help: "Duration of peer discovery in seconds.", + Buckets: prometheus.LinearBuckets(0.001, 0.002, 200), + }, []string{"self", "ip"}) + reg.MustRegister(peerDiscoveryDurationHist) + + peerResponseDurationHist := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "peerd_peer_response_speed_mib_per_second", + Help: "Speed of peer response in Mib per second.", + Buckets: prometheus.LinearBuckets(1, 15, 200), + }, []string{"self", "ip", "op"}) + reg.MustRegister(peerResponseDurationHist) + + upstreamResponseDurationHist := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Name: "peerd_upstream_response_speed_mib_per_seconds", + Help: "Speed of upstream response in Mib per second.", + Buckets: prometheus.LinearBuckets(1, 15, 200), + }, []string{"self", "hostname", "op"}) + reg.MustRegister(upstreamResponseDurationHist) + + return &promMetrics{ + requestDuration: requestDurationHist, + peerDiscoveryDuration: peerDiscoveryDurationHist, + peerResponseSpeed: peerResponseDurationHist, + upstreamResponseSpeed: upstreamResponseDurationHist, + } +} diff --git a/internal/metrics/prometheus_test.go b/internal/metrics/prometheus_test.go new file mode 100644 index 0000000..9bc25f1 --- /dev/null +++ b/internal/metrics/prometheus_test.go @@ -0,0 +1,173 @@ +package metrics + +import ( + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/testutil" +) + +func TestPromMetrics_RecordPeerDiscovery(t *testing.T) { + reg := prometheus.NewPedanticRegistry() + m := NewPromMetrics(reg) + + ip := "192.168.0.1" + duration := 0.001 + m.RecordPeerDiscovery(ip, duration) + + // Verify that the prometheus metric was updated correctly + expected := ` + # HELP peerd_peer_discovery_duration_seconds Duration of peer discovery + # TYPE peerd_peer_discovery_duration_seconds histogram + peerd_peer_discovery_duration_seconds_sum 0.001 + peerd_peer_discovery_duration_seconds_count 1 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_peer_discovery_duration_seconds_sum", "peerd_peer_discovery_duration_seconds_count"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } + + ip = "10.0.01" + duration = 1.0 + m.RecordPeerDiscovery(ip, duration) + + // Verify that the prometheus metric was updated correctly + expected = ` + # HELP peerd_peer_discovery_duration_seconds Duration of peer discovery + # TYPE peerd_peer_discovery_duration_seconds histogram + peerd_peer_discovery_duration_seconds_sum 1.001 + peerd_peer_discovery_duration_seconds_count 2 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_peer_discovery_duration_seconds_sum", "peerd_peer_discovery_duration_seconds_count"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } +} + +func TestPromMetrics_RecordPeerResponse(t *testing.T) { + reg := prometheus.NewPedanticRegistry() + m := NewPromMetrics(reg) + + ip := "192.168.0.1" + key := "key" + op := "operation" + duration := 1.0 + count := int64(1024 * 1024) + m.RecordPeerResponse(ip, key, op, duration, count) + + // Verify that the prometheus metric was updated correctly + expected := ` + # HELP peerd_peer_response_speed Speed of peer response + # TYPE peerd_peer_response_speed histogram + peerd_peer_response_speed_sum 1 + peerd_peer_response_speed_count 1 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_peer_response_speed_sum", "peerd_peer_response_speed_count"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } + + ip = "" + key = "key" + op = "operation" + duration = 2.0 + count = int64(4 * 1024 * 1024) + m.RecordPeerResponse(ip, key, op, duration, count) + + // Verify that the prometheus metric was updated correctly + expected = ` + # HELP peerd_peer_response_speed Speed of peer response + # TYPE peerd_peer_response_speed histogram + peerd_peer_response_speed_sum 3 + peerd_peer_response_speed_count 2 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_peer_response_speed_sum", "peerd_peer_response_speed_count"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } +} + +func TestPromMetrics_RecordRequest(t *testing.T) { + reg := prometheus.NewPedanticRegistry() + m := NewPromMetrics(reg) + + method := "GET" + handler := "files" + duration := 0.5 + m.RecordRequest(method, handler, duration) + + // Verify that the prometheus metric was updated correctly + expected := ` + # HELP peerd_request_duration_seconds Duration of request + # TYPE peerd_request_duration_seconds histogram + peerd_request_duration_seconds_sum 0.5 + peerd_request_duration_seconds_count 1 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_request_duration_seconds_count", "peerd_request_duration_seconds_sum"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } + + method = "HEAD" + handler = "files" + duration = 0.1 + m.RecordRequest(method, handler, duration) + + // Verify that the prometheus metric was updated correctly + expected = ` + # HELP peerd_request_duration_seconds Duration of request + # TYPE peerd_request_duration_seconds histogram + peerd_request_duration_seconds_sum 0.6 + peerd_request_duration_seconds_count 2 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_request_duration_seconds_count", "peerd_request_duration_seconds_sum"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } +} + +func TestPromMetrics_RecordUpstreamResponse(t *testing.T) { + reg := prometheus.NewPedanticRegistry() + m := NewPromMetrics(reg) + + hostname := "localhost" + key := "key" + op := "operation" + duration := 1.0 + count := int64(1024 * 1024) + + m.RecordUpstreamResponse(hostname, key, op, duration, count) + + // Verify that the prometheus metric was updated correctly + expected := ` + # HELP peerd_upstream_response_speed Speed of upstream response + # TYPE peerd_upstream_response_speed histogram + peerd_upstream_response_speed_sum 1 + peerd_upstream_response_speed_count 1 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_upstream_response_speed_sum", "peerd_upstream_response_speed_count"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } + + hostname = "localhost" + key = "key" + op = "operation" + duration = 2.0 + count = int64(4 * 1024 * 1024) + + m.RecordUpstreamResponse(hostname, key, op, duration, count) + + // Verify that the prometheus metric was updated correctly + expected = ` + # HELP peerd_upstream_response_speed Speed of upstream response + # TYPE peerd_upstream_response_speed histogram + peerd_upstream_response_speed_sum 3 + peerd_upstream_response_speed_count 2 + ` + + if err := testutil.GatherAndCompare(reg, strings.NewReader(expected), "peerd_upstream_response_speed_sum", "peerd_upstream_response_speed_count"); err != nil { + t.Errorf("unexpected metric result:\n%s", err) + } +}