Files
Ross Golder 8a19328903 chore: update Kubernetes version to 1.35.7 (#1253)
- Add DefaultKubernetesVersion constant in api/v1alpha1
- Update e2e tests to use the constant
- Update deploy/*.env and config/samples/*.yaml files
2026-07-27 11:33:57 +02:00

400 lines
12 KiB
Go

// Copyright 2022 Clastix Labs
// SPDX-License-Identifier: Apache-2.0
package metrics
import (
"testing"
"github.com/prometheus/client_golang/prometheus"
io_prometheus_client "github.com/prometheus/client_model/go"
"sigs.k8s.io/controller-runtime/pkg/metrics"
kamajiv1alpha1 "github.com/clastix/kamaji/api/v1alpha1"
)
func testRecorder() *Recorder {
return DefaultRecorder()
}
func TestKamajiMetricRegistration(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetDatastoresReadyAndDriverCounts()
recorder.SetDatastoresReadyAndDriverCounts("default", map[string]map[string]int{
ReadyLabelTrue: {
string(kamajiv1alpha1.EtcdDriver): 1,
},
})
recorder.ResetCertificatesStatusCounts()
recorder.SetCertificatesStatusCounts("default", "k8s-133", map[string]map[string]int{
CertificateStatusValid: {
CertificateStrategyX509: 1,
},
})
wanted := map[string]struct{}{
"kamaji_build_info": {},
"kamaji_tenant_control_planes_current": {},
"kamaji_datastores_current": {},
"kamaji_certificates_current": {},
}
families, err := metrics.Registry.Gather()
if err != nil {
t.Fatalf("failed to gather metrics: %v", err)
}
seen := map[string]struct{}{}
for _, family := range families {
if _, ok := wanted[family.GetName()]; ok {
seen[family.GetName()] = struct{}{}
}
}
for name := range wanted {
if _, ok := seen[name]; !ok {
t.Fatalf("metric %s not registered", name)
}
}
}
func TestNewRecorderWithCustomRegistry(t *testing.T) {
t.Helper()
registry := prometheus.NewRegistry()
recorder := NewRecorder(registry)
recorder.SetTenantControlPlanesReadyCounts(map[string]int{
ReadyLabelTrue: 1,
ReadyLabelFalse: 0,
})
family := mustMetricFamilyFromGatherer(t, registry, "kamaji_tenant_control_planes_current")
if got := gaugeValueByLabels(t, family, map[string]string{"ready": ReadyLabelTrue}); got != 1 {
t.Fatalf("expected ready control planes gauge to be 1 on custom registry, got %v", got)
}
}
func TestTenantControlPlanesCountGaugeByStatus(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.SetTenantControlPlanesReadyCounts(map[string]int{
ReadyLabelTrue: 2,
ReadyLabelFalse: 1,
})
family := mustMetricFamily(t, "kamaji_tenant_control_planes_current")
if len(family.GetMetric()) != len(readyLabelValues) {
t.Fatalf("expected %d ready-only gauge metrics, got %d", len(readyLabelValues), len(family.GetMetric()))
}
if got := gaugeValueByLabels(t, family, map[string]string{"ready": ReadyLabelTrue}); got != 2 {
t.Fatalf("expected ready control planes gauge to be 2, got %v", got)
}
if got := gaugeValueByLabels(t, family, map[string]string{"ready": ReadyLabelFalse}); got != 1 {
t.Fatalf("expected not-ready control planes gauge to be 1, got %v", got)
}
}
func TestDatastoresCountGaugeByStatus(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetDatastoresReadyAndDriverCounts()
recorder.SetDatastoresReadyAndDriverCounts("default", map[string]map[string]int{
ReadyLabelTrue: {
string(kamajiv1alpha1.EtcdDriver): 1,
},
ReadyLabelFalse: {
string(kamajiv1alpha1.EtcdDriver): 0,
},
})
recorder.SetDatastoresReadyAndDriverCounts("secondary", map[string]map[string]int{
ReadyLabelTrue: {
string(kamajiv1alpha1.KineMySQLDriver): 1,
},
ReadyLabelFalse: {
string(kamajiv1alpha1.KineMySQLDriver): 0,
},
})
family := mustMetricFamily(t, "kamaji_datastores_current")
if len(family.GetMetric()) != 4 {
t.Fatalf("expected 4 name-ready-driver gauge metrics, got %d", len(family.GetMetric()))
}
assertMetricFamilyHasLabels(t, family, "datastore_name", "ready", "driver")
assertMetricFamilyDoesNotHaveLabels(t, family, "namespace")
if got := gaugeValueByLabels(t, family, map[string]string{"datastore_name": "default", "ready": ReadyLabelTrue, "driver": string(kamajiv1alpha1.EtcdDriver)}); got != 1 {
t.Fatalf("expected ready datastores gauge to be 1, got %v", got)
}
if got := gaugeValueByLabels(t, family, map[string]string{"datastore_name": "default", "ready": ReadyLabelFalse, "driver": string(kamajiv1alpha1.EtcdDriver)}); got != 0 {
t.Fatalf("expected not_ready/etcd datastores gauge to be 0, got %v", got)
}
if got := gaugeValueByLabels(t, family, map[string]string{"datastore_name": "secondary", "ready": ReadyLabelTrue, "driver": string(kamajiv1alpha1.KineMySQLDriver)}); got != 1 {
t.Fatalf("expected secondary/mysql ready datastores gauge to be 1, got %v", got)
}
if got := gaugeValueByLabels(t, family, map[string]string{"datastore_name": "secondary", "ready": ReadyLabelFalse, "driver": string(kamajiv1alpha1.KineMySQLDriver)}); got != 0 {
t.Fatalf("expected secondary/mysql not-ready datastores gauge to be 0, got %v", got)
}
}
func TestDataStoreInfoMetric(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetDataStoreInfo()
recorder.SetDataStoreInfo("default", string(kamajiv1alpha1.EtcdDriver))
family := mustMetricFamily(t, "kamaji_datastore_info")
if len(family.GetMetric()) != 1 {
t.Fatalf("expected one datastore_info sample, got %d", len(family.GetMetric()))
}
labels := map[string]string{
"datastore_name": "default",
"driver": string(kamajiv1alpha1.EtcdDriver),
}
assertMetricFamilyHasLabels(t, family, "datastore_name", "driver")
assertMetricFamilyDoesNotHaveLabels(t, family, "tls", "basic_auth")
if got := gaugeValueByLabels(t, family, labels); got != 1 {
t.Fatalf("expected datastore_info value to be 1, got %v", got)
}
}
func TestDataStoreStatusMetric(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetDataStoreStatus()
recorder.SetDataStoreStatus("default", DataStoreStatusTrue, ReadyLabelTrue)
family := mustMetricFamily(t, "kamaji_datastore_status")
if len(family.GetMetric()) != 1 {
t.Fatalf("expected one datastore_status sample, got %d", len(family.GetMetric()))
}
labels := map[string]string{
"datastore_name": "default",
"status": DataStoreStatusTrue,
"ready": ReadyLabelTrue,
}
if got := gaugeValueByLabels(t, family, labels); got != 1 {
t.Fatalf("expected datastore_status value to be 1, got %v", got)
}
}
func TestBuildInfoMetricHasSingleSample(t *testing.T) {
t.Helper()
family := mustMetricFamily(t, "kamaji_build_info")
if len(family.GetMetric()) != 1 {
t.Fatalf("expected a single build info metric, got %d", len(family.GetMetric()))
}
if got := family.GetMetric()[0].GetGauge().GetValue(); got != 1 {
t.Fatalf("expected build info gauge to be 1, got %v", got)
}
}
func TestTenantControlPlaneInfoMetric(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetTenantControlPlaneInfo()
recorder.SetTenantControlPlaneInfo("default", "test", kamajiv1alpha1.DefaultKubernetesVersion, string(kamajiv1alpha1.EtcdDriver), "https://203.0.113.10:6443", TenantControlPlaneExposureService)
family := mustMetricFamily(t, "kamaji_tenant_control_plane_info")
if len(family.GetMetric()) != 1 {
t.Fatalf("expected one tenant_control_plane_info sample, got %d", len(family.GetMetric()))
}
labels := map[string]string{
"tcp_namespace": "default",
"tcp_name": "test",
"kubernetes_version": kamajiv1alpha1.DefaultKubernetesVersion,
"datastore_driver": string(kamajiv1alpha1.EtcdDriver),
"address": "https://203.0.113.10:6443",
"exposure_strategy": TenantControlPlaneExposureService,
}
assertMetricFamilyHasLabels(t, family, "tcp_namespace", "tcp_name", "kubernetes_version", "datastore_driver", "address", "exposure_strategy")
assertMetricFamilyDoesNotHaveLabels(t, family, "namespace", "name")
if got := gaugeValueByLabels(t, family, labels); got != 1 {
t.Fatalf("expected tenant_control_plane_info value to be 1, got %v", got)
}
}
func TestTenantControlPlaneStatusMetric(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetTenantControlPlaneStatus()
recorder.SetTenantControlPlaneStatus("default", "test", string(kamajiv1alpha1.VersionReady), ReadyLabelTrue)
family := mustMetricFamily(t, "kamaji_tenant_control_plane_status")
if len(family.GetMetric()) != 1 {
t.Fatalf("expected one tenant_control_plane_status sample, got %d", len(family.GetMetric()))
}
labels := map[string]string{
"tcp_namespace": "default",
"tcp_name": "test",
"status": string(kamajiv1alpha1.VersionReady),
"ready": ReadyLabelTrue,
}
assertMetricFamilyHasLabels(t, family, "tcp_namespace", "tcp_name", "status", "ready")
assertMetricFamilyDoesNotHaveLabels(t, family, "namespace", "name")
if got := gaugeValueByLabels(t, family, labels); got != 1 {
t.Fatalf("expected tenant_control_plane_status value to be 1, got %v", got)
}
}
func TestCertificatesCountGaugeByStatusAndStrategy(t *testing.T) {
t.Helper()
recorder := testRecorder()
recorder.ResetCertificatesStatusCounts()
recorder.SetCertificatesStatusCounts("default", "k8s-133", map[string]map[string]int{
CertificateStatusValid: {
CertificateStrategyX509: 4,
CertificateStrategyKubeconfig: 1,
},
CertificateStatusExpiring: {
CertificateStrategyX509: 2,
CertificateStrategyKubeconfig: 0,
},
CertificateStatusInvalid: {
CertificateStrategyX509: 1,
CertificateStrategyKubeconfig: 3,
},
})
family := mustMetricFamily(t, "kamaji_certificates_current")
if len(family.GetMetric()) != len(certificateStatuses)*len(certificateStrategies) {
t.Fatalf("expected %d tcp_namespace-tcp_name-status-strategy gauge metrics, got %d", len(certificateStatuses)*len(certificateStrategies), len(family.GetMetric()))
}
assertMetricFamilyHasLabels(t, family, "tcp_namespace", "tcp_name", "status", "strategy")
assertMetricFamilyDoesNotHaveLabels(t, family, "namespace", "name")
if got := gaugeValueByLabels(t, family, map[string]string{"tcp_namespace": "default", "tcp_name": "k8s-133", "status": CertificateStatusValid, "strategy": CertificateStrategyX509}); got != 4 {
t.Fatalf("expected valid/x509 certificates gauge to be 4, got %v", got)
}
if got := gaugeValueByLabels(t, family, map[string]string{"tcp_namespace": "default", "tcp_name": "k8s-133", "status": CertificateStatusInvalid, "strategy": CertificateStrategyKubeconfig}); got != 3 {
t.Fatalf("expected invalid/kubeconfig certificates gauge to be 3, got %v", got)
}
}
func assertMetricFamilyHasLabels(t *testing.T, family *io_prometheus_client.MetricFamily, labels ...string) {
t.Helper()
if len(family.GetMetric()) == 0 {
t.Fatalf("metric family %s has no samples", family.GetName())
}
present := map[string]struct{}{}
for _, label := range family.GetMetric()[0].GetLabel() {
present[label.GetName()] = struct{}{}
}
for _, expected := range labels {
if _, ok := present[expected]; !ok {
t.Fatalf("metric family %s missing label %s", family.GetName(), expected)
}
}
}
func assertMetricFamilyDoesNotHaveLabels(t *testing.T, family *io_prometheus_client.MetricFamily, labels ...string) {
t.Helper()
if len(family.GetMetric()) == 0 {
t.Fatalf("metric family %s has no samples", family.GetName())
}
present := map[string]struct{}{}
for _, label := range family.GetMetric()[0].GetLabel() {
present[label.GetName()] = struct{}{}
}
for _, forbidden := range labels {
if _, ok := present[forbidden]; ok {
t.Fatalf("metric family %s unexpectedly contains label %s", family.GetName(), forbidden)
}
}
}
func mustMetricFamily(t *testing.T, name string) *io_prometheus_client.MetricFamily {
t.Helper()
return mustMetricFamilyFromGatherer(t, metrics.Registry, name)
}
func mustMetricFamilyFromGatherer(t *testing.T, gatherer prometheus.Gatherer, name string) *io_prometheus_client.MetricFamily {
t.Helper()
families, err := gatherer.Gather()
if err != nil {
t.Fatalf("failed to gather metrics: %v", err)
}
for _, family := range families {
if family.GetName() == name {
return family
}
}
t.Fatalf("metric family %s not found", name)
return nil
}
func gaugeValueByLabels(t *testing.T, family *io_prometheus_client.MetricFamily, labels map[string]string) float64 {
t.Helper()
for _, metric := range family.GetMetric() {
allMatch := true
for name, value := range labels {
matched := false
for _, label := range metric.GetLabel() {
if label.GetName() == name && label.GetValue() == value {
matched = true
break
}
}
if !matched {
allMatch = false
break
}
}
if allMatch {
return metric.GetGauge().GetValue()
}
}
t.Fatalf("metric with labels %v not found", labels)
return 0
}