Skip to content

Commit b87ac88

Browse files
Copilotdevantler
andauthored
feat: Add BaseSecret class and BaseSecretGenerator with required properties pattern (#369)
* Initial plan * feat: add BaseSecret class for common secret properties Co-authored-by: devantler <[email protected]> * feat: add BaseSecretGenerator to reduce code duplication Co-authored-by: devantler <[email protected]> * refactor: replace primary constructor with required property in BaseSecret Co-authored-by: devantler <[email protected]> * refactor: simplify BaseSecret class and improve BaseSecretGenerator structure Co-authored-by: devantler <[email protected]> * fix: remove unnecessary null-conditional operator in BaseSecretGenerator error message Co-authored-by: devantler <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: devantler <[email protected]> Co-authored-by: Nikolai Emil Damm <[email protected]>
1 parent 48053f1 commit b87ac88

File tree

74 files changed

+209
-183
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+209
-183
lines changed
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
using System.Collections.ObjectModel;
2+
using DevantlerTech.KubernetesGenerator.Core;
3+
using DevantlerTech.KubernetesGenerator.Native.Models;
4+
5+
namespace DevantlerTech.KubernetesGenerator.Native;
6+
7+
/// <summary>
8+
/// Base class for secret generators.
9+
/// </summary>
10+
/// <typeparam name="T">The secret model type that inherits from BaseSecret.</typeparam>
11+
public abstract class BaseSecretGenerator<T> : BaseNativeGenerator<T> where T : BaseSecret
12+
{
13+
/// <summary>
14+
/// Gets the command prefix for the specific secret type (e.g., ["create", "secret", "tls"]).
15+
/// </summary>
16+
protected abstract ReadOnlyCollection<string> CommandPrefix { get; }
17+
18+
/// <summary>
19+
/// Generates a secret using the appropriate kubectl create secret command.
20+
/// </summary>
21+
/// <param name="model">The secret object.</param>
22+
/// <param name="outputPath">The output path for the generated YAML.</param>
23+
/// <param name="overwrite">Whether to overwrite existing files.</param>
24+
/// <param name="cancellationToken">The cancellation token.</param>
25+
/// <exception cref="ArgumentNullException">Thrown when model is null.</exception>
26+
/// <exception cref="KubernetesGeneratorException">Thrown when required parameters are missing.</exception>
27+
public override async Task GenerateAsync(T model, string outputPath, bool overwrite = false, CancellationToken cancellationToken = default)
28+
{
29+
ArgumentNullException.ThrowIfNull(model);
30+
31+
var commonArgs = BuildCommonArguments(model);
32+
var specificArgs = await BuildSpecificArgumentsAsync(model, cancellationToken).ConfigureAwait(false);
33+
34+
var args = new ReadOnlyCollection<string>(
35+
[.. CommandPrefix, .. commonArgs, .. specificArgs]
36+
);
37+
string errorMessage = $"Failed to create {GetSecretTypeName()} secret '{model.Metadata.Name}' using kubectl";
38+
await RunKubectlAsync(outputPath, overwrite, args, errorMessage, cancellationToken).ConfigureAwait(false);
39+
}
40+
41+
/// <summary>
42+
/// Builds the common arguments that all secret types share (name and namespace).
43+
/// </summary>
44+
/// <param name="model">The secret model.</param>
45+
/// <returns>The common arguments.</returns>
46+
static ReadOnlyCollection<string> BuildCommonArguments(T model)
47+
{
48+
ArgumentNullException.ThrowIfNull(model);
49+
50+
var args = new List<string>
51+
{
52+
model.Metadata.Name
53+
};
54+
55+
// Add namespace if specified
56+
if (!string.IsNullOrEmpty(model.Metadata.Namespace))
57+
{
58+
args.Add($"--namespace={model.Metadata.Namespace}");
59+
}
60+
61+
return args.AsReadOnly();
62+
}
63+
64+
/// <summary>
65+
/// Builds the specific arguments for the secret type.
66+
/// </summary>
67+
/// <param name="model">The secret model.</param>
68+
/// <param name="cancellationToken">The cancellation token.</param>
69+
/// <returns>The specific arguments for this secret type.</returns>
70+
protected abstract Task<ReadOnlyCollection<string>> BuildSpecificArgumentsAsync(T model, CancellationToken cancellationToken = default);
71+
72+
/// <summary>
73+
/// Gets the human-readable name of the secret type for error messages.
74+
/// </summary>
75+
/// <returns>The secret type name.</returns>
76+
protected virtual string GetSecretTypeName() =>
77+
typeof(T).Name.Replace("Secret", "", StringComparison.Ordinal).ToUpperInvariant();
78+
}
Lines changed: 9 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,29 @@
11
using System.Collections.ObjectModel;
2-
using DevantlerTech.KubernetesGenerator.Core;
32
using DevantlerTech.KubernetesGenerator.Native.Models;
43

54
namespace DevantlerTech.KubernetesGenerator.Native;
65

76
/// <summary>
87
/// A generator for Docker registry Kubernetes Secret objects using 'kubectl create secret docker-registry' commands.
98
/// </summary>
10-
public class DockerRegistrySecretGenerator : BaseNativeGenerator<DockerRegistrySecret>
9+
public class DockerRegistrySecretGenerator : BaseSecretGenerator<DockerRegistrySecret>
1110
{
12-
static readonly string[] _defaultArgs = ["create", "secret", "docker-registry"];
1311
/// <summary>
14-
/// Generates a Docker registry secret using kubectl create secret docker-registry command.
12+
/// Gets the command prefix for Docker registry secrets.
1513
/// </summary>
16-
/// <param name="model">The Docker registry secret object.</param>
17-
/// <param name="outputPath">The output path for the generated YAML.</param>
18-
/// <param name="overwrite">Whether to overwrite existing files.</param>
19-
/// <param name="cancellationToken">The cancellation token.</param>
20-
/// <exception cref="ArgumentNullException">Thrown when model is null.</exception>
21-
/// <exception cref="KubernetesGeneratorException">Thrown when required parameters are missing.</exception>
22-
public override async Task GenerateAsync(DockerRegistrySecret model, string outputPath, bool overwrite = false, CancellationToken cancellationToken = default)
23-
{
24-
ArgumentNullException.ThrowIfNull(model);
25-
26-
var args = new ReadOnlyCollection<string>(
27-
[.. _defaultArgs, .. AddArguments(model)]
28-
);
29-
string errorMessage = $"Failed to create Docker registry secret '{model.Metadata?.Name}' using kubectl";
30-
await RunKubectlAsync(outputPath, overwrite, args, errorMessage, cancellationToken).ConfigureAwait(false);
31-
}
14+
protected override ReadOnlyCollection<string> CommandPrefix => new(["create", "secret", "docker-registry"]);
3215

3316
/// <summary>
34-
/// Builds the kubectl arguments for creating a Docker registry secret from a DockerRegistrySecret object.
17+
/// Builds the specific arguments for creating a Docker registry secret from a DockerRegistrySecret object.
3518
/// </summary>
3619
/// <param name="model">The DockerRegistrySecret object.</param>
20+
/// <param name="cancellationToken">The cancellation token.</param>
3721
/// <returns>The kubectl arguments.</returns>
38-
static ReadOnlyCollection<string> AddArguments(DockerRegistrySecret model)
22+
protected override Task<ReadOnlyCollection<string>> BuildSpecificArgumentsAsync(DockerRegistrySecret model, CancellationToken cancellationToken = default)
3923
{
40-
var args = new List<string>
41-
{
42-
model.Metadata.Name
43-
};
24+
ArgumentNullException.ThrowIfNull(model);
4425

45-
// Add namespace if specified
46-
if (!string.IsNullOrEmpty(model.Metadata.Namespace))
47-
{
48-
args.Add($"--namespace={model.Metadata.Namespace}");
49-
}
26+
var args = new List<string>();
5027

5128
// Add Docker registry specific arguments
5229
if (!string.IsNullOrEmpty(model.DockerServer))
@@ -58,6 +35,6 @@ static ReadOnlyCollection<string> AddArguments(DockerRegistrySecret model)
5835
args.Add($"--docker-password={model.DockerPassword}");
5936
args.Add($"--docker-email={model.DockerEmail}");
6037

61-
return args.AsReadOnly();
38+
return Task.FromResult(args.AsReadOnly());
6239
}
6340
}
Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,29 @@
11
using System.Collections.ObjectModel;
2-
using DevantlerTech.KubernetesGenerator.Core;
32
using DevantlerTech.KubernetesGenerator.Native.Models;
43

54
namespace DevantlerTech.KubernetesGenerator.Native;
65

76
/// <summary>
87
/// A generator for generic Kubernetes Secret objects using 'kubectl create secret generic' commands.
98
/// </summary>
10-
public class GenericSecretGenerator : BaseNativeGenerator<GenericSecret>
9+
public class GenericSecretGenerator : BaseSecretGenerator<GenericSecret>
1110
{
12-
static readonly string[] _defaultArgs = ["create", "secret", "generic"];
1311
/// <summary>
14-
/// Generates a generic secret using kubectl create secret generic command.
12+
/// Gets the command prefix for generic secrets.
1513
/// </summary>
16-
/// <param name="model">The secret object.</param>
17-
/// <param name="outputPath">The output path for the generated YAML.</param>
18-
/// <param name="overwrite">Whether to overwrite existing files.</param>
19-
/// <param name="cancellationToken">The cancellation token.</param>
20-
/// <exception cref="ArgumentNullException">Thrown when model is null.</exception>
21-
/// <exception cref="KubernetesGeneratorException">Thrown when secret name is not provided.</exception>
22-
public override async Task GenerateAsync(GenericSecret model, string outputPath, bool overwrite = false, CancellationToken cancellationToken = default)
23-
{
24-
ArgumentNullException.ThrowIfNull(model);
25-
26-
var args = new ReadOnlyCollection<string>(
27-
[.. _defaultArgs, .. AddArguments(model)]
28-
);
29-
string errorMessage = $"Failed to create generic secret '{model.Metadata?.Name}' using kubectl";
30-
await RunKubectlAsync(outputPath, overwrite, args, errorMessage, cancellationToken).ConfigureAwait(false);
31-
}
14+
protected override ReadOnlyCollection<string> CommandPrefix => new(["create", "secret", "generic"]);
3215

3316
/// <summary>
34-
/// Builds the kubectl arguments for creating a generic secret from a GenericSecret object.
17+
/// Builds the specific arguments for creating a generic secret from a GenericSecret object.
3518
/// </summary>
3619
/// <param name="model">The GenericSecret object.</param>
37-
static ReadOnlyCollection<string> AddArguments(GenericSecret model)
20+
/// <param name="cancellationToken">The cancellation token.</param>
21+
/// <returns>The kubectl arguments.</returns>
22+
protected override Task<ReadOnlyCollection<string>> BuildSpecificArgumentsAsync(GenericSecret model, CancellationToken cancellationToken = default)
3823
{
39-
var args = new List<string>
40-
{
41-
// The secret name is always available from the metadata (required in constructor)
42-
model.Metadata.Name
43-
};
24+
ArgumentNullException.ThrowIfNull(model);
4425

45-
// Add namespace if specified
46-
if (!string.IsNullOrEmpty(model.Metadata.Namespace))
47-
{
48-
args.Add($"--namespace={model.Metadata.Namespace}");
49-
}
26+
var args = new List<string>();
5027

5128
// Add type if specified (but don't require it)
5229
if (!string.IsNullOrEmpty(model.Type))
@@ -60,6 +37,6 @@ static ReadOnlyCollection<string> AddArguments(GenericSecret model)
6037
args.Add($"--from-literal={kvp.Key}={kvp.Value}");
6138
}
6239

63-
return args.AsReadOnly();
40+
return Task.FromResult(args.AsReadOnly());
6441
}
6542
}

