Implement KubectlTop for node and pod metrics#1703
Conversation
Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com>
Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com>
|
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: brendandburns, Copilot The full list of commands accepted by this bot can be found here. The pull request process is described here DetailsNeeds approval from an approver in each of these files:
Approvers can indicate their approval by writing |
There was a problem hiding this comment.
Pull request overview
This PR implements kubectl top functionality for retrieving and sorting node and pod metrics by CPU or memory usage, similar to the Java Kubernetes client implementation. The implementation follows the established kubectl pattern of separating async and sync implementations across partial classes.
Key Changes
- Adds
TopNodesAsyncandTopPodsAsyncmethods to retrieve metrics paired with resources and sort by capacity percentage (nodes) or container sum (pods) - Introduces
ResourceMetrics<TResource, TMetrics>generic class to pair Kubernetes resources with their metrics - Provides comprehensive test coverage with sorting validation and error case handling
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 8 comments.
Show a summary per file
| File | Description |
|---|---|
| src/KubernetesClient.Kubectl/Beta/AsyncKubectl.Top.cs | Implements async methods for retrieving and sorting node/pod metrics with ResourceMetrics class and calculation helpers |
| src/KubernetesClient.Kubectl/Beta/Kubectl.Top.cs | Provides synchronous wrappers for TopNodes and TopPods using GetAwaiter().GetResult() pattern |
| tests/Kubectl.Tests/KubectlTests.Top.cs | Comprehensive test coverage including sorting validation, error cases, and independent calculation verification |
| examples/metrics/metrics.csproj | Adds Kubectl project reference to enable KubectlTop usage in examples |
| examples/metrics/Program.cs | Demonstrates KubectlTop API usage alongside raw metrics API for comparison |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| } | ||
|
|
||
| async Task TopPods(Kubectl kubectl) |
There was a problem hiding this comment.
The TopPods method is synchronous but is being called from an async context. While this works, since TopPods is a synchronous wrapper around TopPodsAsync, it's inconsistent with the pattern used in the existing NodesMetrics and PodsMetrics functions which properly await async methods. Consider either using TopPodsAsync directly with await, or removing the async keyword from the TopPods function signature since it doesn't await anything.
| var nodes = await client.CoreV1.ListNodeAsync(cancellationToken: cancellationToken).ConfigureAwait(false); | ||
|
|
||
| // Get node metrics | ||
| var nodeMetrics = await client.GetKubernetesNodesMetricsAsync().ConfigureAwait(false); |
There was a problem hiding this comment.
The GetKubernetesNodesMetricsAsync extension method doesn't accept a cancellationToken parameter, but the TopNodesAsync method receives one. The cancellationToken is only passed to ListNodeAsync but not to GetKubernetesNodesMetricsAsync, which means the metrics API call cannot be cancelled even when the caller requests cancellation. This creates an inconsistent cancellation behavior where only part of the operation can be cancelled.
| var nodeMetrics = await client.GetKubernetesNodesMetricsAsync().ConfigureAwait(false); | |
| var nodeMetrics = await client.GetKubernetesNodesMetricsAsync(cancellationToken).ConfigureAwait(false); |
| var pods = await client.CoreV1.ListNamespacedPodAsync(@namespace, cancellationToken: cancellationToken).ConfigureAwait(false); | ||
|
|
||
| // Get pod metrics for the namespace | ||
| var podMetrics = await client.GetKubernetesPodsMetricsByNamespaceAsync(@namespace).ConfigureAwait(false); |
There was a problem hiding this comment.
The GetKubernetesPodsMetricsByNamespaceAsync extension method doesn't accept a cancellationToken parameter, but the TopPodsAsync method receives one. The cancellationToken is only passed to ListNamespacedPodAsync but not to GetKubernetesPodsMetricsByNamespaceAsync, which means the metrics API call cannot be cancelled even when the caller requests cancellation. This creates an inconsistent cancellation behavior where only part of the operation can be cancelled.
| var podMetrics = await client.GetKubernetesPodsMetricsByNamespaceAsync(@namespace).ConfigureAwait(false); | |
| var podMetrics = await client.GetKubernetesPodsMetricsByNamespaceAsync(@namespace, cancellationToken).ConfigureAwait(false); |
| #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. | ||
| Assert.Throws<ArgumentException>(() => client.TopPods(null, "cpu")); | ||
| #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. |
There was a problem hiding this comment.
The pragma warning disable/restore is only needed around line 186. Line 184 doesn't require suppressing the nullable warning since string.Empty is not null. Consider moving the pragma directives to only wrap line 186, or use a nullable suppression operator (null!) on line 186 instead of pragma directives for better code clarity.
| #pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. | |
| Assert.Throws<ArgumentException>(() => client.TopPods(null, "cpu")); | |
| #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. | |
| Assert.Throws<ArgumentException>(() => client.TopPods(null!, "cpu")); |
| } | ||
| } | ||
|
|
||
| async Task TopNodes(Kubectl kubectl) |
There was a problem hiding this comment.
The TopNodes method is synchronous but is being called from an async context. While this works, since TopNodes is a synchronous wrapper around TopNodesAsync, it's inconsistent with the pattern used in the existing NodesMetrics and PodsMetrics functions which properly await async methods. Consider either using TopNodesAsync directly with await, or removing the async keyword from the TopNodes function signature since it doesn't await anything.
| async Task TopNodes(Kubectl kubectl) | |
| void TopNodes(Kubectl kubectl) |
| foreach (var container in podMetrics.Containers) | ||
| { | ||
| if (container?.Usage != null && container.Usage.TryGetValue(metric, out var value)) | ||
| { | ||
| sum += value.ToDouble(); | ||
| } | ||
| } |
There was a problem hiding this comment.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| foreach (var container in podMetrics.Containers) | ||
| { | ||
| if (container.Usage.TryGetValue("cpu", out var value)) | ||
| { | ||
| sum += value.ToDouble(); | ||
| } | ||
| } |
There was a problem hiding this comment.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
| foreach (var container in podMetrics.Containers) | ||
| { | ||
| if (container.Usage.TryGetValue("memory", out var value)) | ||
| { | ||
| sum += value.ToDouble(); | ||
| } | ||
| } |
There was a problem hiding this comment.
This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.
Adds
kubectl topfunctionality to retrieve and sort node/pod metrics by CPU or memory usage, similar to the Java client implementation.Changes
AsyncKubectl.Top.cs: Async methods for retrieving sorted metrics
TopNodesAsync(metric, cancellationToken)- nodes sorted by capacity percentageTopPodsAsync(namespace, metric, cancellationToken)- pods sorted by container sumResourceMetrics<TResource, TMetrics>class pairs resources with their metricsKubectl.Top.cs: Synchronous wrappers for
TopNodes()andTopPods()KubectlTests.Top.cs: Test coverage for both metrics, sorting validation, error cases
examples/metrics: Updated to demonstrate KubectlTop usage
Usage
Leverages existing
GetKubernetesNodesMetricsAsync()andGetKubernetesPodsMetricsByNamespaceAsync()extension methods.Warning
Firewall rules blocked me from connecting to one or more addresses (expand for details)
I tried to connect to the following addresses, but was blocked by firewall rules:
crl.comodoca.com/home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/.dotnet/dotnet /home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /home/REDACTED/work/csharp/csharp/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj --packages /home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/emptyFakeDotnetRoot emblyInfo.cs chTe�� ernetesYamlTests0 ializationTests.-j kAssert.cs cAuthTests.cs ernetesClientConfigurationTests.cs OrStringTests.cs ernalExecutionTests.cs(dns block)ocsp.comodoca.com/home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/.dotnet/dotnet /home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/.dotnet/dotnet restore --no-dependencies /home/REDACTED/work/csharp/csharp/src/KubernetesClient.Classic/KubernetesClient.Classic.csproj --packages /home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/packages /p:DisableImplicitNuGetFallbackFolder=true --verbosity normal /p:TargetFrameworkRootPath=/home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/emptyFakeDotnetRoot /p:NetCoreTargetingPackRoot=/home/REDACTED/work/csharp/.codeql-scratch/dbs/csharp/working/emptyFakeDotnetRoot emblyInfo.cs chTe�� ernetesYamlTests0 ializationTests.-j kAssert.cs cAuthTests.cs ernetesClientConfigurationTests.cs OrStringTests.cs ernalExecutionTests.cs(dns block)If you need me to access, download, or install something from one of these locations, you can either:
Original prompt
💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.