Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions src/KubernetesClient.Kubectl/Beta/AsyncKubectl.Get.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
namespace k8s.kubectl.beta;

public partial class AsyncKubectl
{
/// <summary>
/// Get a Kubernetes resource by name.
/// </summary>
/// <typeparam name="T">The type of Kubernetes resource to get.</typeparam>
/// <param name="name">The name of the resource.</param>
/// <param name="namespace">The namespace of the resource (for namespaced resources). Optional.</param>
/// <param name="cancellationToken">Cancellation token.</param>
/// <returns>The requested resource.</returns>
public async Task<T> GetAsync<T>(string name, string? @namespace = null, CancellationToken cancellationToken = default)
where T : IKubernetesObject
{
var metadata = typeof(T).GetKubernetesTypeMetadata();
var genericClient = new GenericClient(client, metadata.Group, metadata.ApiVersion, metadata.PluralName, disposeClient: false);

if (@namespace != null)
{
return await genericClient.ReadNamespacedAsync<T>(@namespace, name, cancellationToken).ConfigureAwait(false);
}
else
{
return await genericClient.ReadAsync<T>(name, cancellationToken).ConfigureAwait(false);
}
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

The GenericClient instance is not disposed after use. While disposeClient: false prevents disposal of the underlying Kubernetes client, the GenericClient itself should be disposed to follow proper IDisposable patterns. Consider wrapping it in a using statement or calling Dispose() after the Read operation.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The GenericClient disposal issue has been addressed. Added using var statement on line 17 of AsyncKubectl.Get.cs to properly dispose the GenericClient instance.

Additionally applied other code review suggestions:

  • Simplified if-else to ternary operator (lines 19-21)
  • Changed generic catch to specific HttpOperationException in tests

Commit: f0dbebb

Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Both branches of this 'if' statement return - consider using '?' to express intent better.

Suggested change
if (@namespace != null)
{
return await genericClient.ReadNamespacedAsync<T>(@namespace, name, cancellationToken).ConfigureAwait(false);
}
else
{
return await genericClient.ReadAsync<T>(name, cancellationToken).ConfigureAwait(false);
}
return @namespace != null
? await genericClient.ReadNamespacedAsync<T>(@namespace, name, cancellationToken).ConfigureAwait(false)
: await genericClient.ReadAsync<T>(name, cancellationToken).ConfigureAwait(false);

Copilot uses AI. Check for mistakes.
}
}
17 changes: 17 additions & 0 deletions src/KubernetesClient.Kubectl/Beta/Kubectl.Get.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace k8s.kubectl.beta;

public partial class Kubectl
{
/// <summary>
/// Get a Kubernetes resource by name.
/// </summary>
/// <typeparam name="T">The type of Kubernetes resource to get.</typeparam>
/// <param name="name">The name of the resource.</param>
/// <param name="namespace">The namespace of the resource (for namespaced resources). Optional.</param>
/// <returns>The requested resource.</returns>
public T Get<T>(string name, string? @namespace = null)
where T : IKubernetesObject
{
return client.GetAsync<T>(name, @namespace).GetAwaiter().GetResult();
}
}
216 changes: 216 additions & 0 deletions tests/Kubectl.Tests/KubectlTests.Get.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
using k8s.E2E;
using k8s.kubectl.beta;
using k8s.Models;
using Xunit;

namespace k8s.kubectl.Tests;

