From ecd912ba340b878ec6b859a7c8262af65756354f Mon Sep 17 00:00:00 2001 From: Nikolai Emil Damm Date: Tue, 15 Oct 2024 20:26:56 +0200 Subject: [PATCH] Add FluxProvisioner (#14) * Added FluxProvisioner * Update README Tree workflow to include Flux GitOps tooling dependency * Refactor FluxProvisioner import statement * Fix FluxProvisioner reconciliation * Fix Flux_InstallsAndReconciles_KustomizationsAsync to use the kube context for all calls --- ...r.KubernetesProvisioner.GitOps.Core.csproj | 14 +++++ .../IKubernetesGitOpsProvisioner.cs | 26 ++++++++++ ...rnetesProvisioner.GitOps.Flux.Tests.csproj | 36 +++++++++++++ .../FluxProvisionerTests/AllMethodsTests.cs | 37 ++++++++++++++ .../assets/kind-config.yaml | 2 + ...r.KubernetesProvisioner.GitOps.Flux.csproj | 23 +++++++++ .../FluxProvisioner.cs | 51 +++++++++++++++++++ .../KubernetesResourceProvisioner.cs | 4 +- Devantler.KubernetesProvisioner.sln | 18 +++++++ README.md | 5 ++ 10 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 Devantler.KubernetesProvisioner.GitOps.Core/Devantler.KubernetesProvisioner.GitOps.Core.csproj create mode 100644 Devantler.KubernetesProvisioner.GitOps.Core/IKubernetesGitOpsProvisioner.cs create mode 100644 Devantler.KubernetesProvisioner.GitOps.Flux.Tests/Devantler.KubernetesProvisioner.GitOps.Flux.Tests.csproj create mode 100644 Devantler.KubernetesProvisioner.GitOps.Flux.Tests/FluxProvisionerTests/AllMethodsTests.cs create mode 100644 Devantler.KubernetesProvisioner.GitOps.Flux.Tests/assets/kind-config.yaml create mode 100644 Devantler.KubernetesProvisioner.GitOps.Flux/Devantler.KubernetesProvisioner.GitOps.Flux.csproj create mode 100644 Devantler.KubernetesProvisioner.GitOps.Flux/FluxProvisioner.cs diff --git a/Devantler.KubernetesProvisioner.GitOps.Core/Devantler.KubernetesProvisioner.GitOps.Core.csproj b/Devantler.KubernetesProvisioner.GitOps.Core/Devantler.KubernetesProvisioner.GitOps.Core.csproj new file mode 100644 index 0000000..8e60925 --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Core/Devantler.KubernetesProvisioner.GitOps.Core.csproj @@ -0,0 +1,14 @@ + + + + net8.0 + enable + enable + preview-all + true + true + true + false + + + \ No newline at end of file diff --git a/Devantler.KubernetesProvisioner.GitOps.Core/IKubernetesGitOpsProvisioner.cs b/Devantler.KubernetesProvisioner.GitOps.Core/IKubernetesGitOpsProvisioner.cs new file mode 100644 index 0000000..fac649d --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Core/IKubernetesGitOpsProvisioner.cs @@ -0,0 +1,26 @@ +namespace Devantler.KubernetesProvisioner.GitOps.Core; + +/// +/// A Kubernetes GitOps provisioner. +/// +public interface IKubernetesGitOpsProvisioner +{ + /// + /// The Kubernetes context. + /// + public string? Context { get; set; } + /// + /// Install the GitOps tooling on the Kubernetes cluster. + /// + public Task InstallAsync(CancellationToken cancellationToken = default); + + /// + /// Uninstall the GitOps tooling from the Kubernetes cluster. + /// + public Task UninstallAsync(CancellationToken cancellationToken = default); + + /// + /// Reconcile resources on the Kubernetes cluster. + /// + public Task ReconcileAsync(CancellationToken cancellationToken = default); +} diff --git a/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/Devantler.KubernetesProvisioner.GitOps.Flux.Tests.csproj b/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/Devantler.KubernetesProvisioner.GitOps.Flux.Tests.csproj new file mode 100644 index 0000000..e128f32 --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/Devantler.KubernetesProvisioner.GitOps.Flux.Tests.csproj @@ -0,0 +1,36 @@ + + + + net8.0 + enable + enable + preview-all + true + true + true + false + true + false + true + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/FluxProvisionerTests/AllMethodsTests.cs b/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/FluxProvisionerTests/AllMethodsTests.cs new file mode 100644 index 0000000..17a4d3f --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/FluxProvisionerTests/AllMethodsTests.cs @@ -0,0 +1,37 @@ +using Devantler.KindCLI; + +namespace Devantler.KubernetesProvisioner.GitOps.Flux.Tests.FluxProvisionerTests; + +/// +/// Tests for all methods in the class. +/// +[Collection("Flux")] +public class AllMethodsTests +{ + /// + /// Test to verify that flux installs and reconciles kustomizations. + /// + /// + [Fact] + public async Task Flux_InstallsAndReconciles_KustomizationsAsync() + { + // Arrange + string clusterName = "test-flux-cluster"; + string context = "kind-" + clusterName; + string configPath = Path.Combine(AppContext.BaseDirectory, "assets/kind-config.yaml"); + var fluxProvisioner = new FluxProvisioner(context); + var cancellationToken = new CancellationToken(); + + // Act + await Kind.DeleteClusterAsync(clusterName, cancellationToken); + await Kind.CreateClusterAsync(clusterName, configPath, cancellationToken); + await fluxProvisioner.InstallAsync(cancellationToken); + await FluxCLI.Flux.CreateOCISourceAsync("podinfo", new Uri("oci://ghcr.io/stefanprodan/manifests/podinfo"), context, cancellationToken: cancellationToken); + await FluxCLI.Flux.CreateKustomizationAsync("podinfo", "OCIRepository/podinfo", "", context, cancellationToken: cancellationToken); + await fluxProvisioner.ReconcileAsync(cancellationToken); + await fluxProvisioner.UninstallAsync(cancellationToken); + + // Cleanup + await Kind.DeleteClusterAsync(clusterName, cancellationToken); + } +} diff --git a/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/assets/kind-config.yaml b/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/assets/kind-config.yaml new file mode 100644 index 0000000..18eb9ae --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Flux.Tests/assets/kind-config.yaml @@ -0,0 +1,2 @@ +kind: Cluster +apiVersion: kind.x-k8s.io/v1alpha4 diff --git a/Devantler.KubernetesProvisioner.GitOps.Flux/Devantler.KubernetesProvisioner.GitOps.Flux.csproj b/Devantler.KubernetesProvisioner.GitOps.Flux/Devantler.KubernetesProvisioner.GitOps.Flux.csproj new file mode 100644 index 0000000..7d3a980 --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Flux/Devantler.KubernetesProvisioner.GitOps.Flux.csproj @@ -0,0 +1,23 @@ + + + + net8.0 + enable + enable + preview-all + true + true + true + false + + + + + + + + + + + + diff --git a/Devantler.KubernetesProvisioner.GitOps.Flux/FluxProvisioner.cs b/Devantler.KubernetesProvisioner.GitOps.Flux/FluxProvisioner.cs new file mode 100644 index 0000000..47ec0be --- /dev/null +++ b/Devantler.KubernetesProvisioner.GitOps.Flux/FluxProvisioner.cs @@ -0,0 +1,51 @@ +using Devantler.KubernetesProvisioner.GitOps.Core; +using Devantler.KubernetesProvisioner.Resources.Native; +using k8s; +using k8s.Models; + +namespace Devantler.KubernetesProvisioner.GitOps.Flux; + +/// +/// A Kubernetes GitOps provisioner using Flux. +/// +/// +/// Initializes a new instance of the class. +/// +/// +public class FluxProvisioner(string? context = default) : IKubernetesGitOpsProvisioner +{ + + /// + public string? Context { get; set; } = context; + + /// + /// Install Flux on the Kubernetes cluster. + /// + /// + /// + public async Task InstallAsync(CancellationToken cancellationToken = default) => + await FluxCLI.Flux.InstallAsync(Context, cancellationToken).ConfigureAwait(false); + + /// + /// Reconcile resources on the Kubernetes cluster. + /// + /// + /// + public async Task ReconcileAsync(CancellationToken cancellationToken = default) + { + using var kubernetesResourceProvisioner = new KubernetesResourceProvisioner(Context); + var kustomizations = await kubernetesResourceProvisioner.CustomObjects.ListNamespacedCustomObjectAsync("kustomize.toolkit.fluxcd.io", "v1", "flux-system", "kustomizations", cancellationToken: cancellationToken).ConfigureAwait(false); + foreach (var kustomization in kustomizations.Items) + { + await FluxCLI.Flux.ReconcileKustomizationAsync(kustomization.Metadata.Name, Context, withSource: true, cancellationToken: cancellationToken).ConfigureAwait(false); + } + } + + /// + /// Uninstall Flux from the Kubernetes cluster. + /// + /// + /// + public async Task UninstallAsync(CancellationToken cancellationToken = default) => + await FluxCLI.Flux.UninstallAsync(Context, cancellationToken).ConfigureAwait(false); +} diff --git a/Devantler.KubernetesProvisioner.Resources.Native/KubernetesResourceProvisioner.cs b/Devantler.KubernetesProvisioner.Resources.Native/KubernetesResourceProvisioner.cs index 070768a..9225160 100644 --- a/Devantler.KubernetesProvisioner.Resources.Native/KubernetesResourceProvisioner.cs +++ b/Devantler.KubernetesProvisioner.Resources.Native/KubernetesResourceProvisioner.cs @@ -9,9 +9,9 @@ namespace Devantler.KubernetesProvisioner.Resources.Native; /// Initializes a new instance of the class. /// /// -public sealed class KubernetesResourceProvisioner(string context) : Kubernetes(BuildConfig(context)) +public sealed class KubernetesResourceProvisioner(string? context = default) : Kubernetes(BuildConfig(context)) { - static KubernetesClientConfiguration BuildConfig(string context) + static KubernetesClientConfiguration BuildConfig(string? context) { var kubeConfig = KubernetesClientConfiguration.LoadKubeConfig(); var config = KubernetesClientConfiguration.BuildConfigFromConfigObject(kubeConfig, context); diff --git a/Devantler.KubernetesProvisioner.sln b/Devantler.KubernetesProvisioner.sln index 2e6013b..72a496c 100644 --- a/Devantler.KubernetesProvisioner.sln +++ b/Devantler.KubernetesProvisioner.sln @@ -17,6 +17,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devantler.KubernetesProvisi EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devantler.KubernetesProvisioner.Resources.Native.Tests", "Devantler.KubernetesProvisioner.Resources.Native.Tests\Devantler.KubernetesProvisioner.Resources.Native.Tests.csproj", "{0F5FB874-6689-4466-914B-BBA2E9EB9C26}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devantler.KubernetesProvisioner.GitOps.Flux", "Devantler.KubernetesProvisioner.GitOps.Flux\Devantler.KubernetesProvisioner.GitOps.Flux.csproj", "{2AC5B5A3-B138-42B9-86A9-ECC9BA3E165A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devantler.KubernetesProvisioner.GitOps.Core", "Devantler.KubernetesProvisioner.GitOps.Core\Devantler.KubernetesProvisioner.GitOps.Core.csproj", "{271CB43E-D09A-4448-A8DD-79BCE5798555}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Devantler.KubernetesProvisioner.GitOps.Flux.Tests", "Devantler.KubernetesProvisioner.GitOps.Flux.Tests\Devantler.KubernetesProvisioner.GitOps.Flux.Tests.csproj", "{4BF9F996-B03B-434A-B913-8406BEE3A5CE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -54,5 +60,17 @@ Global {0F5FB874-6689-4466-914B-BBA2E9EB9C26}.Debug|Any CPU.Build.0 = Debug|Any CPU {0F5FB874-6689-4466-914B-BBA2E9EB9C26}.Release|Any CPU.ActiveCfg = Release|Any CPU {0F5FB874-6689-4466-914B-BBA2E9EB9C26}.Release|Any CPU.Build.0 = Release|Any CPU + {2AC5B5A3-B138-42B9-86A9-ECC9BA3E165A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2AC5B5A3-B138-42B9-86A9-ECC9BA3E165A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2AC5B5A3-B138-42B9-86A9-ECC9BA3E165A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2AC5B5A3-B138-42B9-86A9-ECC9BA3E165A}.Release|Any CPU.Build.0 = Release|Any CPU + {271CB43E-D09A-4448-A8DD-79BCE5798555}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {271CB43E-D09A-4448-A8DD-79BCE5798555}.Debug|Any CPU.Build.0 = Debug|Any CPU + {271CB43E-D09A-4448-A8DD-79BCE5798555}.Release|Any CPU.ActiveCfg = Release|Any CPU + {271CB43E-D09A-4448-A8DD-79BCE5798555}.Release|Any CPU.Build.0 = Release|Any CPU + {4BF9F996-B03B-434A-B913-8406BEE3A5CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BF9F996-B03B-434A-B913-8406BEE3A5CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BF9F996-B03B-434A-B913-8406BEE3A5CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BF9F996-B03B-434A-B913-8406BEE3A5CE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/README.md b/README.md index 4c3b1fa..50aa96d 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Simple provisioners that can provision Kubernetes and Kubernetes resources. Show/hide folder structure + ``` . ├── .github @@ -30,6 +31,7 @@ Simple provisioners that can provision Kubernetes and Kubernetes resources. 15 directories ``` + @@ -49,6 +51,9 @@ dotnet add package Devantler.KubernetesProvisioner.Cluster.K3d # For provisioning a Kind cluster dotnet add package Devantler.KubernetesProvisioner.Cluster.Kind +# For provisioning Flux GitOps tooling +dotnet add package Devantler.KubernetesProvisioner.GitOps.Flux + # For provisioning native Kubernetes resources dotnet add package Devantler.KubernetesProvisioner.Resources.Native ```