diff --git a/test/e2e/framework/metrics/kube_proxy_metrics.go b/test/e2e/framework/metrics/kube_proxy_metrics.go new file mode 100644 index 00000000000..66cdada9d62 --- /dev/null +++ b/test/e2e/framework/metrics/kube_proxy_metrics.go @@ -0,0 +1,42 @@ +/* +Copyright 2024 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 metrics + +import ( + "k8s.io/component-base/metrics/testutil" +) + +// KubeProxyMetrics is metrics for kube-proxy +type KubeProxyMetrics testutil.Metrics + +// GetCounterMetricValue returns value for metric type counter. +func (m *KubeProxyMetrics) GetCounterMetricValue(metricName string) float64 { + return float64(testutil.Metrics(*m)[metricName][0].Value) +} + +func newKubeProxyMetricsMetrics() KubeProxyMetrics { + result := testutil.NewMetrics() + return KubeProxyMetrics(result) +} + +func parseKubeProxyMetrics(data string) (KubeProxyMetrics, error) { + result := newKubeProxyMetricsMetrics() + if err := testutil.ParseMetrics(data, (*testutil.Metrics)(&result)); err != nil { + return KubeProxyMetrics{}, err + } + return result, nil +} diff --git a/test/e2e/framework/metrics/metrics_grabber.go b/test/e2e/framework/metrics/metrics_grabber.go index 76864526887..9919cccd9df 100644 --- a/test/e2e/framework/metrics/metrics_grabber.go +++ b/test/e2e/framework/metrics/metrics_grabber.go @@ -22,6 +22,7 @@ import ( "fmt" "net" "regexp" + "strconv" "sync" "time" @@ -32,8 +33,9 @@ import ( clientset "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" "k8s.io/klog/v2" - + "k8s.io/kubernetes/test/e2e/framework" e2epod "k8s.io/kubernetes/test/e2e/framework/pod" + e2epodoutput "k8s.io/kubernetes/test/e2e/framework/pod/output" ) const ( @@ -43,6 +45,8 @@ const ( kubeControllerManagerPort = 10257 // snapshotControllerPort is the port for the snapshot controller snapshotControllerPort = 9102 + // kubeProxyPort is the default port for the kube-proxy status server. + kubeProxyPort = 10249 ) // MetricsGrabbingDisabledError is an error that is wrapped by the @@ -233,6 +237,45 @@ func (g *Grabber) getMetricsFromNode(ctx context.Context, nodeName string, kubel } } +// GrabFromKubeProxy returns metrics from kube-proxy +func (g *Grabber) GrabFromKubeProxy(ctx context.Context, nodeName string) (KubeProxyMetrics, error) { + nodes, err := g.client.CoreV1().Nodes().List(ctx, metav1.ListOptions{FieldSelector: fields.Set{"metadata.name": nodeName}.AsSelector().String()}) + if err != nil { + return KubeProxyMetrics{}, err + } + + if len(nodes.Items) != 1 { + return KubeProxyMetrics{}, fmt.Errorf("error listing nodes with name %v, got %v", nodeName, nodes.Items) + } + output, err := g.grabFromKubeProxy(ctx, nodeName) + if err != nil { + return KubeProxyMetrics{}, err + } + return parseKubeProxyMetrics(output) +} + +func (g *Grabber) grabFromKubeProxy(ctx context.Context, nodeName string) (string, error) { + hostCmdPodName := fmt.Sprintf("grab-kube-proxy-metrics-%s", framework.RandomSuffix()) + hostCmdPod := e2epod.NewExecPodSpec(metav1.NamespaceSystem, hostCmdPodName, true) + nodeSelection := e2epod.NodeSelection{Name: nodeName} + e2epod.SetNodeSelection(&hostCmdPod.Spec, nodeSelection) + if _, err := g.client.CoreV1().Pods(metav1.NamespaceSystem).Create(ctx, hostCmdPod, metav1.CreateOptions{}); err != nil { + return "", fmt.Errorf("failed to create pod to fetch metrics: %w", err) + } + if err := e2epod.WaitTimeoutForPodReadyInNamespace(ctx, g.client, hostCmdPodName, metav1.NamespaceSystem, 5*time.Minute); err != nil { + return "", fmt.Errorf("error waiting for pod to be up: %w", err) + } + + host := "127.0.0.1" + if framework.TestContext.ClusterIsIPv6() { + host = "::1" + } + + stdout, err := e2epodoutput.RunHostCmd(metav1.NamespaceSystem, hostCmdPodName, fmt.Sprintf("curl --silent %s/metrics", net.JoinHostPort(host, strconv.Itoa(kubeProxyPort)))) + _ = g.client.CoreV1().Pods(metav1.NamespaceSystem).Delete(ctx, hostCmdPodName, metav1.DeleteOptions{}) + return stdout, err +} + // GrabFromScheduler returns metrics from scheduler func (g *Grabber) GrabFromScheduler(ctx context.Context) (SchedulerMetrics, error) { if !g.grabFromScheduler {