public partial class KubectlTests
{
[MinikubeFact]
public void GetNamespace()
{
using var kubernetes = MinikubeTests.CreateClient();
var client = new Kubectl(kubernetes);

// Get the default namespace (cluster-scoped resource, no namespace parameter)
var ns = client.Get<V1Namespace>("default");

Assert.NotNull(ns);
Assert.Equal("default", ns.Metadata.Name);
Assert.Equal("v1", ns.ApiVersion);
Assert.Equal("Namespace", ns.Kind);
}

[MinikubeFact]
public void GetPod()
{
using var kubernetes = MinikubeTests.CreateClient();
var client = new Kubectl(kubernetes);
var namespaceParameter = "default";
var podName = "k8scsharp-e2e-get-pod";

// Create a test pod
var pod = new V1Pod
{
Metadata = new V1ObjectMeta
{
Name = podName,
NamespaceProperty = namespaceParameter,
},
Spec = new V1PodSpec
{
Containers = new[]
{
new V1Container
{
Name = "test",
Image = "nginx:latest",
},
},
},
};

try
{
kubernetes.CoreV1.CreateNamespacedPod(pod, namespaceParameter);

// Get the pod using kubectl generic get
var retrievedPod = client.Get<V1Pod>(podName, namespaceParameter);

Assert.NotNull(retrievedPod);
Assert.Equal(podName, retrievedPod.Metadata.Name);
Assert.Equal(namespaceParameter, retrievedPod.Metadata.NamespaceProperty);
Assert.Equal("Pod", retrievedPod.Kind);
Assert.Equal("v1", retrievedPod.ApiVersion);
}
finally
{
// Cleanup
try
{
kubernetes.CoreV1.DeleteNamespacedPod(podName, namespaceParameter);
}
catch
{
// Ignore cleanup errors
}
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed generic catch to specific catch (HttpOperationException) on line 75 of KubectlTests.Get.cs with descriptive comment explaining it ignores cleanup errors if the resource was already deleted.

Commit: f0dbebb

}
}

[MinikubeFact]
public void GetService()
{
using var kubernetes = MinikubeTests.CreateClient();
var client = new Kubectl(kubernetes);
var namespaceParameter = "default";
var serviceName = "k8scsharp-e2e-get-service";

// Create a test service
var service = new V1Service
{
Metadata = new V1ObjectMeta
{
Name = serviceName,
NamespaceProperty = namespaceParameter,
},
Spec = new V1ServiceSpec
{
Ports = new[]
{
new V1ServicePort
{
Port = 80,
TargetPort = 80,
},
},
Selector = new Dictionary<string, string>
{
{ "app", "test" },
},
},
};

try
{
kubernetes.CoreV1.CreateNamespacedService(service, namespaceParameter);

// Get the service using kubectl generic get
var retrievedService = client.Get<V1Service>(serviceName, namespaceParameter);

Assert.NotNull(retrievedService);
Assert.Equal(serviceName, retrievedService.Metadata.Name);
Assert.Equal(namespaceParameter, retrievedService.Metadata.NamespaceProperty);
Assert.Equal("Service", retrievedService.Kind);
Assert.Equal("v1", retrievedService.ApiVersion);
}
finally
{
// Cleanup
try
{
kubernetes.CoreV1.DeleteNamespacedService(serviceName, namespaceParameter);
}
catch
{
// Ignore cleanup errors
}
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Choose a reason for hiding this comment

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

@copilot apply changes based on this feedback

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed generic catch to specific catch (HttpOperationException) on line 135 of KubectlTests.Get.cs with descriptive comment explaining it ignores cleanup errors if the resource was already deleted.

Commit: f0dbebb

}
}

[MinikubeFact]
public void GetDeployment()
{
using var kubernetes = MinikubeTests.CreateClient();
var client = new Kubectl(kubernetes);
var namespaceParameter = "default";
var deploymentName = "k8scsharp-e2e-get-deployment";

// Create a test deployment
var deployment = new V1Deployment
{
Metadata = new V1ObjectMeta
{
Name = deploymentName,
NamespaceProperty = namespaceParameter,
},
Spec = new V1DeploymentSpec
{
Replicas = 1,
Selector = new V1LabelSelector
{
MatchLabels = new Dictionary<string, string>
{
{ "app", "test" },
},
},
Template = new V1PodTemplateSpec
{
Metadata = new V1ObjectMeta
{
Labels = new Dictionary<string, string>
{
{ "app", "test" },
},
},
Spec = new V1PodSpec
{
Containers = new[]
{
new V1Container
{
Name = "test",
Image = "nginx:latest",
},
},
},
},
},
};

try
{
kubernetes.AppsV1.CreateNamespacedDeployment(deployment, namespaceParameter);

// Get the deployment using kubectl generic get
var retrievedDeployment = client.Get<V1Deployment>(deploymentName, namespaceParameter);

Assert.NotNull(retrievedDeployment);
Assert.Equal(deploymentName, retrievedDeployment.Metadata.Name);
Assert.Equal(namespaceParameter, retrievedDeployment.Metadata.NamespaceProperty);
Assert.Equal("Deployment", retrievedDeployment.Kind);
}
finally
{
// Cleanup
try
{
kubernetes.AppsV1.DeleteNamespacedDeployment(deploymentName, namespaceParameter);
}
catch
{
// Ignore cleanup errors
Copy link

Copilot AI Nov 18, 2025

Choose a reason for hiding this comment

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

Generic catch clause.

Suggested change
catch
{
// Ignore cleanup errors
catch (Exception ex)
{
// Ignore cleanup errors, but log for visibility
Console.WriteLine($"Cleanup error in DeleteNamespacedDeployment: {ex}");

Copilot uses AI. Check for mistakes.
}
}
}
}
Loading