Skip to content

Commit fbbfd22

Browse files
CopilotdevantlerCopilot
authored
feat: implement DeploymentGenerator with kubectl create deployment commands (#363)
* Initial plan * feat: implement DeploymentGenerator with kubectl create deployment commands Co-authored-by: devantler <26203420+devantler@users.noreply.github.com> * Update src/DevantlerTech.KubernetesGenerator.Native/Models/DeploymentSpec.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nikolai Emil Damm <ned@devantler.tech> * Update src/DevantlerTech.KubernetesGenerator.Native/Models/DeploymentSpec.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nikolai Emil Damm <ned@devantler.tech> * test: add null images check in GenerateAsync tests Signed-off-by: Nikolai Emil Damm <nikolaiemildamm@icloud.com> * Update src/DevantlerTech.KubernetesGenerator.Native/DeploymentGenerator.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Signed-off-by: Nikolai Emil Damm <ned@devantler.tech> * test: remove null images exception test from GenerateAsyncTests Signed-off-by: Nikolai Emil Damm <nikolaiemildamm@icloud.com> --------- Signed-off-by: Nikolai Emil Damm <ned@devantler.tech> Signed-off-by: Nikolai Emil Damm <nikolaiemildamm@icloud.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: devantler <26203420+devantler@users.noreply.github.com> Co-authored-by: Nikolai Emil Damm <nikolaiemildamm@icloud.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 63a74ff commit fbbfd22

File tree

7 files changed

+396
-52
lines changed

7 files changed

+396
-52
lines changed
Lines changed: 92 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,100 @@
1+
using System.Collections.ObjectModel;
12
using DevantlerTech.KubernetesGenerator.Core;
2-
using k8s.Models;
3+
using DevantlerTech.KubernetesGenerator.Native.Models;
34

45
namespace DevantlerTech.KubernetesGenerator.Native;
56

67
/// <summary>
7-
/// A generator for Kubernetes Deployment objects.
8+
/// A generator for Kubernetes Deployment objects using 'kubectl create deployment' commands.
89
/// </summary>
9-
public class DeploymentGenerator : BaseKubernetesGenerator<V1Deployment>
10+
public class DeploymentGenerator : BaseNativeGenerator<Deployment>
1011
{
12+
static readonly string[] _defaultArgs = ["create", "deployment"];
13+
14+
/// <summary>
15+
/// Generates a deployment using kubectl create deployment command.
16+
/// </summary>
17+
/// <param name="model">The deployment object.</param>
18+
/// <param name="outputPath">The output path for the generated YAML.</param>
19+
/// <param name="overwrite">Whether to overwrite existing files.</param>
20+
/// <param name="cancellationToken">The cancellation token.</param>
21+
/// <exception cref="ArgumentNullException">Thrown when model is null.</exception>
22+
/// <exception cref="KubernetesGeneratorException">Thrown when deployment name is not provided or no images are specified.</exception>
23+
public override async Task GenerateAsync(Deployment model, string outputPath, bool overwrite = false, CancellationToken cancellationToken = default)
24+
{
25+
ArgumentNullException.ThrowIfNull(model);
26+
27+
if (string.IsNullOrWhiteSpace(model.Metadata.Name))
28+
{
29+
throw new KubernetesGeneratorException("A non-empty Deployment name must be provided.");
30+
}
31+
32+
if (model.Spec.Images.Count == 0)
33+
{
34+
throw new KubernetesGeneratorException("At least one container image must be provided.");
35+
}
36+
37+
if (model.Spec.Images.Count > 1 && model.Spec.Command != null && model.Spec.Command.Count > 0)
38+
{
39+
throw new KubernetesGeneratorException("Cannot specify multiple images with a command. kubectl create deployment does not support this combination.");
40+
}
41+
42+
var args = new ReadOnlyCollection<string>(
43+
[.. _defaultArgs, .. AddArguments(model)]
44+
);
45+
string errorMessage = $"Failed to create deployment '{model.Metadata.Name}' using kubectl";
46+
await RunKubectlAsync(outputPath, overwrite, args, errorMessage, cancellationToken).ConfigureAwait(false);
47+
}
48+
49+
/// <summary>
50+
/// Builds the kubectl arguments for creating a deployment from a Deployment object.
51+
/// </summary>
52+
/// <param name="model">The Deployment object.</param>
53+
/// <returns>The kubectl arguments.</returns>
54+
/// <exception cref="KubernetesGeneratorException">Thrown when required parameters are missing.</exception>
55+
static ReadOnlyCollection<string> AddArguments(Deployment model)
56+
{
57+
var args = new List<string>
58+
{
59+
// Require that a deployment name is provided
60+
model.Metadata.Name
61+
};
62+
63+
// Add namespace if specified
64+
if (!string.IsNullOrWhiteSpace(model.Metadata.Namespace))
65+
{
66+
args.Add("--namespace");
67+
args.Add(model.Metadata.Namespace);
68+
}
69+
70+
// Add container images
71+
foreach (string image in model.Spec.Images)
72+
{
73+
args.Add("--image");
74+
args.Add(image);
75+
}
76+
77+
// Add replicas if specified
78+
if (model.Spec.Replicas.HasValue)
79+
{
80+
args.Add("--replicas");
81+
args.Add(model.Spec.Replicas.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
82+
}
83+
84+
// Add port if specified
85+
if (model.Spec.Port.HasValue)
86+
{
87+
args.Add("--port");
88+
args.Add(model.Spec.Port.Value.ToString(System.Globalization.CultureInfo.InvariantCulture));
89+
}
90+
91+
// Add command if specified
92+
if (model.Spec.Command != null && model.Spec.Command.Count > 0)
93+
{
94+
args.Add("--");
95+
args.AddRange(model.Spec.Command);
96+
}
97+
98+
return args.AsReadOnly();
99+
}
11100
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Diagnostics.CodeAnalysis;
2+
3+
namespace DevantlerTech.KubernetesGenerator.Native.Models;
4+
5+
/// <summary>
6+
/// Represents a Kubernetes Deployment for use with kubectl create deployment.
7+
/// </summary>
8+
[SuppressMessage("Naming", "CA1724:Type names should not match namespaces", Justification = "This is a specific Kubernetes resource model")]
9+
public class Deployment
10+
{
11+
/// <summary>
12+
/// Gets or sets the API version of this Deployment.
13+
/// </summary>
14+
public string ApiVersion { get; set; } = "apps/v1";
15+
16+
/// <summary>
17+
/// Gets or sets the kind of this resource.
18+
/// </summary>
19+
public string Kind { get; set; } = "Deployment";
20+
21+
/// <summary>
22+
/// Gets or sets the metadata for the deployment.
23+
/// </summary>
24+
public required Metadata Metadata { get; set; }
25+
26+
/// <summary>
27+
/// Gets or sets the specification for the deployment.
28+
/// </summary>
29+
public required DeploymentSpec Spec { get; init; }
30+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
namespace DevantlerTech.KubernetesGenerator.Native.Models;
2+
3+
/// <summary>
4+
/// Represents the specification for a Deployment.
5+
/// </summary>
6+
public class DeploymentSpec
7+
{
8+
/// <summary>
9+
/// Gets or sets the number of desired replicas.
10+
/// </summary>
11+
public int? Replicas { get; set; }
12+
13+
/// <summary>
14+
/// Gets or sets the list of container images to run.
15+
/// </summary>
16+
public required IReadOnlyList<string> Images { get; init; }
17+
18+
/// <summary>
19+
/// Gets or sets the port that this deployment exposes.
20+
/// </summary>
21+
public int? Port { get; set; }
22+
23+
/// <summary>
24+
/// Gets or sets the command to run in the container.
25+
/// </summary>
26+
public IReadOnlyList<string>? Command { get; init; }
27+
}

0 commit comments

Comments
 (0)