src/DevantlerTech.KubernetesGenerator.Native/Models/BaseMetadata.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ public abstract class BaseMetadata
2222
/// Gets or sets the annotations for this object.
2323
/// </summary>
2424
public IDictionary<string, string>? Annotations { get; init; }
25-
}
25+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace DevantlerTech.KubernetesGenerator.Native.Models;
2+
3+
/// <summary>
4+
/// Base class for Kubernetes secret models.
5+
/// </summary>
6+
public abstract class BaseSecret
7+
{
8+
/// <summary>
9+
/// Gets or sets the metadata for the secret.
10+
/// </summary>
11+
public required Metadata Metadata { get; set; }
12+
}

src/DevantlerTech.KubernetesGenerator.Native/Models/ClusterRole.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,4 @@ public class ClusterRole
3030
/// Gets or sets the aggregation rule for combining cluster roles.
3131
/// </summary>
3232
public ClusterRoleAggregationRule? AggregationRule { get; set; }
33-
}
33+
}

src/DevantlerTech.KubernetesGenerator.Native/Models/ClusterRoleAggregationRule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@ public class ClusterRoleAggregationRule
99
/// Gets or sets the cluster role selectors for aggregation.
1010
/// </summary>
1111
public required IList<ClusterRoleLabelSelector> ClusterRoleSelectors { get; init; } = [];
12-
}
12+
}

src/DevantlerTech.KubernetesGenerator.Native/Models/ClusterRoleBinding.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,4 @@ public class ClusterRoleBinding
1919
/// Gets or sets the subjects that are bound to the cluster role.
2020
/// </summary>
2121
public required IList<RoleBindingSubject> Subjects { get; init; }
22-
}
22+
}

src/DevantlerTech.KubernetesGenerator.Native/Models/ClusterRoleLabelSelector.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,4 @@ public class ClusterRoleLabelSelector
1414
/// Gets or sets the match expressions for the selector.
1515
/// </summary>
1616
public IList<MatchExpression>? MatchExpressions { get; init; }
17-
}
17+
}

src/DevantlerTech.KubernetesGenerator.Native/Models/ClusterRoleRule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,4 @@ public class ClusterRoleRule
3232
/// Used for cluster-scoped permissions on non-resource endpoints.
3333
/// </summary>
3434
public IList<string>? NonResourceURLs { get; init; }
35-
}
35+
}

0 commit comments

Comments
 (0)