From 61ea0c6e9a10f0f0ad2c4030e9404eed18d70eb6 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 13 Feb 2024 21:54:45 +0000
Subject: [PATCH 01/13] chore(deps): update dependency
microsoft.aspnetcore.mvc.testing to v8.0.2 (#718)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [Microsoft.AspNetCore.Mvc.Testing](https://asp.net/)
([source](https://togithub.com/dotnet/aspnetcore)) | `8.0.1` -> `8.0.2`
|
[![age](https://developer.mend.io/api/mc/badges/age/nuget/Microsoft.AspNetCore.Mvc.Testing/8.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/Microsoft.AspNetCore.Mvc.Testing/8.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/Microsoft.AspNetCore.Mvc.Testing/8.0.1/8.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Microsoft.AspNetCore.Mvc.Testing/8.0.1/8.0.2?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
dotnet/aspnetcore (Microsoft.AspNetCore.Mvc.Testing)
###
[`v8.0.2`](https://togithub.com/dotnet/aspnetcore/compare/v8.0.1...v8.0.2)
---
### Configuration
📅 **Schedule**: Branch creation - "after 9pm,before 6am" in timezone
Europe/Zurich, Automerge - At any time (no schedule defined).
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/buehler/dotnet-operator-sdk).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj b/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj
index 5d88a24a..dcaba8d5 100644
--- a/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj
+++ b/test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj
@@ -1,7 +1,7 @@
-
+
From e5eef72774f3db846afa8f5104c46cb66d5a7050 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Fri, 16 Feb 2024 03:30:01 +0000
Subject: [PATCH 02/13] chore(deps): update xunit-dotnet monorepo (#719)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [xunit](https://togithub.com/xunit/xunit) | `2.6.6` -> `2.7.0` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/xunit/2.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/xunit/2.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/xunit/2.6.6/2.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/xunit/2.6.6/2.7.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
|
[xunit.runner.visualstudio](https://togithub.com/xunit/visualstudio.xunit)
| `2.5.6` -> `2.5.7` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/xunit.runner.visualstudio/2.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/xunit.runner.visualstudio/2.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/xunit.runner.visualstudio/2.5.6/2.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/xunit.runner.visualstudio/2.5.6/2.5.7?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
xunit/xunit (xunit)
### [`v2.7.0`](https://togithub.com/xunit/xunit/compare/2.6.6...2.7.0)
[Compare Source](https://togithub.com/xunit/xunit/compare/2.6.6...2.7.0)
xunit/visualstudio.xunit (xunit.runner.visualstudio)
###
[`v2.5.7`](https://togithub.com/xunit/visualstudio.xunit/compare/2.5.6...2.5.7)
[Compare
Source](https://togithub.com/xunit/visualstudio.xunit/compare/2.5.6...2.5.7)
---
### Configuration
📅 **Schedule**: Branch creation - "after 9pm,before 6am" in timezone
Europe/Zurich, Automerge - At any time (no schedule defined).
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
👻 **Immortal**: This PR will be recreated if closed unmerged. Get
[config help](https://togithub.com/renovatebot/renovate/discussions) if
that's undesired.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/buehler/dotnet-operator-sdk).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
test/Directory.Build.props | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index b3edea99..1a1c7a65 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -11,8 +11,8 @@
-
-
+
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
From 21cdc6eeebd9e1f871526098f3010f6689adf6ac Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Sun, 18 Feb 2024 22:12:55 +0000
Subject: [PATCH 03/13] chore(deps): update dependency docfx to v2.75.3 (#720)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [docfx](https://togithub.com/dotnet/docfx) | `2.75.2` -> `2.75.3` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/docfx/2.75.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/docfx/2.75.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/docfx/2.75.2/2.75.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/docfx/2.75.2/2.75.3?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
dotnet/docfx (docfx)
### [`v2.75.3`](https://togithub.com/dotnet/docfx/releases/tag/v2.75.3)
#### What's Changed
##### 🐞 Bug Fixes
- fix: Toc warnings when toc item with TopicUid but name is not
specified by [@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9665](https://togithub.com/dotnet/docfx/pull/9665)
- fix: Warnings that occurs when bookmark link contains non-ASCII chars
by [@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9660](https://togithub.com/dotnet/docfx/pull/9660)
- fix: Add StackTrace logs for Javascript error by
[@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9694](https://togithub.com/dotnet/docfx/pull/9694)
- fix: InvalidInputFile error occurs if file contains URI escaped
charactors by [@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9700](https://togithub.com/dotnet/docfx/pull/9700)
- fix: Add fail-fast logics when `--serve` option enabled & port is
already used by [@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9690](https://togithub.com/dotnet/docfx/pull/9690)
- fix: filterconfig exclude rule is not works as documented by
[@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9666](https://togithub.com/dotnet/docfx/pull/9666)
##### 🔧 Engineering
- build(deps): bump YamlDotNet from 15.1.0 to 15.1.1 dependencies .NET
by [@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9689](https://togithub.com/dotnet/docfx/pull/9689)
- chore: update NuGet package dependencies
(Microsoft.NET.Test.Sdk,Microsoft.Build.Locator) by
[@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9693](https://togithub.com/dotnet/docfx/pull/9693)
- chore: Update public API snapshot & disable `AutoVerify` on CI build
by [@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9692](https://togithub.com/dotnet/docfx/pull/9692)
- chore: Update node.js version by
[@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9701](https://togithub.com/dotnet/docfx/pull/9701)
- chore: Update Microsoft.Build package version by
[@filzrev](https://togithub.com/filzrev) in
[https://github.com/dotnet/docfx/pull/9698](https://togithub.com/dotnet/docfx/pull/9698)
**Full Changelog**:
https://github.com/dotnet/docfx/compare/v2.75.2...v2.75.3
---
### Configuration
📅 **Schedule**: Branch creation - "after 9pm,before 6am" in timezone
Europe/Zurich, Automerge - At any time (no schedule defined).
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/buehler/dotnet-operator-sdk).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
.config/dotnet-tools.json | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json
index 7f020cbe..3d6bb796 100644
--- a/.config/dotnet-tools.json
+++ b/.config/dotnet-tools.json
@@ -3,7 +3,7 @@
"isRoot": true,
"tools": {
"docfx": {
- "version": "2.75.2",
+ "version": "2.75.3",
"commands": ["docfx"]
}
}
From fc70466190187d2f328f1f3be6f99f100ecaaa33 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Mon, 19 Feb 2024 21:59:20 +0000
Subject: [PATCH 04/13] fix(deps): update dependency roslynator.analyzers to
v4.11.0 (#721)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [Roslynator.Analyzers](https://togithub.com/dotnet/roslynator) |
`4.10.0` -> `4.11.0` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/Roslynator.Analyzers/4.11.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/Roslynator.Analyzers/4.11.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/Roslynator.Analyzers/4.10.0/4.11.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/Roslynator.Analyzers/4.10.0/4.11.0?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
dotnet/roslynator (Roslynator.Analyzers)
###
[`v4.11.0`](https://togithub.com/dotnet/roslynator/blob/HEAD/ChangeLog.md#4110---2024-02-19)
##### Added
- Add analyzer "Use raw string literal"
[RCS1266](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1266)
([PR](https://togithub.com/dotnet/roslynator/pull/1375))
- Add analyzer "Convert 'string.Concat' to interpolated string"
[RCS1267](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1267)
([PR](https://togithub.com/dotnet/roslynator/pull/1379))
- Simplify LINQ query
[RCS1077](https://josefpihrt.github.io/docs/roslynator/analyzers/1077)
([PR](https://togithub.com/dotnet/roslynator/pull/1384))
- `items.Select(selector).Average()` => `items.Average(selector)`
- `items.Select(selector).Sum()` => `items.Sum(selector)`
##### Fixed
- Fix analyzer
[RCS0049](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0049)
([PR](https://togithub.com/dotnet/roslynator/pull/1386))
- Fix analyzer
[RCS1159](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1159)
([PR](https://togithub.com/dotnet/roslynator/pull/1390))
- Fix analyzer
[RCS1019](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1019)
([PR](https://togithub.com/dotnet/roslynator/pull/1402))
- Fix analyzer
[RCS1250](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1250)
([PR](https://togithub.com/dotnet/roslynator/pull/1403),
[PR](https://togithub.com/dotnet/roslynator/pull/1404))
- Fix code fix for
[CS8600](https://josefpihrt.github.io/docs/roslynator/fixes/CS8600)
changing the wrong type when casts or `var` are involved
([PR](https://togithub.com/dotnet/roslynator/pull/1393) by
[@jroessel](https://togithub.com/jroessel))
- Fix Roslyn multi-targeting
([PR](https://togithub.com/dotnet/roslynator/pull/1407))
---
### Configuration
📅 **Schedule**: Branch creation - "after 9pm,before 6am" in timezone
Europe/Zurich, Automerge - At any time (no schedule defined).
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/buehler/dotnet-operator-sdk).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
src/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 3b03b377..db4d4a6f 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -44,7 +44,7 @@
Version="9.19.0.84025"
PrivateAssets="all"
Condition="$(MSBuildProjectExtension) == '.csproj'" />
-
+
From 5b343782d3d40e6f3212f4254defe81262f2f6d1 Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Tue, 20 Feb 2024 22:13:27 +0000
Subject: [PATCH 05/13] chore(deps): update dependency coverlet.collector to
v6.0.1 (#722)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
| [coverlet.collector](https://togithub.com/coverlet-coverage/coverlet)
| `6.0.0` -> `6.0.1` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/coverlet.collector/6.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/coverlet.collector/6.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/coverlet.collector/6.0.0/6.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/coverlet.collector/6.0.0/6.0.1?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
coverlet-coverage/coverlet (coverlet.collector)
###
[`v6.0.1`](https://togithub.com/coverlet-coverage/coverlet/releases/tag/v6.0.1)
##### Fixed
- Uncovered lines in .NET 8 for inheriting records
[#1555](https://togithub.com/coverlet-coverage/coverlet/issues/1555)
- Fix record constructors not covered when SkipAutoProps is true
[#1561](https://togithub.com/coverlet-coverage/coverlet/issues/1561)
- Fix .NET 7 Method Group branch coverage issue
[#1447](https://togithub.com/coverlet-coverage/coverlet/issues/1447)
- Fix ExcludeFromCodeCoverage does not exclude method in a partial class
[#1548](https://togithub.com/coverlet-coverage/coverlet/issues/1548)
- Fix ExcludeFromCodeCoverage does not exclude F# task
[#1547](https://togithub.com/coverlet-coverage/coverlet/issues/1547)
- Fix issues where ExcludeFromCodeCoverage ignored
[#1431](https://togithub.com/coverlet-coverage/coverlet/issues/1431)
- Fix issues with ExcludeFromCodeCoverage attribute
[#1484](https://togithub.com/coverlet-coverage/coverlet/issues/1484)
- Fix broken links in documentation
[#1514](https://togithub.com/coverlet-coverage/coverlet/issues/1514)
- Fix problem with coverage for .net5 WPF application
[#1221](https://togithub.com/coverlet-coverage/coverlet/issues/1221)
by https://github.com/lg2de
- Fix unable to instrument module for Microsoft.AspNetCore.Mvc.Razor
[#1459](https://togithub.com/coverlet-coverage/coverlet/issues/1459)
by https://github.com/lg2de
##### Improvements
- Extended exclude by attribute feature to work with fully qualified
name
[#1589](https://togithub.com/coverlet-coverage/coverlet/issues/1589)
- Use System.CommandLine instead of McMaster.Extensions.CommandLineUtils
[#1474](https://togithub.com/coverlet-coverage/coverlet/issues/1474)
by https://github.com/Bertk
- Fix deadlog in Coverlet.Integration.Tests.BaseTest
[#1541](https://togithub.com/coverlet-coverage/coverlet/pull/1541)
by https://github.com/Bertk
- Add coverlet.msbuild.tasks unit tests
[#1534](https://togithub.com/coverlet-coverage/coverlet/pull/1534)
by https://github.com/Bertk
[Diff between 6.0.0 and
6.0.1](https://togithub.com/coverlet-coverage/coverlet/compare/v6.0.0...v6.0.1)
---
### Configuration
📅 **Schedule**: Branch creation - "after 9pm,before 6am" in timezone
Europe/Zurich, Automerge - At any time (no schedule defined).
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/buehler/dotnet-operator-sdk).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
test/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/Directory.Build.props b/test/Directory.Build.props
index 1a1c7a65..ee6753ab 100644
--- a/test/Directory.Build.props
+++ b/test/Directory.Build.props
@@ -16,7 +16,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
all
-
+
runtime; build; native; contentfiles; analyzers; buildtransitive
all
From 8a6cfe1b1313b75c8a880949fdd0037c4445663b Mon Sep 17 00:00:00 2001
From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com>
Date: Wed, 21 Feb 2024 01:25:45 +0000
Subject: [PATCH 06/13] fix(deps): update dependency sonaranalyzer.csharp to
v9.20.0.85982 (#723)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
[![Mend
Renovate](https://app.renovatebot.com/images/banner.svg)](https://renovatebot.com)
This PR contains the following updates:
| Package | Change | Age | Adoption | Passing | Confidence |
|---|---|---|---|---|---|
|
[SonarAnalyzer.CSharp](https://redirect.sonarsource.com/doc/sonar-visualstudio.html)
([source](https://togithub.com/SonarSource/sonar-dotnet)) |
`9.19.0.84025` -> `9.20.0.85982` |
[![age](https://developer.mend.io/api/mc/badges/age/nuget/SonarAnalyzer.CSharp/9.20.0.85982?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![adoption](https://developer.mend.io/api/mc/badges/adoption/nuget/SonarAnalyzer.CSharp/9.20.0.85982?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![passing](https://developer.mend.io/api/mc/badges/compatibility/nuget/SonarAnalyzer.CSharp/9.19.0.84025/9.20.0.85982?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
[![confidence](https://developer.mend.io/api/mc/badges/confidence/nuget/SonarAnalyzer.CSharp/9.19.0.84025/9.20.0.85982?slim=true)](https://docs.renovatebot.com/merge-confidence/)
|
---
### Release Notes
SonarSource/sonar-dotnet (SonarAnalyzer.CSharp)
###
[`v9.20.0.85982`](https://togithub.com/SonarSource/sonar-dotnet/releases/tag/9.20.0.85982):
9.20
[Compare
Source](https://togithub.com/SonarSource/sonar-dotnet/compare/9.19.0.84025...9.20.0.85982)
Hey everyone!
This release brings a vast number of improvements. The main focus lies
on improving the capabilities of our Symbolic Execution engine, which
results in much more accurate findings. The biggest visible impact is a
significant reduction in false positives around loops for the rules
S2583 and S2589.
And a big thank you to [@rcatley](https://togithub.com/rcatley)
for their external contribution!
##### Bug Fixes
- [8642](https://togithub.com/SonarSource/sonar-dotnet/issues/8642) -
\[C#] Exception in `SonarAnalyzer.Rules.CSharp.SymbolicExecutionRunner`
##### False Positive
- [8678](https://togithub.com/SonarSource/sonar-dotnet/issues/8678) -
\[C#, VB.NET] Fix S2583 FP: Variable Updated in Catch Block
- [8028](https://togithub.com/SonarSource/sonar-dotnet/issues/8028) -
\[C#, VB.NET] Fix S2583 FP: Loop with manually incremented counter
- [8449](https://togithub.com/SonarSource/sonar-dotnet/issues/8449) -
\[C#, VB.NET] Fix S2589 FP: Change this condition so that it does not
always evaluate to 'True'
- [8495](https://togithub.com/SonarSource/sonar-dotnet/issues/8495) -
\[C#, VB.NET] Fix S2583/S2589 FP: Return inside lock and using causes FP
after the block
- [8428](https://togithub.com/SonarSource/sonar-dotnet/issues/8428) -
\[C#, VB.NET] Fix S2583/S2589 FP: For loop with Array.Length
- [8483](https://togithub.com/SonarSource/sonar-dotnet/issues/8483) -
\[C#, VB.NET] Fix S4158 FP: Should not report on HashSet.UnionWith for
readonly fields.
- [8739](https://togithub.com/SonarSource/sonar-dotnet/issues/8739) -
\[C#] Fix S4049 FP: Do not raise on methods with generic parameters
- [8638](https://togithub.com/SonarSource/sonar-dotnet/issues/8638) -
\[C#] Fix S2386 & S3887 FP: should not be raised for FrozenDictionary
and FrozenSet
- [8611](https://togithub.com/SonarSource/sonar-dotnet/issues/8611) -
\[C#] Fix S2372 FP: Add support for method invocations
([@rcatley](https://togithub.com/rcatley))
- [8567](https://togithub.com/SonarSource/sonar-dotnet/issues/8567) -
\[C#] Fix S2325 FP: Primary Constructor Support
##### False Negative
- [8486](https://togithub.com/SonarSource/sonar-dotnet/issues/8486) -
\[C#] Fix S2589 FN: Tuple binary operations (comparison)
##### Improvements
- [8010](https://togithub.com/SonarSource/sonar-dotnet/issues/8010) -
\[C#, VB.NET] S2589: Improve message in the case of null propagating
operator
- [7866](https://togithub.com/SonarSource/sonar-dotnet/issues/7866) -
\[C#, VB.NET] SE: Allow collection tracking even when S4158 is not
active
- [8499](https://togithub.com/SonarSource/sonar-dotnet/issues/8499) -
\[C#] SE: Learn number constraints from relational pattern
- [8651](https://togithub.com/SonarSource/sonar-dotnet/issues/8651) -
Update RSPEC before 9.20 release
---
### Configuration
📅 **Schedule**: Branch creation - "after 9pm,before 6am" in timezone
Europe/Zurich, Automerge - At any time (no schedule defined).
🚦 **Automerge**: Enabled.
♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the
rebase/retry checkbox.
🔕 **Ignore**: Close this PR and you won't be reminded about this update
again.
---
- [ ] If you want to rebase/retry this PR, check
this box
---
This PR has been generated by [Mend
Renovate](https://www.mend.io/free-developer-tools/renovate/). View
repository job log
[here](https://developer.mend.io/github/buehler/dotnet-operator-sdk).
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
---
src/Directory.Build.props | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index db4d4a6f..edd3521c 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -41,7 +41,7 @@
Condition="$(MSBuildProjectExtension) == '.csproj'" />
From 2d17bfff14a142070eb5fd898efefdf266b59724 Mon Sep 17 00:00:00 2001
From: Jasmin
Date: Thu, 7 Mar 2024 14:10:17 +0100
Subject: [PATCH 07/13] feat: `CancellationToken` support and a lot of async
improvements (#725)
This PR adds the ability to cancel (almost) all asynchronous operations
with the built-in `CancellationToken`. Due to the nature of interfaces,
almost all of those changes are breaking the existing API.
It also adds an `WatchAsync` method to the `IKubernetesClient`, which is
making use of the newer asynchronous enumerables.
BREAKING CHANGE: Some methods do now feature the cancellation
token which changed the method signature.
---
.editorconfig | 802 +++----
.gitattributes | 3 +
.github/ISSUE_TEMPLATE/bug_report.yaml | 88 +-
.github/ISSUE_TEMPLATE/documentation.yaml | 24 +-
.github/ISSUE_TEMPLATE/feature_request.yaml | 52 +-
CONTRIBUTING.md | 126 +-
.../Controller/V1TestEntityController.cs | 44 +-
.../Entities/V1TestEntity.cs | 32 +-
.../Entities/V2TestEntity.cs | 36 +-
.../Entities/V3TestEntity.cs | 40 +-
examples/ConversionWebhookOperator/Program.cs | 44 +-
.../Webhooks/TestConversionWebhook.cs | 104 +-
.../Controller/V1TestEntityController.cs | 64 +-
examples/Operator/Entities/V1TestEntity.cs | 36 +-
examples/Operator/Finalizer/FinalizerOne.cs | 26 +-
examples/Operator/Program.cs | 30 +-
.../Controller/V1TestEntityController.cs | 44 +-
.../WebhookOperator/Entities/V1TestEntity.cs | 32 +-
examples/WebhookOperator/Program.cs | 44 +-
.../Webhooks/TestMutationWebhook.cs | 40 +-
.../Webhooks/TestValidationWebhook.cs | 58 +-
.../Builder/IOperatorBuilder.cs | 98 +-
.../Builder/OperatorSettings.cs | 124 +-
.../Controller/IEntityController{TEntity}.cs | 112 +-
.../AdditionalPrinterColumnAttribute.cs | 60 +-
.../Attributes/DescriptionAttribute.cs | 28 +-
.../Attributes/EmbeddedResourceAttribute.cs | 24 +-
.../Attributes/EntityScopeAttribute.cs | 14 +-
.../Attributes/ExternalDocsAttribute.cs | 36 +-
...GenericAdditionalPrinterColumnAttribute.cs | 236 +--
.../Attributes/IgnoreEntityAttribute.cs | 16 +-
.../Entities/Attributes/ItemsAttribute.cs | 52 +-
.../KubernetesEntityShortNamesAttribute.cs | 26 +-
.../Entities/Attributes/LengthAttribute.cs | 52 +-
.../Attributes/MultipleOfAttribute.cs | 26 +-
.../Entities/Attributes/PatternAttribute.cs | 26 +-
.../PreserveUnknownFieldsAttribute.cs | 16 +-
.../Entities/Attributes/RangeMaximum.cs | 36 +-
.../Entities/Attributes/RangeMinimum.cs | 36 +-
.../Entities/Attributes/RequiredAttribute.cs | 14 +-
.../Attributes/StorageVersionAttribute.cs | 22 +-
.../Entities/CustomKubernetesEntity.cs | 32 +-
.../CustomKubernetesEntity{TSpec,TStatus}.cs | 44 +-
.../Entities/CustomKubernetesEntity{TSpec}.cs | 34 +-
.../Entities/EntityList.cs | 44 +-
.../Entities/EntityMetadata.cs | 56 +-
.../Entities/EntityScope.cs | 36 +-
.../Entities/Extensions.cs | 178 +-
.../Entities/PrinterColumnPriority.cs | 34 +-
src/KubeOps.Abstractions/Events/EventType.cs | 40 +-
.../Events/IEventPublisherFactory.cs | 13 +
src/KubeOps.Abstractions/Events/Publisher.cs | 97 +-
.../Finalizer/EntityFinalizerAttacher.cs | 97 +-
.../Finalizer/IEntityFinalizer{TEntity}.cs | 40 +-
.../IEventFinalizerAttacherFactory.cs | 22 +
.../Kustomize/KustomizationConfig.cs | 110 +-
.../KustomizationConfigMapGenerator.cs | 46 +-
.../Kustomize/KustomizationImage.cs | 44 +-
.../Kustomize/KustomizationSecretGenerator.cs | 46 +-
.../Queue/EntityRequeue.cs | 94 +-
.../Queue/IEntityRequeueFactory.cs | 18 +
.../Rbac/EntityRbacAttribute.cs | 60 +-
.../Rbac/GenericRbacAttribute.cs | 90 +-
.../Rbac/RbacAttribute.cs | 14 +-
src/KubeOps.Abstractions/Rbac/RbacVerbs.cs | 106 +-
src/KubeOps.Cli/Arguments.cs | 78 +-
.../Certificates/CertificateGenerator.cs | 258 +--
src/KubeOps.Cli/Certificates/Extensions.cs | 44 +-
.../Commands/Generator/Generate.cs | 46 +-
.../Commands/Generator/OperatorGenerator.cs | 356 ++--
.../Commands/Management/Install.cs | 224 +-
.../Commands/Management/Uninstall.cs | 218 +-
src/KubeOps.Cli/Commands/Utilities/Version.cs | 80 +-
src/KubeOps.Cli/ExitCodes.cs | 18 +-
.../Generators/CertificateGenerator.cs | 46 +-
src/KubeOps.Cli/Generators/CrdGenerator.cs | 92 +-
.../Generators/DeploymentGenerator.cs | 138 +-
.../Generators/DockerfileGenerator.cs | 66 +-
.../Generators/IConfigGenerator.cs | 20 +-
.../Generators/MutationWebhookGenerator.cs | 112 +-
src/KubeOps.Cli/Generators/RbacGenerator.cs | 82 +-
.../Generators/ValidationWebhookGenerator.cs | 112 +-
.../Generators/WebhookDeploymentGenerator.cs | 204 +-
src/KubeOps.Cli/Options.cs | 86 +-
src/KubeOps.Cli/Output/OutputFormat.cs | 58 +-
src/KubeOps.Cli/Output/ResultOutput.cs | 122 +-
src/KubeOps.Cli/Program.cs | 56 +-
.../Transpilation/AssemblyLoader.cs | 412 ++--
src/KubeOps.Cli/Transpilation/BaseWebhook.cs | 40 +-
.../Transpilation/MutationWebhook.cs | 22 +-
src/KubeOps.Cli/Transpilation/TfmComparer.cs | 130 +-
.../Transpilation/ValidationWebhook.cs | 22 +-
.../ControllerRegistrationGenerator.cs | 170 +-
.../Generators/EntityDefinitionGenerator.cs | 176 +-
.../Generators/EntityInitializerGenerator.cs | 274 +--
.../FinalizerRegistrationGenerator.cs | 234 +--
.../Generators/OperatorBuilderGenerator.cs | 120 +-
.../SyntaxReceiver/AttributedEntity.cs | 20 +-
.../SyntaxReceiver/CombinedSyntaxReceiver.cs | 28 +-
.../EntityControllerSyntaxReceiver.cs | 44 +-
.../EntityFinalizerSyntaxReceiver.cs | 44 +-
.../KubernetesEntitySyntaxReceiver.cs | 76 +-
.../IKubernetesClient.cs | 900 ++++----
.../KubernetesClient.cs | 854 ++++----
.../LabelSelectors/EqualsSelector.cs | 26 +-
.../LabelSelectors/ExistsSelector.cs | 20 +-
.../LabelSelectors/Extensions.cs | 24 +-
.../LabelSelectors/LabelSelector.cs | 40 +-
.../LabelSelectors/NotEqualsSelector.cs | 26 +-
.../LabelSelectors/NotExistsSelector.cs | 20 +-
.../Builder/OperatorBuilderExtensions.cs | 108 +-
.../LocalTunnel/DevelopmentTunnelService.cs | 302 +--
.../LocalTunnel/TunnelConfig.cs | 6 +-
.../LocalTunnel/WebhookLoader.cs | 56 +-
.../Webhooks/Admission/AdmissionRequest.cs | 128 +-
.../Webhooks/Admission/AdmissionResponse.cs | 60 +-
.../Webhooks/Admission/AdmissionReview.cs | 30 +-
.../Webhooks/Admission/AdmissionStatus.cs | 28 +-
.../Webhooks/Admission/Mutation/JsonDiffer.cs | 54 +-
.../Admission/Mutation/MutationResult.cs | 154 +-
.../Mutation/MutationWebhookAttribute.cs | 22 +-
.../Mutation/MutationWebhook{TEntity}.cs | 351 ++--
.../Admission/Validation/ValidationResult.cs | 110 +-
.../Validation/ValidationWebhookAttribute.cs | 22 +-
.../Validation/ValidationWebhook{TEntity}.cs | 307 +--
.../Webhooks/Conversion/ConversionRequest.cs | 94 +-
.../Webhooks/Conversion/ConversionResponse.cs | 116 +-
.../Webhooks/Conversion/ConversionReview.cs | 38 +-
.../Webhooks/Conversion/ConversionStatus.cs | 38 +-
.../Webhooks/Conversion/ConversionWebhook.cs | 144 +-
.../Conversion/ConversionWebhookAttribute.cs | 70 +-
.../Webhooks/Conversion/IEntityConverter.cs | 172 +-
.../Builder/OperatorBuilder.cs | 319 +--
.../Events/KubeOpsEventPublisherFactory.cs | 109 +
.../Finalizer/FinalizerRegistration.cs | 3 -
.../KubeOpsEventFinalizerAttacherFactory.cs | 40 +
.../LeaderElection/ILeaderElectorFactory.cs | 15 +
.../KubernetesLeaderElectorFactory.cs | 30 +
.../LeaderElectionBackgroundService.cs | 43 +
.../ServiceCollectionExtensions.cs | 27 +
.../Queue/EntityRequeueBackgroundService.cs | 96 +
.../Queue/KubeOpsEntityRequeueFactory.cs | 29 +
.../Queue/TimedEntityQueue.cs | 158 +-
.../Queue/TimedQueueEntry{TEntity}.cs | 60 +
.../ServiceCollectionExtensions.cs | 54 +-
.../LeaderAwareResourceWatcher{TEntity}.cs | 111 +-
.../Watcher/ResourceWatcher{TEntity}.cs | 521 +++--
src/KubeOps.Transpiler/ContextCreator.cs | 72 +-
src/KubeOps.Transpiler/Crds.cs | 868 ++++----
src/KubeOps.Transpiler/Entities.cs | 212 +-
.../Kubernetes/KubernetesVersionComparer.cs | 146 +-
src/KubeOps.Transpiler/Rbac.cs | 184 +-
src/KubeOps.Transpiler/Utilities.cs | 390 ++--
test/KubeOps.Cli.Test/GlobalUsings.cs | 2 +-
.../Management/Install.Integration.Test.cs | 60 +-
.../ControllerRegistrationGenerator.Test.cs | 114 +-
.../EntityDefinitionGenerator.Test.cs | 134 +-
.../EntityInitializerGenerator.Test.cs | 574 +++---
.../FinalizerRegistrationGenerator.Test.cs | 172 +-
test/KubeOps.Generator.Test/GlobalUsings.cs | 2 +-
.../OperatorBuilderGenerator.Test.cs | 76 +-
.../TestHelperExtensions.cs | 40 +-
.../GlobalUsings.cs | 2 +-
.../IntegrationTestCollection.cs | 20 +-
.../KubernetesClient.Test.cs | 344 +--
.../KubernetesClientAsync.Test.cs | 344 +--
.../LabelSelector.Test.cs | 38 +-
.../Builder/OperatorBuilder.Test.cs | 207 +-
.../CancelEntityRequeue.Integration.Test.cs | 179 +-
.../DeletedEntityRequeue.Integration.Test.cs | 142 +-
.../EntityController.Integration.Test.cs | 262 +--
.../EntityRequeue.Integration.Test.cs | 199 +-
.../Events/EventPublisher.Integration.Test.cs | 205 +-
.../EntityFinalizer.Integration.Test.cs | 499 ++---
test/KubeOps.Operator.Test/GlobalUsings.cs | 2 +-
.../IntegrationTestCollection.cs | 216 +-
.../InvocationCounter.cs | 72 +-
.../LeaderAwareness.Integration.Test.cs | 132 +-
test/KubeOps.Operator.Test/MlcProvider.cs | 110 +-
.../NamespacedOperator.Integration.Test.cs | 184 +-
.../V1OperatorIntegrationTestEntity.cs | 70 +-
.../Builder/OperatorBuilderExtensions.Test.cs | 74 +-
.../KubeOps.Operator.Web.Test/GlobalUsings.cs | 2 +-
.../IntegrationTestCollection.cs | 310 +--
.../DevelopmentTunnelService.Test.cs | 64 +-
.../TestApp/TestMutationWebhook.cs | 38 +-
.../TestApp/TestValidationWebhook.cs | 34 +-
.../V1OperatorWebIntegrationTestEntity.cs | 70 +-
.../MutationWebhook.Integration.Test.cs | 54 +-
.../ValidationWebhook.Integration.Test.cs | 56 +-
test/KubeOps.Transpiler.Test/Crds.Mlc.Test.cs | 1836 ++++++++---------
.../Entities.Mlc.Test.cs | 84 +-
test/KubeOps.Transpiler.Test/Entities.Test.cs | 84 +-
test/KubeOps.Transpiler.Test/GlobalUsings.cs | 2 +-
.../IntegrationTestCollection.cs | 30 +-
test/KubeOps.Transpiler.Test/MlcProvider.cs | 88 +-
test/KubeOps.Transpiler.Test/Rbac.Mlc.Test.cs | 244 +--
197 files changed, 12222 insertions(+), 11602 deletions(-)
create mode 100644 .gitattributes
create mode 100644 src/KubeOps.Abstractions/Events/IEventPublisherFactory.cs
create mode 100644 src/KubeOps.Abstractions/Finalizer/IEventFinalizerAttacherFactory.cs
create mode 100644 src/KubeOps.Abstractions/Queue/IEntityRequeueFactory.cs
create mode 100644 src/KubeOps.Operator/Events/KubeOpsEventPublisherFactory.cs
delete mode 100644 src/KubeOps.Operator/Finalizer/FinalizerRegistration.cs
create mode 100644 src/KubeOps.Operator/Finalizer/KubeOpsEventFinalizerAttacherFactory.cs
create mode 100644 src/KubeOps.Operator/LeaderElection/ILeaderElectorFactory.cs
create mode 100644 src/KubeOps.Operator/LeaderElection/KubernetesLeaderElectorFactory.cs
create mode 100644 src/KubeOps.Operator/LeaderElection/LeaderElectionBackgroundService.cs
create mode 100644 src/KubeOps.Operator/LeaderElection/ServiceCollectionExtensions.cs
create mode 100644 src/KubeOps.Operator/Queue/EntityRequeueBackgroundService.cs
create mode 100644 src/KubeOps.Operator/Queue/KubeOpsEntityRequeueFactory.cs
create mode 100644 src/KubeOps.Operator/Queue/TimedQueueEntry{TEntity}.cs
diff --git a/.editorconfig b/.editorconfig
index e207f021..f1a68021 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -1,401 +1,401 @@
-root = true
-
-# All files
-[*]
-indent_style = space
-max_line_length = 120
-
-# Xml files
-[*.xml]
-indent_size = 2
-
-# C# files
-[*.cs]
-
-#### Core EditorConfig Options ####
-
-# Indentation and spacing
-indent_size = 4
-tab_width = 4
-
-# New line preferences
-end_of_line = crlf
-insert_final_newline = true
-
-#### Custom Analyzer Rules ####
-# Until https://github.com/SonarSource/sonar-dotnet/issues/7624 is shipped.
-dotnet_diagnostic.S3604.severity = none
-
-dotnet_diagnostic.SA0001.severity = none
-dotnet_diagnostic.SA1010.severity = none
-dotnet_diagnostic.SA1600.severity = none
-dotnet_diagnostic.CS1591.severity = none
-dotnet_diagnostic.SA1008.severity = suggestion
-dotnet_diagnostic.SA1009.severity = suggestion
-dotnet_diagnostic.SA1101.severity = none
-dotnet_diagnostic.SA1106.severity = suggestion
-dotnet_diagnostic.SA1118.severity = none
-dotnet_diagnostic.SX1101.severity = suggestion
-dotnet_diagnostic.SA1309.severity = none
-dotnet_diagnostic.SX1309.severity = warning
-dotnet_diagnostic.SA1313.severity = suggestion
-dotnet_diagnostic.SA1501.severity = suggestion
-dotnet_diagnostic.SA1503.severity = suggestion
-dotnet_diagnostic.SA1623.severity = none
-dotnet_diagnostic.SA1633.severity = none
-dotnet_diagnostic.SA1642.severity = none
-dotnet_diagnostic.SA1643.severity = none
-dotnet_diagnostic.SA1649.severity = none
-dotnet_diagnostic.RCS1029.severity = none
-dotnet_diagnostic.RCS1090.severity = none
-
-#### Custom Coding Conventions ####
-resharper_csharp_space_in_singleline_accessorholder = true
-resharper_csharp_space_between_accessors_in_singleline_property = true
-resharper_csharp_space_in_singleline_method = true
-resharper_csharp_space_in_singleline_anonymous_method = true
-resharper_csharp_space_within_single_line_array_initializer_braces = true
-resharper_csharp_trailing_comma_in_multiline_lists = true
-resharper_csharp_place_type_attribute_on_same_line = false
-resharper_csharp_place_attribute_on_same_line = false
-
-#### .NET Coding Conventions ####
-[*.{cs,vb}]
-
-# Organize usings
-dotnet_separate_import_directive_groups = true
-dotnet_sort_system_directives_first = true
-file_header_template = unset
-
-# this. and Me. preferences
-dotnet_style_qualification_for_event = false:silent
-dotnet_style_qualification_for_field = false:silent
-dotnet_style_qualification_for_method = false:silent
-dotnet_style_qualification_for_property = false:silent
-
-# Language keywords vs BCL types preferences
-dotnet_style_predefined_type_for_locals_parameters_members = true:silent
-dotnet_style_predefined_type_for_member_access = true:silent
-
-# Parentheses preferences
-dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
-dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
-dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
-
-# Modifier preferences
-dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
-
-# Expression-level preferences
-dotnet_style_coalesce_expression = true:suggestion
-dotnet_style_collection_initializer = true:suggestion
-dotnet_style_explicit_tuple_names = true:suggestion
-dotnet_style_null_propagation = true:suggestion
-dotnet_style_object_initializer = true:suggestion
-dotnet_style_operator_placement_when_wrapping = beginning_of_line
-dotnet_style_prefer_auto_properties = true:suggestion
-dotnet_style_prefer_compound_assignment = true:suggestion
-dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
-dotnet_style_prefer_conditional_expression_over_return = true:suggestion
-dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
-dotnet_style_prefer_inferred_tuple_names = true:suggestion
-dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
-dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
-dotnet_style_prefer_simplified_interpolation = true:suggestion
-
-# Field preferences
-dotnet_style_readonly_field = true:warning
-
-# Parameter preferences
-dotnet_code_quality_unused_parameters = all:suggestion
-
-# Suppression preferences
-dotnet_remove_unnecessary_suppression_exclusions = none
-
-#### C# Coding Conventions ####
-[*.cs]
-
-# var preferences
-csharp_style_var_elsewhere = false:silent
-csharp_style_var_for_built_in_types = false:silent
-csharp_style_var_when_type_is_apparent = false:silent
-
-# Expression-bodied members
-csharp_style_expression_bodied_accessors = true:silent
-csharp_style_expression_bodied_constructors = false:silent
-csharp_style_expression_bodied_indexers = true:silent
-csharp_style_expression_bodied_lambdas = true:suggestion
-csharp_style_expression_bodied_local_functions = false:silent
-csharp_style_expression_bodied_methods = false:silent
-csharp_style_expression_bodied_operators = false:silent
-csharp_style_expression_bodied_properties = true:silent
-
-# Pattern matching preferences
-csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
-csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
-csharp_style_prefer_not_pattern = true:suggestion
-csharp_style_prefer_pattern_matching = true:silent
-csharp_style_prefer_switch_expression = true:suggestion
-
-# Null-checking preferences
-csharp_style_conditional_delegate_call = true:suggestion
-
-# Modifier preferences
-csharp_prefer_static_local_function = true:warning
-csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
-
-# Code-block preferences
-csharp_prefer_braces = true:silent
-csharp_prefer_simple_using_statement = true:suggestion
-
-# Expression-level preferences
-csharp_prefer_simple_default_expression = true:suggestion
-csharp_style_deconstructed_variable_declaration = true:suggestion
-csharp_style_inlined_variable_declaration = true:suggestion
-csharp_style_pattern_local_over_anonymous_function = true:suggestion
-csharp_style_prefer_index_operator = true:suggestion
-csharp_style_prefer_range_operator = true:suggestion
-csharp_style_throw_expression = true:suggestion
-csharp_style_unused_value_assignment_preference = discard_variable:suggestion
-csharp_style_unused_value_expression_statement_preference = discard_variable:silent
-
-# 'using' directive preferences
-csharp_using_directive_placement = outside_namespace:silent
-
-#### C# Formatting Rules ####
-
-# New line preferences
-csharp_new_line_before_catch = true
-csharp_new_line_before_else = true
-csharp_new_line_before_finally = true
-csharp_new_line_before_members_in_anonymous_types = true
-csharp_new_line_before_members_in_object_initializers = true
-csharp_new_line_before_open_brace = all
-csharp_new_line_between_query_expression_clauses = true
-
-# Indentation preferences
-csharp_indent_block_contents = true
-csharp_indent_braces = false
-csharp_indent_case_contents = true
-csharp_indent_case_contents_when_block = true
-csharp_indent_labels = one_less_than_current
-csharp_indent_switch_labels = true
-
-# Space preferences
-csharp_space_after_cast = false
-csharp_space_after_colon_in_inheritance_clause = true
-csharp_space_after_comma = true
-csharp_space_after_dot = false
-csharp_space_after_keywords_in_control_flow_statements = true
-csharp_space_after_semicolon_in_for_statement = true
-csharp_space_around_binary_operators = before_and_after
-csharp_space_around_declaration_statements = false
-csharp_space_before_colon_in_inheritance_clause = true
-csharp_space_before_comma = false
-csharp_space_before_dot = false
-csharp_space_before_open_square_brackets = false
-csharp_space_before_semicolon_in_for_statement = false
-csharp_space_between_empty_square_brackets = false
-csharp_space_between_method_call_empty_parameter_list_parentheses = false
-csharp_space_between_method_call_name_and_opening_parenthesis = false
-csharp_space_between_method_call_parameter_list_parentheses = false
-csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
-csharp_space_between_method_declaration_name_and_open_parenthesis = false
-csharp_space_between_method_declaration_parameter_list_parentheses = false
-csharp_space_between_parentheses = false
-csharp_space_between_square_brackets = false
-
-# Wrapping preferences
-csharp_preserve_single_line_blocks = true
-csharp_preserve_single_line_statements = true
-
-#### Naming styles ####
-[*.{cs,vb}]
-
-# Naming rules
-
-dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
-dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
-dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
-dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
-
-dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
-dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
-dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
-
-dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
-dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
-dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.events_should_be_pascalcase.symbols = events
-dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
-dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
-dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
-
-dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
-dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
-dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
-
-dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
-dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
-dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
-
-dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
-dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
-dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
-dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
-
-dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
-dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
-dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
-
-dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
-dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
-dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
-dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
-dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
-dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
-dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
-
-dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
-dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
-dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
-
-# Symbol specifications
-
-dotnet_naming_symbols.interfaces.applicable_kinds = interface
-dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.interfaces.required_modifiers =
-
-dotnet_naming_symbols.enums.applicable_kinds = enum
-dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.enums.required_modifiers =
-
-dotnet_naming_symbols.events.applicable_kinds = event
-dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.events.required_modifiers =
-
-dotnet_naming_symbols.methods.applicable_kinds = method
-dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.methods.required_modifiers =
-
-dotnet_naming_symbols.properties.applicable_kinds = property
-dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.properties.required_modifiers =
-
-dotnet_naming_symbols.public_fields.applicable_kinds = field
-dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
-dotnet_naming_symbols.public_fields.required_modifiers =
-
-dotnet_naming_symbols.private_fields.applicable_kinds = field
-dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
-dotnet_naming_symbols.private_fields.required_modifiers =
-
-dotnet_naming_symbols.private_static_fields.applicable_kinds = field
-dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
-dotnet_naming_symbols.private_static_fields.required_modifiers = static
-
-dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
-dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.types_and_namespaces.required_modifiers =
-
-dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
-dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
-dotnet_naming_symbols.non_field_members.required_modifiers =
-
-dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
-dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
-dotnet_naming_symbols.type_parameters.required_modifiers =
-
-dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
-dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
-dotnet_naming_symbols.private_constant_fields.required_modifiers = const
-
-dotnet_naming_symbols.local_variables.applicable_kinds = local
-dotnet_naming_symbols.local_variables.applicable_accessibilities = local
-dotnet_naming_symbols.local_variables.required_modifiers =
-
-dotnet_naming_symbols.local_constants.applicable_kinds = local
-dotnet_naming_symbols.local_constants.applicable_accessibilities = local
-dotnet_naming_symbols.local_constants.required_modifiers = const
-
-dotnet_naming_symbols.parameters.applicable_kinds = parameter
-dotnet_naming_symbols.parameters.applicable_accessibilities = *
-dotnet_naming_symbols.parameters.required_modifiers =
-
-dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
-dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
-dotnet_naming_symbols.public_constant_fields.required_modifiers = const
-
-dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
-dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
-dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
-
-dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
-dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
-dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
-
-dotnet_naming_symbols.local_functions.applicable_kinds = local_function
-dotnet_naming_symbols.local_functions.applicable_accessibilities = *
-dotnet_naming_symbols.local_functions.required_modifiers =
-
-# Naming styles
-
-dotnet_naming_style.pascalcase.required_prefix =
-dotnet_naming_style.pascalcase.required_suffix =
-dotnet_naming_style.pascalcase.word_separator =
-dotnet_naming_style.pascalcase.capitalization = pascal_case
-
-dotnet_naming_style.ipascalcase.required_prefix = I
-dotnet_naming_style.ipascalcase.required_suffix =
-dotnet_naming_style.ipascalcase.word_separator =
-dotnet_naming_style.ipascalcase.capitalization = pascal_case
-
-dotnet_naming_style.tpascalcase.required_prefix = T
-dotnet_naming_style.tpascalcase.required_suffix =
-dotnet_naming_style.tpascalcase.word_separator =
-dotnet_naming_style.tpascalcase.capitalization = pascal_case
-
-dotnet_naming_style._camelcase.required_prefix = _
-dotnet_naming_style._camelcase.required_suffix =
-dotnet_naming_style._camelcase.word_separator =
-dotnet_naming_style._camelcase.capitalization = camel_case
-
-dotnet_naming_style.camelcase.required_prefix =
-dotnet_naming_style.camelcase.required_suffix =
-dotnet_naming_style.camelcase.word_separator =
-dotnet_naming_style.camelcase.capitalization = camel_case
-
-dotnet_naming_style.s_camelcase.required_prefix = s_
-dotnet_naming_style.s_camelcase.required_suffix =
-dotnet_naming_style.s_camelcase.word_separator =
-dotnet_naming_style.s_camelcase.capitalization = camel_case
+root = true
+
+# All files
+[*]
+indent_style = space
+max_line_length = 120
+
+# Xml files
+[*.xml]
+indent_size = 2
+
+# C# files
+[*.cs]
+
+#### Core EditorConfig Options ####
+
+# Indentation and spacing
+indent_size = 4
+tab_width = 4
+
+# New line preferences
+end_of_line = crlf
+insert_final_newline = true
+
+#### Custom Analyzer Rules ####
+# Until https://github.com/SonarSource/sonar-dotnet/issues/7624 is shipped.
+dotnet_diagnostic.S3604.severity = none
+
+dotnet_diagnostic.SA0001.severity = none
+dotnet_diagnostic.SA1010.severity = none
+dotnet_diagnostic.SA1600.severity = none
+dotnet_diagnostic.CS1591.severity = none
+dotnet_diagnostic.SA1008.severity = suggestion
+dotnet_diagnostic.SA1009.severity = suggestion
+dotnet_diagnostic.SA1101.severity = none
+dotnet_diagnostic.SA1106.severity = suggestion
+dotnet_diagnostic.SA1118.severity = none
+dotnet_diagnostic.SX1101.severity = suggestion
+dotnet_diagnostic.SA1309.severity = none
+dotnet_diagnostic.SX1309.severity = warning
+dotnet_diagnostic.SA1313.severity = suggestion
+dotnet_diagnostic.SA1501.severity = suggestion
+dotnet_diagnostic.SA1503.severity = suggestion
+dotnet_diagnostic.SA1623.severity = none
+dotnet_diagnostic.SA1633.severity = none
+dotnet_diagnostic.SA1642.severity = none
+dotnet_diagnostic.SA1643.severity = none
+dotnet_diagnostic.SA1649.severity = none
+dotnet_diagnostic.RCS1029.severity = none
+dotnet_diagnostic.RCS1090.severity = none
+
+#### Custom Coding Conventions ####
+resharper_csharp_space_in_singleline_accessorholder = true
+resharper_csharp_space_between_accessors_in_singleline_property = true
+resharper_csharp_space_in_singleline_method = true
+resharper_csharp_space_in_singleline_anonymous_method = true
+resharper_csharp_space_within_single_line_array_initializer_braces = true
+resharper_csharp_trailing_comma_in_multiline_lists = true
+resharper_csharp_place_type_attribute_on_same_line = false
+resharper_csharp_place_attribute_on_same_line = false
+
+#### .NET Coding Conventions ####
+[*.{cs,vb}]
+
+# Organize usings
+dotnet_separate_import_directive_groups = true
+dotnet_sort_system_directives_first = true
+file_header_template = unset
+
+# this. and Me. preferences
+dotnet_style_qualification_for_event = false:silent
+dotnet_style_qualification_for_field = false:silent
+dotnet_style_qualification_for_method = false:silent
+dotnet_style_qualification_for_property = false:silent
+
+# Language keywords vs BCL types preferences
+dotnet_style_predefined_type_for_locals_parameters_members = true:silent
+dotnet_style_predefined_type_for_member_access = true:silent
+
+# Parentheses preferences
+dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent
+dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent
+dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent
+
+# Modifier preferences
+dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent
+
+# Expression-level preferences
+dotnet_style_coalesce_expression = true:suggestion
+dotnet_style_collection_initializer = true:suggestion
+dotnet_style_explicit_tuple_names = true:suggestion
+dotnet_style_null_propagation = true:suggestion
+dotnet_style_object_initializer = true:suggestion
+dotnet_style_operator_placement_when_wrapping = beginning_of_line
+dotnet_style_prefer_auto_properties = true:suggestion
+dotnet_style_prefer_compound_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion
+dotnet_style_prefer_conditional_expression_over_return = true:suggestion
+dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
+dotnet_style_prefer_inferred_tuple_names = true:suggestion
+dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion
+dotnet_style_prefer_simplified_boolean_expressions = true:suggestion
+dotnet_style_prefer_simplified_interpolation = true:suggestion
+
+# Field preferences
+dotnet_style_readonly_field = true:warning
+
+# Parameter preferences
+dotnet_code_quality_unused_parameters = all:suggestion
+
+# Suppression preferences
+dotnet_remove_unnecessary_suppression_exclusions = none
+
+#### C# Coding Conventions ####
+[*.cs]
+
+# var preferences
+csharp_style_var_elsewhere = false:silent
+csharp_style_var_for_built_in_types = false:silent
+csharp_style_var_when_type_is_apparent = false:silent
+
+# Expression-bodied members
+csharp_style_expression_bodied_accessors = true:silent
+csharp_style_expression_bodied_constructors = false:silent
+csharp_style_expression_bodied_indexers = true:silent
+csharp_style_expression_bodied_lambdas = true:suggestion
+csharp_style_expression_bodied_local_functions = false:silent
+csharp_style_expression_bodied_methods = false:silent
+csharp_style_expression_bodied_operators = false:silent
+csharp_style_expression_bodied_properties = true:silent
+
+# Pattern matching preferences
+csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
+csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
+csharp_style_prefer_not_pattern = true:suggestion
+csharp_style_prefer_pattern_matching = true:silent
+csharp_style_prefer_switch_expression = true:suggestion
+
+# Null-checking preferences
+csharp_style_conditional_delegate_call = true:suggestion
+
+# Modifier preferences
+csharp_prefer_static_local_function = true:warning
+csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:silent
+
+# Code-block preferences
+csharp_prefer_braces = true:silent
+csharp_prefer_simple_using_statement = true:suggestion
+
+# Expression-level preferences
+csharp_prefer_simple_default_expression = true:suggestion
+csharp_style_deconstructed_variable_declaration = true:suggestion
+csharp_style_inlined_variable_declaration = true:suggestion
+csharp_style_pattern_local_over_anonymous_function = true:suggestion
+csharp_style_prefer_index_operator = true:suggestion
+csharp_style_prefer_range_operator = true:suggestion
+csharp_style_throw_expression = true:suggestion
+csharp_style_unused_value_assignment_preference = discard_variable:suggestion
+csharp_style_unused_value_expression_statement_preference = discard_variable:silent
+
+# 'using' directive preferences
+csharp_using_directive_placement = outside_namespace:silent
+
+#### C# Formatting Rules ####
+
+# New line preferences
+csharp_new_line_before_catch = true
+csharp_new_line_before_else = true
+csharp_new_line_before_finally = true
+csharp_new_line_before_members_in_anonymous_types = true
+csharp_new_line_before_members_in_object_initializers = true
+csharp_new_line_before_open_brace = all
+csharp_new_line_between_query_expression_clauses = true
+
+# Indentation preferences
+csharp_indent_block_contents = true
+csharp_indent_braces = false
+csharp_indent_case_contents = true
+csharp_indent_case_contents_when_block = true
+csharp_indent_labels = one_less_than_current
+csharp_indent_switch_labels = true
+
+# Space preferences
+csharp_space_after_cast = false
+csharp_space_after_colon_in_inheritance_clause = true
+csharp_space_after_comma = true
+csharp_space_after_dot = false
+csharp_space_after_keywords_in_control_flow_statements = true
+csharp_space_after_semicolon_in_for_statement = true
+csharp_space_around_binary_operators = before_and_after
+csharp_space_around_declaration_statements = false
+csharp_space_before_colon_in_inheritance_clause = true
+csharp_space_before_comma = false
+csharp_space_before_dot = false
+csharp_space_before_open_square_brackets = false
+csharp_space_before_semicolon_in_for_statement = false
+csharp_space_between_empty_square_brackets = false
+csharp_space_between_method_call_empty_parameter_list_parentheses = false
+csharp_space_between_method_call_name_and_opening_parenthesis = false
+csharp_space_between_method_call_parameter_list_parentheses = false
+csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
+csharp_space_between_method_declaration_name_and_open_parenthesis = false
+csharp_space_between_method_declaration_parameter_list_parentheses = false
+csharp_space_between_parentheses = false
+csharp_space_between_square_brackets = false
+
+# Wrapping preferences
+csharp_preserve_single_line_blocks = true
+csharp_preserve_single_line_statements = true
+
+#### Naming styles ####
+[*.{cs,vb}]
+
+# Naming rules
+
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.symbols = types_and_namespaces
+dotnet_naming_rule.types_and_namespaces_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.interfaces_should_be_ipascalcase.severity = suggestion
+dotnet_naming_rule.interfaces_should_be_ipascalcase.symbols = interfaces
+dotnet_naming_rule.interfaces_should_be_ipascalcase.style = ipascalcase
+
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.severity = suggestion
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.symbols = type_parameters
+dotnet_naming_rule.type_parameters_should_be_tpascalcase.style = tpascalcase
+
+dotnet_naming_rule.methods_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.methods_should_be_pascalcase.symbols = methods
+dotnet_naming_rule.methods_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.properties_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.properties_should_be_pascalcase.symbols = properties
+dotnet_naming_rule.properties_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.events_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.events_should_be_pascalcase.symbols = events
+dotnet_naming_rule.events_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_variables_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_variables_should_be_camelcase.symbols = local_variables
+dotnet_naming_rule.local_variables_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.local_constants_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.local_constants_should_be_camelcase.symbols = local_constants
+dotnet_naming_rule.local_constants_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.parameters_should_be_camelcase.severity = suggestion
+dotnet_naming_rule.parameters_should_be_camelcase.symbols = parameters
+dotnet_naming_rule.parameters_should_be_camelcase.style = camelcase
+
+dotnet_naming_rule.public_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_fields_should_be_pascalcase.symbols = public_fields
+dotnet_naming_rule.public_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_fields_should_be__camelcase.severity = suggestion
+dotnet_naming_rule.private_fields_should_be__camelcase.symbols = private_fields
+dotnet_naming_rule.private_fields_should_be__camelcase.style = _camelcase
+
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.severity = suggestion
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.symbols = private_static_fields
+dotnet_naming_rule.private_static_fields_should_be_s_camelcase.style = s_camelcase
+
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.symbols = public_constant_fields
+dotnet_naming_rule.public_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.symbols = private_constant_fields
+dotnet_naming_rule.private_constant_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.symbols = public_static_readonly_fields
+dotnet_naming_rule.public_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.symbols = private_static_readonly_fields
+dotnet_naming_rule.private_static_readonly_fields_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.enums_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.enums_should_be_pascalcase.symbols = enums
+dotnet_naming_rule.enums_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.local_functions_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.local_functions_should_be_pascalcase.symbols = local_functions
+dotnet_naming_rule.local_functions_should_be_pascalcase.style = pascalcase
+
+dotnet_naming_rule.non_field_members_should_be_pascalcase.severity = suggestion
+dotnet_naming_rule.non_field_members_should_be_pascalcase.symbols = non_field_members
+dotnet_naming_rule.non_field_members_should_be_pascalcase.style = pascalcase
+
+# Symbol specifications
+
+dotnet_naming_symbols.interfaces.applicable_kinds = interface
+dotnet_naming_symbols.interfaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.interfaces.required_modifiers =
+
+dotnet_naming_symbols.enums.applicable_kinds = enum
+dotnet_naming_symbols.enums.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.enums.required_modifiers =
+
+dotnet_naming_symbols.events.applicable_kinds = event
+dotnet_naming_symbols.events.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.events.required_modifiers =
+
+dotnet_naming_symbols.methods.applicable_kinds = method
+dotnet_naming_symbols.methods.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.methods.required_modifiers =
+
+dotnet_naming_symbols.properties.applicable_kinds = property
+dotnet_naming_symbols.properties.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.properties.required_modifiers =
+
+dotnet_naming_symbols.public_fields.applicable_kinds = field
+dotnet_naming_symbols.public_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_fields.required_modifiers =
+
+dotnet_naming_symbols.private_fields.applicable_kinds = field
+dotnet_naming_symbols.private_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_fields.required_modifiers =
+
+dotnet_naming_symbols.private_static_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_fields.required_modifiers = static
+
+dotnet_naming_symbols.types_and_namespaces.applicable_kinds = namespace, class, struct, interface, enum
+dotnet_naming_symbols.types_and_namespaces.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.types_and_namespaces.required_modifiers =
+
+dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
+dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
+dotnet_naming_symbols.non_field_members.required_modifiers =
+
+dotnet_naming_symbols.type_parameters.applicable_kinds = namespace
+dotnet_naming_symbols.type_parameters.applicable_accessibilities = *
+dotnet_naming_symbols.type_parameters.required_modifiers =
+
+dotnet_naming_symbols.private_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.private_constant_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.local_variables.applicable_kinds = local
+dotnet_naming_symbols.local_variables.applicable_accessibilities = local
+dotnet_naming_symbols.local_variables.required_modifiers =
+
+dotnet_naming_symbols.local_constants.applicable_kinds = local
+dotnet_naming_symbols.local_constants.applicable_accessibilities = local
+dotnet_naming_symbols.local_constants.required_modifiers = const
+
+dotnet_naming_symbols.parameters.applicable_kinds = parameter
+dotnet_naming_symbols.parameters.applicable_accessibilities = *
+dotnet_naming_symbols.parameters.required_modifiers =
+
+dotnet_naming_symbols.public_constant_fields.applicable_kinds = field
+dotnet_naming_symbols.public_constant_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_constant_fields.required_modifiers = const
+
+dotnet_naming_symbols.public_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.public_static_readonly_fields.applicable_accessibilities = public, internal
+dotnet_naming_symbols.public_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.private_static_readonly_fields.applicable_kinds = field
+dotnet_naming_symbols.private_static_readonly_fields.applicable_accessibilities = private, protected, protected_internal, private_protected
+dotnet_naming_symbols.private_static_readonly_fields.required_modifiers = readonly, static
+
+dotnet_naming_symbols.local_functions.applicable_kinds = local_function
+dotnet_naming_symbols.local_functions.applicable_accessibilities = *
+dotnet_naming_symbols.local_functions.required_modifiers =
+
+# Naming styles
+
+dotnet_naming_style.pascalcase.required_prefix =
+dotnet_naming_style.pascalcase.required_suffix =
+dotnet_naming_style.pascalcase.word_separator =
+dotnet_naming_style.pascalcase.capitalization = pascal_case
+
+dotnet_naming_style.ipascalcase.required_prefix = I
+dotnet_naming_style.ipascalcase.required_suffix =
+dotnet_naming_style.ipascalcase.word_separator =
+dotnet_naming_style.ipascalcase.capitalization = pascal_case
+
+dotnet_naming_style.tpascalcase.required_prefix = T
+dotnet_naming_style.tpascalcase.required_suffix =
+dotnet_naming_style.tpascalcase.word_separator =
+dotnet_naming_style.tpascalcase.capitalization = pascal_case
+
+dotnet_naming_style._camelcase.required_prefix = _
+dotnet_naming_style._camelcase.required_suffix =
+dotnet_naming_style._camelcase.word_separator =
+dotnet_naming_style._camelcase.capitalization = camel_case
+
+dotnet_naming_style.camelcase.required_prefix =
+dotnet_naming_style.camelcase.required_suffix =
+dotnet_naming_style.camelcase.word_separator =
+dotnet_naming_style.camelcase.capitalization = camel_case
+
+dotnet_naming_style.s_camelcase.required_prefix = s_
+dotnet_naming_style.s_camelcase.required_suffix =
+dotnet_naming_style.s_camelcase.word_separator =
+dotnet_naming_style.s_camelcase.capitalization = camel_case
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000..c42c3331
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,3 @@
+# Normalize line endings in Git: https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/
+* text=auto
+*.cs text eol=crlf
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml
index d39bd205..f06cb016 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.yaml
+++ b/.github/ISSUE_TEMPLATE/bug_report.yaml
@@ -1,44 +1,44 @@
-name: Bug Report
-description: "Create a report to help fix a problem."
-title: "[bug]: "
-labels: ["bug"]
-body:
-- type: markdown
- attributes:
- value: |
- Thanks for taking the time to fill out this bug report!
-- type: textarea
- id: description
- attributes:
- label: Describe the bug
- description: A clear and concise description of what the bug is.
- validations:
- required: true
-- type: textarea
- id: reproduce
- attributes:
- label: To reproduce
- description: Steps to reproduce the behaviour
- placeholder: |
- Steps to reproduce the behavior:
- 1. Go to '...'
- 2. Click on '....'
- 3. Scroll down to '....'
- 4. See error
- validations:
- required: true
-- type: textarea
- id: expected
- attributes:
- label: Expected behavior
- description: A clear and concise description of what you expected to happen.
-- type: textarea
- id: screenshots
- attributes:
- label: Screenshots
- description: If applicable, add screenshots to help explain your problem.
-- type: textarea
- id: additional
- attributes:
- label: Additional Context
- description: Please add any other infos that could be useful.
+name: Bug Report
+description: "Create a report to help fix a problem."
+title: "[bug]: "
+labels: ["bug"]
+body:
+- type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this bug report!
+- type: textarea
+ id: description
+ attributes:
+ label: Describe the bug
+ description: A clear and concise description of what the bug is.
+ validations:
+ required: true
+- type: textarea
+ id: reproduce
+ attributes:
+ label: To reproduce
+ description: Steps to reproduce the behaviour
+ placeholder: |
+ Steps to reproduce the behavior:
+ 1. Go to '...'
+ 2. Click on '....'
+ 3. Scroll down to '....'
+ 4. See error
+ validations:
+ required: true
+- type: textarea
+ id: expected
+ attributes:
+ label: Expected behavior
+ description: A clear and concise description of what you expected to happen.
+- type: textarea
+ id: screenshots
+ attributes:
+ label: Screenshots
+ description: If applicable, add screenshots to help explain your problem.
+- type: textarea
+ id: additional
+ attributes:
+ label: Additional Context
+ description: Please add any other infos that could be useful.
diff --git a/.github/ISSUE_TEMPLATE/documentation.yaml b/.github/ISSUE_TEMPLATE/documentation.yaml
index ad2905a0..2cfa4a20 100644
--- a/.github/ISSUE_TEMPLATE/documentation.yaml
+++ b/.github/ISSUE_TEMPLATE/documentation.yaml
@@ -1,12 +1,12 @@
-name: Documentation
-description: "Suggest a topic that is not correctly documented (or not documented at all)"
-title: "[docs]: "
-labels: ["documentation"]
-body:
-- type: textarea
- id: description
- attributes:
- label: Describe the missing piece of documentation
- description: Describe what you miss in the docs (or what is wrong).
- validations:
- required: true
+name: Documentation
+description: "Suggest a topic that is not correctly documented (or not documented at all)"
+title: "[docs]: "
+labels: ["documentation"]
+body:
+- type: textarea
+ id: description
+ attributes:
+ label: Describe the missing piece of documentation
+ description: Describe what you miss in the docs (or what is wrong).
+ validations:
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yaml b/.github/ISSUE_TEMPLATE/feature_request.yaml
index 8639a4f6..55a9b0ca 100644
--- a/.github/ISSUE_TEMPLATE/feature_request.yaml
+++ b/.github/ISSUE_TEMPLATE/feature_request.yaml
@@ -1,26 +1,26 @@
-name: Feature Request
-description: "Suggest a new feature for this project."
-title: "[feature]: "
-labels: ["enhancement"]
-body:
-- type: markdown
- attributes:
- value: |
- Thanks for taking the time to fill out this feature request!
-- type: textarea
- id: description
- attributes:
- label: Is your feature request related to a problem? Please describe.
- description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
-- type: textarea
- id: solution
- attributes:
- label: Describe the solution you would like
- description: A clear and concise description of what you want to happen.
- validations:
- required: true
-- type: textarea
- id: additional
- attributes:
- label: Additional Context
- description: Please add any other infos that could be useful.
+name: Feature Request
+description: "Suggest a new feature for this project."
+title: "[feature]: "
+labels: ["enhancement"]
+body:
+- type: markdown
+ attributes:
+ value: |
+ Thanks for taking the time to fill out this feature request!
+- type: textarea
+ id: description
+ attributes:
+ label: Is your feature request related to a problem? Please describe.
+ description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+- type: textarea
+ id: solution
+ attributes:
+ label: Describe the solution you would like
+ description: A clear and concise description of what you want to happen.
+ validations:
+ required: true
+- type: textarea
+ id: additional
+ attributes:
+ label: Additional Context
+ description: Please add any other infos that could be useful.
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index d814d829..ae2bb2d8 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,63 +1,63 @@
-# Contributing to KubeOps
-
-First of all, thank you for considering contributing to KubeOps.
-This is an open souce project and shall be driven by the community.
-
-This document describes how contributions may be done and what is required
-to develop on KubeOps.
-
-## Creating/Reporting Issues
-
-Feel free to open an issue in the [issues section](https://github.com/buehler/dotnet-operator-sdk/issues).
-There are three issue templates:
-- Bug: to report an issue/bug that prevents usage or is an inconvenience of KubeOps
-- Feature request: to report a new feature that would enhance KubeOps
-- Documentation: to report missing / wrong documentation
-
-Please search through the already created issues to find similarities.
-
-## Creating Pull Requests
-
-To directly contribute to the solution, create a fork of the repository
-and implement your addition. Please keep in mind that reviewing takes some
-time and is not done instantly.
-
-Please adhere to the linting rules and the general code style in the repository.
-Also, add tests for your changes to ensure that the system works well
-when other changes happen.
-
-The PR can have any name, but it would be nice if you adhere to
-the repositories standard naming. Please name your PR
-with [Convential Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary).
-
-**NOTE for breaking changes**: please state breaking changes
-in the PR description. The review process will be faster when
-breaking changes are well documented.
-
-A few examples:
-- "fix: Null exception during watcher process"
-- "feat(core): Add new functionality"
-- "feat(testing): expose kubernetes client for testing"
-- "refactor: changed this and that"
-- "docs: Add docs about KubeOps core"
-
-The PR will be squashed and merged into the default branch.
-
-## Local Development
-
-To setup a local development environment, you'll need to perform the follwing steps:
-
-- Check out the repository (or your fork)
-- If you want to run the Operator locally, you'll need some Kubernetes instance.
- This can be any Kubernetes instance you'd like:
- - Local Kubernetes in Docker for Mac/Windows
- - minikube / any other local Kubernetes
- - Deployed Kubernetes (e.g. GCP Kubernetes instance)
-- You can now code your stuff.
-- `tests/KubeOps.TestOperator` is a developed small operator that can be run
- locally to test your implementations.
-- Write tests for your changes
-- Build the whole solution and check for linting errors / warnings.
- **NOTE** that any warning will result in an error when building
- with `Release` configuration.
-- Do not change the linting rules without creating a discussion/issue first.
+# Contributing to KubeOps
+
+First of all, thank you for considering contributing to KubeOps.
+This is an open souce project and shall be driven by the community.
+
+This document describes how contributions may be done and what is required
+to develop on KubeOps.
+
+## Creating/Reporting Issues
+
+Feel free to open an issue in the [issues section](https://github.com/buehler/dotnet-operator-sdk/issues).
+There are three issue templates:
+- Bug: to report an issue/bug that prevents usage or is an inconvenience of KubeOps
+- Feature request: to report a new feature that would enhance KubeOps
+- Documentation: to report missing / wrong documentation
+
+Please search through the already created issues to find similarities.
+
+## Creating Pull Requests
+
+To directly contribute to the solution, create a fork of the repository
+and implement your addition. Please keep in mind that reviewing takes some
+time and is not done instantly.
+
+Please adhere to the linting rules and the general code style in the repository.
+Also, add tests for your changes to ensure that the system works well
+when other changes happen.
+
+The PR can have any name, but it would be nice if you adhere to
+the repositories standard naming. Please name your PR
+with [Convential Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary).
+
+**NOTE for breaking changes**: please state breaking changes
+in the PR description. The review process will be faster when
+breaking changes are well documented.
+
+A few examples:
+- "fix: Null exception during watcher process"
+- "feat(core): Add new functionality"
+- "feat(testing): expose kubernetes client for testing"
+- "refactor: changed this and that"
+- "docs: Add docs about KubeOps core"
+
+The PR will be squashed and merged into the default branch.
+
+## Local Development
+
+To setup a local development environment, you'll need to perform the follwing steps:
+
+- Check out the repository (or your fork)
+- If you want to run the Operator locally, you'll need some Kubernetes instance.
+ This can be any Kubernetes instance you'd like:
+ - Local Kubernetes in Docker for Mac/Windows
+ - minikube / any other local Kubernetes
+ - Deployed Kubernetes (e.g. GCP Kubernetes instance)
+- You can now code your stuff.
+- `tests/KubeOps.TestOperator` is a developed small operator that can be run
+ locally to test your implementations.
+- Write tests for your changes
+- Build the whole solution and check for linting errors / warnings.
+ **NOTE** that any warning will result in an error when building
+ with `Release` configuration.
+- Do not change the linting rules without creating a discussion/issue first.
diff --git a/examples/ConversionWebhookOperator/Controller/V1TestEntityController.cs b/examples/ConversionWebhookOperator/Controller/V1TestEntityController.cs
index c8380f17..7746041a 100644
--- a/examples/ConversionWebhookOperator/Controller/V1TestEntityController.cs
+++ b/examples/ConversionWebhookOperator/Controller/V1TestEntityController.cs
@@ -1,22 +1,22 @@
-using ConversionWebhookOperator.Entities;
-
-using KubeOps.Abstractions.Controller;
-using KubeOps.Abstractions.Rbac;
-
-namespace ConversionWebhookOperator.Controller;
-
-[EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
-public class V1TestEntityController(ILogger logger) : IEntityController
-{
- public Task ReconcileAsync(V1TestEntity entity)
- {
- logger.LogInformation("Reconciling entity {Entity}.", entity);
- return Task.CompletedTask;
- }
-
- public Task DeletedAsync(V1TestEntity entity)
- {
- logger.LogInformation("Deleted entity {Entity}.", entity);
- return Task.CompletedTask;
- }
-}
+using ConversionWebhookOperator.Entities;
+
+using KubeOps.Abstractions.Controller;
+using KubeOps.Abstractions.Rbac;
+
+namespace ConversionWebhookOperator.Controller;
+
+[EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
+public class V1TestEntityController(ILogger logger) : IEntityController
+{
+ public Task ReconcileAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ logger.LogInformation("Reconciling entity {Entity}.", entity);
+ return Task.CompletedTask;
+ }
+
+ public Task DeletedAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ logger.LogInformation("Deleted entity {Entity}.", entity);
+ return Task.CompletedTask;
+ }
+}
diff --git a/examples/ConversionWebhookOperator/Entities/V1TestEntity.cs b/examples/ConversionWebhookOperator/Entities/V1TestEntity.cs
index 90feac2c..a3c3d611 100644
--- a/examples/ConversionWebhookOperator/Entities/V1TestEntity.cs
+++ b/examples/ConversionWebhookOperator/Entities/V1TestEntity.cs
@@ -1,16 +1,16 @@
-using k8s.Models;
-
-using KubeOps.Abstractions.Entities;
-
-namespace ConversionWebhookOperator.Entities;
-
-[KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v1", Kind = "TestEntity")]
-public partial class V1TestEntity : CustomKubernetesEntity
-{
- public override string ToString() => $"Test Entity v1 ({Metadata.Name}): {Spec.Name}";
-
- public class EntitySpec
- {
- public string Name { get; set; } = string.Empty;
- }
-}
+using k8s.Models;
+
+using KubeOps.Abstractions.Entities;
+
+namespace ConversionWebhookOperator.Entities;
+
+[KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v1", Kind = "TestEntity")]
+public partial class V1TestEntity : CustomKubernetesEntity
+{
+ public override string ToString() => $"Test Entity v1 ({Metadata.Name}): {Spec.Name}";
+
+ public class EntitySpec
+ {
+ public string Name { get; set; } = string.Empty;
+ }
+}
diff --git a/examples/ConversionWebhookOperator/Entities/V2TestEntity.cs b/examples/ConversionWebhookOperator/Entities/V2TestEntity.cs
index 477c842e..8525bea5 100644
--- a/examples/ConversionWebhookOperator/Entities/V2TestEntity.cs
+++ b/examples/ConversionWebhookOperator/Entities/V2TestEntity.cs
@@ -1,18 +1,18 @@
-using k8s.Models;
-
-using KubeOps.Abstractions.Entities;
-
-namespace ConversionWebhookOperator.Entities;
-
-[KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v2", Kind = "TestEntity")]
-public partial class V2TestEntity : CustomKubernetesEntity
-{
- public override string ToString() => $"Test Entity v2 ({Metadata.Name}): {Spec.Firstname} {Spec.Lastname}";
-
- public class EntitySpec
- {
- public string Firstname { get; set; } = string.Empty;
-
- public string Lastname { get; set; } = string.Empty;
- }
-}
+using k8s.Models;
+
+using KubeOps.Abstractions.Entities;
+
+namespace ConversionWebhookOperator.Entities;
+
+[KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v2", Kind = "TestEntity")]
+public partial class V2TestEntity : CustomKubernetesEntity
+{
+ public override string ToString() => $"Test Entity v2 ({Metadata.Name}): {Spec.Firstname} {Spec.Lastname}";
+
+ public class EntitySpec
+ {
+ public string Firstname { get; set; } = string.Empty;
+
+ public string Lastname { get; set; } = string.Empty;
+ }
+}
diff --git a/examples/ConversionWebhookOperator/Entities/V3TestEntity.cs b/examples/ConversionWebhookOperator/Entities/V3TestEntity.cs
index cfcd8407..b4fef937 100644
--- a/examples/ConversionWebhookOperator/Entities/V3TestEntity.cs
+++ b/examples/ConversionWebhookOperator/Entities/V3TestEntity.cs
@@ -1,20 +1,20 @@
-using k8s.Models;
-
-using KubeOps.Abstractions.Entities;
-
-namespace ConversionWebhookOperator.Entities;
-
-[KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v3", Kind = "TestEntity")]
-public partial class V3TestEntity : CustomKubernetesEntity
-{
- public override string ToString() => $"Test Entity v3 ({Metadata.Name}): {Spec.Firstname} {Spec.MiddleName} {Spec.Lastname}";
-
- public class EntitySpec
- {
- public string Firstname { get; set; } = string.Empty;
-
- public string Lastname { get; set; } = string.Empty;
-
- public string? MiddleName { get; set; }
- }
-}
+using k8s.Models;
+
+using KubeOps.Abstractions.Entities;
+
+namespace ConversionWebhookOperator.Entities;
+
+[KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v3", Kind = "TestEntity")]
+public partial class V3TestEntity : CustomKubernetesEntity
+{
+ public override string ToString() => $"Test Entity v3 ({Metadata.Name}): {Spec.Firstname} {Spec.MiddleName} {Spec.Lastname}";
+
+ public class EntitySpec
+ {
+ public string Firstname { get; set; } = string.Empty;
+
+ public string Lastname { get; set; } = string.Empty;
+
+ public string? MiddleName { get; set; }
+ }
+}
diff --git a/examples/ConversionWebhookOperator/Program.cs b/examples/ConversionWebhookOperator/Program.cs
index cf328b30..44a63cea 100644
--- a/examples/ConversionWebhookOperator/Program.cs
+++ b/examples/ConversionWebhookOperator/Program.cs
@@ -1,22 +1,22 @@
-using KubeOps.Operator;
-using KubeOps.Operator.Web.Builder;
-
-var builder = WebApplication.CreateBuilder(args);
-builder.Services
- .AddKubernetesOperator()
- .RegisterComponents()
-#if DEBUG
- .AddDevelopmentTunnel(5000)
-#endif
- ;
-
-builder.Services
- .AddControllers();
-
-var app = builder.Build();
-
-app.UseRouting();
-app.UseDeveloperExceptionPage();
-app.MapControllers();
-
-await app.RunAsync();
+using KubeOps.Operator;
+using KubeOps.Operator.Web.Builder;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services
+ .AddKubernetesOperator()
+ .RegisterComponents()
+#if DEBUG
+ .AddDevelopmentTunnel(5000)
+#endif
+ ;
+
+builder.Services
+ .AddControllers();
+
+var app = builder.Build();
+
+app.UseRouting();
+app.UseDeveloperExceptionPage();
+app.MapControllers();
+
+await app.RunAsync();
diff --git a/examples/ConversionWebhookOperator/Webhooks/TestConversionWebhook.cs b/examples/ConversionWebhookOperator/Webhooks/TestConversionWebhook.cs
index 49b1bd65..086b7957 100644
--- a/examples/ConversionWebhookOperator/Webhooks/TestConversionWebhook.cs
+++ b/examples/ConversionWebhookOperator/Webhooks/TestConversionWebhook.cs
@@ -1,52 +1,52 @@
-using ConversionWebhookOperator.Entities;
-
-using KubeOps.Operator.Web.Webhooks.Conversion;
-
-namespace ConversionWebhookOperator.Webhooks;
-
-[ConversionWebhook(typeof(V3TestEntity))]
-public class TestConversionWebhook : ConversionWebhook
-{
- protected override IEnumerable> Converters => new IEntityConverter[]
- {
- new V1ToV3(), new V2ToV3(),
- };
-
- private class V1ToV3 : IEntityConverter
- {
- public V3TestEntity Convert(V1TestEntity from)
- {
- var nameSplit = from.Spec.Name.Split(' ');
- var result = new V3TestEntity { Metadata = from.Metadata };
- result.Spec.Firstname = nameSplit[0];
- result.Spec.Lastname = string.Join(' ', nameSplit[1..]);
- return result;
- }
-
- public V1TestEntity Revert(V3TestEntity to)
- {
- var result = new V1TestEntity { Metadata = to.Metadata };
- result.Spec.Name = $"{to.Spec.Firstname} {to.Spec.Lastname}";
- return result;
- }
- }
-
- private class V2ToV3 : IEntityConverter
- {
- public V3TestEntity Convert(V2TestEntity from)
- {
- var result = new V3TestEntity { Metadata = from.Metadata };
- result.Spec.Firstname = from.Spec.Firstname;
- result.Spec.Lastname = from.Spec.Lastname;
- return result;
- }
-
- public V2TestEntity Revert(V3TestEntity to)
- {
- var result = new V2TestEntity { Metadata = to.Metadata };
- result.Spec.Firstname = to.Spec.Firstname;
- result.Spec.Lastname = to.Spec.Lastname;
- return result;
- }
- }
-}
+using ConversionWebhookOperator.Entities;
+
+using KubeOps.Operator.Web.Webhooks.Conversion;
+
+namespace ConversionWebhookOperator.Webhooks;
+
+[ConversionWebhook(typeof(V3TestEntity))]
+public class TestConversionWebhook : ConversionWebhook
+{
+ protected override IEnumerable> Converters => new IEntityConverter[]
+ {
+ new V1ToV3(), new V2ToV3(),
+ };
+
+ private class V1ToV3 : IEntityConverter
+ {
+ public V3TestEntity Convert(V1TestEntity from)
+ {
+ var nameSplit = from.Spec.Name.Split(' ');
+ var result = new V3TestEntity { Metadata = from.Metadata };
+ result.Spec.Firstname = nameSplit[0];
+ result.Spec.Lastname = string.Join(' ', nameSplit[1..]);
+ return result;
+ }
+
+ public V1TestEntity Revert(V3TestEntity to)
+ {
+ var result = new V1TestEntity { Metadata = to.Metadata };
+ result.Spec.Name = $"{to.Spec.Firstname} {to.Spec.Lastname}";
+ return result;
+ }
+ }
+
+ private class V2ToV3 : IEntityConverter
+ {
+ public V3TestEntity Convert(V2TestEntity from)
+ {
+ var result = new V3TestEntity { Metadata = from.Metadata };
+ result.Spec.Firstname = from.Spec.Firstname;
+ result.Spec.Lastname = from.Spec.Lastname;
+ return result;
+ }
+
+ public V2TestEntity Revert(V3TestEntity to)
+ {
+ var result = new V2TestEntity { Metadata = to.Metadata };
+ result.Spec.Firstname = to.Spec.Firstname;
+ result.Spec.Lastname = to.Spec.Lastname;
+ return result;
+ }
+ }
+}
diff --git a/examples/Operator/Controller/V1TestEntityController.cs b/examples/Operator/Controller/V1TestEntityController.cs
index c7fb311b..d2c28cbf 100644
--- a/examples/Operator/Controller/V1TestEntityController.cs
+++ b/examples/Operator/Controller/V1TestEntityController.cs
@@ -1,32 +1,32 @@
-using KubeOps.Abstractions.Controller;
-using KubeOps.Abstractions.Events;
-using KubeOps.Abstractions.Queue;
-using KubeOps.Abstractions.Rbac;
-
-using Microsoft.Extensions.Logging;
-
-using Operator.Entities;
-
-namespace Operator.Controller;
-
-[EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
-public class V1TestEntityController(ILogger logger,
- EntityRequeue requeue,
- EventPublisher eventPublisher)
- : IEntityController
-{
- public async Task ReconcileAsync(V1TestEntity entity)
- {
- logger.LogInformation("Reconciling entity {Entity}.", entity);
-
- await eventPublisher(entity, "RECONCILED", "Entity was reconciled.");
-
- requeue(entity, TimeSpan.FromSeconds(5));
- }
-
- public Task DeletedAsync(V1TestEntity entity)
- {
- logger.LogInformation("Deleting entity {Entity}.", entity);
- return Task.CompletedTask;
- }
-}
+using KubeOps.Abstractions.Controller;
+using KubeOps.Abstractions.Events;
+using KubeOps.Abstractions.Queue;
+using KubeOps.Abstractions.Rbac;
+
+using Microsoft.Extensions.Logging;
+
+using Operator.Entities;
+
+namespace Operator.Controller;
+
+[EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
+public class V1TestEntityController(ILogger logger,
+ EntityRequeue requeue,
+ EventPublisher eventPublisher)
+ : IEntityController
+{
+ public async Task ReconcileAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ logger.LogInformation("Reconciling entity {Entity}.", entity);
+
+ await eventPublisher(entity, "RECONCILED", "Entity was reconciled.");
+
+ requeue(entity, TimeSpan.FromSeconds(5));
+ }
+
+ public Task DeletedAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ logger.LogInformation("Deleting entity {Entity}.", entity);
+ return Task.CompletedTask;
+ }
+}
diff --git a/examples/Operator/Entities/V1TestEntity.cs b/examples/Operator/Entities/V1TestEntity.cs
index 2ec912a7..b76650c1 100644
--- a/examples/Operator/Entities/V1TestEntity.cs
+++ b/examples/Operator/Entities/V1TestEntity.cs
@@ -1,18 +1,18 @@
-using k8s.Models;
-
-using KubeOps.Abstractions.Entities;
-
-namespace Operator.Entities;
-
-[KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")]
-public partial class V1TestEntity : CustomKubernetesEntity
-{
- public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username} ({Spec.Email})";
-
- public class EntitySpec
- {
- public string Username { get; set; } = string.Empty;
-
- public string Email { get; set; } = string.Empty;
- }
-}
+using k8s.Models;
+
+using KubeOps.Abstractions.Entities;
+
+namespace Operator.Entities;
+
+[KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")]
+public partial class V1TestEntity : CustomKubernetesEntity
+{
+ public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username} ({Spec.Email})";
+
+ public class EntitySpec
+ {
+ public string Username { get; set; } = string.Empty;
+
+ public string Email { get; set; } = string.Empty;
+ }
+}
diff --git a/examples/Operator/Finalizer/FinalizerOne.cs b/examples/Operator/Finalizer/FinalizerOne.cs
index f46a173f..319bbabf 100644
--- a/examples/Operator/Finalizer/FinalizerOne.cs
+++ b/examples/Operator/Finalizer/FinalizerOne.cs
@@ -1,13 +1,13 @@
-using KubeOps.Abstractions.Finalizer;
-
-using Operator.Entities;
-
-namespace Operator.Finalizer;
-
-public class FinalizerOne : IEntityFinalizer
-{
- public Task FinalizeAsync(V1TestEntity entity)
- {
- return Task.CompletedTask;
- }
-}
+using KubeOps.Abstractions.Finalizer;
+
+using Operator.Entities;
+
+namespace Operator.Finalizer;
+
+public class FinalizerOne : IEntityFinalizer
+{
+ public Task FinalizeAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ return Task.CompletedTask;
+ }
+}
diff --git a/examples/Operator/Program.cs b/examples/Operator/Program.cs
index 0bd007a9..ba2acd6a 100644
--- a/examples/Operator/Program.cs
+++ b/examples/Operator/Program.cs
@@ -1,15 +1,15 @@
-using KubeOps.Operator;
-
-using Microsoft.Extensions.Hosting;
-using Microsoft.Extensions.Logging;
-
-var builder = Host.CreateApplicationBuilder(args);
-
-builder.Logging.SetMinimumLevel(LogLevel.Trace);
-
-builder.Services
- .AddKubernetesOperator()
- .RegisterComponents();
-
-using var host = builder.Build();
-await host.RunAsync();
+using KubeOps.Operator;
+
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+
+var builder = Host.CreateApplicationBuilder(args);
+
+builder.Logging.SetMinimumLevel(LogLevel.Trace);
+
+builder.Services
+ .AddKubernetesOperator()
+ .RegisterComponents();
+
+using var host = builder.Build();
+await host.RunAsync();
diff --git a/examples/WebhookOperator/Controller/V1TestEntityController.cs b/examples/WebhookOperator/Controller/V1TestEntityController.cs
index 5fff076f..c511b7a7 100644
--- a/examples/WebhookOperator/Controller/V1TestEntityController.cs
+++ b/examples/WebhookOperator/Controller/V1TestEntityController.cs
@@ -1,22 +1,22 @@
-using KubeOps.Abstractions.Controller;
-using KubeOps.Abstractions.Rbac;
-
-using WebhookOperator.Entities;
-
-namespace WebhookOperator.Controller;
-
-[EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
-public class V1TestEntityController(ILogger logger) : IEntityController
-{
- public Task ReconcileAsync(V1TestEntity entity)
- {
- logger.LogInformation("Reconciling entity {Entity}.", entity);
- return Task.CompletedTask;
- }
-
- public Task DeletedAsync(V1TestEntity entity)
- {
- logger.LogInformation("Deleted entity {Entity}.", entity);
- return Task.CompletedTask;
- }
-}
+using KubeOps.Abstractions.Controller;
+using KubeOps.Abstractions.Rbac;
+
+using WebhookOperator.Entities;
+
+namespace WebhookOperator.Controller;
+
+[EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
+public class V1TestEntityController(ILogger logger) : IEntityController
+{
+ public Task ReconcileAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ logger.LogInformation("Reconciling entity {Entity}.", entity);
+ return Task.CompletedTask;
+ }
+
+ public Task DeletedAsync(V1TestEntity entity, CancellationToken cancellationToken)
+ {
+ logger.LogInformation("Deleted entity {Entity}.", entity);
+ return Task.CompletedTask;
+ }
+}
diff --git a/examples/WebhookOperator/Entities/V1TestEntity.cs b/examples/WebhookOperator/Entities/V1TestEntity.cs
index 7855d56b..8610df26 100644
--- a/examples/WebhookOperator/Entities/V1TestEntity.cs
+++ b/examples/WebhookOperator/Entities/V1TestEntity.cs
@@ -1,16 +1,16 @@
-using k8s.Models;
-
-using KubeOps.Abstractions.Entities;
-
-namespace WebhookOperator.Entities;
-
-[KubernetesEntity(Group = "webhook.dev", ApiVersion = "v1", Kind = "TestEntity")]
-public partial class V1TestEntity : CustomKubernetesEntity
-{
- public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username}";
-
- public class EntitySpec
- {
- public string Username { get; set; } = string.Empty;
- }
-}
+using k8s.Models;
+
+using KubeOps.Abstractions.Entities;
+
+namespace WebhookOperator.Entities;
+
+[KubernetesEntity(Group = "webhook.dev", ApiVersion = "v1", Kind = "TestEntity")]
+public partial class V1TestEntity : CustomKubernetesEntity
+{
+ public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username}";
+
+ public class EntitySpec
+ {
+ public string Username { get; set; } = string.Empty;
+ }
+}
diff --git a/examples/WebhookOperator/Program.cs b/examples/WebhookOperator/Program.cs
index cf328b30..44a63cea 100644
--- a/examples/WebhookOperator/Program.cs
+++ b/examples/WebhookOperator/Program.cs
@@ -1,22 +1,22 @@
-using KubeOps.Operator;
-using KubeOps.Operator.Web.Builder;
-
-var builder = WebApplication.CreateBuilder(args);
-builder.Services
- .AddKubernetesOperator()
- .RegisterComponents()
-#if DEBUG
- .AddDevelopmentTunnel(5000)
-#endif
- ;
-
-builder.Services
- .AddControllers();
-
-var app = builder.Build();
-
-app.UseRouting();
-app.UseDeveloperExceptionPage();
-app.MapControllers();
-
-await app.RunAsync();
+using KubeOps.Operator;
+using KubeOps.Operator.Web.Builder;
+
+var builder = WebApplication.CreateBuilder(args);
+builder.Services
+ .AddKubernetesOperator()
+ .RegisterComponents()
+#if DEBUG
+ .AddDevelopmentTunnel(5000)
+#endif
+ ;
+
+builder.Services
+ .AddControllers();
+
+var app = builder.Build();
+
+app.UseRouting();
+app.UseDeveloperExceptionPage();
+app.MapControllers();
+
+await app.RunAsync();
diff --git a/examples/WebhookOperator/Webhooks/TestMutationWebhook.cs b/examples/WebhookOperator/Webhooks/TestMutationWebhook.cs
index e1e2034e..c017c486 100644
--- a/examples/WebhookOperator/Webhooks/TestMutationWebhook.cs
+++ b/examples/WebhookOperator/Webhooks/TestMutationWebhook.cs
@@ -1,20 +1,20 @@
-using KubeOps.Operator.Web.Webhooks.Admission.Mutation;
-
-using WebhookOperator.Entities;
-
-namespace WebhookOperator.Webhooks;
-
-[MutationWebhook(typeof(V1TestEntity))]
-public class TestMutationWebhook : MutationWebhook
-{
- public override MutationResult Create(V1TestEntity entity, bool dryRun)
- {
- if (entity.Spec.Username == "overwrite")
- {
- entity.Spec.Username = "random overwritten";
- return Modified(entity);
- }
-
- return NoChanges();
- }
-}
+using KubeOps.Operator.Web.Webhooks.Admission.Mutation;
+
+using WebhookOperator.Entities;
+
+namespace WebhookOperator.Webhooks;
+
+[MutationWebhook(typeof(V1TestEntity))]
+public class TestMutationWebhook : MutationWebhook
+{
+ public override MutationResult Create(V1TestEntity entity, bool dryRun)
+ {
+ if (entity.Spec.Username == "overwrite")
+ {
+ entity.Spec.Username = "random overwritten";
+ return Modified(entity);
+ }
+
+ return NoChanges();
+ }
+}
diff --git a/examples/WebhookOperator/Webhooks/TestValidationWebhook.cs b/examples/WebhookOperator/Webhooks/TestValidationWebhook.cs
index 0422c552..c1c1d53d 100644
--- a/examples/WebhookOperator/Webhooks/TestValidationWebhook.cs
+++ b/examples/WebhookOperator/Webhooks/TestValidationWebhook.cs
@@ -1,29 +1,29 @@
-using KubeOps.Operator.Web.Webhooks.Admission.Validation;
-
-using WebhookOperator.Entities;
-
-namespace WebhookOperator.Webhooks;
-
-[ValidationWebhook(typeof(V1TestEntity))]
-public class TestValidationWebhook : ValidationWebhook
-{
- public override ValidationResult Create(V1TestEntity entity, bool dryRun)
- {
- if (entity.Spec.Username == "forbidden")
- {
- return Fail("name may not be 'forbidden'.", 422);
- }
-
- return Success();
- }
-
- public override ValidationResult Update(V1TestEntity oldEntity, V1TestEntity newEntity, bool dryRun)
- {
- if (newEntity.Spec.Username == "forbidden")
- {
- return Fail("name may not be 'forbidden'.");
- }
-
- return Success();
- }
-}
+using KubeOps.Operator.Web.Webhooks.Admission.Validation;
+
+using WebhookOperator.Entities;
+
+namespace WebhookOperator.Webhooks;
+
+[ValidationWebhook(typeof(V1TestEntity))]
+public class TestValidationWebhook : ValidationWebhook
+{
+ public override ValidationResult Create(V1TestEntity entity, bool dryRun)
+ {
+ if (entity.Spec.Username == "forbidden")
+ {
+ return Fail("name may not be 'forbidden'.", 422);
+ }
+
+ return Success();
+ }
+
+ public override ValidationResult Update(V1TestEntity oldEntity, V1TestEntity newEntity, bool dryRun)
+ {
+ if (newEntity.Spec.Username == "forbidden")
+ {
+ return Fail("name may not be 'forbidden'.");
+ }
+
+ return Success();
+ }
+}
diff --git a/src/KubeOps.Abstractions/Builder/IOperatorBuilder.cs b/src/KubeOps.Abstractions/Builder/IOperatorBuilder.cs
index 2553ffd9..0088276c 100644
--- a/src/KubeOps.Abstractions/Builder/IOperatorBuilder.cs
+++ b/src/KubeOps.Abstractions/Builder/IOperatorBuilder.cs
@@ -1,49 +1,49 @@
-using k8s;
-using k8s.Models;
-
-using KubeOps.Abstractions.Controller;
-using KubeOps.Abstractions.Finalizer;
-
-using Microsoft.Extensions.DependencyInjection;
-
-namespace KubeOps.Abstractions.Builder;
-
-///
-/// KubeOps operator builder.
-///
-public interface IOperatorBuilder
-{
- ///
- /// The original service collection.
- ///
- IServiceCollection Services { get; }
-
- ///
- /// Add a controller implementation for a specific entity to the operator.
- /// The metadata for the entity must be added as well.
- ///
- /// Implementation type of the controller.
- /// Entity type.
- /// The builder for chaining.
- IOperatorBuilder AddController()
- where TImplementation : class, IEntityController
- where TEntity : IKubernetesObject;
-
- ///
- /// Add a finalizer implementation for a specific entity.
- /// This adds the implementation as a transient service and registers
- /// the finalizer with the provided identifier. Then an
- /// is registered to
- /// provide a delegate for attaching the finalizer to an entity.
- ///
- ///
- /// The identifier for the finalizer.
- /// This string is added to the Kubernetes entity as a finalizer.
- ///
- /// Type of the finalizer implementation.
- /// Type of the Kubernetes entity.
- /// The builder for chaining.
- IOperatorBuilder AddFinalizer(string identifier)
- where TImplementation : class, IEntityFinalizer
- where TEntity : IKubernetesObject;
-}
+using k8s;
+using k8s.Models;
+
+using KubeOps.Abstractions.Controller;
+using KubeOps.Abstractions.Finalizer;
+
+using Microsoft.Extensions.DependencyInjection;
+
+namespace KubeOps.Abstractions.Builder;
+
+///
+/// KubeOps operator builder.
+///
+public interface IOperatorBuilder
+{
+ ///
+ /// The original service collection.
+ ///
+ IServiceCollection Services { get; }
+
+ ///
+ /// Add a controller implementation for a specific entity to the operator.
+ /// The metadata for the entity must be added as well.
+ ///
+ /// Implementation type of the controller.
+ /// Entity type.
+ /// The builder for chaining.
+ IOperatorBuilder AddController()
+ where TImplementation : class, IEntityController
+ where TEntity : IKubernetesObject;
+
+ ///
+ /// Add a finalizer implementation for a specific entity.
+ /// This adds the implementation as a transient service and registers
+ /// the finalizer with the provided identifier. Then an
+ /// is registered to
+ /// provide a delegate for attaching the finalizer to an entity.
+ ///
+ ///
+ /// The identifier for the finalizer.
+ /// This string is added to the Kubernetes entity as a finalizer.
+ ///
+ /// Type of the finalizer implementation.
+ /// Type of the Kubernetes entity.
+ /// The builder for chaining.
+ IOperatorBuilder AddFinalizer(string identifier)
+ where TImplementation : class, IEntityFinalizer
+ where TEntity : IKubernetesObject;
+}
diff --git a/src/KubeOps.Abstractions/Builder/OperatorSettings.cs b/src/KubeOps.Abstractions/Builder/OperatorSettings.cs
index ac110659..2faed930 100644
--- a/src/KubeOps.Abstractions/Builder/OperatorSettings.cs
+++ b/src/KubeOps.Abstractions/Builder/OperatorSettings.cs
@@ -1,62 +1,62 @@
-using System.Text.RegularExpressions;
-
-namespace KubeOps.Abstractions.Builder;
-
-///
-/// Operator settings.
-///
-public sealed class OperatorSettings
-{
- private const string DefaultOperatorName = "KubernetesOperator";
- private const string NonCharReplacement = "-";
-
- ///
- /// The name of the operator that appears in logs and other elements.
- /// Defaults to "kubernetesoperator" when not set.
- ///
- public string Name { get; set; } =
- new Regex(@"(\W|_)", RegexOptions.CultureInvariant).Replace(
- DefaultOperatorName,
- NonCharReplacement)
- .ToLowerInvariant();
-
- ///
- ///
- /// Controls the namespace which is watched by the operator.
- /// If this field is left `null`, all namespaces are watched for
- /// CRD instances.
- ///
- ///
- public string? Namespace { get; set; }
-
- ///
- ///
- /// Whether the leader elector should run. You should enable
- /// this if you plan to run the operator redundantly.
- ///
- ///
- /// If this is disabled and an operator runs in multiple instances
- /// (in the same namespace), it can lead to a "split brain" problem.
- ///
- ///
- /// Defaults to `false`.
- ///
- ///
- public bool EnableLeaderElection { get; set; } = false;
-
- ///
- /// Defines how long one lease is valid for any leader.
- /// Defaults to 15 seconds.
- ///
- public TimeSpan LeaderElectionLeaseDuration { get; set; } = TimeSpan.FromSeconds(15);
-
- ///
- /// When the leader elector tries to refresh the leadership lease.
- ///
- public TimeSpan LeaderElectionRenewDeadline { get; set; } = TimeSpan.FromSeconds(10);
-
- ///
- /// The wait timeout if the lease cannot be acquired.
- ///
- public TimeSpan LeaderElectionRetryPeriod { get; set; } = TimeSpan.FromSeconds(2);
-}
+using System.Text.RegularExpressions;
+
+namespace KubeOps.Abstractions.Builder;
+
+///
+/// Operator settings.
+///
+public sealed class OperatorSettings
+{
+ private const string DefaultOperatorName = "KubernetesOperator";
+ private const string NonCharReplacement = "-";
+
+ ///
+ /// The name of the operator that appears in logs and other elements.
+ /// Defaults to "kubernetesoperator" when not set.
+ ///
+ public string Name { get; set; } =
+ new Regex(@"(\W|_)", RegexOptions.CultureInvariant).Replace(
+ DefaultOperatorName,
+ NonCharReplacement)
+ .ToLowerInvariant();
+
+ ///
+ ///
+ /// Controls the namespace which is watched by the operator.
+ /// If this field is left `null`, all namespaces are watched for
+ /// CRD instances.
+ ///
+ ///
+ public string? Namespace { get; set; }
+
+ ///
+ ///
+ /// Whether the leader elector should run. You should enable
+ /// this if you plan to run the operator redundantly.
+ ///
+ ///
+ /// If this is disabled and an operator runs in multiple instances
+ /// (in the same namespace), it can lead to a "split brain" problem.
+ ///
+ ///
+ /// Defaults to `false`.
+ ///
+ ///
+ public bool EnableLeaderElection { get; set; } = false;
+
+ ///
+ /// Defines how long one lease is valid for any leader.
+ /// Defaults to 15 seconds.
+ ///
+ public TimeSpan LeaderElectionLeaseDuration { get; set; } = TimeSpan.FromSeconds(15);
+
+ ///
+ /// When the leader elector tries to refresh the leadership lease.
+ ///
+ public TimeSpan LeaderElectionRenewDeadline { get; set; } = TimeSpan.FromSeconds(10);
+
+ ///
+ /// The wait timeout if the lease cannot be acquired.
+ ///
+ public TimeSpan LeaderElectionRetryPeriod { get; set; } = TimeSpan.FromSeconds(2);
+}
diff --git a/src/KubeOps.Abstractions/Controller/IEntityController{TEntity}.cs b/src/KubeOps.Abstractions/Controller/IEntityController{TEntity}.cs
index 7d3c3892..87856087 100644
--- a/src/KubeOps.Abstractions/Controller/IEntityController{TEntity}.cs
+++ b/src/KubeOps.Abstractions/Controller/IEntityController{TEntity}.cs
@@ -1,56 +1,56 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Controller;
-
-///
-/// Generic entity controller. The controller manages the reconcile loop
-/// for a given entity type.
-///
-/// The type of the Kubernetes entity.
-///
-/// Simple example controller that just logs the entity.
-///
-/// public class V1TestEntityController : IEntityController<V1TestEntity>
-/// {
-/// private readonly ILogger<V1TestEntityController> _logger;
-///
-/// public V1TestEntityController(
-/// ILogger<V1TestEntityController> logger)
-/// {
-/// _logger = logger;
-/// }
-///
-/// public async Task ReconcileAsync(V1TestEntity entity)
-/// {
-/// _logger.LogInformation("Reconciling entity {Entity}.", entity);
-/// }
-///
-/// public async Task DeletedAsync(V1TestEntity entity)
-/// {
-/// _logger.LogInformation("Deleting entity {Entity}.", entity);
-/// }
-/// }
-///
-///
-public interface IEntityController
- where TEntity : IKubernetesObject
-{
- ///
- /// Called for `added` and `modified` events from the watcher.
- ///
- /// The entity that fired the reconcile event.
- /// A task that completes when the reconciliation is done.
- Task ReconcileAsync(TEntity entity) =>
- Task.CompletedTask;
-
- ///
- /// Called for `delete` events for a given entity.
- ///
- /// The entity that fired the deleted event.
- ///
- /// A task that completes, when the reconciliation is done.
- ///
- Task DeletedAsync(TEntity entity) =>
- Task.CompletedTask;
-}
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Controller;
+
+///
+/// Generic entity controller. The controller manages the reconcile loop
+/// for a given entity type.
+///
+/// The type of the Kubernetes entity.
+///
+/// Simple example controller that just logs the entity.
+///
+/// public class V1TestEntityController : IEntityController<V1TestEntity>
+/// {
+/// private readonly ILogger<V1TestEntityController> _logger;
+///
+/// public V1TestEntityController(
+/// ILogger<V1TestEntityController> logger)
+/// {
+/// _logger = logger;
+/// }
+///
+/// public async Task ReconcileAsync(V1TestEntity entity, CancellationToken token)
+/// {
+/// _logger.LogInformation("Reconciling entity {Entity}.", entity);
+/// }
+///
+/// public async Task DeletedAsync(V1TestEntity entity, CancellationToken token)
+/// {
+/// _logger.LogInformation("Deleting entity {Entity}.", entity);
+/// }
+/// }
+///
+///
+public interface IEntityController
+ where TEntity : IKubernetesObject
+{
+ ///
+ /// Called for `added` and `modified` events from the watcher.
+ ///
+ /// The entity that fired the reconcile event.
+ /// The token to monitor for cancellation requests.
+ /// A task that completes when the reconciliation is done.
+ Task ReconcileAsync(TEntity entity, CancellationToken cancellationToken);
+
+ ///
+ /// Called for `delete` events for a given entity.
+ ///
+ /// The entity that fired the deleted event.
+ /// The token to monitor for cancellation requests.
+ ///
+ /// A task that completes, when the reconciliation is done.
+ ///
+ Task DeletedAsync(TEntity entity, CancellationToken cancellationToken);
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/AdditionalPrinterColumnAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/AdditionalPrinterColumnAttribute.cs
index b561fdcd..286565bd 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/AdditionalPrinterColumnAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/AdditionalPrinterColumnAttribute.cs
@@ -1,30 +1,30 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a property as an additional printer column.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class AdditionalPrinterColumnAttribute(PrinterColumnPriority priority = default, string? name = null)
- : Attribute
-{
- ///
- /// The name of the column. Defaults to the property-name.
- ///
- public string? Name => name;
-
- ///
- /// The priority of the additional printer column.
- /// As documented in
- /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#priority
- /// the following rules apply to priority:
- ///
- /// -
- /// Columns with priority `0` are shown in standard view
- ///
- /// -
- /// Columns with priority greater than `0` are shown only in wide view
- ///
- ///
- ///
- public PrinterColumnPriority Priority => priority;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a property as an additional printer column.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class AdditionalPrinterColumnAttribute(PrinterColumnPriority priority = default, string? name = null)
+ : Attribute
+{
+ ///
+ /// The name of the column. Defaults to the property-name.
+ ///
+ public string? Name => name;
+
+ ///
+ /// The priority of the additional printer column.
+ /// As documented in
+ /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#priority
+ /// the following rules apply to priority:
+ ///
+ /// -
+ /// Columns with priority `0` are shown in standard view
+ ///
+ /// -
+ /// Columns with priority greater than `0` are shown only in wide view
+ ///
+ ///
+ ///
+ public PrinterColumnPriority Priority => priority;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/DescriptionAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/DescriptionAttribute.cs
index 9db44fef..3095000c 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/DescriptionAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/DescriptionAttribute.cs
@@ -1,14 +1,14 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a description for a property. This precedes the description found in a
-/// XML documentation file.
-///
-[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
-public class DescriptionAttribute(string description) : Attribute
-{
- ///
- /// The given description for the property.
- ///
- public string Description => description;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a description for a property. This precedes the description found in a
+/// XML documentation file.
+///
+[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)]
+public class DescriptionAttribute(string description) : Attribute
+{
+ ///
+ /// The given description for the property.
+ ///
+ public string Description => description;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/EmbeddedResourceAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/EmbeddedResourceAttribute.cs
index 60e6818e..b09ddecb 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/EmbeddedResourceAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/EmbeddedResourceAttribute.cs
@@ -1,12 +1,12 @@
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a property as an embedded resource.
-/// This property can contain another Kubernetes object
-/// (e.g. a or a ).
-/// This implicitly sets the .
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class EmbeddedResourceAttribute : Attribute;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a property as an embedded resource.
+/// This property can contain another Kubernetes object
+/// (e.g. a or a ).
+/// This implicitly sets the .
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class EmbeddedResourceAttribute : Attribute;
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/EntityScopeAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/EntityScopeAttribute.cs
index 7468850e..08ecfb83 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/EntityScopeAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/EntityScopeAttribute.cs
@@ -1,7 +1,7 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-[AttributeUsage(AttributeTargets.Class, Inherited = false)]
-public class EntityScopeAttribute(EntityScope scope = default) : Attribute
-{
- public EntityScope Scope => scope;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+[AttributeUsage(AttributeTargets.Class, Inherited = false)]
+public class EntityScopeAttribute(EntityScope scope = default) : Attribute
+{
+ public EntityScope Scope => scope;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/ExternalDocsAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/ExternalDocsAttribute.cs
index 237b0f35..864c9488 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/ExternalDocsAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/ExternalDocsAttribute.cs
@@ -1,18 +1,18 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines that the property has an external documentation.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class ExternalDocsAttribute(string url, string? description = null) : Attribute
-{
- ///
- /// Additional description.
- ///
- public string? Description => description;
-
- ///
- /// Url where to find the documentation.
- ///
- public string Url => url;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines that the property has an external documentation.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class ExternalDocsAttribute(string url, string? description = null) : Attribute
+{
+ ///
+ /// Additional description.
+ ///
+ public string? Description => description;
+
+ ///
+ /// Url where to find the documentation.
+ ///
+ public string Url => url;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/GenericAdditionalPrinterColumnAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/GenericAdditionalPrinterColumnAttribute.cs
index fc39dabb..912bd164 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/GenericAdditionalPrinterColumnAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/GenericAdditionalPrinterColumnAttribute.cs
@@ -1,118 +1,118 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a generic additional printer column.
-/// With this, other elements (such as Metadata.Name)
-/// can be referenced. In contrast to the ,
-/// all needed information must be provided.
-///
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-public class GenericAdditionalPrinterColumnAttribute : Attribute
-{
- ///
- /// Create a generic additional printer column.
- ///
- /// JsonPath as in .
- /// Name as in .
- /// Type as in .
- public GenericAdditionalPrinterColumnAttribute(string jsonPath, string name, string type)
- {
- JsonPath = jsonPath;
- Name = name;
- Type = type;
- }
-
- ///
- /// The json path for the property inside the resource.
- /// .spec.replicas
- /// .metadata.namespace
- /// .metadata.creationTimestamp
- ///
- public string JsonPath { get; }
-
- ///
- /// The name of the column.
- ///
- public string Name { get; }
-
- ///
- /// Description for the column.
- ///
- public string? Description { get; init; }
-
- ///
- /// The type of the column.
- /// As documented in
- /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#type.
- /// The type field can be any of the following (from OpenAPI v3 data types):
- ///
- /// -
- /// `integer` - non-floating-point number
- ///
- /// -
- /// `number` - floating point number
- ///
- /// -
- /// `string` - strings
- ///
- /// -
- /// `boolean`- `true` or `false`
- ///
- /// -
- /// `date` - rendered differentially as time since this timestamp
- ///
- ///
- /// If the value inside a CustomResource does not match the type specified for the column, the value is omitted.
- ///
- public string Type { get; }
-
- ///
- /// The format of the column.
- /// As documented in
- /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#format.
- /// The format field can be any of the following:
- ///
- /// -
- /// `int32`
- ///
- /// -
- /// `int64`
- ///
- /// -
- /// `float`
- ///
- /// -
- /// `double`
- ///
- /// -
- /// `byte`
- ///
- /// -
- /// `date`
- ///
- /// -
- /// `date-time`
- ///
- /// -
- /// `password`
- ///
- ///
- ///
- public string? Format { get; init; }
-
- ///
- /// The priority of the additional printer column.
- /// As documented in
- /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#priority
- /// the following rules apply to priority:
- ///
- /// -
- /// Columns with priority `0` are shown in standard view
- ///
- /// -
- /// Columns with priority greater than `0` are shown only in wide view
- ///
- ///
- ///
- public PrinterColumnPriority Priority { get; init; }
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a generic additional printer column.
+/// With this, other elements (such as Metadata.Name)
+/// can be referenced. In contrast to the ,
+/// all needed information must be provided.
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public class GenericAdditionalPrinterColumnAttribute : Attribute
+{
+ ///
+ /// Create a generic additional printer column.
+ ///
+ /// JsonPath as in .
+ /// Name as in .
+ /// Type as in .
+ public GenericAdditionalPrinterColumnAttribute(string jsonPath, string name, string type)
+ {
+ JsonPath = jsonPath;
+ Name = name;
+ Type = type;
+ }
+
+ ///
+ /// The json path for the property inside the resource.
+ /// .spec.replicas
+ /// .metadata.namespace
+ /// .metadata.creationTimestamp
+ ///
+ public string JsonPath { get; }
+
+ ///
+ /// The name of the column.
+ ///
+ public string Name { get; }
+
+ ///
+ /// Description for the column.
+ ///
+ public string? Description { get; init; }
+
+ ///
+ /// The type of the column.
+ /// As documented in
+ /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#type.
+ /// The type field can be any of the following (from OpenAPI v3 data types):
+ ///
+ /// -
+ /// `integer` - non-floating-point number
+ ///
+ /// -
+ /// `number` - floating point number
+ ///
+ /// -
+ /// `string` - strings
+ ///
+ /// -
+ /// `boolean`- `true` or `false`
+ ///
+ /// -
+ /// `date` - rendered differentially as time since this timestamp
+ ///
+ ///
+ /// If the value inside a CustomResource does not match the type specified for the column, the value is omitted.
+ ///
+ public string Type { get; }
+
+ ///
+ /// The format of the column.
+ /// As documented in
+ /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#format.
+ /// The format field can be any of the following:
+ ///
+ /// -
+ /// `int32`
+ ///
+ /// -
+ /// `int64`
+ ///
+ /// -
+ /// `float`
+ ///
+ /// -
+ /// `double`
+ ///
+ /// -
+ /// `byte`
+ ///
+ /// -
+ /// `date`
+ ///
+ /// -
+ /// `date-time`
+ ///
+ /// -
+ /// `password`
+ ///
+ ///
+ ///
+ public string? Format { get; init; }
+
+ ///
+ /// The priority of the additional printer column.
+ /// As documented in
+ /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#priority
+ /// the following rules apply to priority:
+ ///
+ /// -
+ /// Columns with priority `0` are shown in standard view
+ ///
+ /// -
+ /// Columns with priority greater than `0` are shown only in wide view
+ ///
+ ///
+ ///
+ public PrinterColumnPriority Priority { get; init; }
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/IgnoreEntityAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/IgnoreEntityAttribute.cs
index f55bf314..fc16b717 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/IgnoreEntityAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/IgnoreEntityAttribute.cs
@@ -1,8 +1,8 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Attribute that states that the given entity or property should be
-/// ignored during CRD generation.
-///
-[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
-public class IgnoreAttribute : Attribute;
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Attribute that states that the given entity or property should be
+/// ignored during CRD generation.
+///
+[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)]
+public class IgnoreAttribute : Attribute;
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/ItemsAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/ItemsAttribute.cs
index 5253044c..7faeb71d 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/ItemsAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/ItemsAttribute.cs
@@ -1,26 +1,26 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Define minimum and maximum items count for an array property.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class ItemsAttribute(long minItems = -1, long maxItems = -1) : Attribute
-{
- ///
- /// Defines the minimal item count for the property.
- ///
- public long? MinItems => minItems switch
- {
- -1 => null,
- _ => minItems,
- };
-
- ///
- /// Defines the maximal item count for the property.
- ///
- public long? MaxItems => maxItems switch
- {
- -1 => null,
- _ => maxItems,
- };
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Define minimum and maximum items count for an array property.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class ItemsAttribute(long minItems = -1, long maxItems = -1) : Attribute
+{
+ ///
+ /// Defines the minimal item count for the property.
+ ///
+ public long? MinItems => minItems switch
+ {
+ -1 => null,
+ _ => minItems,
+ };
+
+ ///
+ /// Defines the maximal item count for the property.
+ ///
+ public long? MaxItems => maxItems switch
+ {
+ -1 => null,
+ _ => maxItems,
+ };
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/KubernetesEntityShortNamesAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/KubernetesEntityShortNamesAttribute.cs
index 6ae22093..67b42040 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/KubernetesEntityShortNamesAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/KubernetesEntityShortNamesAttribute.cs
@@ -1,13 +1,13 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Define "shortNames" for CRDs.
-///
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-public class KubernetesEntityShortNamesAttribute(params string[] shortNames) : Attribute
-{
- ///
- /// Array of shortnames that should be attached to CRDs.
- ///
- public string[] ShortNames => shortNames;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Define "shortNames" for CRDs.
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public class KubernetesEntityShortNamesAttribute(params string[] shortNames) : Attribute
+{
+ ///
+ /// Array of shortnames that should be attached to CRDs.
+ ///
+ public string[] ShortNames => shortNames;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/LengthAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/LengthAttribute.cs
index a960302b..22611cf4 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/LengthAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/LengthAttribute.cs
@@ -1,26 +1,26 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines length limits for properties.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class LengthAttribute(long minLength = -1, long maxLength = -1) : Attribute
-{
- ///
- /// Define the minimum length.
- ///
- public long? MinLength => minLength switch
- {
- -1 => null,
- _ => minLength,
- };
-
- ///
- /// Define the maximum length.
- ///
- public long? MaxLength => maxLength switch
- {
- -1 => null,
- _ => maxLength,
- };
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines length limits for properties.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class LengthAttribute(long minLength = -1, long maxLength = -1) : Attribute
+{
+ ///
+ /// Define the minimum length.
+ ///
+ public long? MinLength => minLength switch
+ {
+ -1 => null,
+ _ => minLength,
+ };
+
+ ///
+ /// Define the maximum length.
+ ///
+ public long? MaxLength => maxLength switch
+ {
+ -1 => null,
+ _ => maxLength,
+ };
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/MultipleOfAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/MultipleOfAttribute.cs
index 66fe6f64..6bd8c32e 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/MultipleOfAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/MultipleOfAttribute.cs
@@ -1,13 +1,13 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines the factor that a numeric value must adhere to.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class MultipleOfAttribute(double value) : Attribute
-{
- ///
- /// The property should be a multiple of this value.
- ///
- public double Value => value;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines the factor that a numeric value must adhere to.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class MultipleOfAttribute(double value) : Attribute
+{
+ ///
+ /// The property should be a multiple of this value.
+ ///
+ public double Value => value;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/PatternAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/PatternAttribute.cs
index ad4fd054..ee0ea269 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/PatternAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/PatternAttribute.cs
@@ -1,13 +1,13 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Define a regex validator for the property.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class PatternAttribute(string regexPattern) : Attribute
-{
- ///
- /// The regex pattern to be used.
- ///
- public string RegexPattern => regexPattern;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Define a regex validator for the property.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class PatternAttribute(string regexPattern) : Attribute
+{
+ ///
+ /// The regex pattern to be used.
+ ///
+ public string RegexPattern => regexPattern;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/PreserveUnknownFieldsAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/PreserveUnknownFieldsAttribute.cs
index 8d092cea..d2181d87 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/PreserveUnknownFieldsAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/PreserveUnknownFieldsAttribute.cs
@@ -1,8 +1,8 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines that a property should keep unknown fields
-/// so that kubernetes does not purge additional structures.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class PreserveUnknownFieldsAttribute : Attribute;
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines that a property should keep unknown fields
+/// so that kubernetes does not purge additional structures.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class PreserveUnknownFieldsAttribute : Attribute;
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/RangeMaximum.cs b/src/KubeOps.Abstractions/Entities/Attributes/RangeMaximum.cs
index aed36e26..ba388b00 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/RangeMaximum.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/RangeMaximum.cs
@@ -1,18 +1,18 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a range maximum for a numeric property.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class RangeMaximumAttribute(double maximum, bool exclusiveMaximum = false) : Attribute
-{
- ///
- /// Maximum value to be set.
- ///
- public double Maximum => maximum;
-
- ///
- /// Defines if the maximum value is included or excluded.
- ///
- public bool ExclusiveMaximum => exclusiveMaximum;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a range maximum for a numeric property.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class RangeMaximumAttribute(double maximum, bool exclusiveMaximum = false) : Attribute
+{
+ ///
+ /// Maximum value to be set.
+ ///
+ public double Maximum => maximum;
+
+ ///
+ /// Defines if the maximum value is included or excluded.
+ ///
+ public bool ExclusiveMaximum => exclusiveMaximum;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/RangeMinimum.cs b/src/KubeOps.Abstractions/Entities/Attributes/RangeMinimum.cs
index 78bc5fb6..21168260 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/RangeMinimum.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/RangeMinimum.cs
@@ -1,18 +1,18 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a range minimum for a numeric property.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class RangeMinimumAttribute(double minimum, bool exclusiveMinimum = false) : Attribute
-{
- ///
- /// Minimum value to be set.
- ///
- public double Minimum => minimum;
-
- ///
- /// Defines if the minimum value is included or excluded.
- ///
- public bool ExclusiveMinimum => exclusiveMinimum;
-}
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a range minimum for a numeric property.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class RangeMinimumAttribute(double minimum, bool exclusiveMinimum = false) : Attribute
+{
+ ///
+ /// Minimum value to be set.
+ ///
+ public double Minimum => minimum;
+
+ ///
+ /// Defines if the minimum value is included or excluded.
+ ///
+ public bool ExclusiveMinimum => exclusiveMinimum;
+}
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/RequiredAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/RequiredAttribute.cs
index 2ca6d6c5..9574fa74 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/RequiredAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/RequiredAttribute.cs
@@ -1,7 +1,7 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// Defines a property of a specification as required.
-///
-[AttributeUsage(AttributeTargets.Property)]
-public class RequiredAttribute : Attribute;
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// Defines a property of a specification as required.
+///
+[AttributeUsage(AttributeTargets.Property)]
+public class RequiredAttribute : Attribute;
diff --git a/src/KubeOps.Abstractions/Entities/Attributes/StorageVersionAttribute.cs b/src/KubeOps.Abstractions/Entities/Attributes/StorageVersionAttribute.cs
index db48b663..cff4ab19 100644
--- a/src/KubeOps.Abstractions/Entities/Attributes/StorageVersionAttribute.cs
+++ b/src/KubeOps.Abstractions/Entities/Attributes/StorageVersionAttribute.cs
@@ -1,11 +1,11 @@
-namespace KubeOps.Abstractions.Entities.Attributes;
-
-///
-/// This attribute marks an entity as the storage version of
-/// an entity. Only one storage version must be set.
-/// If none of the versions define this attribute, the "newest"
-/// one is taken according to the kubernetes versioning rules.
-/// GA > Beta > Alpha > non versions.
-///
-[AttributeUsage(AttributeTargets.Class)]
-public class StorageVersionAttribute : Attribute;
+namespace KubeOps.Abstractions.Entities.Attributes;
+
+///
+/// This attribute marks an entity as the storage version of
+/// an entity. Only one storage version must be set.
+/// If none of the versions define this attribute, the "newest"
+/// one is taken according to the kubernetes versioning rules.
+/// GA > Beta > Alpha > non versions.
+///
+[AttributeUsage(AttributeTargets.Class)]
+public class StorageVersionAttribute : Attribute;
diff --git a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity.cs b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity.cs
index 7c453c75..90feafef 100644
--- a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity.cs
+++ b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity.cs
@@ -1,16 +1,16 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Base class for custom Kubernetes entities. The interface
-/// can be used on its own, but this class provides convenience initializers.
-///
-public abstract class CustomKubernetesEntity : KubernetesObject, IKubernetesObject
-{
- ///
- /// The metadata of the kubernetes object.
- ///
- public V1ObjectMeta Metadata { get; set; } = new();
-}
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Base class for custom Kubernetes entities. The interface
+/// can be used on its own, but this class provides convenience initializers.
+///
+public abstract class CustomKubernetesEntity : KubernetesObject, IKubernetesObject
+{
+ ///
+ /// The metadata of the kubernetes object.
+ ///
+ public V1ObjectMeta Metadata { get; set; } = new();
+}
diff --git a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs
index b6837686..36ee3d97 100644
--- a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs
+++ b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs
@@ -1,22 +1,22 @@
-using k8s;
-
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Defines a custom Kubernetes entity.
-/// This entity contains a spec (like )
-/// and a status () which can be updated to reflect the state
-/// of the entity.
-///
-/// The type of the specified data.
-/// The type of the status data.
-public abstract class CustomKubernetesEntity : CustomKubernetesEntity, IStatus
- where TSpec : new()
- where TStatus : new()
-{
- ///
- /// Status object for the entity.
- ///
- // [JsonPropertyName("status")]
- public TStatus Status { get; set; } = new();
-}
+using k8s;
+
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Defines a custom Kubernetes entity.
+/// This entity contains a spec (like )
+/// and a status () which can be updated to reflect the state
+/// of the entity.
+///
+/// The type of the specified data.
+/// The type of the status data.
+public abstract class CustomKubernetesEntity : CustomKubernetesEntity, IStatus
+ where TSpec : new()
+ where TStatus : new()
+{
+ ///
+ /// Status object for the entity.
+ ///
+ // [JsonPropertyName("status")]
+ public TStatus Status { get; set; } = new();
+}
diff --git a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs
index 5a0353d4..d9d13e83 100644
--- a/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs
+++ b/src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs
@@ -1,17 +1,17 @@
-using k8s;
-
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Defines a custom kubernetes entity which can be used in finalizers and controllers.
-/// This entity contains a , which means in contains specified data.
-///
-/// The type of the specified data.
-public abstract class CustomKubernetesEntity : CustomKubernetesEntity, ISpec
- where TSpec : new()
-{
- ///
- /// Specification of the kubernetes object.
- ///
- public TSpec Spec { get; set; } = new();
-}
+using k8s;
+
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Defines a custom kubernetes entity which can be used in finalizers and controllers.
+/// This entity contains a , which means in contains specified data.
+///
+/// The type of the specified data.
+public abstract class CustomKubernetesEntity : CustomKubernetesEntity, ISpec
+ where TSpec : new()
+{
+ ///
+ /// Specification of the kubernetes object.
+ ///
+ public TSpec Spec { get; set; } = new();
+}
diff --git a/src/KubeOps.Abstractions/Entities/EntityList.cs b/src/KubeOps.Abstractions/Entities/EntityList.cs
index 769f917e..5071e7eb 100644
--- a/src/KubeOps.Abstractions/Entities/EntityList.cs
+++ b/src/KubeOps.Abstractions/Entities/EntityList.cs
@@ -1,22 +1,22 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Type for a list of entities.
-///
-/// Type for the list entries.
-public class EntityList : KubernetesObject
- where T : IKubernetesObject
-{
- ///
- /// Official list metadata object of kubernetes.
- ///
- public V1ListMeta Metadata { get; set; } = new();
-
- ///
- /// The list of items.
- ///
- public IList Items { get; set; } = new List();
-}
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Type for a list of entities.
+///
+/// Type for the list entries.
+public class EntityList : KubernetesObject
+ where T : IKubernetesObject
+{
+ ///
+ /// Official list metadata object of kubernetes.
+ ///
+ public V1ListMeta Metadata { get; set; } = new();
+
+ ///
+ /// The list of items.
+ ///
+ public IList Items { get; set; } = new List();
+}
diff --git a/src/KubeOps.Abstractions/Entities/EntityMetadata.cs b/src/KubeOps.Abstractions/Entities/EntityMetadata.cs
index c72c2ecc..3b035caa 100644
--- a/src/KubeOps.Abstractions/Entities/EntityMetadata.cs
+++ b/src/KubeOps.Abstractions/Entities/EntityMetadata.cs
@@ -1,28 +1,28 @@
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Metadata for a given entity.
-///
-/// The kind of the entity (e.g. deployment).
-/// Version (e.g. v1 or v2-alpha).
-/// The group in Kubernetes (e.g. "testing.dev").
-/// An optional plural name. Defaults to the singular name with an added "s".
-public record EntityMetadata(string Kind, string Version, string? Group = null, string? Plural = null)
-{
- ///
- /// Kind of the entity when used in a list.
- ///
- public string ListKind => $"{Kind}List";
-
- ///
- /// Name of the singular entity.
- ///
- public string SingularName => Kind.ToLowerInvariant();
-
- ///
- /// Name of the plural entity.
- ///
- public string PluralName => (Plural ?? $"{Kind}s").ToLowerInvariant();
-
- public string GroupWithVersion => $"{Group ?? string.Empty}/{Version}".TrimStart('/');
-}
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Metadata for a given entity.
+///
+/// The kind of the entity (e.g. deployment).
+/// Version (e.g. v1 or v2-alpha).
+/// The group in Kubernetes (e.g. "testing.dev").
+/// An optional plural name. Defaults to the singular name with an added "s".
+public record EntityMetadata(string Kind, string Version, string? Group = null, string? Plural = null)
+{
+ ///
+ /// Kind of the entity when used in a list.
+ ///
+ public string ListKind => $"{Kind}List";
+
+ ///
+ /// Name of the singular entity.
+ ///
+ public string SingularName => Kind.ToLowerInvariant();
+
+ ///
+ /// Name of the plural entity.
+ ///
+ public string PluralName => (Plural ?? $"{Kind}s").ToLowerInvariant();
+
+ public string GroupWithVersion => $"{Group ?? string.Empty}/{Version}".TrimStart('/');
+}
diff --git a/src/KubeOps.Abstractions/Entities/EntityScope.cs b/src/KubeOps.Abstractions/Entities/EntityScope.cs
index b7be754d..ad627ca7 100644
--- a/src/KubeOps.Abstractions/Entities/EntityScope.cs
+++ b/src/KubeOps.Abstractions/Entities/EntityScope.cs
@@ -1,18 +1,18 @@
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Scope of the resource. Custom entities (resources) in Kubernetes
-/// can either be namespaced or cluster-wide.
-///
-public enum EntityScope
-{
- ///
- /// The resource is namespace.
- ///
- Namespaced,
-
- ///
- /// The resource is cluster-wide.
- ///
- Cluster,
-}
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Scope of the resource. Custom entities (resources) in Kubernetes
+/// can either be namespaced or cluster-wide.
+///
+public enum EntityScope
+{
+ ///
+ /// The resource is namespace.
+ ///
+ Namespaced,
+
+ ///
+ /// The resource is cluster-wide.
+ ///
+ Cluster,
+}
diff --git a/src/KubeOps.Abstractions/Entities/Extensions.cs b/src/KubeOps.Abstractions/Entities/Extensions.cs
index 28aa46ac..cd69d1c8 100644
--- a/src/KubeOps.Abstractions/Entities/Extensions.cs
+++ b/src/KubeOps.Abstractions/Entities/Extensions.cs
@@ -1,89 +1,89 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Method extensions for .
-///
-public static class Extensions
-{
- ///
- /// Sets the resource version of the specified Kubernetes object to the specified value.
- ///
- /// The type of the Kubernetes object.
- /// The Kubernetes object.
- /// The resource version to set.
- /// The Kubernetes object with the updated resource version.
- public static TEntity WithResourceVersion(
- this TEntity entity,
- string resourceVersion)
- where TEntity : IKubernetesObject
- {
- entity.EnsureMetadata().ResourceVersion = resourceVersion;
- return entity;
- }
-
- ///
- /// Sets the resource version of the specified Kubernetes object to the resource version of another object.
- ///
- /// The type of the Kubernetes object.
- /// The Kubernetes object.
- /// The other Kubernetes object.
- /// The Kubernetes object with the updated resource version.
- public static TEntity WithResourceVersion(
- this TEntity entity,
- TEntity other)
- where TEntity : IKubernetesObject
- {
- entity.EnsureMetadata().ResourceVersion = other.ResourceVersion();
- return entity;
- }
-
- ///
- /// Create a of a kubernetes object.
- ///
- /// The object that should be translated.
- /// The created .
- public static V1ObjectReference MakeObjectReference(this IKubernetesObject kubernetesObject)
- => new()
- {
- ApiVersion = kubernetesObject.ApiVersion,
- Kind = kubernetesObject.Kind,
- Name = kubernetesObject.Metadata.Name,
- NamespaceProperty = kubernetesObject.Metadata.NamespaceProperty,
- ResourceVersion = kubernetesObject.Metadata.ResourceVersion,
- Uid = kubernetesObject.Metadata.Uid,
- };
-
- ///
- /// Ensures the object contains owner references and adds the owner to the list.
- ///
- /// The resource that is owned by another resource.
- /// The owner to add.
- /// The type of the entity.
- /// The resource with the added owner reference.
- public static TEntity WithOwnerReference(
- this TEntity resource,
- IKubernetesObject owner)
- where TEntity : IKubernetesObject
- {
- resource.EnsureMetadata().EnsureOwnerReferences().Add(owner.MakeOwnerReference());
- return resource;
- }
-
- ///
- /// Create a out of a kubernetes object.
- ///
- /// The object that should be translated.
- /// The created .
- public static V1OwnerReference MakeOwnerReference(this IKubernetesObject kubernetesObject)
- => new(
- kubernetesObject.ApiVersion,
- kubernetesObject.Kind,
- kubernetesObject.Metadata.Name,
- kubernetesObject.Metadata.Uid);
-
- private static IList EnsureOwnerReferences(this V1ObjectMeta meta) =>
- meta.OwnerReferences ??= new List();
-}
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Method extensions for .
+///
+public static class Extensions
+{
+ ///
+ /// Sets the resource version of the specified Kubernetes object to the specified value.
+ ///
+ /// The type of the Kubernetes object.
+ /// The Kubernetes object.
+ /// The resource version to set.
+ /// The Kubernetes object with the updated resource version.
+ public static TEntity WithResourceVersion(
+ this TEntity entity,
+ string resourceVersion)
+ where TEntity : IKubernetesObject
+ {
+ entity.EnsureMetadata().ResourceVersion = resourceVersion;
+ return entity;
+ }
+
+ ///
+ /// Sets the resource version of the specified Kubernetes object to the resource version of another object.
+ ///
+ /// The type of the Kubernetes object.
+ /// The Kubernetes object.
+ /// The other Kubernetes object.
+ /// The Kubernetes object with the updated resource version.
+ public static TEntity WithResourceVersion(
+ this TEntity entity,
+ TEntity other)
+ where TEntity : IKubernetesObject
+ {
+ entity.EnsureMetadata().ResourceVersion = other.ResourceVersion();
+ return entity;
+ }
+
+ ///
+ /// Create a of a kubernetes object.
+ ///
+ /// The object that should be translated.
+ /// The created .
+ public static V1ObjectReference MakeObjectReference(this IKubernetesObject kubernetesObject)
+ => new()
+ {
+ ApiVersion = kubernetesObject.ApiVersion,
+ Kind = kubernetesObject.Kind,
+ Name = kubernetesObject.Metadata.Name,
+ NamespaceProperty = kubernetesObject.Metadata.NamespaceProperty,
+ ResourceVersion = kubernetesObject.Metadata.ResourceVersion,
+ Uid = kubernetesObject.Metadata.Uid,
+ };
+
+ ///
+ /// Ensures the object contains owner references and adds the owner to the list.
+ ///
+ /// The resource that is owned by another resource.
+ /// The owner to add.
+ /// The type of the entity.
+ /// The resource with the added owner reference.
+ public static TEntity WithOwnerReference(
+ this TEntity resource,
+ IKubernetesObject owner)
+ where TEntity : IKubernetesObject
+ {
+ resource.EnsureMetadata().EnsureOwnerReferences().Add(owner.MakeOwnerReference());
+ return resource;
+ }
+
+ ///
+ /// Create a out of a kubernetes object.
+ ///
+ /// The object that should be translated.
+ /// The created .
+ public static V1OwnerReference MakeOwnerReference(this IKubernetesObject kubernetesObject)
+ => new(
+ kubernetesObject.ApiVersion,
+ kubernetesObject.Kind,
+ kubernetesObject.Metadata.Name,
+ kubernetesObject.Metadata.Uid);
+
+ private static IList EnsureOwnerReferences(this V1ObjectMeta meta) =>
+ meta.OwnerReferences ??= new List();
+}
diff --git a/src/KubeOps.Abstractions/Entities/PrinterColumnPriority.cs b/src/KubeOps.Abstractions/Entities/PrinterColumnPriority.cs
index ec1af51d..fb205292 100644
--- a/src/KubeOps.Abstractions/Entities/PrinterColumnPriority.cs
+++ b/src/KubeOps.Abstractions/Entities/PrinterColumnPriority.cs
@@ -1,17 +1,17 @@
-namespace KubeOps.Abstractions.Entities;
-
-///
-/// Specifies the priority of a column in an additional printer view.
-///
-public enum PrinterColumnPriority
-{
- ///
- /// The column is displayed in the standard view.
- ///
- StandardView,
-
- ///
- /// The column is displayed in the wide view.
- ///
- WideView,
-}
+namespace KubeOps.Abstractions.Entities;
+
+///
+/// Specifies the priority of a column in an additional printer view.
+///
+public enum PrinterColumnPriority
+{
+ ///
+ /// The column is displayed in the standard view.
+ ///
+ StandardView,
+
+ ///
+ /// The column is displayed in the wide view.
+ ///
+ WideView,
+}
diff --git a/src/KubeOps.Abstractions/Events/EventType.cs b/src/KubeOps.Abstractions/Events/EventType.cs
index 68183ca0..4d4ce659 100644
--- a/src/KubeOps.Abstractions/Events/EventType.cs
+++ b/src/KubeOps.Abstractions/Events/EventType.cs
@@ -1,20 +1,20 @@
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Events;
-
-///
-/// The type of a .
-/// The event type will be stringified and used as .
-///
-public enum EventType
-{
- ///
- /// A normal event, informative value.
- ///
- Normal,
-
- ///
- /// A warning, something might went wrong.
- ///
- Warning,
-}
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Events;
+
+///
+/// The type of a .
+/// The event type will be stringified and used as .
+///
+public enum EventType
+{
+ ///
+ /// A normal event, informative value.
+ ///
+ Normal,
+
+ ///
+ /// A warning, something might went wrong.
+ ///
+ Warning,
+}
diff --git a/src/KubeOps.Abstractions/Events/IEventPublisherFactory.cs b/src/KubeOps.Abstractions/Events/IEventPublisherFactory.cs
new file mode 100644
index 00000000..6ad7d930
--- /dev/null
+++ b/src/KubeOps.Abstractions/Events/IEventPublisherFactory.cs
@@ -0,0 +1,13 @@
+namespace KubeOps.Abstractions.Events;
+
+///
+/// Represents a type used to create s for clients and controllers.
+///
+public interface IEventPublisherFactory
+{
+ ///
+ /// Creates a new event publisher.
+ ///
+ /// The .
+ EventPublisher Create();
+}
diff --git a/src/KubeOps.Abstractions/Events/Publisher.cs b/src/KubeOps.Abstractions/Events/Publisher.cs
index cef72725..769133c4 100644
--- a/src/KubeOps.Abstractions/Events/Publisher.cs
+++ b/src/KubeOps.Abstractions/Events/Publisher.cs
@@ -1,46 +1,51 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Events;
-
-///
-/// This injectable delegate publishes events on entities. Events are created in the same
-/// namespace as the provided entity. However, if no namespace is provided (for example in
-/// cluster wide entities), the "default" namespace is used.
-///
-/// The delegate creates a if none does exist or updates the
-/// count and last seen timestamp if the same event already fired.
-///
-/// Events have a hex encoded name of a SHA512 hash. For the delegate to update
-/// an event, the entity, reason, message, and type must be the same.
-///
-/// The entity that is involved with the event.
-/// The reason string. This should be a machine readable reason string.
-/// A human readable string for the event.
-/// The of the event (either normal or warning).
-/// A task that finishes when the event is created or updated.
-///
-/// Controller that fires a simple reconcile event on any entity it encounters.
-/// Note that the publication of an event does not trigger another reconcile.
-///
-/// public class V1TestEntityController : IEntityController<V1TestEntity>
-/// {
-/// private readonly EventPublisher _eventPublisher;
-///
-/// public V1TestEntityController()
-/// {
-/// _eventPublisher = eventPublisher;
-/// }
-///
-/// public async Task ReconcileAsync(V1TestEntity entity)
-/// {
-/// await _eventPublisher(entity, "Reconciled", "Entity was reconciled.");
-/// }
-/// }
-///
-///
-public delegate Task EventPublisher(
- IKubernetesObject entity,
- string reason,
- string message,
- EventType type = EventType.Normal);
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Events;
+
+///
+///
+/// Publish a new event for . Events are created in the same namespace. However,
+/// if no namespace is provided (e.g. for cluster-wide entities), the "default" namespace is used.
+///
+///
+/// This effectively creates a new or updates the and
+/// of the existing .
+///
+///
+///
+/// Events have a hex encoded name of a SHA512 hash. For the delegate to update an event,
+/// , , and must be the same.
+///
+/// The entity that is involved with the event.
+/// The reason string. This should be a machine readable reason string.
+/// A human readable string for the event.
+/// The of the event.
+/// The token to monitor for cancellation requests.
+/// A task that finishes when the event is created or updated.
+///
+/// Controller that fires a simple reconcile event on any entity it encounters.
+/// Note that the publication of an event does not trigger another reconcile.
+///
+/// public class V1TestEntityController : IEntityController<V1TestEntity>
+/// {
+/// private readonly EventPublisher _eventPublisher;
+///
+/// public V1TestEntityController()
+/// {
+/// _eventPublisher = eventPublisher;
+/// }
+///
+/// public async Task ReconcileAsync(V1TestEntity entity, CancellationToken token)
+/// {
+/// await _eventPublisher(entity, "Reconciled", "Entity was reconciled.", cancellationToken: token);
+/// }
+/// }
+///
+///
+public delegate Task EventPublisher(
+ IKubernetesObject entity,
+ string reason,
+ string message,
+ EventType type = EventType.Normal,
+ CancellationToken cancellationToken = default);
diff --git a/src/KubeOps.Abstractions/Finalizer/EntityFinalizerAttacher.cs b/src/KubeOps.Abstractions/Finalizer/EntityFinalizerAttacher.cs
index 742a0d17..fb910242 100644
--- a/src/KubeOps.Abstractions/Finalizer/EntityFinalizerAttacher.cs
+++ b/src/KubeOps.Abstractions/Finalizer/EntityFinalizerAttacher.cs
@@ -1,47 +1,50 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Finalizer;
-
-///
-///
-/// Injectable delegate for finalizers. This delegate is used to attach a finalizer
-/// with its identifier to an entity. When injected, simply call the delegate with
-/// the entity to attach the finalizer.
-///
-///
-/// As with other (possibly) mutating calls, use the returned entity for further
-/// modification and Kubernetes client interactions, since the resource version
-/// is updated each time the entity is modified.
-///
-///
-/// Note that the operator needs RBAC access to modify the list of
-/// finalizers on the entity.
-///
-///
-/// The type of the entity finalizer.
-/// The type of the Kubernetes entity.
-/// The instance of the entity, that the finalizer is attached if needed.
-/// A that resolves when the finalizer was attached.
-///
-/// Use the finalizer delegate to attach the "FinalizerOne" to the entity as soon
-/// as the entity gets reconciled.
-///
-/// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
-/// public class V1TestEntityController : IEntityController<V1TestEntity>
-/// {
-/// private readonly EntityFinalizerAttacher<FinalizerOne, V1TestEntity> _finalizer1;
-///
-/// public V1TestEntityController(
-/// EntityFinalizerAttacher<FinalizerOne, V1TestEntity> finalizer1) => _finalizer1 = finalizer1;
-///
-/// public async Task ReconcileAsync(V1TestEntity entity)
-/// {
-/// entity = await _finalizer1(entity);
-/// }
-/// }
-///
-///
-public delegate Task EntityFinalizerAttacher(TEntity entity)
- where TImplementation : IEntityFinalizer
- where TEntity : IKubernetesObject;
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Finalizer;
+
+///
+///
+/// Injectable delegate for finalizers. This delegate is used to attach a finalizer
+/// with its identifier to an entity. When injected, simply call the delegate with
+/// the entity to attach the finalizer.
+///
+///
+/// As with other (possibly) mutating calls, use the returned entity for further
+/// modification and Kubernetes client interactions, since the resource version
+/// is updated each time the entity is modified.
+///
+///
+/// Note that the operator needs RBAC access to modify the list of
+/// finalizers on the entity.
+///
+///
+/// The type of the entity finalizer.
+/// The type of the Kubernetes entity.
+/// The instance of the entity, that the finalizer is attached if needed.
+/// The token to monitor for cancellation requests.
+/// A that resolves when the finalizer was attached.
+///
+/// Use the finalizer delegate to attach the "FinalizerOne" to the entity as soon
+/// as the entity gets reconciled.
+///
+/// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
+/// public class V1TestEntityController : IEntityController<V1TestEntity>
+/// {
+/// private readonly EntityFinalizerAttacher<FinalizerOne, V1TestEntity> _finalizer1;
+///
+/// public V1TestEntityController(
+/// EntityFinalizerAttacher<FinalizerOne, V1TestEntity> finalizer1) => _finalizer1 = finalizer1;
+///
+/// public async Task ReconcileAsync(V1TestEntity entity, CancellationToken token)
+/// {
+/// entity = await _finalizer1(entity, token);
+/// }
+/// }
+///
+///
+public delegate Task EntityFinalizerAttacher(
+ TEntity entity,
+ CancellationToken cancellationToken = default)
+ where TImplementation : IEntityFinalizer
+ where TEntity : IKubernetesObject;
diff --git a/src/KubeOps.Abstractions/Finalizer/IEntityFinalizer{TEntity}.cs b/src/KubeOps.Abstractions/Finalizer/IEntityFinalizer{TEntity}.cs
index 1bb9114c..d0af39fc 100644
--- a/src/KubeOps.Abstractions/Finalizer/IEntityFinalizer{TEntity}.cs
+++ b/src/KubeOps.Abstractions/Finalizer/IEntityFinalizer{TEntity}.cs
@@ -1,20 +1,20 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Finalizer;
-
-///
-/// Finalizer for an entity.
-///
-/// The type of the entity.
-public interface IEntityFinalizer
- where TEntity : IKubernetesObject
-{
- ///
- /// Finalize an entity that is pending for deletion.
- ///
- /// The kubernetes entity that needs to be finalized.
- /// A task that resolves when the operation is done.
- Task FinalizeAsync(TEntity entity) =>
- Task.CompletedTask;
-}
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Finalizer;
+
+///
+/// Finalizer for an entity.
+///
+/// The type of the entity.
+public interface IEntityFinalizer
+ where TEntity : IKubernetesObject
+{
+ ///
+ /// Finalize an entity that is pending for deletion.
+ ///
+ /// The kubernetes entity that needs to be finalized.
+ /// The token to monitor for cancellation requests.
+ /// A task that resolves when the operation is done.
+ Task FinalizeAsync(TEntity entity, CancellationToken cancellationToken);
+}
diff --git a/src/KubeOps.Abstractions/Finalizer/IEventFinalizerAttacherFactory.cs b/src/KubeOps.Abstractions/Finalizer/IEventFinalizerAttacherFactory.cs
new file mode 100644
index 00000000..0f53adb7
--- /dev/null
+++ b/src/KubeOps.Abstractions/Finalizer/IEventFinalizerAttacherFactory.cs
@@ -0,0 +1,22 @@
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Finalizer;
+
+///
+/// Represents a type used to create for controllers.
+///
+public interface IEventFinalizerAttacherFactory
+{
+ ///
+ /// Creates a new , which attaches the finalizer of
+ /// type to .
+ ///
+ /// The finalizer identifier.
+ /// The finalizer.
+ /// The entity.
+ /// A delegate to attach the finalizer implementation to the entity.
+ EntityFinalizerAttacher Create(string identifier)
+ where TImplementation : class, IEntityFinalizer
+ where TEntity : IKubernetesObject;
+}
diff --git a/src/KubeOps.Abstractions/Kustomize/KustomizationConfig.cs b/src/KubeOps.Abstractions/Kustomize/KustomizationConfig.cs
index a650edab..aee0a57f 100644
--- a/src/KubeOps.Abstractions/Kustomize/KustomizationConfig.cs
+++ b/src/KubeOps.Abstractions/Kustomize/KustomizationConfig.cs
@@ -1,55 +1,55 @@
-using k8s;
-
-namespace KubeOps.Abstractions.Kustomize;
-
-///
-/// (Partial) definition for a kustomization yaml.
-///
-public class KustomizationConfig : KubernetesObject
-{
- public KustomizationConfig()
- {
- ApiVersion = "kustomize.config.k8s.io/v1beta1";
- Kind = "Kustomization";
- }
-
- ///
- /// Namespace that should be set.
- ///
- public string? Namespace { get; set; }
-
- ///
- /// Name prefix that should be set.
- ///
- public string? NamePrefix { get; set; }
-
- ///
- /// Common labels for the resources.
- ///
- public IDictionary? CommonLabels { get; set; }
-
- ///
- /// Resource list.
- ///
- public IList? Resources { get; set; }
-
- ///
- /// List of merge patches.
- ///
- public IList? PatchesStrategicMerge { get; set; }
-
- ///
- /// List of .
- ///
- public IList? Images { get; set; }
-
- ///
- /// List of .
- ///
- public IList? ConfigMapGenerator { get; set; }
-
- ///
- /// List of .
- ///
- public IList? SecretGenerator { get; set; }
-}
+using k8s;
+
+namespace KubeOps.Abstractions.Kustomize;
+
+///
+/// (Partial) definition for a kustomization yaml.
+///
+public class KustomizationConfig : KubernetesObject
+{
+ public KustomizationConfig()
+ {
+ ApiVersion = "kustomize.config.k8s.io/v1beta1";
+ Kind = "Kustomization";
+ }
+
+ ///
+ /// Namespace that should be set.
+ ///
+ public string? Namespace { get; set; }
+
+ ///
+ /// Name prefix that should be set.
+ ///
+ public string? NamePrefix { get; set; }
+
+ ///
+ /// Common labels for the resources.
+ ///
+ public IDictionary? CommonLabels { get; set; }
+
+ ///
+ /// Resource list.
+ ///
+ public IList? Resources { get; set; }
+
+ ///
+ /// List of merge patches.
+ ///
+ public IList? PatchesStrategicMerge { get; set; }
+
+ ///
+ /// List of .
+ ///
+ public IList? Images { get; set; }
+
+ ///
+ /// List of .
+ ///
+ public IList? ConfigMapGenerator { get; set; }
+
+ ///
+ /// List of .
+ ///
+ public IList? SecretGenerator { get; set; }
+}
diff --git a/src/KubeOps.Abstractions/Kustomize/KustomizationConfigMapGenerator.cs b/src/KubeOps.Abstractions/Kustomize/KustomizationConfigMapGenerator.cs
index c2cfa022..16b1bd22 100644
--- a/src/KubeOps.Abstractions/Kustomize/KustomizationConfigMapGenerator.cs
+++ b/src/KubeOps.Abstractions/Kustomize/KustomizationConfigMapGenerator.cs
@@ -1,23 +1,23 @@
-namespace KubeOps.Abstractions.Kustomize;
-
-///
-/// Entity for config map generators in a kustomization.yaml file.
-///
-public class KustomizationConfigMapGenerator
-{
- ///
- /// The name of the config map.
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// List of files that should be added to the generated config map.
- ///
- public IList? Files { get; set; }
-
- ///
- /// Config literals to add to the config map in the form of:
- /// - NAME=value.
- ///
- public IList? Literals { get; set; }
-}
+namespace KubeOps.Abstractions.Kustomize;
+
+///
+/// Entity for config map generators in a kustomization.yaml file.
+///
+public class KustomizationConfigMapGenerator
+{
+ ///
+ /// The name of the config map.
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// List of files that should be added to the generated config map.
+ ///
+ public IList? Files { get; set; }
+
+ ///
+ /// Config literals to add to the config map in the form of:
+ /// - NAME=value.
+ ///
+ public IList? Literals { get; set; }
+}
diff --git a/src/KubeOps.Abstractions/Kustomize/KustomizationImage.cs b/src/KubeOps.Abstractions/Kustomize/KustomizationImage.cs
index ecef2b61..dc0e1009 100644
--- a/src/KubeOps.Abstractions/Kustomize/KustomizationImage.cs
+++ b/src/KubeOps.Abstractions/Kustomize/KustomizationImage.cs
@@ -1,22 +1,22 @@
-namespace KubeOps.Abstractions.Kustomize;
-
-///
-/// Definition for an "image" in a kustomization yaml.
-///
-public class KustomizationImage
-{
- ///
- /// Name of the image.
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// New name of the image.
- ///
- public string NewName { get; set; } = string.Empty;
-
- ///
- /// New tag of the image.
- ///
- public string NewTag { get; set; } = string.Empty;
-}
+namespace KubeOps.Abstractions.Kustomize;
+
+///
+/// Definition for an "image" in a kustomization yaml.
+///
+public class KustomizationImage
+{
+ ///
+ /// Name of the image.
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// New name of the image.
+ ///
+ public string NewName { get; set; } = string.Empty;
+
+ ///
+ /// New tag of the image.
+ ///
+ public string NewTag { get; set; } = string.Empty;
+}
diff --git a/src/KubeOps.Abstractions/Kustomize/KustomizationSecretGenerator.cs b/src/KubeOps.Abstractions/Kustomize/KustomizationSecretGenerator.cs
index 856dce6e..e8cc080e 100644
--- a/src/KubeOps.Abstractions/Kustomize/KustomizationSecretGenerator.cs
+++ b/src/KubeOps.Abstractions/Kustomize/KustomizationSecretGenerator.cs
@@ -1,23 +1,23 @@
-namespace KubeOps.Abstractions.Kustomize;
-
-///
-/// Entitiy for config map generators in a kustomization.yaml file.
-///
-public class KustomizationSecretGenerator
-{
- ///
- /// The name of the config map.
- ///
- public string Name { get; set; } = string.Empty;
-
- ///
- /// List of files that should be added to the generated config map.
- ///
- public IList? Files { get; set; }
-
- ///
- /// Config literals to add to the config map in the form of:
- /// - NAME=value.
- ///
- public IList? Literals { get; set; }
-}
+namespace KubeOps.Abstractions.Kustomize;
+
+///
+/// Entitiy for config map generators in a kustomization.yaml file.
+///
+public class KustomizationSecretGenerator
+{
+ ///
+ /// The name of the config map.
+ ///
+ public string Name { get; set; } = string.Empty;
+
+ ///
+ /// List of files that should be added to the generated config map.
+ ///
+ public IList? Files { get; set; }
+
+ ///
+ /// Config literals to add to the config map in the form of:
+ /// - NAME=value.
+ ///
+ public IList? Literals { get; set; }
+}
diff --git a/src/KubeOps.Abstractions/Queue/EntityRequeue.cs b/src/KubeOps.Abstractions/Queue/EntityRequeue.cs
index 036e4ae5..ce951440 100644
--- a/src/KubeOps.Abstractions/Queue/EntityRequeue.cs
+++ b/src/KubeOps.Abstractions/Queue/EntityRequeue.cs
@@ -1,47 +1,47 @@
-using k8s;
-using k8s.Models;
-
-namespace KubeOps.Abstractions.Queue;
-
-///
-/// Injectable delegate for requeueing entities.
-///
-/// Use this delegate when you need to pro-actively reconcile an entity after a
-/// certain amount of time. This is useful if you want to check your entities
-/// periodically.
-///
-///
-/// After the timeout is reached, the entity is fetched
-/// from the API and passed to the controller for reconciliation.
-/// If the entity was deleted in the meantime, the controller will not be called.
-///
-///
-/// If the entity gets modified while the timeout is running, the timer
-/// is canceled and restarted, if another requeue is requested.
-///
-///
-/// The type of the entity.
-/// The instance of the entity that should be requeued.
-/// The time to wait before another reconcile loop is fired.
-///
-/// Use the requeue delegate to repeatedly reconcile an entity after 5 seconds.
-///
-/// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
-/// public class V1TestEntityController : IEntityController<V1TestEntity>
-/// {
-/// private readonly EntityRequeue<V1TestEntity> _requeue;
-///
-/// public V1TestEntityController(EntityRequeue<V1TestEntity> requeue)
-/// {
-/// _requeue = requeue;
-/// }
-///
-/// public async Task ReconcileAsync(V1TestEntity entity)
-/// {
-/// _requeue(entity, TimeSpan.FromSeconds(5));
-/// }
-/// }
-///
-///
-public delegate void EntityRequeue(TEntity entity, TimeSpan requeueIn)
- where TEntity : IKubernetesObject;
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Queue;
+
+///
+/// Injectable delegate for requeueing entities.
+///
+/// Use this delegate when you need to pro-actively reconcile an entity after a
+/// certain amount of time. This is useful, if you want to check your entities
+/// periodically.
+///
+///
+/// After the timeout is reached, the entity is fetched
+/// from the API and passed to the controller for reconciliation.
+/// If the entity was deleted in the meantime, the controller will not be called.
+///
+///
+/// If the entity gets modified while the timeout is running, the timer
+/// is canceled and restarted, if another requeue is requested.
+///
+///
+/// The type of the entity.
+/// The instance of the entity that should be requeued.
+/// The time to wait before another reconcile loop is fired.
+///
+/// Use the requeue delegate to repeatedly reconcile an entity after 5 seconds.
+///
+/// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
+/// public class V1TestEntityController : IEntityController<V1TestEntity>
+/// {
+/// private readonly EntityRequeue<V1TestEntity> _requeue;
+///
+/// public V1TestEntityController(EntityRequeue<V1TestEntity> requeue)
+/// {
+/// _requeue = requeue;
+/// }
+///
+/// public async Task ReconcileAsync(V1TestEntity entity, CancellationToken token)
+/// {
+/// _requeue(entity, TimeSpan.FromSeconds(5));
+/// }
+/// }
+///
+///
+public delegate void EntityRequeue(TEntity entity, TimeSpan requeueIn)
+ where TEntity : IKubernetesObject;
diff --git a/src/KubeOps.Abstractions/Queue/IEntityRequeueFactory.cs b/src/KubeOps.Abstractions/Queue/IEntityRequeueFactory.cs
new file mode 100644
index 00000000..d32ef8aa
--- /dev/null
+++ b/src/KubeOps.Abstractions/Queue/IEntityRequeueFactory.cs
@@ -0,0 +1,18 @@
+using k8s;
+using k8s.Models;
+
+namespace KubeOps.Abstractions.Queue;
+
+///
+/// Represents a type used to create delegates of type for requeueing entities.
+///
+public interface IEntityRequeueFactory
+{
+ ///
+ /// Creates a new for the given type.
+ ///
+ /// The entity type.
+ /// A .
+ EntityRequeue Create()
+ where TEntity : IKubernetesObject;
+}
diff --git a/src/KubeOps.Abstractions/Rbac/EntityRbacAttribute.cs b/src/KubeOps.Abstractions/Rbac/EntityRbacAttribute.cs
index aaf86688..7c244a73 100644
--- a/src/KubeOps.Abstractions/Rbac/EntityRbacAttribute.cs
+++ b/src/KubeOps.Abstractions/Rbac/EntityRbacAttribute.cs
@@ -1,30 +1,30 @@
-namespace KubeOps.Abstractions.Rbac;
-
-///
-/// Generate rbac information for a type.
-/// Attach this attribute to a controller with the type reference to
-/// a custom entity to define rbac needs for this given type(s).
-///
-///
-/// Allow the operator "ALL" access to the V1TestEntity.
-///
-/// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
-///
-///
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-public class EntityRbacAttribute(params Type[] entities) : RbacAttribute
-{
- ///
- /// List of types that this rbac verbs are valid.
- ///
- public IEnumerable Entities => entities;
-
- ///
- /// Flags ("list") of allowed verbs.
- ///
- /// Yaml example:
- /// "verbs: ["get", "list", "watch"]".
- ///
- ///
- public RbacVerb Verbs { get; init; }
-}
+namespace KubeOps.Abstractions.Rbac;
+
+///
+/// Generate rbac information for a type.
+/// Attach this attribute to a controller with the type reference to
+/// a custom entity to define rbac needs for this given type(s).
+///
+///
+/// Allow the operator "ALL" access to the V1TestEntity.
+///
+/// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)]
+///
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public class EntityRbacAttribute(params Type[] entities) : RbacAttribute
+{
+ ///
+ /// List of types that this rbac verbs are valid.
+ ///
+ public IEnumerable Entities => entities;
+
+ ///
+ /// Flags ("list") of allowed verbs.
+ ///
+ /// Yaml example:
+ /// "verbs: ["get", "list", "watch"]".
+ ///
+ ///
+ public RbacVerb Verbs { get; init; }
+}
diff --git a/src/KubeOps.Abstractions/Rbac/GenericRbacAttribute.cs b/src/KubeOps.Abstractions/Rbac/GenericRbacAttribute.cs
index 26e38928..9a7cbad2 100644
--- a/src/KubeOps.Abstractions/Rbac/GenericRbacAttribute.cs
+++ b/src/KubeOps.Abstractions/Rbac/GenericRbacAttribute.cs
@@ -1,45 +1,45 @@
-namespace KubeOps.Abstractions.Rbac;
-
-///
-///
-/// Generic attribute to define rbac needs for the operator.
-/// This needs get generated into rbac - yaml style resources
-/// for installation on a cluster.
-///
-/// The attribute essentially defines the role definition of kubernetes.
-///
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-public class GenericRbacAttribute : RbacAttribute
-{
- ///
- /// List of groups.
- ///
- /// Yaml example:
- /// "apiGroups: ...".
- ///
- ///
- public string[] Groups { get; init; } = Array.Empty();
-
- ///
- /// List of resources.
- ///
- /// Yaml example:
- /// "resources: ["pods"]".
- ///
- ///
- public string[] Resources { get; init; } = Array.Empty();
-
- ///
- /// List of urls.
- ///
- public string[] Urls { get; init; } = Array.Empty();
-
- ///
- /// Flags ("list") of allowed verbs.
- ///
- /// Yaml example:
- /// "verbs: ["get", "list", "watch"]".
- ///
- ///
- public RbacVerb Verbs { get; init; }
-}
+namespace KubeOps.Abstractions.Rbac;
+
+///
+///
+/// Generic attribute to define rbac needs for the operator.
+/// This needs get generated into rbac - yaml style resources
+/// for installation on a cluster.
+///
+/// The attribute essentially defines the role definition of kubernetes.
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public class GenericRbacAttribute : RbacAttribute
+{
+ ///
+ /// List of groups.
+ ///
+ /// Yaml example:
+ /// "apiGroups: ...".
+ ///
+ ///
+ public string[] Groups { get; init; } = Array.Empty();
+
+ ///
+ /// List of resources.
+ ///
+ /// Yaml example:
+ /// "resources: ["pods"]".
+ ///
+ ///
+ public string[] Resources { get; init; } = Array.Empty();
+
+ ///
+ /// List of urls.
+ ///
+ public string[] Urls { get; init; } = Array.Empty();
+
+ ///
+ /// Flags ("list") of allowed verbs.
+ ///
+ /// Yaml example:
+ /// "verbs: ["get", "list", "watch"]".
+ ///
+ ///
+ public RbacVerb Verbs { get; init; }
+}
diff --git a/src/KubeOps.Abstractions/Rbac/RbacAttribute.cs b/src/KubeOps.Abstractions/Rbac/RbacAttribute.cs
index 007d7e32..34d16934 100644
--- a/src/KubeOps.Abstractions/Rbac/RbacAttribute.cs
+++ b/src/KubeOps.Abstractions/Rbac/RbacAttribute.cs
@@ -1,7 +1,7 @@
-namespace KubeOps.Abstractions.Rbac;
-
-///
-/// Abstract base class for all RBAC attributes.
-///
-[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
-public abstract class RbacAttribute : Attribute;
+namespace KubeOps.Abstractions.Rbac;
+
+///
+/// Abstract base class for all RBAC attributes.
+///
+[AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
+public abstract class RbacAttribute : Attribute;
diff --git a/src/KubeOps.Abstractions/Rbac/RbacVerbs.cs b/src/KubeOps.Abstractions/Rbac/RbacVerbs.cs
index 725d70b9..a51671b7 100644
--- a/src/KubeOps.Abstractions/Rbac/RbacVerbs.cs
+++ b/src/KubeOps.Abstractions/Rbac/RbacVerbs.cs
@@ -1,53 +1,53 @@
-namespace KubeOps.Abstractions.Rbac;
-
-///
-/// List of possible rbac verbs.
-///
-[Flags]
-public enum RbacVerb
-{
- ///
- /// No permissions on the resource.
- ///
- None = 0,
-
- ///
- /// All possible permissions.
- ///
- All = 1 << 0,
-
- ///
- /// Retrieve the resource from the api.
- ///
- Get = 1 << 1,
-
- ///
- /// List resources on the api.
- ///
- List = 1 << 2,
-
- ///
- /// Watch for events on resources.
- ///
- Watch = 1 << 3,
-
- ///
- /// Create new instances of the resource.
- ///
- Create = 1 << 4,
-
- ///
- /// Update existing resources.
- ///
- Update = 1 << 5,
-
- ///
- /// Patch resources.
- ///
- Patch = 1 << 6,
-
- ///
- /// Delete resources on the api.
- ///
- Delete = 1 << 7,
-}
+namespace KubeOps.Abstractions.Rbac;
+
+///
+/// List of possible rbac verbs.
+///
+[Flags]
+public enum RbacVerb
+{
+ ///
+ /// No permissions on the resource.
+ ///
+ None = 0,
+
+ ///
+ /// All possible permissions.
+ ///
+ All = 1 << 0,
+
+ ///
+ /// Retrieve the resource from the api.
+ ///
+ Get = 1 << 1,
+
+ ///
+ /// List resources on the api.
+ ///
+ List = 1 << 2,
+
+ ///
+ /// Watch for events on resources.
+ ///
+ Watch = 1 << 3,
+
+ ///
+ /// Create new instances of the resource.
+ ///
+ Create = 1 << 4,
+
+ ///
+ /// Update existing resources.
+ ///
+ Update = 1 << 5,
+
+ ///
+ /// Patch resources.
+ ///
+ Patch = 1 << 6,
+
+ ///
+ /// Delete resources on the api.
+ ///
+ Delete = 1 << 7,
+}
diff --git a/src/KubeOps.Cli/Arguments.cs b/src/KubeOps.Cli/Arguments.cs
index 20991908..1de25a40 100644
--- a/src/KubeOps.Cli/Arguments.cs
+++ b/src/KubeOps.Cli/Arguments.cs
@@ -1,39 +1,39 @@
-using System.CommandLine;
-
-namespace KubeOps.Cli;
-
-internal static class Arguments
-{
- public static readonly Argument SolutionOrProjectFile = new(
- "sln/csproj file",
- () =>
- {
- var projectFile
- = Directory.EnumerateFiles(
- Directory.GetCurrentDirectory(),
- "*.csproj")
- .Select(f => new FileInfo(f))
- .FirstOrDefault();
- var slnFile
- = Directory.EnumerateFiles(
- Directory.GetCurrentDirectory(),
- "*.sln")
- .Select(f => new FileInfo(f))
- .FirstOrDefault();
-
- return (projectFile, slnFile) switch
- {
- ({ } prj, _) => prj,
- (_, { } sln) => sln,
- _ => null,
- };
- },
- "A solution or project file where entities are located. " +
- "If omitted, the current directory is searched for a *.csproj or *.sln file. " +
- "If an *.sln file is used, all projects in the solution (with the newest framework) will be searched for entities. " +
- "This behaviour can be filtered by using the --project and --target-framework option.");
-
- public static readonly Argument OperatorName = new(
- "name",
- "Name of the operator.");
-}
+using System.CommandLine;
+
+namespace KubeOps.Cli;
+
+internal static class Arguments
+{
+ public static readonly Argument SolutionOrProjectFile = new(
+ "sln/csproj file",
+ () =>
+ {
+ var projectFile
+ = Directory.EnumerateFiles(
+ Directory.GetCurrentDirectory(),
+ "*.csproj")
+ .Select(f => new FileInfo(f))
+ .FirstOrDefault();
+ var slnFile
+ = Directory.EnumerateFiles(
+ Directory.GetCurrentDirectory(),
+ "*.sln")
+ .Select(f => new FileInfo(f))
+ .FirstOrDefault();
+
+ return (projectFile, slnFile) switch
+ {
+ ({ } prj, _) => prj,
+ (_, { } sln) => sln,
+ _ => null,
+ };
+ },
+ "A solution or project file where entities are located. " +
+ "If omitted, the current directory is searched for a *.csproj or *.sln file. " +
+ "If an *.sln file is used, all projects in the solution (with the newest framework) will be searched for entities. " +
+ "This behaviour can be filtered by using the --project and --target-framework option.");
+
+ public static readonly Argument OperatorName = new(
+ "name",
+ "Name of the operator.");
+}
diff --git a/src/KubeOps.Cli/Certificates/CertificateGenerator.cs b/src/KubeOps.Cli/Certificates/CertificateGenerator.cs
index cb52f2d3..5d4d04c7 100644
--- a/src/KubeOps.Cli/Certificates/CertificateGenerator.cs
+++ b/src/KubeOps.Cli/Certificates/CertificateGenerator.cs
@@ -1,129 +1,129 @@
-using Org.BouncyCastle.Asn1.X509;
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.Crypto.Generators;
-using Org.BouncyCastle.Crypto.Operators;
-using Org.BouncyCastle.Crypto.Prng;
-using Org.BouncyCastle.Math;
-using Org.BouncyCastle.Security;
-using Org.BouncyCastle.Utilities;
-using Org.BouncyCastle.X509;
-using Org.BouncyCastle.X509.Extension;
-
-namespace KubeOps.Cli.Certificates;
-
-internal static class CertificateGenerator
-{
- public static (X509Certificate Certificate, AsymmetricCipherKeyPair Key) CreateCaCertificate()
- {
- var randomGenerator = new CryptoApiRandomGenerator();
- var random = new SecureRandom(randomGenerator);
-
- // The Certificate Generator
- var certificateGenerator = new X509V3CertificateGenerator();
-
- // Serial Number
- var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
- certificateGenerator.SetSerialNumber(serialNumber);
-
- // Issuer and Subject Name
- var name = new X509Name("CN=Operator Root CA, C=DEV, L=Kubernetes");
- certificateGenerator.SetIssuerDN(name);
- certificateGenerator.SetSubjectDN(name);
-
- // Valid For
- var notBefore = DateTime.UtcNow.Date;
- var notAfter = notBefore.AddYears(5);
- certificateGenerator.SetNotBefore(notBefore);
- certificateGenerator.SetNotAfter(notAfter);
-
- // Cert Extensions
- certificateGenerator.AddExtension(
- X509Extensions.BasicConstraints,
- true,
- new BasicConstraints(true));
- certificateGenerator.AddExtension(
- X509Extensions.KeyUsage,
- true,
- new KeyUsage(KeyUsage.KeyCertSign | KeyUsage.CrlSign | KeyUsage.KeyEncipherment));
-
- // Subject Public Key
- const int keyStrength = 256;
- var keyGenerator = new ECKeyPairGenerator("ECDSA");
- keyGenerator.Init(new KeyGenerationParameters(random, keyStrength));
- var key = keyGenerator.GenerateKeyPair();
-
- certificateGenerator.SetPublicKey(key.Public);
-
- var signatureFactory = new Asn1SignatureFactory("SHA512WITHECDSA", key.Private, random);
- var certificate = certificateGenerator.Generate(signatureFactory);
-
- return (certificate, key);
- }
-
- public static (X509Certificate Certificate, AsymmetricCipherKeyPair Key) CreateServerCertificate(
- (X509Certificate Certificate, AsymmetricCipherKeyPair Key) ca, string serverName, string serverNamespace)
- {
- var randomGenerator = new CryptoApiRandomGenerator();
- var random = new SecureRandom(randomGenerator);
-
- // The Certificate Generator
- var certificateGenerator = new X509V3CertificateGenerator();
-
- // Serial Number
- var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
- certificateGenerator.SetSerialNumber(serialNumber);
-
- // Issuer and Subject Name
- certificateGenerator.SetIssuerDN(ca.Certificate.SubjectDN);
- certificateGenerator.SetSubjectDN(new X509Name("CN=Operator Service, C=DEV, L=Kubernetes"));
-
- // Valid For
- var notBefore = DateTime.UtcNow.Date;
- var notAfter = notBefore.AddYears(5);
- certificateGenerator.SetNotBefore(notBefore);
- certificateGenerator.SetNotAfter(notAfter);
-
- // Cert Extensions
- certificateGenerator.AddExtension(
- X509Extensions.BasicConstraints,
- false,
- new BasicConstraints(false));
- certificateGenerator.AddExtension(
- X509Extensions.KeyUsage,
- true,
- new KeyUsage(KeyUsage.NonRepudiation | KeyUsage.KeyEncipherment | KeyUsage.DigitalSignature));
- certificateGenerator.AddExtension(
- X509Extensions.ExtendedKeyUsage,
- false,
- new ExtendedKeyUsage(KeyPurposeID.id_kp_clientAuth, KeyPurposeID.id_kp_serverAuth));
- certificateGenerator.AddExtension(
- X509Extensions.SubjectKeyIdentifier,
- false,
- new SubjectKeyIdentifierStructure(ca.Key.Public));
- certificateGenerator.AddExtension(
- X509Extensions.AuthorityKeyIdentifier,
- false,
- new AuthorityKeyIdentifierStructure(ca.Certificate));
- certificateGenerator.AddExtension(
- X509Extensions.SubjectAlternativeName,
- false,
- new GeneralNames([
- new GeneralName(GeneralName.DnsName, $"{serverName}.{serverNamespace}.svc"),
- new GeneralName(GeneralName.DnsName, $"*.{serverNamespace}.svc"),
- new GeneralName(GeneralName.DnsName, "*.svc"),
- ]));
-
- // Subject Public Key
- const int keyStrength = 256;
- var keyGenerator = new ECKeyPairGenerator("ECDSA");
- keyGenerator.Init(new KeyGenerationParameters(random, keyStrength));
- var key = keyGenerator.GenerateKeyPair();
-
- certificateGenerator.SetPublicKey(key.Public);
-
- var signatureFactory = new Asn1SignatureFactory("SHA512WITHECDSA", ca.Key.Private, random);
- var certificate = certificateGenerator.Generate(signatureFactory);
-
- return (certificate, key);
- }
-}
+using Org.BouncyCastle.Asn1.X509;
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.Crypto.Generators;
+using Org.BouncyCastle.Crypto.Operators;
+using Org.BouncyCastle.Crypto.Prng;
+using Org.BouncyCastle.Math;
+using Org.BouncyCastle.Security;
+using Org.BouncyCastle.Utilities;
+using Org.BouncyCastle.X509;
+using Org.BouncyCastle.X509.Extension;
+
+namespace KubeOps.Cli.Certificates;
+
+internal static class CertificateGenerator
+{
+ public static (X509Certificate Certificate, AsymmetricCipherKeyPair Key) CreateCaCertificate()
+ {
+ var randomGenerator = new CryptoApiRandomGenerator();
+ var random = new SecureRandom(randomGenerator);
+
+ // The Certificate Generator
+ var certificateGenerator = new X509V3CertificateGenerator();
+
+ // Serial Number
+ var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
+ certificateGenerator.SetSerialNumber(serialNumber);
+
+ // Issuer and Subject Name
+ var name = new X509Name("CN=Operator Root CA, C=DEV, L=Kubernetes");
+ certificateGenerator.SetIssuerDN(name);
+ certificateGenerator.SetSubjectDN(name);
+
+ // Valid For
+ var notBefore = DateTime.UtcNow.Date;
+ var notAfter = notBefore.AddYears(5);
+ certificateGenerator.SetNotBefore(notBefore);
+ certificateGenerator.SetNotAfter(notAfter);
+
+ // Cert Extensions
+ certificateGenerator.AddExtension(
+ X509Extensions.BasicConstraints,
+ true,
+ new BasicConstraints(true));
+ certificateGenerator.AddExtension(
+ X509Extensions.KeyUsage,
+ true,
+ new KeyUsage(KeyUsage.KeyCertSign | KeyUsage.CrlSign | KeyUsage.KeyEncipherment));
+
+ // Subject Public Key
+ const int keyStrength = 256;
+ var keyGenerator = new ECKeyPairGenerator("ECDSA");
+ keyGenerator.Init(new KeyGenerationParameters(random, keyStrength));
+ var key = keyGenerator.GenerateKeyPair();
+
+ certificateGenerator.SetPublicKey(key.Public);
+
+ var signatureFactory = new Asn1SignatureFactory("SHA512WITHECDSA", key.Private, random);
+ var certificate = certificateGenerator.Generate(signatureFactory);
+
+ return (certificate, key);
+ }
+
+ public static (X509Certificate Certificate, AsymmetricCipherKeyPair Key) CreateServerCertificate(
+ (X509Certificate Certificate, AsymmetricCipherKeyPair Key) ca, string serverName, string serverNamespace)
+ {
+ var randomGenerator = new CryptoApiRandomGenerator();
+ var random = new SecureRandom(randomGenerator);
+
+ // The Certificate Generator
+ var certificateGenerator = new X509V3CertificateGenerator();
+
+ // Serial Number
+ var serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
+ certificateGenerator.SetSerialNumber(serialNumber);
+
+ // Issuer and Subject Name
+ certificateGenerator.SetIssuerDN(ca.Certificate.SubjectDN);
+ certificateGenerator.SetSubjectDN(new X509Name("CN=Operator Service, C=DEV, L=Kubernetes"));
+
+ // Valid For
+ var notBefore = DateTime.UtcNow.Date;
+ var notAfter = notBefore.AddYears(5);
+ certificateGenerator.SetNotBefore(notBefore);
+ certificateGenerator.SetNotAfter(notAfter);
+
+ // Cert Extensions
+ certificateGenerator.AddExtension(
+ X509Extensions.BasicConstraints,
+ false,
+ new BasicConstraints(false));
+ certificateGenerator.AddExtension(
+ X509Extensions.KeyUsage,
+ true,
+ new KeyUsage(KeyUsage.NonRepudiation | KeyUsage.KeyEncipherment | KeyUsage.DigitalSignature));
+ certificateGenerator.AddExtension(
+ X509Extensions.ExtendedKeyUsage,
+ false,
+ new ExtendedKeyUsage(KeyPurposeID.id_kp_clientAuth, KeyPurposeID.id_kp_serverAuth));
+ certificateGenerator.AddExtension(
+ X509Extensions.SubjectKeyIdentifier,
+ false,
+ new SubjectKeyIdentifierStructure(ca.Key.Public));
+ certificateGenerator.AddExtension(
+ X509Extensions.AuthorityKeyIdentifier,
+ false,
+ new AuthorityKeyIdentifierStructure(ca.Certificate));
+ certificateGenerator.AddExtension(
+ X509Extensions.SubjectAlternativeName,
+ false,
+ new GeneralNames([
+ new GeneralName(GeneralName.DnsName, $"{serverName}.{serverNamespace}.svc"),
+ new GeneralName(GeneralName.DnsName, $"*.{serverNamespace}.svc"),
+ new GeneralName(GeneralName.DnsName, "*.svc"),
+ ]));
+
+ // Subject Public Key
+ const int keyStrength = 256;
+ var keyGenerator = new ECKeyPairGenerator("ECDSA");
+ keyGenerator.Init(new KeyGenerationParameters(random, keyStrength));
+ var key = keyGenerator.GenerateKeyPair();
+
+ certificateGenerator.SetPublicKey(key.Public);
+
+ var signatureFactory = new Asn1SignatureFactory("SHA512WITHECDSA", ca.Key.Private, random);
+ var certificate = certificateGenerator.Generate(signatureFactory);
+
+ return (certificate, key);
+ }
+}
diff --git a/src/KubeOps.Cli/Certificates/Extensions.cs b/src/KubeOps.Cli/Certificates/Extensions.cs
index a72bf103..dfe53b83 100644
--- a/src/KubeOps.Cli/Certificates/Extensions.cs
+++ b/src/KubeOps.Cli/Certificates/Extensions.cs
@@ -1,22 +1,22 @@
-using System.Text;
-
-using Org.BouncyCastle.Crypto;
-using Org.BouncyCastle.OpenSsl;
-using Org.BouncyCastle.X509;
-
-namespace KubeOps.Cli.Certificates;
-
-internal static class Extensions
-{
- public static string ToPem(this X509Certificate cert) => ObjToPem(cert);
-
- public static string ToPem(this AsymmetricCipherKeyPair key) => ObjToPem(key);
-
- private static string ObjToPem(object obj)
- {
- var sb = new StringBuilder();
- using var writer = new PemWriter(new StringWriter(sb));
- writer.WriteObject(obj);
- return sb.ToString();
- }
-}
+using System.Text;
+
+using Org.BouncyCastle.Crypto;
+using Org.BouncyCastle.OpenSsl;
+using Org.BouncyCastle.X509;
+
+namespace KubeOps.Cli.Certificates;
+
+internal static class Extensions
+{
+ public static string ToPem(this X509Certificate cert) => ObjToPem(cert);
+
+ public static string ToPem(this AsymmetricCipherKeyPair key) => ObjToPem(key);
+
+ private static string ObjToPem(object obj)
+ {
+ var sb = new StringBuilder();
+ using var writer = new PemWriter(new StringWriter(sb));
+ writer.WriteObject(obj);
+ return sb.ToString();
+ }
+}
diff --git a/src/KubeOps.Cli/Commands/Generator/Generate.cs b/src/KubeOps.Cli/Commands/Generator/Generate.cs
index 5287f2a8..ce81a081 100644
--- a/src/KubeOps.Cli/Commands/Generator/Generate.cs
+++ b/src/KubeOps.Cli/Commands/Generator/Generate.cs
@@ -1,23 +1,23 @@
-using System.CommandLine;
-using System.CommandLine.Help;
-
-namespace KubeOps.Cli.Commands.Generator;
-
-internal static class Generate
-{
- public static Command Command
- {
- get
- {
- var cmd = new Command("generate", "Generates elements related to an operator.")
- {
- OperatorGenerator.Command,
- };
- cmd.AddAlias("gen");
- cmd.AddAlias("g");
- cmd.SetHandler(ctx => ctx.HelpBuilder.Write(cmd, Console.Out));
-
- return cmd;
- }
- }
-}
+using System.CommandLine;
+using System.CommandLine.Help;
+
+namespace KubeOps.Cli.Commands.Generator;
+
+internal static class Generate
+{
+ public static Command Command
+ {
+ get
+ {
+ var cmd = new Command("generate", "Generates elements related to an operator.")
+ {
+ OperatorGenerator.Command,
+ };
+ cmd.AddAlias("gen");
+ cmd.AddAlias("g");
+ cmd.SetHandler(ctx => ctx.HelpBuilder.Write(cmd, Console.Out));
+
+ return cmd;
+ }
+ }
+}
diff --git a/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs b/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs
index 8313afe5..7e9a86a6 100644
--- a/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs
+++ b/src/KubeOps.Cli/Commands/Generator/OperatorGenerator.cs
@@ -1,178 +1,178 @@
-using System.CommandLine;
-using System.CommandLine.Invocation;
-using System.Text;
-
-using k8s;
-using k8s.Models;
-
-using KubeOps.Abstractions.Kustomize;
-using KubeOps.Cli.Generators;
-using KubeOps.Cli.Output;
-using KubeOps.Cli.Transpilation;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Commands.Generator;
-
-internal static class OperatorGenerator
-{
- public static Command Command
- {
- get
- {
- var cmd =
- new Command(
- "operator",
- "Generates all required resources and configs for the operator to be built and run.")
- {
- Options.ClearOutputPath,
- Options.OutputFormat,
- Options.OutputPath,
- Options.SolutionProjectRegex,
- Options.TargetFramework,
- Arguments.OperatorName,
- Arguments.SolutionOrProjectFile,
- };
- cmd.AddAlias("op");
- cmd.SetHandler(Handler);
-
- return cmd;
- }
- }
-
- private static async Task Handler(InvocationContext ctx)
- {
- var name = ctx.ParseResult.GetValueForArgument(Arguments.OperatorName);
- var file = ctx.ParseResult.GetValueForArgument(Arguments.SolutionOrProjectFile);
- var outPath = ctx.ParseResult.GetValueForOption(Options.OutputPath);
- var format = ctx.ParseResult.GetValueForOption(Options.OutputFormat);
-
- var result = new ResultOutput(AnsiConsole.Console, format);
- AnsiConsole.Console.WriteLine("Generate operator resources.");
-
- AnsiConsole.Console.MarkupLine("[green]Load Project/Solution file.[/]");
- var parser = file switch
- {
- { Extension: ".csproj", Exists: true } => await AssemblyLoader.ForProject(AnsiConsole.Console, file),
- { Extension: ".sln", Exists: true } => await AssemblyLoader.ForSolution(
- AnsiConsole.Console,
- file,
- ctx.ParseResult.GetValueForOption(Options.SolutionProjectRegex),
- ctx.ParseResult.GetValueForOption(Options.TargetFramework)),
- { Exists: false } => throw new FileNotFoundException($"The file {file.Name} does not exist."),
- _ => throw new NotSupportedException("Only *.csproj and *.sln files are supported."),
- };
-
- var mutators = parser.GetMutatedEntities().ToList();
- var validators = parser.GetValidatedEntities().ToList();
- var hasWebhooks = mutators.Count > 0 || validators.Count > 0 || parser.GetConvertedEntities().Any();
-
- AnsiConsole.Console.MarkupLine("[green]Generate RBAC rules.[/]");
- new RbacGenerator(parser, format).Generate(result);
-
- AnsiConsole.Console.MarkupLine("[green]Generate Dockerfile.[/]");
- new DockerfileGenerator(hasWebhooks).Generate(result);
-
- if (hasWebhooks)
- {
- AnsiConsole.Console.MarkupLine(
- "[yellow]The operator contains webhooks of some sort, generating webhook operator specific resources.[/]");
-
- AnsiConsole.Console.MarkupLine("[green]Generate CA and Server certificates.[/]");
- new CertificateGenerator(name, $"{name}-system").Generate(result);
-
- AnsiConsole.Console.MarkupLine("[green]Generate Deployment and Service.[/]");
- new WebhookDeploymentGenerator(format).Generate(result);
-
- var caBundle =
- Encoding.ASCII.GetBytes(
- Convert.ToBase64String(Encoding.ASCII.GetBytes(result["ca.pem"].ToString() ?? string.Empty)));
-
- AnsiConsole.Console.MarkupLine("[green]Generate Validation Webhooks.[/]");
- new ValidationWebhookGenerator(validators, caBundle, format).Generate(result);
-
- AnsiConsole.Console.MarkupLine("[green]Generate Mutation Webhooks.[/]");
- new MutationWebhookGenerator(mutators, caBundle, format).Generate(result);
-
- AnsiConsole.Console.MarkupLine("[green]Generate CRDs.[/]");
- new CrdGenerator(parser, caBundle, format).Generate(result);
- }
- else
- {
- AnsiConsole.Console.MarkupLine("[green]Generate Deployment.[/]");
- new DeploymentGenerator(format).Generate(result);
-
- AnsiConsole.Console.MarkupLine("[green]Generate CRDs.[/]");
- new CrdGenerator(parser, Array.Empty(), format).Generate(result);
- }
-
- result.Add(
- $"namespace.{format.GetFileExtension()}",
- new V1Namespace(metadata: new(name: "system")).Initialize());
-
- result.Add(
- $"kustomization.{format.GetFileExtension()}",
- new KustomizationConfig
- {
- NamePrefix = $"{name}-",
- Namespace = $"{name}-system",
- CommonLabels = new Dictionary { { "operator", name }, },
- Resources = result.DefaultFormatFiles.ToList(),
- Images =
- new List
- {
- new() { Name = "operator", NewName = "accessible-docker-image", NewTag = "latest", },
- },
- ConfigMapGenerator = hasWebhooks
- ? new List
- {
- new()
- {
- Name = "webhook-config",
- Literals = new List
- {
- "KESTREL__ENDPOINTS__HTTP__URL=http://0.0.0.0:5000",
- "KESTREL__ENDPOINTS__HTTPS__URL=https://0.0.0.0:5001",
- "KESTREL__ENDPOINTS__HTTPS__CERTIFICATE__PATH=/certs/svc.pem",
- "KESTREL__ENDPOINTS__HTTPS__CERTIFICATE__KEYPATH=/certs/svc-key.pem",
- },
- },
- }
- : null,
- SecretGenerator = hasWebhooks
- ? new List
- {
- new() { Name = "webhook-ca", Files = new List { "ca.pem", "ca-key.pem", }, },
- new() { Name = "webhook-cert", Files = new List { "svc.pem", "svc-key.pem", }, },
- }
- : null,
- });
-
- if (outPath is not null)
- {
- if (ctx.ParseResult.GetValueForOption(Options.ClearOutputPath))
- {
- AnsiConsole.Console.MarkupLine("[yellow]Clear output path.[/]");
- try
- {
- Directory.Delete(outPath, true);
- }
- catch (DirectoryNotFoundException)
- {
- // the dir is not present, so we don't need to delete it.
- }
- catch (Exception e)
- {
- AnsiConsole.Console.MarkupLine($"[red]Could not clear output path: {e.Message}[/]");
- }
- }
-
- AnsiConsole.Console.MarkupLine($"[green]Write output to {outPath}.[/]");
- await result.Write(outPath);
- }
- else
- {
- result.Write();
- }
- }
-}
+using System.CommandLine;
+using System.CommandLine.Invocation;
+using System.Text;
+
+using k8s;
+using k8s.Models;
+
+using KubeOps.Abstractions.Kustomize;
+using KubeOps.Cli.Generators;
+using KubeOps.Cli.Output;
+using KubeOps.Cli.Transpilation;
+
+using Spectre.Console;
+
+namespace KubeOps.Cli.Commands.Generator;
+
+internal static class OperatorGenerator
+{
+ public static Command Command
+ {
+ get
+ {
+ var cmd =
+ new Command(
+ "operator",
+ "Generates all required resources and configs for the operator to be built and run.")
+ {
+ Options.ClearOutputPath,
+ Options.OutputFormat,
+ Options.OutputPath,
+ Options.SolutionProjectRegex,
+ Options.TargetFramework,
+ Arguments.OperatorName,
+ Arguments.SolutionOrProjectFile,
+ };
+ cmd.AddAlias("op");
+ cmd.SetHandler(Handler);
+
+ return cmd;
+ }
+ }
+
+ private static async Task Handler(InvocationContext ctx)
+ {
+ var name = ctx.ParseResult.GetValueForArgument(Arguments.OperatorName);
+ var file = ctx.ParseResult.GetValueForArgument(Arguments.SolutionOrProjectFile);
+ var outPath = ctx.ParseResult.GetValueForOption(Options.OutputPath);
+ var format = ctx.ParseResult.GetValueForOption(Options.OutputFormat);
+
+ var result = new ResultOutput(AnsiConsole.Console, format);
+ AnsiConsole.Console.WriteLine("Generate operator resources.");
+
+ AnsiConsole.Console.MarkupLine("[green]Load Project/Solution file.[/]");
+ var parser = file switch
+ {
+ { Extension: ".csproj", Exists: true } => await AssemblyLoader.ForProject(AnsiConsole.Console, file),
+ { Extension: ".sln", Exists: true } => await AssemblyLoader.ForSolution(
+ AnsiConsole.Console,
+ file,
+ ctx.ParseResult.GetValueForOption(Options.SolutionProjectRegex),
+ ctx.ParseResult.GetValueForOption(Options.TargetFramework)),
+ { Exists: false } => throw new FileNotFoundException($"The file {file.Name} does not exist."),
+ _ => throw new NotSupportedException("Only *.csproj and *.sln files are supported."),
+ };
+
+ var mutators = parser.GetMutatedEntities().ToList();
+ var validators = parser.GetValidatedEntities().ToList();
+ var hasWebhooks = mutators.Count > 0 || validators.Count > 0 || parser.GetConvertedEntities().Any();
+
+ AnsiConsole.Console.MarkupLine("[green]Generate RBAC rules.[/]");
+ new RbacGenerator(parser, format).Generate(result);
+
+ AnsiConsole.Console.MarkupLine("[green]Generate Dockerfile.[/]");
+ new DockerfileGenerator(hasWebhooks).Generate(result);
+
+ if (hasWebhooks)
+ {
+ AnsiConsole.Console.MarkupLine(
+ "[yellow]The operator contains webhooks of some sort, generating webhook operator specific resources.[/]");
+
+ AnsiConsole.Console.MarkupLine("[green]Generate CA and Server certificates.[/]");
+ new CertificateGenerator(name, $"{name}-system").Generate(result);
+
+ AnsiConsole.Console.MarkupLine("[green]Generate Deployment and Service.[/]");
+ new WebhookDeploymentGenerator(format).Generate(result);
+
+ var caBundle =
+ Encoding.ASCII.GetBytes(
+ Convert.ToBase64String(Encoding.ASCII.GetBytes(result["ca.pem"].ToString() ?? string.Empty)));
+
+ AnsiConsole.Console.MarkupLine("[green]Generate Validation Webhooks.[/]");
+ new ValidationWebhookGenerator(validators, caBundle, format).Generate(result);
+
+ AnsiConsole.Console.MarkupLine("[green]Generate Mutation Webhooks.[/]");
+ new MutationWebhookGenerator(mutators, caBundle, format).Generate(result);
+
+ AnsiConsole.Console.MarkupLine("[green]Generate CRDs.[/]");
+ new CrdGenerator(parser, caBundle, format).Generate(result);
+ }
+ else
+ {
+ AnsiConsole.Console.MarkupLine("[green]Generate Deployment.[/]");
+ new DeploymentGenerator(format).Generate(result);
+
+ AnsiConsole.Console.MarkupLine("[green]Generate CRDs.[/]");
+ new CrdGenerator(parser, Array.Empty(), format).Generate(result);
+ }
+
+ result.Add(
+ $"namespace.{format.GetFileExtension()}",
+ new V1Namespace(metadata: new(name: "system")).Initialize());
+
+ result.Add(
+ $"kustomization.{format.GetFileExtension()}",
+ new KustomizationConfig
+ {
+ NamePrefix = $"{name}-",
+ Namespace = $"{name}-system",
+ CommonLabels = new Dictionary { { "operator", name }, },
+ Resources = result.DefaultFormatFiles.ToList(),
+ Images =
+ new List
+ {
+ new() { Name = "operator", NewName = "accessible-docker-image", NewTag = "latest", },
+ },
+ ConfigMapGenerator = hasWebhooks
+ ? new List
+ {
+ new()
+ {
+ Name = "webhook-config",
+ Literals = new List
+ {
+ "KESTREL__ENDPOINTS__HTTP__URL=http://0.0.0.0:5000",
+ "KESTREL__ENDPOINTS__HTTPS__URL=https://0.0.0.0:5001",
+ "KESTREL__ENDPOINTS__HTTPS__CERTIFICATE__PATH=/certs/svc.pem",
+ "KESTREL__ENDPOINTS__HTTPS__CERTIFICATE__KEYPATH=/certs/svc-key.pem",
+ },
+ },
+ }
+ : null,
+ SecretGenerator = hasWebhooks
+ ? new List
+ {
+ new() { Name = "webhook-ca", Files = new List { "ca.pem", "ca-key.pem", }, },
+ new() { Name = "webhook-cert", Files = new List { "svc.pem", "svc-key.pem", }, },
+ }
+ : null,
+ });
+
+ if (outPath is not null)
+ {
+ if (ctx.ParseResult.GetValueForOption(Options.ClearOutputPath))
+ {
+ AnsiConsole.Console.MarkupLine("[yellow]Clear output path.[/]");
+ try
+ {
+ Directory.Delete(outPath, true);
+ }
+ catch (DirectoryNotFoundException)
+ {
+ // the dir is not present, so we don't need to delete it.
+ }
+ catch (Exception e)
+ {
+ AnsiConsole.Console.MarkupLine($"[red]Could not clear output path: {e.Message}[/]");
+ }
+ }
+
+ AnsiConsole.Console.MarkupLine($"[green]Write output to {outPath}.[/]");
+ await result.Write(outPath);
+ }
+ else
+ {
+ result.Write();
+ }
+ }
+}
diff --git a/src/KubeOps.Cli/Commands/Management/Install.cs b/src/KubeOps.Cli/Commands/Management/Install.cs
index ed16f0ba..91706bdf 100644
--- a/src/KubeOps.Cli/Commands/Management/Install.cs
+++ b/src/KubeOps.Cli/Commands/Management/Install.cs
@@ -1,112 +1,112 @@
-using System.CommandLine;
-using System.CommandLine.Invocation;
-
-using k8s;
-using k8s.Autorest;
-using k8s.Models;
-
-using KubeOps.Cli.Transpilation;
-using KubeOps.Transpiler;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Commands.Management;
-
-internal static class Install
-{
- public static Command Command
- {
- get
- {
- var cmd =
- new Command("install", "Install CRDs into the cluster of the actually selected context.")
- {
- Options.Force,
- Options.SolutionProjectRegex,
- Options.TargetFramework,
- Arguments.SolutionOrProjectFile,
- };
- cmd.AddAlias("i");
- cmd.SetHandler(ctx => Handler(
- AnsiConsole.Console,
- new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig()),
- ctx));
-
- return cmd;
- }
- }
-
- internal static async Task Handler(IAnsiConsole console, IKubernetes client, InvocationContext ctx)
- {
- var file = ctx.ParseResult.GetValueForArgument(Arguments.SolutionOrProjectFile);
- var force = ctx.ParseResult.GetValueForOption(Options.Force);
-
- var parser = file switch
- {
- { Extension: ".csproj", Exists: true } => await AssemblyLoader.ForProject(console, file),
- { Extension: ".sln", Exists: true } => await AssemblyLoader.ForSolution(
- console,
- file,
- ctx.ParseResult.GetValueForOption(Options.SolutionProjectRegex),
- ctx.ParseResult.GetValueForOption(Options.TargetFramework)),
- { Exists: false } => throw new FileNotFoundException($"The file {file.Name} does not exist."),
- _ => throw new NotSupportedException("Only *.csproj and *.sln files are supported."),
- };
-
- console.WriteLine($"Install CRDs from {file.Name}.");
- var crds = parser.Transpile(parser.GetEntities()).ToList();
- if (crds.Count == 0)
- {
- console.WriteLine("No CRDs found. Exiting.");
- ctx.ExitCode = ExitCodes.Success;
- return;
- }
-
- console.WriteLine($"Found {crds.Count} CRDs.");
- console.WriteLine($"""Starting install into cluster with url "{client.BaseUri}".""");
-
- foreach (var crd in crds)
- {
- console.MarkupLineInterpolated(
- $"""Install [cyan]"{crd.Spec.Group}/{crd.Spec.Names.Kind}"[/] into the cluster.""");
-
- try
- {
- switch (await client.ApiextensionsV1.ListCustomResourceDefinitionAsync(
- fieldSelector: $"metadata.name={crd.Name()}"))
- {
- case { Items: [var existing] }:
- console.MarkupLineInterpolated(
- $"""[yellow]CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}" already exists.[/]""");
- if (!force && !console.Confirm("[yellow]Should the CRD be overwritten?[/]"))
- {
- ctx.ExitCode = ExitCodes.Aborted;
- return;
- }
-
- crd.Metadata.ResourceVersion = existing.ResourceVersion();
- await client.ApiextensionsV1.ReplaceCustomResourceDefinitionAsync(crd, crd.Name());
- break;
- default:
- await client.ApiextensionsV1.CreateCustomResourceDefinitionAsync(crd);
- break;
- }
-
- console.MarkupLineInterpolated(
- $"""[green]Installed / Updated CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
- }
- catch (HttpOperationException)
- {
- console.WriteLine(
- $"""[red]There was a http (api) error while installing "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
- throw;
- }
- catch (Exception)
- {
- console.WriteLine(
- $"""[red]There was an error while installing "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
- throw;
- }
- }
- }
-}
+using System.CommandLine;
+using System.CommandLine.Invocation;
+
+using k8s;
+using k8s.Autorest;
+using k8s.Models;
+
+using KubeOps.Cli.Transpilation;
+using KubeOps.Transpiler;
+
+using Spectre.Console;
+
+namespace KubeOps.Cli.Commands.Management;
+
+internal static class Install
+{
+ public static Command Command
+ {
+ get
+ {
+ var cmd =
+ new Command("install", "Install CRDs into the cluster of the actually selected context.")
+ {
+ Options.Force,
+ Options.SolutionProjectRegex,
+ Options.TargetFramework,
+ Arguments.SolutionOrProjectFile,
+ };
+ cmd.AddAlias("i");
+ cmd.SetHandler(ctx => Handler(
+ AnsiConsole.Console,
+ new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig()),
+ ctx));
+
+ return cmd;
+ }
+ }
+
+ internal static async Task Handler(IAnsiConsole console, IKubernetes client, InvocationContext ctx)
+ {
+ var file = ctx.ParseResult.GetValueForArgument(Arguments.SolutionOrProjectFile);
+ var force = ctx.ParseResult.GetValueForOption(Options.Force);
+
+ var parser = file switch
+ {
+ { Extension: ".csproj", Exists: true } => await AssemblyLoader.ForProject(console, file),
+ { Extension: ".sln", Exists: true } => await AssemblyLoader.ForSolution(
+ console,
+ file,
+ ctx.ParseResult.GetValueForOption(Options.SolutionProjectRegex),
+ ctx.ParseResult.GetValueForOption(Options.TargetFramework)),
+ { Exists: false } => throw new FileNotFoundException($"The file {file.Name} does not exist."),
+ _ => throw new NotSupportedException("Only *.csproj and *.sln files are supported."),
+ };
+
+ console.WriteLine($"Install CRDs from {file.Name}.");
+ var crds = parser.Transpile(parser.GetEntities()).ToList();
+ if (crds.Count == 0)
+ {
+ console.WriteLine("No CRDs found. Exiting.");
+ ctx.ExitCode = ExitCodes.Success;
+ return;
+ }
+
+ console.WriteLine($"Found {crds.Count} CRDs.");
+ console.WriteLine($"""Starting install into cluster with url "{client.BaseUri}".""");
+
+ foreach (var crd in crds)
+ {
+ console.MarkupLineInterpolated(
+ $"""Install [cyan]"{crd.Spec.Group}/{crd.Spec.Names.Kind}"[/] into the cluster.""");
+
+ try
+ {
+ switch (await client.ApiextensionsV1.ListCustomResourceDefinitionAsync(
+ fieldSelector: $"metadata.name={crd.Name()}"))
+ {
+ case { Items: [var existing] }:
+ console.MarkupLineInterpolated(
+ $"""[yellow]CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}" already exists.[/]""");
+ if (!force && !console.Confirm("[yellow]Should the CRD be overwritten?[/]"))
+ {
+ ctx.ExitCode = ExitCodes.Aborted;
+ return;
+ }
+
+ crd.Metadata.ResourceVersion = existing.ResourceVersion();
+ await client.ApiextensionsV1.ReplaceCustomResourceDefinitionAsync(crd, crd.Name());
+ break;
+ default:
+ await client.ApiextensionsV1.CreateCustomResourceDefinitionAsync(crd);
+ break;
+ }
+
+ console.MarkupLineInterpolated(
+ $"""[green]Installed / Updated CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
+ }
+ catch (HttpOperationException)
+ {
+ console.WriteLine(
+ $"""[red]There was a http (api) error while installing "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
+ throw;
+ }
+ catch (Exception)
+ {
+ console.WriteLine(
+ $"""[red]There was an error while installing "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
+ throw;
+ }
+ }
+ }
+}
diff --git a/src/KubeOps.Cli/Commands/Management/Uninstall.cs b/src/KubeOps.Cli/Commands/Management/Uninstall.cs
index e09fbd39..8c249806 100644
--- a/src/KubeOps.Cli/Commands/Management/Uninstall.cs
+++ b/src/KubeOps.Cli/Commands/Management/Uninstall.cs
@@ -1,109 +1,109 @@
-using System.CommandLine;
-using System.CommandLine.Invocation;
-
-using k8s;
-using k8s.Autorest;
-using k8s.Models;
-
-using KubeOps.Cli.Transpilation;
-using KubeOps.Transpiler;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Commands.Management;
-
-internal static class Uninstall
-{
- public static Command Command
- {
- get
- {
- var cmd =
- new Command("uninstall", "Uninstall CRDs from the cluster of the actually selected context.")
- {
- Options.Force,
- Options.SolutionProjectRegex,
- Options.TargetFramework,
- Arguments.SolutionOrProjectFile,
- };
- cmd.AddAlias("u");
- cmd.SetHandler(ctx => Handler(
- AnsiConsole.Console,
- new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig()),
- ctx));
-
- return cmd;
- }
- }
-
- internal static async Task Handler(IAnsiConsole console, IKubernetes client, InvocationContext ctx)
- {
- var file = ctx.ParseResult.GetValueForArgument(Arguments.SolutionOrProjectFile);
- var force = ctx.ParseResult.GetValueForOption(Options.Force);
-
- var parser = file switch
- {
- { Extension: ".csproj", Exists: true } => await AssemblyLoader.ForProject(console, file),
- { Extension: ".sln", Exists: true } => await AssemblyLoader.ForSolution(
- console,
- file,
- ctx.ParseResult.GetValueForOption(Options.SolutionProjectRegex),
- ctx.ParseResult.GetValueForOption(Options.TargetFramework)),
- { Exists: false } => throw new FileNotFoundException($"The file {file.Name} does not exist."),
- _ => throw new NotSupportedException("Only *.csproj and *.sln files are supported."),
- };
-
- console.WriteLine($"Uninstall CRDs from {file.Name}.");
- var crds = parser.Transpile(parser.GetEntities()).ToList();
- if (crds.Count == 0)
- {
- console.WriteLine("No CRDs found. Exiting.");
- ctx.ExitCode = ExitCodes.Success;
- return;
- }
-
- console.WriteLine($"Found {crds.Count} CRDs.");
- if (!force && !console.Confirm("[red]Should the CRDs be uninstalled?[/]", false))
- {
- ctx.ExitCode = ExitCodes.Aborted;
- return;
- }
-
- console.WriteLine($"""Starting uninstall from cluster with url "{client.BaseUri}".""");
-
- foreach (var crd in crds)
- {
- console.MarkupLineInterpolated(
- $"""Uninstall [cyan]"{crd.Spec.Group}/{crd.Spec.Names.Kind}"[/] from the cluster.""");
-
- try
- {
- switch (await client.ApiextensionsV1.ListCustomResourceDefinitionAsync(
- fieldSelector: $"metadata.name={crd.Name()}"))
- {
- case { Items: [var existing] }:
- await client.ApiextensionsV1.DeleteCustomResourceDefinitionAsync(existing.Name());
- console.MarkupLineInterpolated(
- $"""[green]CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}" deleted.[/]""");
- break;
- default:
- console.MarkupLineInterpolated(
- $"""[green]CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}" did not exist.[/]""");
- break;
- }
- }
- catch (HttpOperationException)
- {
- console.WriteLine(
- $"""[red]There was a http (api) error while uninstalling "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
- throw;
- }
- catch (Exception)
- {
- console.WriteLine(
- $"""[red]There was an error while uninstalling "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
- throw;
- }
- }
- }
-}
+using System.CommandLine;
+using System.CommandLine.Invocation;
+
+using k8s;
+using k8s.Autorest;
+using k8s.Models;
+
+using KubeOps.Cli.Transpilation;
+using KubeOps.Transpiler;
+
+using Spectre.Console;
+
+namespace KubeOps.Cli.Commands.Management;
+
+internal static class Uninstall
+{
+ public static Command Command
+ {
+ get
+ {
+ var cmd =
+ new Command("uninstall", "Uninstall CRDs from the cluster of the actually selected context.")
+ {
+ Options.Force,
+ Options.SolutionProjectRegex,
+ Options.TargetFramework,
+ Arguments.SolutionOrProjectFile,
+ };
+ cmd.AddAlias("u");
+ cmd.SetHandler(ctx => Handler(
+ AnsiConsole.Console,
+ new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig()),
+ ctx));
+
+ return cmd;
+ }
+ }
+
+ internal static async Task Handler(IAnsiConsole console, IKubernetes client, InvocationContext ctx)
+ {
+ var file = ctx.ParseResult.GetValueForArgument(Arguments.SolutionOrProjectFile);
+ var force = ctx.ParseResult.GetValueForOption(Options.Force);
+
+ var parser = file switch
+ {
+ { Extension: ".csproj", Exists: true } => await AssemblyLoader.ForProject(console, file),
+ { Extension: ".sln", Exists: true } => await AssemblyLoader.ForSolution(
+ console,
+ file,
+ ctx.ParseResult.GetValueForOption(Options.SolutionProjectRegex),
+ ctx.ParseResult.GetValueForOption(Options.TargetFramework)),
+ { Exists: false } => throw new FileNotFoundException($"The file {file.Name} does not exist."),
+ _ => throw new NotSupportedException("Only *.csproj and *.sln files are supported."),
+ };
+
+ console.WriteLine($"Uninstall CRDs from {file.Name}.");
+ var crds = parser.Transpile(parser.GetEntities()).ToList();
+ if (crds.Count == 0)
+ {
+ console.WriteLine("No CRDs found. Exiting.");
+ ctx.ExitCode = ExitCodes.Success;
+ return;
+ }
+
+ console.WriteLine($"Found {crds.Count} CRDs.");
+ if (!force && !console.Confirm("[red]Should the CRDs be uninstalled?[/]", false))
+ {
+ ctx.ExitCode = ExitCodes.Aborted;
+ return;
+ }
+
+ console.WriteLine($"""Starting uninstall from cluster with url "{client.BaseUri}".""");
+
+ foreach (var crd in crds)
+ {
+ console.MarkupLineInterpolated(
+ $"""Uninstall [cyan]"{crd.Spec.Group}/{crd.Spec.Names.Kind}"[/] from the cluster.""");
+
+ try
+ {
+ switch (await client.ApiextensionsV1.ListCustomResourceDefinitionAsync(
+ fieldSelector: $"metadata.name={crd.Name()}"))
+ {
+ case { Items: [var existing] }:
+ await client.ApiextensionsV1.DeleteCustomResourceDefinitionAsync(existing.Name());
+ console.MarkupLineInterpolated(
+ $"""[green]CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}" deleted.[/]""");
+ break;
+ default:
+ console.MarkupLineInterpolated(
+ $"""[green]CRD "{crd.Spec.Group}/{crd.Spec.Names.Kind}" did not exist.[/]""");
+ break;
+ }
+ }
+ catch (HttpOperationException)
+ {
+ console.WriteLine(
+ $"""[red]There was a http (api) error while uninstalling "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
+ throw;
+ }
+ catch (Exception)
+ {
+ console.WriteLine(
+ $"""[red]There was an error while uninstalling "{crd.Spec.Group}/{crd.Spec.Names.Kind}".[/]""");
+ throw;
+ }
+ }
+ }
+}
diff --git a/src/KubeOps.Cli/Commands/Utilities/Version.cs b/src/KubeOps.Cli/Commands/Utilities/Version.cs
index 5fc70cb9..a22b5fb7 100644
--- a/src/KubeOps.Cli/Commands/Utilities/Version.cs
+++ b/src/KubeOps.Cli/Commands/Utilities/Version.cs
@@ -1,40 +1,40 @@
-using System.CommandLine;
-
-using k8s;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Commands.Utilities;
-
-internal static class Version
-{
- public static Command Command
- {
- get
- {
- var cmd = new Command(
- "api-version",
- "Prints the actual server version of the connected kubernetes cluster.");
- cmd.AddAlias("av");
- cmd.SetHandler(() =>
- Handler(AnsiConsole.Console, new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig())));
-
- return cmd;
- }
- }
-
- internal static async Task Handler(IAnsiConsole console, IKubernetes client)
- {
- var version = await client.Version.GetCodeAsync();
- console.Write(new Table()
- .Title("Kubernetes API Version")
- .HideHeaders()
- .AddColumns("Info", "Value")
- .AddRow("Git-Version", version.GitVersion)
- .AddRow("Major", version.Major)
- .AddRow("Minor", version.Minor)
- .AddRow("Platform", version.Platform));
-
- return ExitCodes.Success;
- }
-}
+using System.CommandLine;
+
+using k8s;
+
+using Spectre.Console;
+
+namespace KubeOps.Cli.Commands.Utilities;
+
+internal static class Version
+{
+ public static Command Command
+ {
+ get
+ {
+ var cmd = new Command(
+ "api-version",
+ "Prints the actual server version of the connected kubernetes cluster.");
+ cmd.AddAlias("av");
+ cmd.SetHandler(() =>
+ Handler(AnsiConsole.Console, new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig())));
+
+ return cmd;
+ }
+ }
+
+ internal static async Task Handler(IAnsiConsole console, IKubernetes client)
+ {
+ var version = await client.Version.GetCodeAsync();
+ console.Write(new Table()
+ .Title("Kubernetes API Version")
+ .HideHeaders()
+ .AddColumns("Info", "Value")
+ .AddRow("Git-Version", version.GitVersion)
+ .AddRow("Major", version.Major)
+ .AddRow("Minor", version.Minor)
+ .AddRow("Platform", version.Platform));
+
+ return ExitCodes.Success;
+ }
+}
diff --git a/src/KubeOps.Cli/ExitCodes.cs b/src/KubeOps.Cli/ExitCodes.cs
index 0a6c912c..f3caf944 100644
--- a/src/KubeOps.Cli/ExitCodes.cs
+++ b/src/KubeOps.Cli/ExitCodes.cs
@@ -1,9 +1,9 @@
-namespace KubeOps.Cli;
-
-internal static class ExitCodes
-{
- public const int Success = 0;
- public const int Error = 1;
- public const int Aborted = 2;
- public const int UsageError = 99;
-}
+namespace KubeOps.Cli;
+
+internal static class ExitCodes
+{
+ public const int Success = 0;
+ public const int Error = 1;
+ public const int Aborted = 2;
+ public const int UsageError = 99;
+}
diff --git a/src/KubeOps.Cli/Generators/CertificateGenerator.cs b/src/KubeOps.Cli/Generators/CertificateGenerator.cs
index f0edc328..101a5c0e 100644
--- a/src/KubeOps.Cli/Generators/CertificateGenerator.cs
+++ b/src/KubeOps.Cli/Generators/CertificateGenerator.cs
@@ -1,23 +1,23 @@
-using KubeOps.Cli.Certificates;
-using KubeOps.Cli.Output;
-
-namespace KubeOps.Cli.Generators;
-
-internal class CertificateGenerator(string serverName, string namespaceName) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- var (caCert, caKey) = Certificates.CertificateGenerator.CreateCaCertificate();
-
- output.Add("ca.pem", caCert.ToPem(), OutputFormat.Plain);
- output.Add("ca-key.pem", caKey.ToPem(), OutputFormat.Plain);
-
- var (srvCert, srvKey) = Certificates.CertificateGenerator.CreateServerCertificate(
- (caCert, caKey),
- serverName,
- namespaceName);
-
- output.Add("svc.pem", srvCert.ToPem(), OutputFormat.Plain);
- output.Add("svc-key.pem", srvKey.ToPem(), OutputFormat.Plain);
- }
-}
+using KubeOps.Cli.Certificates;
+using KubeOps.Cli.Output;
+
+namespace KubeOps.Cli.Generators;
+
+internal class CertificateGenerator(string serverName, string namespaceName) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ var (caCert, caKey) = Certificates.CertificateGenerator.CreateCaCertificate();
+
+ output.Add("ca.pem", caCert.ToPem(), OutputFormat.Plain);
+ output.Add("ca-key.pem", caKey.ToPem(), OutputFormat.Plain);
+
+ var (srvCert, srvKey) = Certificates.CertificateGenerator.CreateServerCertificate(
+ (caCert, caKey),
+ serverName,
+ namespaceName);
+
+ output.Add("svc.pem", srvCert.ToPem(), OutputFormat.Plain);
+ output.Add("svc-key.pem", srvKey.ToPem(), OutputFormat.Plain);
+ }
+}
diff --git a/src/KubeOps.Cli/Generators/CrdGenerator.cs b/src/KubeOps.Cli/Generators/CrdGenerator.cs
index 1a9572f8..129a6a0f 100644
--- a/src/KubeOps.Cli/Generators/CrdGenerator.cs
+++ b/src/KubeOps.Cli/Generators/CrdGenerator.cs
@@ -1,46 +1,46 @@
-using System.Reflection;
-
-using k8s.Models;
-
-using KubeOps.Cli.Output;
-using KubeOps.Cli.Transpilation;
-using KubeOps.Transpiler;
-
-namespace KubeOps.Cli.Generators;
-
-internal class CrdGenerator(MetadataLoadContext parser, byte[] caBundle,
- OutputFormat outputFormat) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- var crds = parser.Transpile(parser.GetEntities()).ToList();
- var conversionWebhooks = parser.GetConvertedEntities().ToList();
-
- foreach (var crd in crds)
- {
- if (conversionWebhooks
- .Find(wh => crd.Spec.Group == wh.Group && crd.Spec.Names.Kind == wh.Kind) is not null)
- {
- crd.Spec.Conversion = new V1CustomResourceConversion
- {
- Strategy = "Webhook",
- Webhook = new V1WebhookConversion
- {
- ConversionReviewVersions = new[] { "v1" },
- ClientConfig = new Apiextensionsv1WebhookClientConfig
- {
- CaBundle = caBundle,
- Service = new Apiextensionsv1ServiceReference
- {
- Path = $"/convert/{crd.Spec.Group}/{crd.Spec.Names.Plural}",
- Name = "service",
- },
- },
- },
- };
- }
-
- output.Add($"{crd.Metadata.Name.Replace('.', '_')}.{outputFormat.GetFileExtension()}", crd);
- }
- }
-}
+using System.Reflection;
+
+using k8s.Models;
+
+using KubeOps.Cli.Output;
+using KubeOps.Cli.Transpilation;
+using KubeOps.Transpiler;
+
+namespace KubeOps.Cli.Generators;
+
+internal class CrdGenerator(MetadataLoadContext parser, byte[] caBundle,
+ OutputFormat outputFormat) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ var crds = parser.Transpile(parser.GetEntities()).ToList();
+ var conversionWebhooks = parser.GetConvertedEntities().ToList();
+
+ foreach (var crd in crds)
+ {
+ if (conversionWebhooks
+ .Find(wh => crd.Spec.Group == wh.Group && crd.Spec.Names.Kind == wh.Kind) is not null)
+ {
+ crd.Spec.Conversion = new V1CustomResourceConversion
+ {
+ Strategy = "Webhook",
+ Webhook = new V1WebhookConversion
+ {
+ ConversionReviewVersions = new[] { "v1" },
+ ClientConfig = new Apiextensionsv1WebhookClientConfig
+ {
+ CaBundle = caBundle,
+ Service = new Apiextensionsv1ServiceReference
+ {
+ Path = $"/convert/{crd.Spec.Group}/{crd.Spec.Names.Plural}",
+ Name = "service",
+ },
+ },
+ },
+ };
+ }
+
+ output.Add($"{crd.Metadata.Name.Replace('.', '_')}.{outputFormat.GetFileExtension()}", crd);
+ }
+ }
+}
diff --git a/src/KubeOps.Cli/Generators/DeploymentGenerator.cs b/src/KubeOps.Cli/Generators/DeploymentGenerator.cs
index a976ee01..f8741960 100644
--- a/src/KubeOps.Cli/Generators/DeploymentGenerator.cs
+++ b/src/KubeOps.Cli/Generators/DeploymentGenerator.cs
@@ -1,69 +1,69 @@
-using k8s;
-using k8s.Models;
-
-using KubeOps.Cli.Output;
-
-namespace KubeOps.Cli.Generators;
-
-internal class DeploymentGenerator(OutputFormat format) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- var deployment = new V1Deployment(metadata: new V1ObjectMeta(
- labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } },
- name: "operator")).Initialize();
- deployment.Spec = new V1DeploymentSpec
- {
- Replicas = 1,
- RevisionHistoryLimit = 0,
- Selector = new V1LabelSelector(
- matchLabels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }),
- Template = new V1PodTemplateSpec
- {
- Metadata = new V1ObjectMeta(
- labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }),
- Spec = new V1PodSpec
- {
- TerminationGracePeriodSeconds = 10,
- Containers = new List
- {
- new()
- {
- Image = "operator",
- Name = "operator",
- Env = new List
- {
- new()
- {
- Name = "POD_NAMESPACE",
- ValueFrom =
- new V1EnvVarSource
- {
- FieldRef = new V1ObjectFieldSelector
- {
- FieldPath = "metadata.namespace",
- },
- },
- },
- },
- Resources = new V1ResourceRequirements
- {
- Requests = new Dictionary
- {
- { "cpu", new ResourceQuantity("100m") },
- { "memory", new ResourceQuantity("64Mi") },
- },
- Limits = new Dictionary
- {
- { "cpu", new ResourceQuantity("100m") },
- { "memory", new ResourceQuantity("128Mi") },
- },
- },
- },
- },
- },
- },
- };
- output.Add($"deployment.{format.GetFileExtension()}", deployment);
- }
-}
+using k8s;
+using k8s.Models;
+
+using KubeOps.Cli.Output;
+
+namespace KubeOps.Cli.Generators;
+
+internal class DeploymentGenerator(OutputFormat format) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ var deployment = new V1Deployment(metadata: new V1ObjectMeta(
+ labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } },
+ name: "operator")).Initialize();
+ deployment.Spec = new V1DeploymentSpec
+ {
+ Replicas = 1,
+ RevisionHistoryLimit = 0,
+ Selector = new V1LabelSelector(
+ matchLabels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }),
+ Template = new V1PodTemplateSpec
+ {
+ Metadata = new V1ObjectMeta(
+ labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } }),
+ Spec = new V1PodSpec
+ {
+ TerminationGracePeriodSeconds = 10,
+ Containers = new List
+ {
+ new()
+ {
+ Image = "operator",
+ Name = "operator",
+ Env = new List
+ {
+ new()
+ {
+ Name = "POD_NAMESPACE",
+ ValueFrom =
+ new V1EnvVarSource
+ {
+ FieldRef = new V1ObjectFieldSelector
+ {
+ FieldPath = "metadata.namespace",
+ },
+ },
+ },
+ },
+ Resources = new V1ResourceRequirements
+ {
+ Requests = new Dictionary
+ {
+ { "cpu", new ResourceQuantity("100m") },
+ { "memory", new ResourceQuantity("64Mi") },
+ },
+ Limits = new Dictionary
+ {
+ { "cpu", new ResourceQuantity("100m") },
+ { "memory", new ResourceQuantity("128Mi") },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+ output.Add($"deployment.{format.GetFileExtension()}", deployment);
+ }
+}
diff --git a/src/KubeOps.Cli/Generators/DockerfileGenerator.cs b/src/KubeOps.Cli/Generators/DockerfileGenerator.cs
index 728263d0..7bb7d113 100644
--- a/src/KubeOps.Cli/Generators/DockerfileGenerator.cs
+++ b/src/KubeOps.Cli/Generators/DockerfileGenerator.cs
@@ -1,33 +1,33 @@
-using KubeOps.Cli.Output;
-
-namespace KubeOps.Cli.Generators;
-
-internal class DockerfileGenerator(bool hasWebhooks) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- output.Add(
- "Dockerfile",
- $"""
- FROM mcr.microsoft.com/dotnet/sdk:latest as build
- WORKDIR /operator
-
- COPY ./ ./
- RUN dotnet publish -c Release /p:AssemblyName=operator -o out
-
- # The runner for the application
- FROM mcr.microsoft.com/dotnet/{(hasWebhooks ? "aspnet" : "runtime")}:latest as final
-
- RUN addgroup k8s-operator && useradd -G k8s-operator operator-user
-
- WORKDIR /operator
- COPY --from=build /operator/out/ ./
- RUN chown operator-user:k8s-operator -R .
-
- USER operator-user
-
- ENTRYPOINT [ "dotnet", "operator.dll" ]
- """,
- OutputFormat.Plain);
- }
-}
+using KubeOps.Cli.Output;
+
+namespace KubeOps.Cli.Generators;
+
+internal class DockerfileGenerator(bool hasWebhooks) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ output.Add(
+ "Dockerfile",
+ $"""
+ FROM mcr.microsoft.com/dotnet/sdk:latest as build
+ WORKDIR /operator
+
+ COPY ./ ./
+ RUN dotnet publish -c Release /p:AssemblyName=operator -o out
+
+ # The runner for the application
+ FROM mcr.microsoft.com/dotnet/{(hasWebhooks ? "aspnet" : "runtime")}:latest as final
+
+ RUN addgroup k8s-operator && useradd -G k8s-operator operator-user
+
+ WORKDIR /operator
+ COPY --from=build /operator/out/ ./
+ RUN chown operator-user:k8s-operator -R .
+
+ USER operator-user
+
+ ENTRYPOINT [ "dotnet", "operator.dll" ]
+ """,
+ OutputFormat.Plain);
+ }
+}
diff --git a/src/KubeOps.Cli/Generators/IConfigGenerator.cs b/src/KubeOps.Cli/Generators/IConfigGenerator.cs
index e25eeac5..46dd3df7 100644
--- a/src/KubeOps.Cli/Generators/IConfigGenerator.cs
+++ b/src/KubeOps.Cli/Generators/IConfigGenerator.cs
@@ -1,10 +1,10 @@
-using KubeOps.Cli.Output;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Generators;
-
-internal interface IConfigGenerator
-{
- void Generate(ResultOutput output);
-}
+using KubeOps.Cli.Output;
+
+using Spectre.Console;
+
+namespace KubeOps.Cli.Generators;
+
+internal interface IConfigGenerator
+{
+ void Generate(ResultOutput output);
+}
diff --git a/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs b/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs
index 5e84d4fb..d69b0719 100644
--- a/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs
+++ b/src/KubeOps.Cli/Generators/MutationWebhookGenerator.cs
@@ -1,56 +1,56 @@
-using k8s;
-using k8s.Models;
-
-using KubeOps.Cli.Output;
-using KubeOps.Cli.Transpilation;
-
-namespace KubeOps.Cli.Generators;
-
-internal class MutationWebhookGenerator
- (List webhooks, byte[] caBundle, OutputFormat format) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- if (webhooks.Count == 0)
- {
- return;
- }
-
- var mutatorConfig = new V1MutatingWebhookConfiguration(
- metadata: new V1ObjectMeta(name: "mutators"),
- webhooks: new List()).Initialize();
-
- foreach (var hook in webhooks)
- {
- mutatorConfig.Webhooks.Add(new V1MutatingWebhook
- {
- Name = $"mutate.{hook.Metadata.SingularName}.{hook.Metadata.Group}.{hook.Metadata.Version}",
- MatchPolicy = "Exact",
- AdmissionReviewVersions = new[] { "v1" },
- SideEffects = "None",
- Rules = new[]
- {
- new V1RuleWithOperations
- {
- Operations = hook.GetOperations(),
- Resources = new[] { hook.Metadata.PluralName },
- ApiGroups = new[] { hook.Metadata.Group },
- ApiVersions = new[] { hook.Metadata.Version },
- },
- },
- ClientConfig = new Admissionregistrationv1WebhookClientConfig
- {
- CaBundle = caBundle,
- Service = new Admissionregistrationv1ServiceReference
- {
- Name = "operator",
- Path = hook.WebhookPath,
- },
- },
- });
- }
-
- output.Add(
- $"mutators.{format.GetFileExtension()}", mutatorConfig);
- }
-}
+using k8s;
+using k8s.Models;
+
+using KubeOps.Cli.Output;
+using KubeOps.Cli.Transpilation;
+
+namespace KubeOps.Cli.Generators;
+
+internal class MutationWebhookGenerator
+ (List webhooks, byte[] caBundle, OutputFormat format) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ if (webhooks.Count == 0)
+ {
+ return;
+ }
+
+ var mutatorConfig = new V1MutatingWebhookConfiguration(
+ metadata: new V1ObjectMeta(name: "mutators"),
+ webhooks: new List()).Initialize();
+
+ foreach (var hook in webhooks)
+ {
+ mutatorConfig.Webhooks.Add(new V1MutatingWebhook
+ {
+ Name = $"mutate.{hook.Metadata.SingularName}.{hook.Metadata.Group}.{hook.Metadata.Version}",
+ MatchPolicy = "Exact",
+ AdmissionReviewVersions = new[] { "v1" },
+ SideEffects = "None",
+ Rules = new[]
+ {
+ new V1RuleWithOperations
+ {
+ Operations = hook.GetOperations(),
+ Resources = new[] { hook.Metadata.PluralName },
+ ApiGroups = new[] { hook.Metadata.Group },
+ ApiVersions = new[] { hook.Metadata.Version },
+ },
+ },
+ ClientConfig = new Admissionregistrationv1WebhookClientConfig
+ {
+ CaBundle = caBundle,
+ Service = new Admissionregistrationv1ServiceReference
+ {
+ Name = "operator",
+ Path = hook.WebhookPath,
+ },
+ },
+ });
+ }
+
+ output.Add(
+ $"mutators.{format.GetFileExtension()}", mutatorConfig);
+ }
+}
diff --git a/src/KubeOps.Cli/Generators/RbacGenerator.cs b/src/KubeOps.Cli/Generators/RbacGenerator.cs
index 0b73564d..30e74633 100644
--- a/src/KubeOps.Cli/Generators/RbacGenerator.cs
+++ b/src/KubeOps.Cli/Generators/RbacGenerator.cs
@@ -1,41 +1,41 @@
-using System.Reflection;
-
-using k8s;
-using k8s.Models;
-
-using KubeOps.Abstractions.Rbac;
-using KubeOps.Cli.Output;
-using KubeOps.Cli.Transpilation;
-using KubeOps.Transpiler;
-
-namespace KubeOps.Cli.Generators;
-
-internal class RbacGenerator(MetadataLoadContext parser,
- OutputFormat outputFormat) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- var attributes = parser
- .GetRbacAttributes()
- .Concat(parser.GetContextType().GetCustomAttributesData())
- .ToList();
-
- var role = new V1ClusterRole(rules: parser.Transpile(attributes).ToList()).Initialize();
- role.Metadata.Name = "operator-role";
- output.Add($"operator-role.{outputFormat.GetFileExtension()}", role);
-
- var roleBinding = new V1ClusterRoleBinding(
- roleRef: new V1RoleRef(V1ClusterRole.KubeGroup, V1ClusterRole.KubeKind, "operator-role"),
- subjects: new List
- {
- new(V1ServiceAccount.KubeKind, "default", namespaceProperty: "system"),
- })
- .Initialize();
- roleBinding.Metadata.Name = "operator-role-binding";
- output.Add($"operator-role-binding.{outputFormat.GetFileExtension()}", roleBinding);
- }
-
- [EntityRbac(typeof(Corev1Event), Verbs = RbacVerb.Get | RbacVerb.List | RbacVerb.Create | RbacVerb.Update)]
- [EntityRbac(typeof(V1Lease), Verbs = RbacVerb.All)]
- private sealed class DefaultRbacAttributes;
-}
+using System.Reflection;
+
+using k8s;
+using k8s.Models;
+
+using KubeOps.Abstractions.Rbac;
+using KubeOps.Cli.Output;
+using KubeOps.Cli.Transpilation;
+using KubeOps.Transpiler;
+
+namespace KubeOps.Cli.Generators;
+
+internal class RbacGenerator(MetadataLoadContext parser,
+ OutputFormat outputFormat) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ var attributes = parser
+ .GetRbacAttributes()
+ .Concat(parser.GetContextType().GetCustomAttributesData())
+ .ToList();
+
+ var role = new V1ClusterRole(rules: parser.Transpile(attributes).ToList()).Initialize();
+ role.Metadata.Name = "operator-role";
+ output.Add($"operator-role.{outputFormat.GetFileExtension()}", role);
+
+ var roleBinding = new V1ClusterRoleBinding(
+ roleRef: new V1RoleRef(V1ClusterRole.KubeGroup, V1ClusterRole.KubeKind, "operator-role"),
+ subjects: new List
+ {
+ new(V1ServiceAccount.KubeKind, "default", namespaceProperty: "system"),
+ })
+ .Initialize();
+ roleBinding.Metadata.Name = "operator-role-binding";
+ output.Add($"operator-role-binding.{outputFormat.GetFileExtension()}", roleBinding);
+ }
+
+ [EntityRbac(typeof(Corev1Event), Verbs = RbacVerb.Get | RbacVerb.List | RbacVerb.Create | RbacVerb.Update)]
+ [EntityRbac(typeof(V1Lease), Verbs = RbacVerb.All)]
+ private sealed class DefaultRbacAttributes;
+}
diff --git a/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs b/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs
index a0febbef..6861d671 100644
--- a/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs
+++ b/src/KubeOps.Cli/Generators/ValidationWebhookGenerator.cs
@@ -1,56 +1,56 @@
-using k8s;
-using k8s.Models;
-
-using KubeOps.Cli.Output;
-using KubeOps.Cli.Transpilation;
-
-namespace KubeOps.Cli.Generators;
-
-internal class ValidationWebhookGenerator
- (List webhooks, byte[] caBundle, OutputFormat format) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- if (webhooks.Count == 0)
- {
- return;
- }
-
- var validatorConfig = new V1ValidatingWebhookConfiguration(
- metadata: new V1ObjectMeta(name: "validators"),
- webhooks: new List()).Initialize();
-
- foreach (var hook in webhooks)
- {
- validatorConfig.Webhooks.Add(new V1ValidatingWebhook
- {
- Name = $"validate.{hook.Metadata.SingularName}.{hook.Metadata.Group}.{hook.Metadata.Version}",
- MatchPolicy = "Exact",
- AdmissionReviewVersions = new[] { "v1" },
- SideEffects = "None",
- Rules = new[]
- {
- new V1RuleWithOperations
- {
- Operations = hook.GetOperations(),
- Resources = new[] { hook.Metadata.PluralName },
- ApiGroups = new[] { hook.Metadata.Group },
- ApiVersions = new[] { hook.Metadata.Version },
- },
- },
- ClientConfig = new Admissionregistrationv1WebhookClientConfig
- {
- CaBundle = caBundle,
- Service = new Admissionregistrationv1ServiceReference
- {
- Name = "operator",
- Path = hook.WebhookPath,
- },
- },
- });
- }
-
- output.Add(
- $"validators.{format.GetFileExtension()}", validatorConfig);
- }
-}
+using k8s;
+using k8s.Models;
+
+using KubeOps.Cli.Output;
+using KubeOps.Cli.Transpilation;
+
+namespace KubeOps.Cli.Generators;
+
+internal class ValidationWebhookGenerator
+ (List webhooks, byte[] caBundle, OutputFormat format) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ if (webhooks.Count == 0)
+ {
+ return;
+ }
+
+ var validatorConfig = new V1ValidatingWebhookConfiguration(
+ metadata: new V1ObjectMeta(name: "validators"),
+ webhooks: new List()).Initialize();
+
+ foreach (var hook in webhooks)
+ {
+ validatorConfig.Webhooks.Add(new V1ValidatingWebhook
+ {
+ Name = $"validate.{hook.Metadata.SingularName}.{hook.Metadata.Group}.{hook.Metadata.Version}",
+ MatchPolicy = "Exact",
+ AdmissionReviewVersions = new[] { "v1" },
+ SideEffects = "None",
+ Rules = new[]
+ {
+ new V1RuleWithOperations
+ {
+ Operations = hook.GetOperations(),
+ Resources = new[] { hook.Metadata.PluralName },
+ ApiGroups = new[] { hook.Metadata.Group },
+ ApiVersions = new[] { hook.Metadata.Version },
+ },
+ },
+ ClientConfig = new Admissionregistrationv1WebhookClientConfig
+ {
+ CaBundle = caBundle,
+ Service = new Admissionregistrationv1ServiceReference
+ {
+ Name = "operator",
+ Path = hook.WebhookPath,
+ },
+ },
+ });
+ }
+
+ output.Add(
+ $"validators.{format.GetFileExtension()}", validatorConfig);
+ }
+}
diff --git a/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs b/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs
index 00884a99..6f840cd6 100644
--- a/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs
+++ b/src/KubeOps.Cli/Generators/WebhookDeploymentGenerator.cs
@@ -1,102 +1,102 @@
-using System.Reflection;
-
-using k8s;
-using k8s.Models;
-
-using KubeOps.Cli.Output;
-using KubeOps.Cli.Transpilation;
-using KubeOps.Transpiler;
-
-namespace KubeOps.Cli.Generators;
-
-internal class WebhookDeploymentGenerator(OutputFormat format) : IConfigGenerator
-{
- public void Generate(ResultOutput output)
- {
- var deployment = new V1Deployment(metadata: new V1ObjectMeta(
- labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } },
- name: "operator")).Initialize();
- deployment.Spec = new V1DeploymentSpec
- {
- Replicas = 1,
- RevisionHistoryLimit = 0,
- Selector = new V1LabelSelector(
- matchLabels:
- new Dictionary { { "operator-deployment", "kubernetes-operator" } }),
- Template = new V1PodTemplateSpec
- {
- Metadata = new V1ObjectMeta(
- labels:
- new Dictionary { { "operator-deployment", "kubernetes-operator" }, }),
- Spec = new V1PodSpec
- {
- TerminationGracePeriodSeconds = 10,
- Volumes = new List
- {
- new() { Name = "certificates", Secret = new() { SecretName = "webhook-cert" }, },
- new() { Name = "ca-certificates", Secret = new() { SecretName = "webhook-ca" }, },
- },
- Containers = new List
- {
- new()
- {
- Image = "operator",
- Name = "operator",
- VolumeMounts = new List
- {
- new() { Name = "certificates", MountPath = "/certs", ReadOnlyProperty = true, },
- new() { Name = "ca-certificates", MountPath = "/ca", ReadOnlyProperty = true, },
- },
- Env = new List
- {
- new()
- {
- Name = "POD_NAMESPACE",
- ValueFrom =
- new V1EnvVarSource
- {
- FieldRef = new V1ObjectFieldSelector
- {
- FieldPath = "metadata.namespace",
- },
- },
- },
- },
- EnvFrom =
- new List
- {
- new() { ConfigMapRef = new() { Name = "webhook-config" } },
- },
- Ports = new List { new(5001, name: "https"), },
- Resources = new V1ResourceRequirements
- {
- Requests = new Dictionary
- {
- { "cpu", new ResourceQuantity("100m") },
- { "memory", new ResourceQuantity("64Mi") },
- },
- Limits = new Dictionary
- {
- { "cpu", new ResourceQuantity("100m") },
- { "memory", new ResourceQuantity("128Mi") },
- },
- },
- },
- },
- },
- },
- };
- output.Add($"deployment.{format.GetFileExtension()}", deployment);
-
- output.Add(
- $"service.{format.GetFileExtension()}",
- new V1Service(
- metadata: new V1ObjectMeta(name: "operator"),
- spec: new V1ServiceSpec
- {
- Ports =
- new List { new() { Name = "https", TargetPort = "https", Port = 443, }, },
- Selector = new Dictionary { { "operator-deployment", "kubernetes-operator" }, },
- }).Initialize());
- }
-}
+using System.Reflection;
+
+using k8s;
+using k8s.Models;
+
+using KubeOps.Cli.Output;
+using KubeOps.Cli.Transpilation;
+using KubeOps.Transpiler;
+
+namespace KubeOps.Cli.Generators;
+
+internal class WebhookDeploymentGenerator(OutputFormat format) : IConfigGenerator
+{
+ public void Generate(ResultOutput output)
+ {
+ var deployment = new V1Deployment(metadata: new V1ObjectMeta(
+ labels: new Dictionary { { "operator-deployment", "kubernetes-operator" } },
+ name: "operator")).Initialize();
+ deployment.Spec = new V1DeploymentSpec
+ {
+ Replicas = 1,
+ RevisionHistoryLimit = 0,
+ Selector = new V1LabelSelector(
+ matchLabels:
+ new Dictionary { { "operator-deployment", "kubernetes-operator" } }),
+ Template = new V1PodTemplateSpec
+ {
+ Metadata = new V1ObjectMeta(
+ labels:
+ new Dictionary { { "operator-deployment", "kubernetes-operator" }, }),
+ Spec = new V1PodSpec
+ {
+ TerminationGracePeriodSeconds = 10,
+ Volumes = new List
+ {
+ new() { Name = "certificates", Secret = new() { SecretName = "webhook-cert" }, },
+ new() { Name = "ca-certificates", Secret = new() { SecretName = "webhook-ca" }, },
+ },
+ Containers = new List
+ {
+ new()
+ {
+ Image = "operator",
+ Name = "operator",
+ VolumeMounts = new List
+ {
+ new() { Name = "certificates", MountPath = "/certs", ReadOnlyProperty = true, },
+ new() { Name = "ca-certificates", MountPath = "/ca", ReadOnlyProperty = true, },
+ },
+ Env = new List
+ {
+ new()
+ {
+ Name = "POD_NAMESPACE",
+ ValueFrom =
+ new V1EnvVarSource
+ {
+ FieldRef = new V1ObjectFieldSelector
+ {
+ FieldPath = "metadata.namespace",
+ },
+ },
+ },
+ },
+ EnvFrom =
+ new List
+ {
+ new() { ConfigMapRef = new() { Name = "webhook-config" } },
+ },
+ Ports = new List { new(5001, name: "https"), },
+ Resources = new V1ResourceRequirements
+ {
+ Requests = new Dictionary
+ {
+ { "cpu", new ResourceQuantity("100m") },
+ { "memory", new ResourceQuantity("64Mi") },
+ },
+ Limits = new Dictionary
+ {
+ { "cpu", new ResourceQuantity("100m") },
+ { "memory", new ResourceQuantity("128Mi") },
+ },
+ },
+ },
+ },
+ },
+ },
+ };
+ output.Add($"deployment.{format.GetFileExtension()}", deployment);
+
+ output.Add(
+ $"service.{format.GetFileExtension()}",
+ new V1Service(
+ metadata: new V1ObjectMeta(name: "operator"),
+ spec: new V1ServiceSpec
+ {
+ Ports =
+ new List { new() { Name = "https", TargetPort = "https", Port = 443, }, },
+ Selector = new Dictionary { { "operator-deployment", "kubernetes-operator" }, },
+ }).Initialize());
+ }
+}
diff --git a/src/KubeOps.Cli/Options.cs b/src/KubeOps.Cli/Options.cs
index 81e0b6db..5bf3df69 100644
--- a/src/KubeOps.Cli/Options.cs
+++ b/src/KubeOps.Cli/Options.cs
@@ -1,43 +1,43 @@
-using System.CommandLine;
-using System.Text.RegularExpressions;
-
-using KubeOps.Cli.Output;
-
-namespace KubeOps.Cli;
-
-internal static class Options
-{
- public static readonly Option OutputFormat = new(
- "--format",
- () => Output.OutputFormat.Yaml,
- "The format of the generated output.");
-
- public static readonly Option OutputPath = new(
- "--out",
- "The path the command will write the files to. If omitted, prints output to console.");
-
- public static readonly Option TargetFramework = new(
- ["--target-framework", "--tfm"],
- description: "Target framework of projects in the solution to search for entities. " +
- "If omitted, the newest framework is used.");
-
- public static readonly Option SolutionProjectRegex = new(
- "--project",
- parseArgument: result =>
- {
- var value = result.Tokens.Single().Value;
- return new Regex(value);
- },
- description: "Regex pattern to filter projects in the solution to search for entities. " +
- "If omitted, all projects are searched.");
-
- public static readonly Option Force = new(
- ["--force", "-f"],
- () => false,
- description: "Do not bother the user with questions and just do it.");
-
- public static readonly Option ClearOutputPath = new(
- ["--clear-out"],
- () => false,
- description: "Clear the output path before generating resources.");
-}
+using System.CommandLine;
+using System.Text.RegularExpressions;
+
+using KubeOps.Cli.Output;
+
+namespace KubeOps.Cli;
+
+internal static class Options
+{
+ public static readonly Option OutputFormat = new(
+ "--format",
+ () => Output.OutputFormat.Yaml,
+ "The format of the generated output.");
+
+ public static readonly Option OutputPath = new(
+ "--out",
+ "The path the command will write the files to. If omitted, prints output to console.");
+
+ public static readonly Option TargetFramework = new(
+ ["--target-framework", "--tfm"],
+ description: "Target framework of projects in the solution to search for entities. " +
+ "If omitted, the newest framework is used.");
+
+ public static readonly Option SolutionProjectRegex = new(
+ "--project",
+ parseArgument: result =>
+ {
+ var value = result.Tokens.Single().Value;
+ return new Regex(value);
+ },
+ description: "Regex pattern to filter projects in the solution to search for entities. " +
+ "If omitted, all projects are searched.");
+
+ public static readonly Option Force = new(
+ ["--force", "-f"],
+ () => false,
+ description: "Do not bother the user with questions and just do it.");
+
+ public static readonly Option ClearOutputPath = new(
+ ["--clear-out"],
+ () => false,
+ description: "Clear the output path before generating resources.");
+}
diff --git a/src/KubeOps.Cli/Output/OutputFormat.cs b/src/KubeOps.Cli/Output/OutputFormat.cs
index d8bd2ac0..c4567c58 100644
--- a/src/KubeOps.Cli/Output/OutputFormat.cs
+++ b/src/KubeOps.Cli/Output/OutputFormat.cs
@@ -1,29 +1,29 @@
-namespace KubeOps.Cli.Output;
-
-internal enum OutputFormat
-{
- ///
- /// Format the output in Kubernetes YAML style.
- ///
- Yaml,
-
- ///
- /// Format the output in Kubernetes JSON style.
- ///
- Json,
-
- ///
- /// Format the output in plain text style.
- ///
- Plain,
-}
-
-internal static class OutputFormatExtensions
-{
- public static string GetFileExtension(this OutputFormat format) => format switch
- {
- OutputFormat.Yaml => "yaml",
- OutputFormat.Json => "json",
- _ => string.Empty,
- };
-}
+namespace KubeOps.Cli.Output;
+
+internal enum OutputFormat
+{
+ ///
+ /// Format the output in Kubernetes YAML style.
+ ///
+ Yaml,
+
+ ///
+ /// Format the output in Kubernetes JSON style.
+ ///
+ Json,
+
+ ///
+ /// Format the output in plain text style.
+ ///
+ Plain,
+}
+
+internal static class OutputFormatExtensions
+{
+ public static string GetFileExtension(this OutputFormat format) => format switch
+ {
+ OutputFormat.Yaml => "yaml",
+ OutputFormat.Json => "json",
+ _ => string.Empty,
+ };
+}
diff --git a/src/KubeOps.Cli/Output/ResultOutput.cs b/src/KubeOps.Cli/Output/ResultOutput.cs
index e234e710..09be5e00 100644
--- a/src/KubeOps.Cli/Output/ResultOutput.cs
+++ b/src/KubeOps.Cli/Output/ResultOutput.cs
@@ -1,61 +1,61 @@
-using System.Text;
-
-using k8s;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Output;
-
-internal class ResultOutput(IAnsiConsole console, OutputFormat defaultFormat)
-{
- private readonly Dictionary _files = new();
-
- public IEnumerable Files => _files.Keys;
-
- public IEnumerable DefaultFormatFiles =>
- _files
- .Where(f => f.Value.Item2 == defaultFormat)
- .Select(f => f.Key);
-
- public object this[string filename]
- {
- get => _files[filename].Item1;
- }
-
- public void Add(string filename, object content) => _files.Add(filename, (content, defaultFormat));
-
- public void Add(string filename, object content, OutputFormat format) => _files.Add(filename, (content, format));
-
- public async Task Write(string outputDirectory)
- {
- Directory.CreateDirectory(outputDirectory);
- foreach (var (filename, content) in _files)
- {
- await using var file = File.Open(
- Path.Join(
- outputDirectory,
- filename),
- FileMode.Create);
- await file.WriteAsync(Encoding.UTF8.GetBytes(Serialize(content)));
- }
- }
-
- public void Write()
- {
- console.Write(new Rule());
- foreach (var (filename, content) in _files)
- {
- console.MarkupLineInterpolated($"[bold]File:[/] [underline]{filename}[/]");
- console.WriteLine(Serialize(content));
- console.Write(new Rule());
- }
- }
-
- private static string Serialize((object Object, OutputFormat Format) data) => data.Format switch
- {
- OutputFormat.Yaml => KubernetesYaml.Serialize(data.Object),
- OutputFormat.Json => KubernetesJson.Serialize(data.Object),
- OutputFormat.Plain => data.Object.ToString() ?? string.Empty,
- _ => throw new ArgumentException("Unknown output format."),
- };
-}
+using System.Text;
+
+using k8s;
+
+using Spectre.Console;
+
+namespace KubeOps.Cli.Output;
+
+internal class ResultOutput(IAnsiConsole console, OutputFormat defaultFormat)
+{
+ private readonly Dictionary _files = new();
+
+ public IEnumerable Files => _files.Keys;
+
+ public IEnumerable DefaultFormatFiles =>
+ _files
+ .Where(f => f.Value.Item2 == defaultFormat)
+ .Select(f => f.Key);
+
+ public object this[string filename]
+ {
+ get => _files[filename].Item1;
+ }
+
+ public void Add(string filename, object content) => _files.Add(filename, (content, defaultFormat));
+
+ public void Add(string filename, object content, OutputFormat format) => _files.Add(filename, (content, format));
+
+ public async Task Write(string outputDirectory)
+ {
+ Directory.CreateDirectory(outputDirectory);
+ foreach (var (filename, content) in _files)
+ {
+ await using var file = File.Open(
+ Path.Join(
+ outputDirectory,
+ filename),
+ FileMode.Create);
+ await file.WriteAsync(Encoding.UTF8.GetBytes(Serialize(content)));
+ }
+ }
+
+ public void Write()
+ {
+ console.Write(new Rule());
+ foreach (var (filename, content) in _files)
+ {
+ console.MarkupLineInterpolated($"[bold]File:[/] [underline]{filename}[/]");
+ console.WriteLine(Serialize(content));
+ console.Write(new Rule());
+ }
+ }
+
+ private static string Serialize((object Object, OutputFormat Format) data) => data.Format switch
+ {
+ OutputFormat.Yaml => KubernetesYaml.Serialize(data.Object),
+ OutputFormat.Json => KubernetesJson.Serialize(data.Object),
+ OutputFormat.Plain => data.Object.ToString() ?? string.Empty,
+ _ => throw new ArgumentException("Unknown output format."),
+ };
+}
diff --git a/src/KubeOps.Cli/Program.cs b/src/KubeOps.Cli/Program.cs
index 45bb09f6..77c0865f 100644
--- a/src/KubeOps.Cli/Program.cs
+++ b/src/KubeOps.Cli/Program.cs
@@ -1,28 +1,28 @@
-using System.CommandLine;
-using System.CommandLine.Builder;
-using System.CommandLine.Parsing;
-
-using KubeOps.Cli;
-using KubeOps.Cli.Commands.Generator;
-using KubeOps.Cli.Commands.Management;
-
-using Spectre.Console;
-
-using Version = KubeOps.Cli.Commands.Utilities.Version;
-
-return await new CommandLineBuilder(new RootCommand(
- "CLI for KubeOps. Commandline tool to help with management tasks such as generating or installing CRDs.")
- {
- Generate.Command, Version.Command, Install.Command, Uninstall.Command,
- })
- .UseDefaults()
- .UseParseErrorReporting(ExitCodes.UsageError)
- .UseExceptionHandler((ex, ctx) =>
- {
- AnsiConsole.MarkupLineInterpolated(
- $"[red]An error occurred whiled executing {ctx.ParseResult.CommandResult.Command}[/]");
- AnsiConsole.MarkupLineInterpolated($"[red]{ex.Message}[/]");
- ctx.ExitCode = ExitCodes.Error;
- })
- .Build()
- .InvokeAsync(args);
+using System.CommandLine;
+using System.CommandLine.Builder;
+using System.CommandLine.Parsing;
+
+using KubeOps.Cli;
+using KubeOps.Cli.Commands.Generator;
+using KubeOps.Cli.Commands.Management;
+
+using Spectre.Console;
+
+using Version = KubeOps.Cli.Commands.Utilities.Version;
+
+return await new CommandLineBuilder(new RootCommand(
+ "CLI for KubeOps. Commandline tool to help with management tasks such as generating or installing CRDs.")
+ {
+ Generate.Command, Version.Command, Install.Command, Uninstall.Command,
+ })
+ .UseDefaults()
+ .UseParseErrorReporting(ExitCodes.UsageError)
+ .UseExceptionHandler((ex, ctx) =>
+ {
+ AnsiConsole.MarkupLineInterpolated(
+ $"[red]An error occurred whiled executing {ctx.ParseResult.CommandResult.Command}[/]");
+ AnsiConsole.MarkupLineInterpolated($"[red]{ex.Message}[/]");
+ ctx.ExitCode = ExitCodes.Error;
+ })
+ .Build()
+ .InvokeAsync(args);
diff --git a/src/KubeOps.Cli/Transpilation/AssemblyLoader.cs b/src/KubeOps.Cli/Transpilation/AssemblyLoader.cs
index 04a3db67..c69d5adc 100644
--- a/src/KubeOps.Cli/Transpilation/AssemblyLoader.cs
+++ b/src/KubeOps.Cli/Transpilation/AssemblyLoader.cs
@@ -1,206 +1,206 @@
-using System.Diagnostics.CodeAnalysis;
-using System.Reflection;
-using System.Text;
-using System.Text.RegularExpressions;
-
-using k8s.Models;
-
-using KubeOps.Abstractions.Entities;
-using KubeOps.Abstractions.Entities.Attributes;
-using KubeOps.Abstractions.Rbac;
-using KubeOps.Operator.Web.Webhooks.Admission.Mutation;
-using KubeOps.Operator.Web.Webhooks.Admission.Validation;
-using KubeOps.Operator.Web.Webhooks.Conversion;
-using KubeOps.Transpiler;
-
-using Microsoft.Build.Locator;
-using Microsoft.CodeAnalysis.MSBuild;
-
-using Spectre.Console;
-
-namespace KubeOps.Cli.Transpilation;
-
-///
-/// AssemblyLoader.
-///
-[SuppressMessage(
- "Usage",
- "CA2252:This API requires opting into preview features",
- Justification = "It is the CLI that uses the libraries.")]
-internal static partial class AssemblyLoader
-{
- static AssemblyLoader()
- {
- MSBuildLocator.RegisterDefaults();
- }
-
- public static Task ForProject(
- IAnsiConsole console,
- FileInfo projectFile)
- => console.Status().StartAsync($"Compiling {projectFile.Name}...", async _ =>
- {
- console.MarkupLineInterpolated($"Compile project [aqua]{projectFile.FullName}[/].");
- using var workspace = MSBuildWorkspace.Create();
- workspace.SkipUnrecognizedProjects = true;
- workspace.LoadMetadataForReferencedProjects = true;
- console.WriteLine("Load project.");
- var project = await workspace.OpenProjectAsync(projectFile.FullName);
- console.MarkupLine("[green]Project loaded.[/]");
- console.WriteLine("Load compilation context.");
- var compilation = await project.GetCompilationAsync();
- console.MarkupLine("[green]Compilation context loaded.[/]");
- if (compilation is null)
- {
- throw new AggregateException("Compilation could not be found.");
- }
-
- using var assemblyStream = new MemoryStream();
- console.WriteLine("Start compilation.");
- switch (compilation.Emit(assemblyStream))
- {
- case { Success: false, Diagnostics: var diag }:
- throw new AggregateException(
- $"Compilation failed: {diag.Aggregate(new StringBuilder(), (sb, d) => sb.AppendLine(d.ToString()))}");
- }
-
- console.MarkupLine("[green]Compilation successful.[/]");
- console.WriteLine();
- var mlc = new MetadataLoadContext(
- new PathAssemblyResolver(project.MetadataReferences.Select(m => m.Display ?? string.Empty)
- .Concat(new[] { typeof(object).Assembly.Location })));
- mlc.LoadFromByteArray(assemblyStream.ToArray());
-
- return mlc;
- });
-
- public static Task ForSolution(
- IAnsiConsole console,
- FileInfo slnFile,
- Regex? projectFilter = null,
- string? tfm = null)
- => console.Status().StartAsync($"Compiling {slnFile.Name}...", async _ =>
- {
- projectFilter ??= DefaultRegex();
- tfm ??= "latest";
-
- console.MarkupLineInterpolated($"Compile solution [aqua]{slnFile.FullName}[/].");
-#pragma warning disable RCS1097
- console.MarkupLineInterpolated($"[grey]With project filter:[/] {projectFilter.ToString()}");
-#pragma warning restore RCS1097
- console.MarkupLineInterpolated($"[grey]With Target Platform:[/] {tfm}");
-
- using var workspace = MSBuildWorkspace.Create();
- workspace.SkipUnrecognizedProjects = true;
- workspace.LoadMetadataForReferencedProjects = true;
- console.WriteLine("Load solution.");
- var solution = await workspace.OpenSolutionAsync(slnFile.FullName);
- console.MarkupLine("[green]Solution loaded.[/]");
-
- var assemblies = await Task.WhenAll(solution.Projects
- .Select(p =>
- {
- var name = TfmComparer.TfmRegex().Replace(p.Name, string.Empty);
- var tfm = TfmComparer.TfmRegex().Match(p.Name).Groups["tfm"].Value;
- return (name, tfm, project: p);
- })
- .Where(p => projectFilter.IsMatch(p.name))
- .Where(p => tfm == "latest" || p.tfm.Length == 0 || p.tfm == tfm)
- .OrderByDescending(p => p.tfm, new TfmComparer())
- .GroupBy(p => p.name)
- .Select(p => p.FirstOrDefault())
- .Where(p => p != default)
- .Select(async p =>
- {
- console.MarkupLineInterpolated(
- $"Load compilation context for [aqua]{p.name}[/]{(p.tfm.Length > 0 ? $" [grey]{p.tfm}[/]" : string.Empty)}.");
- var compilation = await p.project.GetCompilationAsync();
- console.MarkupLineInterpolated($"[green]Compilation context loaded for {p.name}.[/]");
- if (compilation is null)
- {
- throw new AggregateException("Compilation could not be found.");
- }
-
- using var assemblyStream = new MemoryStream();
- console.MarkupLineInterpolated(
- $"Start compilation for [aqua]{p.name}[/]{(p.tfm.Length > 0 ? $" [grey]{p.tfm}[/]" : string.Empty)}.");
- switch (compilation.Emit(assemblyStream))
- {
- case { Success: false, Diagnostics: var diag }:
- throw new AggregateException(
- $"Compilation failed: {diag.Aggregate(new StringBuilder(), (sb, d) => sb.AppendLine(d.ToString()))}");
- }
-
- console.MarkupLineInterpolated($"[green]Compilation successful for {p.name}.[/]");
- return (Assembly: assemblyStream.ToArray(),
- Refs: p.project.MetadataReferences.Select(m => m.Display ?? string.Empty));
- }));
-
- console.WriteLine();
- var mlc = new MetadataLoadContext(
- new PathAssemblyResolver(assemblies.SelectMany(a => a.Refs)
- .Concat(new[] { typeof(object).Assembly.Location }).Distinct()));
- foreach (var assembly in assemblies)
- {
- mlc.LoadFromByteArray(assembly.Assembly);
- }
-
- return mlc;
- });
-
- public static IEnumerable GetEntities(this MetadataLoadContext context) => context.GetAssemblies()
- .SelectMany(a => a.DefinedTypes)
- .Select(t => (t, attrs: CustomAttributeData.GetCustomAttributes(t)))
- .Where(e => e.attrs.Any(a => a.AttributeType.Name == nameof(KubernetesEntityAttribute)) &&
- e.attrs.All(a => a.AttributeType.Name != nameof(IgnoreAttribute)))
- .Select(e => e.t);
-
- public static IEnumerable GetRbacAttributes(this MetadataLoadContext context)
- {
- foreach (var type in context.GetAssemblies()
- .SelectMany(a => a.DefinedTypes)
- .SelectMany(t =>
- t.GetCustomAttributesData