Skip to content

Implement KubectlTop for node and pod metrics#1703

Merged
k8s-ci-robot merged 3 commits intomasterfrom
copilot/implement-kubectl-top
Dec 12, 2025
Merged

Implement KubectlTop for node and pod metrics#1703
k8s-ci-robot merged 3 commits intomasterfrom
copilot/implement-kubectl-top

Conversation

Copy link
Contributor

Copilot AI commented Dec 12, 2025

Adds kubectl top functionality 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 percentage
    • TopPodsAsync(namespace, metric, cancellationToken) - pods sorted by container sum
    • ResourceMetrics<TResource, TMetrics> class pairs resources with their metrics
  • Kubectl.Top.cs: Synchronous wrappers for TopNodes() and TopPods()

  • KubectlTests.Top.cs: Test coverage for both metrics, sorting validation, error cases

  • examples/metrics: Updated to demonstrate KubectlTop usage

Usage

var kubectl = new Kubectl(client);

// Get top nodes by CPU usage (sorted by % of capacity)
var topNodes = kubectl.TopNodes("cpu");
foreach (var item in topNodes)
{
    var cpuUsage = item.Metrics.Usage["cpu"];
    var cpuCapacity = item.Resource.Status.Capacity["cpu"];
    Console.WriteLine($"{item.Resource.Metadata.Name}: {cpuUsage} / {cpuCapacity}");
}

// Get top pods by memory usage (sorted by sum across containers)
var topPods = kubectl.TopPods("kube-system", "memory");
foreach (var item in topPods)
{
    var memSum = item.Metrics.Containers.Sum(c => c.Usage["memory"].ToDouble());
    Console.WriteLine($"{item.Resource.Metadata.Name}: {memSum} bytes");
}

Leverages existing GetKubernetesNodesMetricsAsync() and GetKubernetesPodsMetricsByNamespaceAsync() 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
    • Triggering command: /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
    • Triggering command: /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

This section details on the original issue you should resolve

<issue_title>Implement KubectlTop</issue_title>
<issue_description>Similar to:
https://github.com/kubernetes-client/java/blob/master/extended/src/main/java/io/kubernetes/client/extended/kubectl/KubectlTop.java

And:
https://kubernetes.io/docs/reference/kubectl/generated/kubectl_top/

We should implement the ability to get top pod and node metrics along dimensions like CPU and Memory.</issue_description>

Comments on the Issue (you are @copilot in this section)


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

@k8s-ci-robot k8s-ci-robot added do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. labels Dec 12, 2025
Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com>
@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Dec 12, 2025
Co-authored-by: brendandburns <5751682+brendandburns@users.noreply.github.com>
@k8s-ci-robot k8s-ci-robot added size/XL Denotes a PR that changes 500-999 lines, ignoring generated files. and removed size/L Denotes a PR that changes 100-499 lines, ignoring generated files. labels Dec 12, 2025
Copilot AI changed the title [WIP] Implement function to get top pod and node metrics Implement KubectlTop for node and pod metrics Dec 12, 2025
Copilot AI requested a review from brendandburns December 12, 2025 17:28
@brendandburns brendandburns marked this pull request as ready for review December 12, 2025 17:35
Copilot AI review requested due to automatic review settings December 12, 2025 17:35
@k8s-ci-robot k8s-ci-robot removed the do-not-merge/work-in-progress Indicates that a PR should not merge because it is a work in progress. label Dec 12, 2025
@k8s-ci-robot k8s-ci-robot requested a review from tg123 December 12, 2025 17:35
Copy link
Contributor

@brendandburns brendandburns left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Dec 12, 2025
@k8s-ci-robot
Copy link
Contributor

[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

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Dec 12, 2025
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 TopNodesAsync and TopPodsAsync methods 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)
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
var nodes = await client.CoreV1.ListNodeAsync(cancellationToken: cancellationToken).ConfigureAwait(false);

// Get node metrics
var nodeMetrics = await client.GetKubernetesNodesMetricsAsync().ConfigureAwait(false);
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
var nodeMetrics = await client.GetKubernetesNodesMetricsAsync().ConfigureAwait(false);
var nodeMetrics = await client.GetKubernetesNodesMetricsAsync(cancellationToken).ConfigureAwait(false);

Copilot uses AI. Check for mistakes.
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);
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
var podMetrics = await client.GetKubernetesPodsMetricsByNamespaceAsync(@namespace).ConfigureAwait(false);
var podMetrics = await client.GetKubernetesPodsMetricsByNamespaceAsync(@namespace, cancellationToken).ConfigureAwait(false);

Copilot uses AI. Check for mistakes.
Comment on lines +185 to +187
#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.
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
#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"));

Copilot uses AI. Check for mistakes.
}
}

async Task TopNodes(Kubectl kubectl)
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
async Task TopNodes(Kubectl kubectl)
void TopNodes(Kubectl kubectl)

Copilot uses AI. Check for mistakes.
Comment on lines +162 to +168
foreach (var container in podMetrics.Containers)
{
if (container?.Usage != null && container.Usage.TryGetValue(metric, out var value))
{
sum += value.ToDouble();
}
}
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Comment on lines +223 to +229
foreach (var container in podMetrics.Containers)
{
if (container.Usage.TryGetValue("cpu", out var value))
{
sum += value.ToDouble();
}
}
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Comment on lines +237 to +243
foreach (var container in podMetrics.Containers)
{
if (container.Usage.TryGetValue("memory", out var value))
{
sum += value.ToDouble();
}
}
Copy link

Copilot AI Dec 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This foreach loop implicitly filters its target sequence - consider filtering the sequence explicitly using '.Where(...)'.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. lgtm "Looks good to me", indicates that a PR is ready to be merged. size/XL Denotes a PR that changes 500-999 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement KubectlTop

4 participants