├── docs ├── static │ ├── .nojekyll │ └── img │ │ └── logo.png ├── docs │ ├── operator │ │ ├── testing │ │ │ └── _category_.json │ │ └── building-blocks │ │ │ └── _category_.json │ └── packages │ │ ├── cli.mdx │ │ ├── operator.mdx │ │ ├── operator-web.mdx │ │ ├── templates.mdx │ │ ├── kubernetes-client.mdx │ │ ├── transpiler.mdx │ │ ├── abstractions.mdx │ │ ├── generator.mdx │ │ └── index.mdx ├── .gitignore ├── sidebars.js ├── src │ ├── pages │ │ ├── index.module.css │ │ └── index.js │ └── css │ │ └── custom.css └── package.json ├── examples ├── .gitignore ├── WebhookOperator │ ├── test_entity.yaml │ ├── invalid_test_entity.yaml │ ├── appsettings.json │ ├── Properties │ │ └── launchSettings.json │ ├── Entities │ │ └── V1TestEntity.cs │ ├── Webhooks │ │ ├── TestMutationWebhook.cs │ │ └── TestValidationWebhook.cs │ ├── WebhookOperator.csproj │ ├── Program.cs │ └── Controller │ │ └── V1TestEntityController.cs ├── ConversionWebhookOperator │ ├── Entities │ │ ├── v1.yaml │ │ ├── V1TestEntity.cs │ │ ├── V2TestEntity.cs │ │ └── V3TestEntity.cs │ ├── appsettings.json │ ├── Properties │ │ └── launchSettings.json │ ├── ConversionWebhookOperator.csproj │ ├── Program.cs │ └── Controller │ │ └── V1TestEntityController.cs └── Operator │ ├── test_entity.yaml │ ├── Properties │ └── launchSettings.json │ ├── Finalizer │ └── FinalizerOne.cs │ ├── Program.cs │ ├── Entities │ └── V1TestEntity.cs │ ├── Operator.csproj │ └── Controller │ └── V1TestEntityController.cs ├── .config ├── nuget-license-ignore.json ├── update-notice.sh ├── dotnet-tools.json └── nuget-license-input.json ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ ├── documentation.yaml │ ├── feature_request.yaml │ └── bug_report.yaml └── workflows │ ├── dotnet-test.yml │ ├── docs-test.yml │ ├── security-analysis.yml │ ├── gh-pages.yml │ ├── dotnet-pre-release.yml │ └── dotnet-release.yml ├── .release ├── release.main.mjs ├── release.main-pre.mjs └── release.base.mjs ├── res ├── icon.png ├── logo.png └── logo_big.png ├── .gitattributes ├── CODE_OF_CONDUCT.md ├── test ├── KubeOps.Abstractions.Test │ ├── KubeOps.Abstractions.Test.csproj │ └── Properties │ │ └── GlobalAssemblyInfo.cs ├── KubeOps.KubernetesClient.Test │ ├── KubeOps.KubernetesClient.Test.csproj │ ├── Properties │ │ └── GlobalAssemblyInfo.cs │ ├── IntegrationTestCollection.cs │ └── LabelSelector.Test.cs ├── KubeOps.Cli.Test │ ├── Properties │ │ └── GlobalAssemblyInfo.cs │ └── KubeOps.Cli.Test.csproj ├── KubeOps.Generator.Test │ ├── Properties │ │ └── GlobalAssemblyInfo.cs │ ├── KubeOps.Generator.Test.csproj │ └── TestHelperExtensions.cs ├── KubeOps.Operator.Test │ ├── Properties │ │ └── GlobalAssemblyInfo.cs │ ├── KubeOps.Operator.Test.csproj │ ├── TestEntities │ │ └── V1OperatorIntegrationTestEntity.cs │ ├── InvocationCounter.cs │ └── HostedServices │ │ └── LeaderResourceWatcher.Integration.Test.cs ├── KubeOps.Transpiler.Test │ ├── Properties │ │ └── GlobalAssemblyInfo.cs │ ├── IntegrationTestCollection.cs │ ├── KubeOps.Transpiler.Test.csproj │ ├── Entities.Test.cs │ ├── Entities.Mlc.Test.cs │ └── MlcProvider.cs ├── KubeOps.Operator.Web.Test │ ├── Properties │ │ └── GlobalAssemblyInfo.cs │ ├── TestApp │ │ ├── TestValidationWebhook.cs │ │ ├── TestMutationWebhook.cs │ │ └── V1OperatorWebIntegrationTestEntity.cs │ ├── Webhooks │ │ ├── MutationWebhook.Integration.Test.cs │ │ └── ValidationWebhook.Integration.Test.cs │ ├── KubeOps.Operator.Web.Test.csproj │ └── Certificates │ │ └── CertificateGenerator.Test.cs └── Directory.Build.props ├── src ├── KubeOps.Templates │ ├── Templates │ │ ├── WebOperator.CSharp │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── GeneratedOperatorProject.csproj │ │ │ ├── Entities │ │ │ │ └── V1DemoEntity.cs │ │ │ ├── Webhooks │ │ │ │ ├── TestMutationWebhook.cs │ │ │ │ └── TestValidationWebhook.cs │ │ │ ├── Finalizer │ │ │ │ └── DemoFinalizer.cs │ │ │ ├── Controller │ │ │ │ └── DemoController.cs │ │ │ └── Program.cs │ │ ├── EmptyWebOperator.CSharp │ │ │ ├── GeneratedOperatorProject.csproj │ │ │ └── Program.cs │ │ ├── Operator.CSharp │ │ │ ├── GeneratedOperatorProject.csproj │ │ │ ├── Program.cs │ │ │ ├── Entities │ │ │ │ └── V1DemoEntity.cs │ │ │ ├── Finalizer │ │ │ │ └── DemoFinalizer.cs │ │ │ ├── Controller │ │ │ │ └── DemoController.cs │ │ │ └── .template.config │ │ │ │ └── template.json │ │ └── EmptyOperator.CSharp │ │ │ ├── GeneratedOperatorProject.csproj │ │ │ ├── Program.cs │ │ │ └── .template.config │ │ │ └── template.json │ └── KubeOps.Templates.csproj ├── KubeOps.Generator │ ├── Properties │ │ └── launchSettings.json │ ├── SyntaxReceiver │ │ ├── AttributedEntity.cs │ │ └── CombinedSyntaxReceiver.cs │ ├── KubeOps.Generator.csproj │ └── Generators │ │ └── AutoGeneratedSyntaxTrivia.cs ├── KubeOps.Operator.Web │ ├── Webhooks │ │ ├── WebhookConfig.cs │ │ ├── Admission │ │ │ ├── AdmissionReview.cs │ │ │ ├── Mutation │ │ │ │ └── MutationWebhookAttribute.cs │ │ │ ├── Validation │ │ │ │ └── ValidationWebhookAttribute.cs │ │ │ ├── AdmissionStatus.cs │ │ │ └── AdmissionResponse.cs │ │ ├── Conversion │ │ │ ├── ConversionReview.cs │ │ │ ├── ConversionStatus.cs │ │ │ └── ConversionWebhookAttribute.cs │ │ └── WebhookLoader.cs │ ├── Certificates │ │ └── CertificateWebhookService.cs │ ├── KubeOps.Operator.Web.csproj │ └── LocalTunnel │ │ ├── TunnelWebhookService.cs │ │ └── DevelopmentTunnel.cs ├── KubeOps.Abstractions │ ├── Kustomize │ │ ├── KustomizationCommonLabelsPair.cs │ │ ├── KustomizationImage.cs │ │ ├── KustomizationSecretGenerator.cs │ │ ├── KustomizationConfigMapGenerator.cs │ │ └── KustomizationCommonLabels.cs │ ├── Certificates │ │ ├── CertificatePair.cs │ │ └── ICertificateProvider.cs │ ├── Rbac │ │ ├── RbacAttribute.cs │ │ ├── EntityRbacAttribute.cs │ │ ├── RbacVerbs.cs │ │ └── GenericRbacAttribute.cs │ ├── Entities │ │ ├── Attributes │ │ │ ├── RequiredAttribute.cs │ │ │ ├── EntityScopeAttribute.cs │ │ │ ├── IgnoreEntityAttribute.cs │ │ │ ├── PreserveUnknownFieldsAttribute.cs │ │ │ ├── PatternAttribute.cs │ │ │ ├── MultipleOfAttribute.cs │ │ │ ├── DescriptionAttribute.cs │ │ │ ├── KubernetesEntityShortNamesAttribute.cs │ │ │ ├── StorageVersionAttribute.cs │ │ │ ├── EmbeddedResourceAttribute.cs │ │ │ ├── ExternalDocsAttribute.cs │ │ │ ├── RangeMaximum.cs │ │ │ ├── RangeMinimum.cs │ │ │ ├── LengthAttribute.cs │ │ │ ├── ItemsAttribute.cs │ │ │ └── AdditionalPrinterColumnAttribute.cs │ │ ├── DefaultEntityLabelSelector{TEntity}.cs │ │ ├── EntityScope.cs │ │ ├── PrinterColumnPriority.cs │ │ ├── CustomKubernetesEntity.cs │ │ ├── IEntityLabelSelector{TEntity}.cs │ │ ├── EntityList.cs │ │ ├── CustomKubernetesEntity{TSpec}.cs │ │ ├── CustomKubernetesEntity{TSpec,TStatus}.cs │ │ └── EntityMetadata.cs │ ├── Events │ │ ├── IEventPublisherFactory.cs │ │ └── EventType.cs │ ├── KubeOps.Abstractions.csproj │ ├── Reconciliation │ │ ├── Queue │ │ │ ├── RequeueType.cs │ │ │ ├── IEntityRequeueFactory.cs │ │ │ └── EntityRequeue.cs │ │ ├── Finalizer │ │ │ ├── IEntityFinalizer{TEntity}.cs │ │ │ └── IEventFinalizerAttacherFactory.cs │ │ ├── ReconciliationTriggerSource.cs │ │ └── IReconciler{TEntity}.cs │ ├── Crds │ │ └── CrdInstallerSettings.cs │ ├── Builder │ │ ├── RequeueStrategy.cs │ │ └── LeaderElectionType.cs │ └── README.md ├── KubeOps.Cli │ ├── Generators │ │ ├── IConfigGenerator.cs │ │ ├── CertificateGenerator.cs │ │ └── DockerfileGenerator.cs │ ├── ExitCodes.cs │ ├── Transpilation │ │ ├── MutationWebhook.cs │ │ ├── ValidationWebhook.cs │ │ └── BaseWebhook.cs │ ├── Commands │ │ ├── Generator │ │ │ └── Generate.cs │ │ └── Utilities │ │ │ └── Version.cs │ ├── Program.cs │ ├── Output │ │ └── OutputFormat.cs │ └── Properties │ │ └── launchSettings.json ├── KubeOps.KubernetesClient │ ├── LabelSelectors │ │ ├── ExistsSelector.cs │ │ ├── NotExistsSelector.cs │ │ ├── Extensions.cs │ │ ├── EqualsSelector.cs │ │ ├── NotEqualsSelector.cs │ │ └── LabelSelector.cs │ └── KubeOps.KubernetesClient.csproj ├── KubeOps.Operator │ ├── LeaderElection │ │ ├── ILeaderElectorFactory.cs │ │ ├── KubernetesLeaderElectorFactory.cs │ │ └── ServiceCollectionExtensions.cs │ ├── Queue │ │ ├── RequeueEntry{TEntity}.cs │ │ ├── KubeOpsEntityRequeueFactory.cs │ │ └── TimedEntityQueueExtensions.cs │ ├── Constants │ │ └── CacheConstants.cs │ ├── ServiceCollectionExtensions.cs │ ├── KubeOps.Operator.csproj │ └── Finalizer │ │ └── KubeOpsEventFinalizerAttacherFactory.cs └── KubeOps.Transpiler │ ├── KubeOps.Transpiler.csproj │ └── ContextCreator.cs ├── .gitignore └── renovate.json /docs/static/.nojekyll: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/static/img/logo.png: -------------------------------------------------------------------------------- 1 | ../../../res/logo.png -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | config/ 2 | Dockerfile 3 | -------------------------------------------------------------------------------- /.config/nuget-license-ignore.json: -------------------------------------------------------------------------------- 1 | ["SonarAnalyzer.CSharp"] 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [buehler] 2 | buy_me_a_coffee: cbue 3 | -------------------------------------------------------------------------------- /.release/release.main.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | branches: ["main"], 3 | }; 4 | -------------------------------------------------------------------------------- /res/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/dotnet-operator-sdk/HEAD/res/icon.png -------------------------------------------------------------------------------- /res/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/dotnet-operator-sdk/HEAD/res/logo.png -------------------------------------------------------------------------------- /res/logo_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dotnet/dotnet-operator-sdk/HEAD/res/logo_big.png -------------------------------------------------------------------------------- /.release/release.main-pre.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | branches: [{ name: "main", prerelease: "prerelease" }], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/docs/operator/testing/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position": 10, 3 | "label": "Testing", 4 | "collapsible": true, 5 | "collapsed": true 6 | } 7 | -------------------------------------------------------------------------------- /.config/update-notice.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | dotnet nuget-license -ji .config/nuget-license-input.json -ignore .config/nuget-license-ignore.json -o Markdown > NOTICE.md 3 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Normalize line endings in Git: https://www.aleksandrhovhannisyan.com/blog/crlf-vs-lf-normalizing-line-endings-in-git/ 2 | * text=auto 3 | *.cs text eol=crlf 4 | -------------------------------------------------------------------------------- /examples/WebhookOperator/test_entity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: webhook.dev/v1 2 | kind: TestEntity 3 | metadata: 4 | name: test-webhook-entity 5 | spec: 6 | username: overwrite 7 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Entities/v1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: conversionwebhook.dev/v1 2 | kind: TestEntity 3 | metadata: 4 | name: v1-entity 5 | spec: 6 | name: Max Muster 7 | -------------------------------------------------------------------------------- /docs/docs/operator/building-blocks/_category_.json: -------------------------------------------------------------------------------- 1 | { 2 | "position": 4, 3 | "label": "Building Blocks of an Operator", 4 | "collapsible": true, 5 | "collapsed": false 6 | } 7 | -------------------------------------------------------------------------------- /examples/Operator/test_entity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: testing.dev/v1 2 | kind: TestEntity 3 | metadata: 4 | name: my-test-entity 5 | spec: 6 | username: my-username 7 | email: foobar@test.ch 8 | -------------------------------------------------------------------------------- /examples/WebhookOperator/invalid_test_entity.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: webhook.dev/v1 2 | kind: TestEntity 3 | metadata: 4 | name: invalid-test-webhook-entity 5 | spec: 6 | username: forbidden 7 | -------------------------------------------------------------------------------- /examples/Operator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "Operator": { 5 | "commandName": "Project" 6 | } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | This project adheres to the .NET Foundation's Code of Conduct. 4 | For more details, please see the full text at: 5 | https://dotnetfoundation.org/about/policies/code-of-conduct 6 | -------------------------------------------------------------------------------- /test/KubeOps.Abstractions.Test/KubeOps.Abstractions.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /examples/WebhookOperator/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information", 6 | "KubeOps": "Trace" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /test/KubeOps.KubernetesClient.Test/KubeOps.KubernetesClient.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.config/dotnet-tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 1, 3 | "isRoot": true, 4 | "tools": { 5 | "nuget-license": { 6 | "version": "4.0.3", 7 | "commands": [ 8 | "nuget-license" 9 | ], 10 | "rollForward": false 11 | } 12 | } 13 | } -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft.AspNetCore": "Information", 6 | "KubeOps": "Trace" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /src/KubeOps.Generator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "KubeOps.Generator": { 5 | "commandName": "DebugRoslynComponent", 6 | "targetProject": "../../examples/Operator/Operator.csproj" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | /node_modules 3 | 4 | # Production 5 | /build 6 | 7 | # Generated files 8 | .docusaurus 9 | .cache-loader 10 | 11 | # Misc 12 | .DS_Store 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | npm-debug.log* 19 | yarn-debug.log* 20 | yarn-error.log* 21 | -------------------------------------------------------------------------------- /docs/docs/packages/cli.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Cli 3 | description: Command-line tools for KubeOps 4 | sidebar_position: 8 5 | hide_title: true 6 | --- 7 | 8 | import Cli from "../../../src/KubeOps.Cli/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Operator](./operator) - Core operator functionality 15 | -------------------------------------------------------------------------------- /examples/WebhookOperator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "WebhookOperator": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "http://localhost:5000", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: Q&A and Discussions 4 | url: https://github.com/dotnet/dotnet-operator-sdk/discussions 5 | about: Please use Discussions for Q&A 6 | - name: Discord Community 7 | url: https://discord.gg/ucUcxpPW8P 8 | about: Join our Discord community for real-time discussions 9 | -------------------------------------------------------------------------------- /test/KubeOps.Cli.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /test/KubeOps.Generator.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /test/KubeOps.Transpiler.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /test/KubeOps.Abstractions.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /docs/sidebars.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | /** 3 | @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} 4 | */ 5 | export default { 6 | packages: [ 7 | { 8 | type: "autogenerated", 9 | dirName: "packages", 10 | }, 11 | ], 12 | operator: [ 13 | { 14 | type: "autogenerated", 15 | dirName: "operator", 16 | }, 17 | ], 18 | }; 19 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "ConversionWebhookOperator": { 4 | "commandName": "Project", 5 | "dotnetRunMessages": true, 6 | "applicationUrl": "http://localhost:5000", 7 | "environmentVariables": { 8 | "ASPNETCORE_ENVIRONMENT": "Development" 9 | } 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /test/KubeOps.KubernetesClient.Test/Properties/GlobalAssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Diagnostics.CodeAnalysis; 6 | 7 | [assembly: ExcludeFromCodeCoverage] 8 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/WebhookConfig.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Operator.Web.Webhooks; 6 | 7 | internal record WebhookConfig(string Hostname, ushort Port); 8 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Kustomize/KustomizationCommonLabelsPair.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Kustomize; 6 | 7 | public class KustomizationCommonLabelsPair : Dictionary; 8 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/GeneratedOperatorProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | 12.0 6 | enable 7 | true 8 | false 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/EmptyWebOperator.CSharp/GeneratedOperatorProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | 12.0 6 | enable 7 | true 8 | false 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yaml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | description: "Suggest a topic that is not correctly documented (or not documented at all)" 3 | title: "[docs]: " 4 | labels: ["documentation"] 5 | body: 6 | - type: textarea 7 | id: description 8 | attributes: 9 | label: Describe the missing piece of documentation 10 | description: Describe what you miss in the docs (or what is wrong). 11 | validations: 12 | required: true 13 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Generators/IConfigGenerator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Cli.Output; 6 | 7 | using Spectre.Console; 8 | 9 | namespace KubeOps.Cli.Generators; 10 | 11 | internal interface IConfigGenerator 12 | { 13 | void Generate(ResultOutput output); 14 | } 15 | -------------------------------------------------------------------------------- /.config/nuget-license-input.json: -------------------------------------------------------------------------------- 1 | [ 2 | "src/KubeOps.Abstractions/KubeOps.Abstractions.csproj", 3 | "src/KubeOps.Cli/KubeOps.Cli.csproj", 4 | "src/KubeOps.Generator/KubeOps.Generator.csproj", 5 | "src/KubeOps.KubernetesClient/KubeOps.KubernetesClient.csproj", 6 | "src/KubeOps.Operator/KubeOps.Operator.csproj", 7 | "src/KubeOps.Operator.Web/KubeOps.Operator.Web.csproj", 8 | "src/KubeOps.Templates/KubeOps.Templates.csproj", 9 | "src/KubeOps.Transpiler/KubeOps.Transpiler.csproj" 10 | ] 11 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/ExitCodes.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Cli; 6 | 7 | internal static class ExitCodes 8 | { 9 | public const int Success = 0; 10 | public const int Error = 1; 11 | public const int Aborted = 2; 12 | public const int UsageError = 99; 13 | } 14 | -------------------------------------------------------------------------------- /docs/docs/packages/operator.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Operator 3 | description: Main operator engine for KubeOps 4 | sidebar_position: 3 5 | hide_title: true 6 | --- 7 | 8 | import Operator from "../../../src/KubeOps.Operator/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Abstractions](./abstractions) - Core interfaces and attributes 15 | - [KubeOps.Operator.Web](./operator-web) - Web support for webhooks 16 | - [KubeOps.KubernetesClient](./kubernetes-client) - Enhanced Kubernetes client 17 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Certificates/CertificatePair.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Security.Cryptography; 6 | using System.Security.Cryptography.X509Certificates; 7 | 8 | namespace KubeOps.Abstractions.Certificates; 9 | 10 | public record CertificatePair(X509Certificate2 Certificate, AsymmetricAlgorithm Key); 11 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Rbac/RbacAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Rbac; 6 | 7 | /// 8 | /// Abstract base class for all RBAC attributes. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 11 | public abstract class RbacAttribute : Attribute; 12 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/Operator.CSharp/GeneratedOperatorProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | 12.0 7 | enable 8 | true 9 | false 10 | false 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/EmptyOperator.CSharp/GeneratedOperatorProject.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | 12.0 7 | enable 8 | true 9 | false 10 | false 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /docs/docs/packages/operator-web.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Operator.Web 3 | description: ASP.NET Core integration for KubeOps 4 | sidebar_position: 4 5 | hide_title: true 6 | --- 7 | 8 | import OperatorWeb from "../../../src/KubeOps.Operator.Web/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Operator](./operator) - Core operator functionality 15 | - [KubeOps.Abstractions](./abstractions) - Core interfaces and attributes 16 | - [KubeOps.KubernetesClient](./kubernetes-client) - Enhanced Kubernetes client 17 | -------------------------------------------------------------------------------- /docs/docs/packages/templates.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Templates 3 | description: Project templates for KubeOps 4 | sidebar_position: 9 5 | hide_title: true 6 | --- 7 | 8 | import Templates from "../../../src/KubeOps.Templates/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Operator](./operator) - Core operator functionality 15 | - [KubeOps.Operator.Web](./operator-web) - Web support for webhooks 16 | - [KubeOps.Cli](./cli) - Command-line tools 17 | - [KubeOps.Generator](./generator) - Source generators 18 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/RequiredAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines a property of a specification as required. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class RequiredAttribute : Attribute; 12 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/EntityScopeAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | [AttributeUsage(AttributeTargets.Class, Inherited = false)] 8 | public class EntityScopeAttribute(EntityScope scope = default) : Attribute 9 | { 10 | public EntityScope Scope => scope; 11 | } 12 | -------------------------------------------------------------------------------- /docs/docs/packages/kubernetes-client.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.KubernetesClient 3 | description: Enhanced Kubernetes client for KubeOps 4 | sidebar_position: 5 5 | hide_title: true 6 | --- 7 | 8 | import KubernetesClient from "../../../src/KubeOps.KubernetesClient/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Operator](./operator) - Core operator functionality 15 | - [KubeOps.Abstractions](./abstractions) - Core interfaces and attributes 16 | - [KubeOps.Operator.Web](./operator-web) - Web support for webhooks 17 | -------------------------------------------------------------------------------- /docs/docs/packages/transpiler.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Transpiler 3 | description: YAML manifest generation for KubeOps 4 | sidebar_position: 7 5 | hide_title: true 6 | --- 7 | 8 | import Transpiler from "../../../src/KubeOps.Transpiler/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Abstractions](./abstractions) - Core interfaces and attributes 15 | - [KubeOps.Generator](./generator) - Source generators 16 | - [KubeOps.Cli](./cli) - Command-line tools 17 | - [KubeOps.Operator](./operator) - Core operator functionality 18 | -------------------------------------------------------------------------------- /docs/docs/packages/abstractions.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Abstractions 3 | description: Core interfaces and attributes for KubeOps 4 | sidebar_position: 2 5 | hide_title: true 6 | --- 7 | 8 | import Abstractions from "../../../src/KubeOps.Abstractions/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Operator](./operator) - Implements the core operator functionality 15 | - [KubeOps.Generator](./generator) - Generates code based on these abstractions 16 | - [KubeOps.Operator.Web](./operator-web) - Adds web support for webhooks 17 | -------------------------------------------------------------------------------- /src/KubeOps.Generator/SyntaxReceiver/AttributedEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Microsoft.CodeAnalysis.CSharp.Syntax; 6 | 7 | namespace KubeOps.Generator.SyntaxReceiver; 8 | 9 | internal record struct AttributedEntity( 10 | ClassDeclarationSyntax Class, 11 | string Kind, 12 | string Version, 13 | string? Group, 14 | string? Plural); 15 | -------------------------------------------------------------------------------- /docs/docs/packages/generator.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: KubeOps.Generator 3 | description: Source generators for KubeOps 4 | sidebar_position: 6 5 | hide_title: true 6 | --- 7 | 8 | import Generator from "../../../src/KubeOps.Generator/README.md"; 9 | 10 | 11 | 12 | ## Related Packages 13 | 14 | - [KubeOps.Abstractions](./abstractions) - Core interfaces and attributes 15 | - [KubeOps.Operator](./operator) - Core operator functionality 16 | - [KubeOps.Operator.Web](./operator-web) - Web support for webhooks 17 | - [KubeOps.Transpiler](./transpiler) - YAML manifest generation 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific files 2 | *.suo 3 | *.user 4 | *.userosscache 5 | *.sln.docstates 6 | 7 | # Ci things 8 | node_modules/ 9 | tools/ 10 | nuget/ 11 | artifacts/ 12 | coverage/ 13 | .tmp/ 14 | .nuke/temp 15 | .nuke/build.schema.json 16 | 17 | # Build results 18 | [Dd]ebug/ 19 | [Dd]ebugPublic/ 20 | [Rr]elease/ 21 | [Rr]eleases/ 22 | x64/ 23 | x86/ 24 | bld/ 25 | [Bb]in/ 26 | [Oo]bj/ 27 | [Oo]ut/ 28 | 29 | # Testing results 30 | coverage.json 31 | coverage.info 32 | .vs 33 | [Tt]est[Rr]esults/ 34 | 35 | # Docs 36 | _site 37 | 38 | #OSX 39 | .DS_Store 40 | 41 | #Rider 42 | .idea 43 | .fake -------------------------------------------------------------------------------- /docs/src/pages/index.module.css: -------------------------------------------------------------------------------- 1 | /** 2 | * CSS files with the .module.css suffix will be treated as CSS modules 3 | * and scoped locally. 4 | */ 5 | 6 | .heroBanner { 7 | padding: 4rem 0; 8 | text-align: center; 9 | position: relative; 10 | overflow: hidden; 11 | } 12 | 13 | @media screen and (max-width: 996px) { 14 | .heroBanner { 15 | padding: 2rem; 16 | } 17 | } 18 | 19 | .buttons { 20 | display: flex; 21 | align-items: center; 22 | justify-content: center; 23 | } 24 | 25 | .logo { 26 | border-radius: 50%; 27 | width: 128px; 28 | height: 128px; 29 | object-fit: cover; 30 | } 31 | -------------------------------------------------------------------------------- /test/KubeOps.KubernetesClient.Test/IntegrationTestCollection.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.Test; 6 | 7 | [CollectionDefinition(Name, DisableParallelization = true)] 8 | public class IntegrationTestCollection 9 | { 10 | public const string Name = "Integration Tests"; 11 | } 12 | 13 | [Collection(IntegrationTestCollection.Name)] 14 | public abstract class IntegrationTestBase; 15 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/IgnoreEntityAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Attribute that states that the given entity or property should be 9 | /// ignored during CRD generation. 10 | /// 11 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property)] 12 | public class IgnoreAttribute : Attribute; 13 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/Operator.CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | using KubeOps.Operator; 2 | 3 | using Microsoft.Extensions.Hosting; 4 | 5 | var builder = Host.CreateApplicationBuilder(args); 6 | 7 | builder.Services 8 | .AddKubernetesOperator() 9 | //-:cnd:noEmit 10 | #if DEBUG 11 | .AddCrdInstaller(c => 12 | { 13 | // Careful, this can be very destructive. 14 | // c.OverwriteExisting = true; 15 | // c.DeleteOnShutdown = true; 16 | }) 17 | #endif 18 | //+:cnd:noEmit 19 | .RegisterComponents(); 20 | 21 | using var host = builder.Build(); 22 | await host.RunAsync(); 23 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/EmptyOperator.CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | using KubeOps.Operator; 2 | 3 | using Microsoft.Extensions.Hosting; 4 | 5 | var builder = Host.CreateApplicationBuilder(args); 6 | 7 | builder.Services 8 | .AddKubernetesOperator() 9 | //-:cnd:noEmit 10 | #if DEBUG 11 | .AddCrdInstaller(c => 12 | { 13 | // Careful, this can be very destructive. 14 | // c.OverwriteExisting = true; 15 | // c.DeleteOnShutdown = true; 16 | }) 17 | #endif 18 | //+:cnd:noEmit 19 | .RegisterComponents(); 20 | 21 | using var host = builder.Build(); 22 | await host.RunAsync(); 23 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/PreserveUnknownFieldsAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines that a property should keep unknown fields 9 | /// so that kubernetes does not purge additional structures. 10 | /// 11 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] 12 | public class PreserveUnknownFieldsAttribute : Attribute; 13 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Transpilation/MutationWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace KubeOps.Cli.Transpilation; 10 | 11 | internal record MutationWebhook(TypeInfo Validator, EntityMetadata Metadata) : BaseWebhook(Validator, Metadata) 12 | { 13 | public override string WebhookPath => 14 | $"/mutate/{Validator.BaseType!.GenericTypeArguments[0].Name.ToLowerInvariant()}"; 15 | } 16 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/LabelSelectors/ExistsSelector.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | /// 8 | /// Selector that checks if a certain label exists. 9 | /// 10 | /// The label that needs to exist on the entity/resource. 11 | public record ExistsSelector(string Label) : LabelSelector 12 | { 13 | protected override string ToExpression() => $"{Label}"; 14 | } 15 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/DefaultEntityLabelSelector{TEntity}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Entities; 9 | 10 | public class DefaultEntityLabelSelector : IEntityLabelSelector 11 | where TEntity : IKubernetesObject 12 | { 13 | public ValueTask GetLabelSelectorAsync(CancellationToken cancellationToken) => ValueTask.FromResult(null); 14 | } 15 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Transpilation/ValidationWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace KubeOps.Cli.Transpilation; 10 | 11 | internal record ValidationWebhook(TypeInfo Validator, EntityMetadata Metadata) : BaseWebhook(Validator, Metadata) 12 | { 13 | public override string WebhookPath => 14 | $"/validate/{Validator.BaseType!.GenericTypeArguments[0].Name.ToLowerInvariant()}"; 15 | } 16 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/LabelSelectors/NotExistsSelector.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | /// 8 | /// Selector that checks if a certain label does not exist. 9 | /// 10 | /// The label that must not exist on the entity/resource. 11 | public record NotExistsSelector(string Label) : LabelSelector 12 | { 13 | protected override string ToExpression() => $"!{Label}"; 14 | } 15 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/Operator.CSharp/Entities/V1DemoEntity.cs: -------------------------------------------------------------------------------- 1 | using k8s.Models; 2 | 3 | using KubeOps.Abstractions.Entities; 4 | 5 | namespace GeneratedOperatorProject.Entities; 6 | 7 | [KubernetesEntity(Group = "demo.kubeops.dev", ApiVersion = "v1", Kind = "DemoEntity")] 8 | public sealed class V1DemoEntity : CustomKubernetesEntity 9 | { 10 | public class V1DemoEntitySpec 11 | { 12 | public string Username { get; set; } = string.Empty; 13 | } 14 | 15 | public class V1DemoEntityStatus 16 | { 17 | public string DemoStatus { get; set; } = string.Empty; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/Entities/V1DemoEntity.cs: -------------------------------------------------------------------------------- 1 | using k8s.Models; 2 | 3 | using KubeOps.Abstractions.Entities; 4 | 5 | namespace GeneratedOperatorProject.Entities; 6 | 7 | [KubernetesEntity(Group = "demo.kubeops.dev", ApiVersion = "v1", Kind = "DemoEntity")] 8 | public sealed class V1DemoEntity : CustomKubernetesEntity 9 | { 10 | public class V1DemoEntitySpec 11 | { 12 | public string Username { get; set; } = string.Empty; 13 | } 14 | 15 | public class V1DemoEntityStatus 16 | { 17 | public string DemoStatus { get; set; } = string.Empty; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/PatternAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Define a regex validator for the property. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class PatternAttribute(string regexPattern) : Attribute 12 | { 13 | /// 14 | /// The regex pattern to be used. 15 | /// 16 | public string RegexPattern => regexPattern; 17 | } 18 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Events/IEventPublisherFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Events; 6 | 7 | /// 8 | /// Represents a type used to create s for clients and controllers. 9 | /// 10 | public interface IEventPublisherFactory 11 | { 12 | /// 13 | /// Creates a new event publisher. 14 | /// 15 | /// The . 16 | EventPublisher Create(); 17 | } 18 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/Webhooks/TestMutationWebhook.cs: -------------------------------------------------------------------------------- 1 | using KubeOps.Operator.Web.Webhooks.Admission.Mutation; 2 | 3 | using GeneratedOperatorProject.Entities; 4 | 5 | namespace GeneratedOperatorProject.Webhooks; 6 | 7 | [MutationWebhook(typeof(V1DemoEntity))] 8 | public class TestMutationWebhook : MutationWebhook 9 | { 10 | public override MutationResult Create(V1DemoEntity entity, bool dryRun) 11 | { 12 | if (entity.Spec.Username == "overwrite") 13 | { 14 | entity.Spec.Username = "random overwritten"; 15 | return Modified(entity); 16 | } 17 | 18 | return NoChanges(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/MultipleOfAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines the factor that a numeric value must adhere to. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class MultipleOfAttribute(double value) : Attribute 12 | { 13 | /// 14 | /// The property should be a multiple of this value. 15 | /// 16 | public double Value => value; 17 | } 18 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Admission/AdmissionReview.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Text.Json.Serialization; 6 | 7 | namespace KubeOps.Operator.Web.Webhooks.Admission; 8 | 9 | /// 10 | /// Base class for admission review requests. 11 | /// 12 | public abstract class AdmissionReview 13 | { 14 | [JsonPropertyName("apiVersion")] 15 | public string ApiVersion => "admission.k8s.io/v1"; 16 | 17 | [JsonPropertyName("kind")] 18 | public string Kind => "AdmissionReview"; 19 | } 20 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/DescriptionAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines a description for a property. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] 11 | public class DescriptionAttribute(string description) : Attribute 12 | { 13 | /// 14 | /// The given description for the property. 15 | /// 16 | public string Description => description; 17 | } 18 | -------------------------------------------------------------------------------- /examples/Operator/Finalizer/FinalizerOne.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Abstractions.Reconciliation; 6 | using KubeOps.Abstractions.Reconciliation.Finalizer; 7 | 8 | using Operator.Entities; 9 | 10 | namespace Operator.Finalizer; 11 | 12 | public sealed class FinalizerOne : IEntityFinalizer 13 | { 14 | public Task> FinalizeAsync(V1TestEntity entity, CancellationToken cancellationToken) 15 | => Task.FromResult(ReconciliationResult.Success(entity)); 16 | } 17 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/EntityScope.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities; 6 | 7 | /// 8 | /// Scope of the resource. Custom entities (resources) in Kubernetes 9 | /// can either be namespaced or cluster-wide. 10 | /// 11 | public enum EntityScope 12 | { 13 | /// 14 | /// The resource is namespace. 15 | /// 16 | Namespaced, 17 | 18 | /// 19 | /// The resource is cluster-wide. 20 | /// 21 | Cluster, 22 | } 23 | -------------------------------------------------------------------------------- /test/KubeOps.Generator.Test/KubeOps.Generator.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/PrinterColumnPriority.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities; 6 | 7 | /// 8 | /// Specifies the priority of a column in an additional printer view. 9 | /// 10 | public enum PrinterColumnPriority 11 | { 12 | /// 13 | /// The column is displayed in the standard view. 14 | /// 15 | StandardView, 16 | 17 | /// 18 | /// The column is displayed in the wide view. 19 | /// 20 | WideView, 21 | } 22 | -------------------------------------------------------------------------------- /src/KubeOps.Generator/SyntaxReceiver/CombinedSyntaxReceiver.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Microsoft.CodeAnalysis; 6 | 7 | namespace KubeOps.Generator.SyntaxReceiver; 8 | 9 | internal sealed class CombinedSyntaxReceiver(params ISyntaxContextReceiver[] receivers) : ISyntaxContextReceiver 10 | { 11 | public void OnVisitSyntaxNode(GeneratorSyntaxContext context) 12 | { 13 | foreach (var syntaxContextReceiver in receivers) 14 | { 15 | syntaxContextReceiver.OnVisitSyntaxNode(context); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /test/KubeOps.Transpiler.Test/IntegrationTestCollection.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | namespace KubeOps.Transpiler.Test; 8 | 9 | [CollectionDefinition(Name, DisableParallelization = true)] 10 | public class TranspilerTestCollection : ICollectionFixture 11 | { 12 | public const string Name = "Transpiler Tests"; 13 | } 14 | 15 | [Collection(TranspilerTestCollection.Name)] 16 | public abstract class TranspilerTestBase(MlcProvider provider) 17 | { 18 | protected readonly MetadataLoadContext _mlc = provider.Mlc; 19 | } 20 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/KubernetesEntityShortNamesAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Define "shortNames" for CRDs. 9 | /// 10 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 11 | public class KubernetesEntityShortNamesAttribute(params string[] shortNames) : Attribute 12 | { 13 | /// 14 | /// Array of shortnames that should be attached to CRDs. 15 | /// 16 | public string[] ShortNames => shortNames; 17 | } 18 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-test.yml: -------------------------------------------------------------------------------- 1 | name: .NET Testing 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - "**" 7 | 8 | concurrency: 9 | group: testing-${{ github.ref }} 10 | cancel-in-progress: true 11 | 12 | jobs: 13 | test: 14 | name: Testing 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v6 18 | 19 | - name: Setup .NET 20 | uses: actions/setup-dotnet@v5 21 | with: 22 | dotnet-version: 9.x 23 | 24 | - name: Linting 25 | run: dotnet format --verify-no-changes 26 | 27 | - name: Create Kubernetes Cluster 28 | uses: helm/kind-action@v1.13.0 29 | 30 | - name: Execute Tests 31 | run: dotnet test --configuration ${{ runner.debug == '1' && 'Debug' || 'Release' }} 32 | -------------------------------------------------------------------------------- /.github/workflows/docs-test.yml: -------------------------------------------------------------------------------- 1 | name: Test Documentation Build 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | paths: 8 | - "docs/**" 9 | - ".github/workflows/docs-test.yml" 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | test-build: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v6 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v6 21 | with: 22 | node-version: "24" 23 | cache: "npm" 24 | cache-dependency-path: docs/package-lock.json 25 | 26 | - name: Install Dependencies 27 | working-directory: docs 28 | run: npm ci 29 | 30 | - name: Build Docusaurus 31 | working-directory: docs 32 | run: npm run build 33 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/StorageVersionAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// This attribute marks an entity as the storage version of 9 | /// an entity. Only one storage version must be set. 10 | /// If none of the versions define this attribute, the "newest" 11 | /// one is taken according to the kubernetes versioning rules. 12 | /// GA > Beta > Alpha > non versions. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class)] 15 | public class StorageVersionAttribute : Attribute; 16 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/EmbeddedResourceAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | namespace KubeOps.Abstractions.Entities.Attributes; 8 | 9 | /// 10 | /// Defines a property as an embedded resource. 11 | /// This property can contain another Kubernetes object 12 | /// (e.g. a or a ). 13 | /// This implicitly sets the . 14 | /// 15 | [AttributeUsage(AttributeTargets.Property)] 16 | public class EmbeddedResourceAttribute : Attribute; 17 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Events/EventType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | namespace KubeOps.Abstractions.Events; 8 | 9 | /// 10 | /// The type of a . 11 | /// The event type will be stringified and used as . 12 | /// 13 | public enum EventType 14 | { 15 | /// 16 | /// A normal event, informative value. 17 | /// 18 | Normal, 19 | 20 | /// 21 | /// A warning, something might went wrong. 22 | /// 23 | Warning, 24 | } 25 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/LeaderElection/ILeaderElectorFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.LeaderElection; 6 | 7 | namespace KubeOps.Operator.LeaderElection; 8 | 9 | /// 10 | /// Represents a type used to configure the election system and create instances of . 11 | /// 12 | public interface ILeaderElectorFactory 13 | { 14 | /// 15 | /// Creates a new instance. 16 | /// 17 | /// The . 18 | LeaderElector CreateElector(); 19 | } 20 | -------------------------------------------------------------------------------- /examples/WebhookOperator/Entities/V1TestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace WebhookOperator.Entities; 10 | 11 | [KubernetesEntity(Group = "webhook.dev", ApiVersion = "v1", Kind = "TestEntity")] 12 | public partial class V1TestEntity : CustomKubernetesEntity 13 | { 14 | public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username}"; 15 | 16 | public class EntitySpec 17 | { 18 | public string Username { get; set; } = string.Empty; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/Operator.CSharp/Finalizer/DemoFinalizer.cs: -------------------------------------------------------------------------------- 1 | using k8s.Models; 2 | 3 | using KubeOps.Abstractions.Reconciliation; 4 | using KubeOps.Abstractions.Reconciliation.Finalizer; 5 | 6 | using Microsoft.Extensions.Logging; 7 | 8 | using GeneratedOperatorProject.Entities; 9 | 10 | namespace GeneratedOperatorProject.Finalizer; 11 | 12 | public sealed class DemoFinalizer(ILogger logger) : IEntityFinalizer 13 | { 14 | public Task> FinalizeAsync(V1DemoEntity entity, CancellationToken cancellationToken) 15 | { 16 | logger.LogInformation($"entity {entity.Name()} called {nameof(FinalizeAsync)}."); 17 | 18 | return Task.FromResult(ReconciliationResult.Success(entity)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/Finalizer/DemoFinalizer.cs: -------------------------------------------------------------------------------- 1 | using k8s.Models; 2 | 3 | using KubeOps.Abstractions.Reconciliation; 4 | using KubeOps.Abstractions.Reconciliation.Finalizer; 5 | 6 | using Microsoft.Extensions.Logging; 7 | 8 | using GeneratedOperatorProject.Entities; 9 | 10 | namespace GeneratedOperatorProject.Finalizer; 11 | 12 | public sealed class DemoFinalizer(ILogger logger) : IEntityFinalizer 13 | { 14 | public Task> FinalizeAsync(V1DemoEntity entity, CancellationToken cancellationToken) 15 | { 16 | logger.LogInformation($"entity {entity.Name()} called {nameof(FinalizeAsync)}."); 17 | 18 | return Task.FromResult(ReconciliationResult.Success(entity)); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /examples/Operator/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Operator; 6 | 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | var builder = Host.CreateApplicationBuilder(args); 11 | 12 | builder.Logging.SetMinimumLevel(LogLevel.Trace); 13 | 14 | builder.Services 15 | .AddKubernetesOperator() 16 | #if DEBUG 17 | .AddCrdInstaller(c => 18 | { 19 | c.OverwriteExisting = true; 20 | c.DeleteOnShutdown = true; 21 | }) 22 | #endif 23 | .RegisterComponents(); 24 | 25 | using var host = builder.Build(); 26 | await host.RunAsync(); 27 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Entities/V1TestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace ConversionWebhookOperator.Entities; 10 | 11 | [KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v1", Kind = "TestEntity")] 12 | public partial class V1TestEntity : CustomKubernetesEntity 13 | { 14 | public override string ToString() => $"Test Entity v1 ({Metadata.Name}): {Spec.Name}"; 15 | 16 | public class EntitySpec 17 | { 18 | public string Name { get; set; } = string.Empty; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/LabelSelectors/Extensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | public static class Extensions 8 | { 9 | /// 10 | /// Convert an enumerable list of s to a string. 11 | /// 12 | /// The list of selectors. 13 | /// A comma-joined string with all selectors converted to their expressions. 14 | public static string ToExpression(this IEnumerable selectors) => 15 | string.Join(",", selectors.Select(x => (string)x)); 16 | } 17 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Commands/Generator/Generate.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.CommandLine; 6 | using System.CommandLine.Help; 7 | 8 | namespace KubeOps.Cli.Commands.Generator; 9 | 10 | internal static class Generate 11 | { 12 | public static Command Command 13 | { 14 | get 15 | { 16 | var cmd = new Command("generate", "Generates elements related to an operator.") 17 | { 18 | OperatorGenerator.Command, 19 | }; 20 | cmd.Aliases.Add("gen"); 21 | cmd.Aliases.Add("g"); 22 | 23 | return cmd; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Admission/Mutation/MutationWebhookAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace KubeOps.Operator.Web.Webhooks.Admission.Mutation; 8 | 9 | /// 10 | /// Defines an MVC controller as "mutation webhook". The route is automatically set to 11 | /// /mutate/[lower-case-name-of-the-type]. 12 | /// This must be used in conjunction with the class. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class)] 15 | public class MutationWebhookAttribute(Type entityType) : RouteAttribute($"/mutate/{entityType.Name.ToLowerInvariant()}"); 16 | -------------------------------------------------------------------------------- /examples/Operator/Entities/V1TestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace Operator.Entities; 10 | 11 | [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "TestEntity")] 12 | public partial class V1TestEntity : CustomKubernetesEntity 13 | { 14 | public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username} ({Spec.Email})"; 15 | 16 | public class EntitySpec 17 | { 18 | public string Username { get; set; } = string.Empty; 19 | 20 | public string Email { get; set; } = string.Empty; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Admission/Validation/ValidationWebhookAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Microsoft.AspNetCore.Mvc; 6 | 7 | namespace KubeOps.Operator.Web.Webhooks.Admission.Validation; 8 | 9 | /// 10 | /// Defines an MVC controller as "validation webhook". The route is automatically set to 11 | /// /validate/[lower-case-name-of-the-type]. 12 | /// This must be used in conjunction with the class. 13 | /// 14 | [AttributeUsage(AttributeTargets.Class)] 15 | public class ValidationWebhookAttribute(Type entityType) : RouteAttribute($"/validate/{entityType.Name.ToLowerInvariant()}"); 16 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/CustomKubernetesEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Entities; 9 | 10 | /// 11 | /// Base class for custom Kubernetes entities. The interface 12 | /// can be used on its own, but this class provides convenience initializers. 13 | /// 14 | public abstract class CustomKubernetesEntity : KubernetesObject, IKubernetesObject 15 | { 16 | /// 17 | /// The metadata of the kubernetes object. 18 | /// 19 | public V1ObjectMeta Metadata { get; set; } = new(); 20 | } 21 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/ExternalDocsAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines that the property has an external documentation. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class ExternalDocsAttribute(string url, string? description = null) : Attribute 12 | { 13 | /// 14 | /// Additional description. 15 | /// 16 | public string? Description => description; 17 | 18 | /// 19 | /// Url where to find the documentation. 20 | /// 21 | public string Url => url; 22 | } 23 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Admission/AdmissionStatus.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Text.Json.Serialization; 6 | 7 | using Microsoft.AspNetCore.Http; 8 | 9 | namespace KubeOps.Operator.Web.Webhooks.Admission; 10 | 11 | /// 12 | /// The admission status for the response to the API. 13 | /// 14 | /// A message that is passed to the API. 15 | /// A custom status code to provide more detailed information. 16 | public sealed record AdmissionStatus([property: JsonPropertyName("message")] 17 | string Message, [property: JsonPropertyName("code")] 18 | int? Code = StatusCodes.Status200OK); 19 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/IEntityLabelSelector{TEntity}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Entities; 9 | 10 | // This is the same pattern used by Microsoft on ILogger. 11 | // An alternative would be to use a KeyedSingleton when registering this however that's only valid from .NET 8 and above. 12 | // Other methods are far less elegant 13 | #pragma warning disable S2326 14 | public interface IEntityLabelSelector 15 | where TEntity : IKubernetesObject 16 | { 17 | ValueTask GetLabelSelectorAsync(CancellationToken cancellationToken); 18 | } 19 | #pragma warning restore S2326 20 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/TestApp/TestValidationWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Operator.Web.Webhooks.Admission.Validation; 6 | 7 | namespace KubeOps.Operator.Web.Test.TestApp; 8 | 9 | [ValidationWebhook(typeof(V1OperatorWebIntegrationTestEntity))] 10 | public class TestValidationWebhook : ValidationWebhook 11 | { 12 | public override ValidationResult Create(V1OperatorWebIntegrationTestEntity entity, bool dryRun) 13 | { 14 | if (entity.Spec.Username == "forbidden") 15 | { 16 | return Fail("name may not be 'forbidden'.", 422); 17 | } 18 | 19 | return Success(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/RangeMaximum.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines a range maximum for a numeric property. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class RangeMaximumAttribute(double maximum, bool exclusiveMaximum = false) : Attribute 12 | { 13 | /// 14 | /// Maximum value to be set. 15 | /// 16 | public double Maximum => maximum; 17 | 18 | /// 19 | /// Defines if the maximum value is included or excluded. 20 | /// 21 | public bool ExclusiveMaximum => exclusiveMaximum; 22 | } 23 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/RangeMinimum.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines a range minimum for a numeric property. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class RangeMinimumAttribute(double minimum, bool exclusiveMinimum = false) : Attribute 12 | { 13 | /// 14 | /// Minimum value to be set. 15 | /// 16 | public double Minimum => minimum; 17 | 18 | /// 19 | /// Defines if the minimum value is included or excluded. 20 | /// 21 | public bool ExclusiveMinimum => exclusiveMinimum; 22 | } 23 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/LabelSelectors/EqualsSelector.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | /// 8 | /// Label-selector that checks if a certain label contains 9 | /// a specific value (out of a list of values). 10 | /// Note that "label in (value)" is the same as "label == value". 11 | /// 12 | /// The label that needs to equal to one of the values. 13 | /// The possible values. 14 | public record EqualsSelector(string Label, params string[] Values) : LabelSelector 15 | { 16 | protected override string ToExpression() => $"{Label} in ({string.Join(",", Values)})"; 17 | } 18 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/Queue/RequeueEntry{TEntity}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Abstractions.Reconciliation.Queue; 6 | 7 | namespace KubeOps.Operator.Queue; 8 | 9 | /// 10 | /// Represents an entry in a requeue system for managing entities of type . 11 | /// The requeue system facilitates the categorization and reprocessing of entities based on their 12 | /// lifecycle events, such as added, modified, or deleted. 13 | /// 14 | /// 15 | /// The type of the entity associated with the requeue entry. 16 | /// 17 | public readonly record struct RequeueEntry(TEntity Entity, RequeueType RequeueType); 18 | -------------------------------------------------------------------------------- /examples/WebhookOperator/Webhooks/TestMutationWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Operator.Web.Webhooks.Admission.Mutation; 6 | 7 | using WebhookOperator.Entities; 8 | 9 | namespace WebhookOperator.Webhooks; 10 | 11 | [MutationWebhook(typeof(V1TestEntity))] 12 | public sealed class TestMutationWebhook : MutationWebhook 13 | { 14 | public override MutationResult Create(V1TestEntity entity, bool dryRun) 15 | { 16 | if (entity.Spec.Username == "overwrite") 17 | { 18 | entity.Spec.Username = "random overwritten"; 19 | return Modified(entity); 20 | } 21 | 22 | return NoChanges(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Kustomize/KustomizationImage.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Kustomize; 6 | 7 | /// 8 | /// Definition for an "image" in a kustomization yaml. 9 | /// 10 | public class KustomizationImage 11 | { 12 | /// 13 | /// Name of the image. 14 | /// 15 | public string Name { get; set; } = string.Empty; 16 | 17 | /// 18 | /// New name of the image. 19 | /// 20 | public string NewName { get; set; } = string.Empty; 21 | 22 | /// 23 | /// New tag of the image. 24 | /// 25 | public string NewTag { get; set; } = string.Empty; 26 | } 27 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Certificates/ICertificateProvider.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Certificates; 6 | 7 | /// 8 | /// Defines properties for certificate/key pair so a custom certificate/key provider may be implemented. 9 | /// The provider is used by the CertificateWebhookService to provide a caBundle to the webhooks. 10 | /// 11 | public interface ICertificateProvider : IDisposable 12 | { 13 | /// 14 | /// The server certificate and key. 15 | /// 16 | CertificatePair Server { get; } 17 | 18 | /// 19 | /// The root certificate and key. 20 | /// 21 | CertificatePair Root { get; } 22 | } 23 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/EntityList.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Entities; 9 | 10 | /// 11 | /// Type for a list of entities. 12 | /// 13 | /// Type for the list entries. 14 | public class EntityList : KubernetesObject 15 | where T : IKubernetesObject 16 | { 17 | /// 18 | /// Official list metadata object of kubernetes. 19 | /// 20 | public V1ListMeta Metadata { get; set; } = new(); 21 | 22 | /// 23 | /// The list of items. 24 | /// 25 | public IList Items { get; set; } = new List(); 26 | } 27 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Entities/V2TestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace ConversionWebhookOperator.Entities; 10 | 11 | [KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v2", Kind = "TestEntity")] 12 | public partial class V2TestEntity : CustomKubernetesEntity 13 | { 14 | public override string ToString() => $"Test Entity v2 ({Metadata.Name}): {Spec.Firstname} {Spec.Lastname}"; 15 | 16 | public class EntitySpec 17 | { 18 | public string Firstname { get; set; } = string.Empty; 19 | 20 | public string Lastname { get; set; } = string.Empty; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/LabelSelectors/NotEqualsSelector.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | /// 8 | /// Label-selector that checks if a certain label does not contain 9 | /// a specific value (out of a list of values). 10 | /// Note that "label notin (value)" is the same as "label != value". 11 | /// 12 | /// The label that must not equal to one of the values. 13 | /// The possible values. 14 | public record NotEqualsSelector(string Label, params string[] Values) : LabelSelector 15 | { 16 | protected override string ToExpression() => $"{Label} notin ({string.Join(",", Values)})"; 17 | } 18 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | namespace KubeOps.Abstractions.Entities; 8 | 9 | /// 10 | /// Defines a custom kubernetes entity which can be used in finalizers and controllers. 11 | /// This entity contains a , which means in contains specified data. 12 | /// 13 | /// The type of the specified data. 14 | public abstract class CustomKubernetesEntity : CustomKubernetesEntity, ISpec 15 | where TSpec : new() 16 | { 17 | /// 18 | /// Specification of the kubernetes object. 19 | /// 20 | public TSpec Spec { get; set; } = new(); 21 | } 22 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.CommandLine; 6 | using System.CommandLine.Invocation; 7 | 8 | using KubeOps.Cli.Commands.Generator; 9 | using KubeOps.Cli.Commands.Management; 10 | 11 | using Version = KubeOps.Cli.Commands.Utilities.Version; 12 | 13 | var parseResult = new RootCommand( 14 | "CLI for KubeOps. Commandline tool to help with management tasks such as generating or installing CRDs.") 15 | { 16 | Generate.Command, Version.Command, Install.Command, Uninstall.Command, 17 | }.Parse(args); 18 | if (parseResult.Action is ParseErrorAction errorAction) 19 | { 20 | errorAction.ShowHelp = true; 21 | errorAction.ShowTypoCorrections = true; 22 | } 23 | 24 | await parseResult.InvokeAsync(); 25 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yaml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: "Suggest a new feature for this project." 3 | title: "[feature]: " 4 | labels: ["enhancement"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this feature request! 10 | - type: textarea 11 | id: description 12 | attributes: 13 | label: Is your feature request related to a problem? Please describe. 14 | description: A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 15 | - type: textarea 16 | id: solution 17 | attributes: 18 | label: Describe the solution you would like 19 | description: A clear and concise description of what you want to happen. 20 | validations: 21 | required: true 22 | - type: textarea 23 | id: additional 24 | attributes: 25 | label: Additional Context 26 | description: Please add any other infos that could be useful. 27 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/KubeOps.Abstractions.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;net9.0;net10.0 5 | 6 | 7 | 8 | KubeOps.Abstractions 9 | Kubernetes Operator SDK Abstractions 10 | 11 | Abstractions for the Kubernetes Operator SDK. 12 | Used by KubeOps in general. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/KubeOps.Transpiler/KubeOps.Transpiler.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;net9.0;net10.0 5 | 6 | 7 | 8 | KubeOps.Transpiler 9 | Kubernetes Operator SDK Transpilation CRD 10 | 11 | Transpilation elements for the Kubernetes Operator SDK. 12 | Enables the developer to create CRDs from types and other 13 | Kubernetes related elements via reflection. 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.github/workflows/security-analysis.yml: -------------------------------------------------------------------------------- 1 | name: Code Security Testing 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | branches: 9 | - main 10 | schedule: 11 | - cron: "0 20 * * 5" 12 | 13 | permissions: 14 | security-events: write 15 | 16 | concurrency: 17 | group: security-${{ github.ref }} 18 | cancel-in-progress: true 19 | 20 | jobs: 21 | codeQL: 22 | name: Analyze 23 | runs-on: ubuntu-latest 24 | 25 | steps: 26 | - uses: actions/checkout@v6 27 | 28 | - name: Initialize CodeQL 29 | uses: github/codeql-action/init@v4 30 | with: 31 | languages: csharp 32 | 33 | - name: Setup .NET 34 | uses: actions/setup-dotnet@v5 35 | with: 36 | dotnet-version: 9.x 37 | 38 | - name: Build 39 | run: dotnet build --configuration Release 40 | 41 | - name: Perform CodeQL Analysis 42 | uses: github/codeql-action/analyze@v4 43 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/Queue/RequeueType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Reconciliation.Queue; 6 | 7 | /// 8 | /// Specifies the types of requeue operations that can occur on an entity. 9 | /// 10 | public enum RequeueType 11 | { 12 | /// 13 | /// Indicates that an entity should be added and is scheduled for requeue. 14 | /// 15 | Added, 16 | 17 | /// 18 | /// Indicates that an entity has been modified and is scheduled for requeue. 19 | /// 20 | Modified, 21 | 22 | /// 23 | /// Indicates that an entity should be deleted and is scheduled for requeue. 24 | /// 25 | Deleted, 26 | } 27 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/Constants/CacheConstants.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Operator.Constants; 6 | 7 | /// 8 | /// Provides constant values used for caching purposes within the operator. 9 | /// 10 | public static class CacheConstants 11 | { 12 | /// 13 | /// Contains constant values representing names used within the operator's caching mechanisms. 14 | /// 15 | public static class CacheNames 16 | { 17 | /// 18 | /// Represents a constant string used as a name for the resource watcher 19 | /// in the operator's caching mechanisms. 20 | /// 21 | public const string ResourceWatcher = "ResourceWatcher"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/TestApp/TestMutationWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Operator.Web.Webhooks.Admission.Mutation; 6 | 7 | namespace KubeOps.Operator.Web.Test.TestApp; 8 | 9 | [MutationWebhook(typeof(V1OperatorWebIntegrationTestEntity))] 10 | public class TestMutationWebhook : MutationWebhook 11 | { 12 | public override MutationResult Create(V1OperatorWebIntegrationTestEntity entity, 13 | bool dryRun) 14 | { 15 | if (entity.Spec.Username == "overwrite") 16 | { 17 | entity.Spec.Username = "overwritten"; 18 | return Modified(entity); 19 | } 20 | 21 | return NoChanges(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Conversion/ConversionReview.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Runtime.Versioning; 6 | using System.Text.Json.Serialization; 7 | 8 | namespace KubeOps.Operator.Web.Webhooks.Conversion; 9 | 10 | /// 11 | /// Base class for conversion review requests. 12 | /// 13 | [RequiresPreviewFeatures( 14 | "Conversion webhooks API is not yet stable, the way that conversion " + 15 | "webhooks are implemented may change in the future based on user feedback.")] 16 | public abstract class ConversionReview 17 | { 18 | [JsonPropertyName("apiVersion")] 19 | public string ApiVersion => "apiextensions.k8s.io/v1"; 20 | 21 | [JsonPropertyName("kind")] 22 | public string Kind => "ConversionReview"; 23 | } 24 | -------------------------------------------------------------------------------- /test/KubeOps.Cli.Test/KubeOps.Cli.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | true 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Entities/V3TestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace ConversionWebhookOperator.Entities; 10 | 11 | [KubernetesEntity(Group = "conversionwebhook.dev", ApiVersion = "v3", Kind = "TestEntity")] 12 | public partial class V3TestEntity : CustomKubernetesEntity 13 | { 14 | public override string ToString() => $"Test Entity v3 ({Metadata.Name}): {Spec.Firstname} {Spec.MiddleName} {Spec.Lastname}"; 15 | 16 | public class EntitySpec 17 | { 18 | public string Firstname { get; set; } = string.Empty; 19 | 20 | public string Lastname { get; set; } = string.Empty; 21 | 22 | public string? MiddleName { get; set; } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/Webhooks/TestValidationWebhook.cs: -------------------------------------------------------------------------------- 1 | using KubeOps.Operator.Web.Webhooks.Admission.Validation; 2 | 3 | using GeneratedOperatorProject.Entities; 4 | 5 | namespace GeneratedOperatorProject.Webhooks; 6 | 7 | [ValidationWebhook(typeof(V1DemoEntity))] 8 | public class TestValidationWebhook : ValidationWebhook 9 | { 10 | public override ValidationResult Create(V1DemoEntity entity, bool dryRun) 11 | { 12 | if (entity.Spec.Username == "forbidden") 13 | { 14 | return Fail("name may not be 'forbidden'.", 422); 15 | } 16 | 17 | return Success(); 18 | } 19 | 20 | public override ValidationResult Update(V1DemoEntity oldEntity, V1DemoEntity newEntity, bool dryRun) 21 | { 22 | if (newEntity.Spec.Username == "forbidden") 23 | { 24 | return Fail("name may not be 'forbidden'."); 25 | } 26 | 27 | return Success(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/KubeOps.Transpiler.Test/KubeOps.Transpiler.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Output/OutputFormat.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Cli.Output; 6 | 7 | internal enum OutputFormat 8 | { 9 | /// 10 | /// Format the output in Kubernetes YAML style. 11 | /// 12 | Yaml, 13 | 14 | /// 15 | /// Format the output in Kubernetes JSON style. 16 | /// 17 | Json, 18 | 19 | /// 20 | /// Format the output in plain text style. 21 | /// 22 | Plain, 23 | } 24 | 25 | internal static class OutputFormatExtensions 26 | { 27 | public static string GetFileExtension(this OutputFormat format) => format switch 28 | { 29 | OutputFormat.Yaml => "yaml", 30 | OutputFormat.Json => "json", 31 | _ => string.Empty, 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/LengthAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines length limits for properties. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class LengthAttribute(long minLength = -1, long maxLength = -1) : Attribute 12 | { 13 | /// 14 | /// Define the minimum length. 15 | /// 16 | public long? MinLength => minLength switch 17 | { 18 | -1 => null, 19 | _ => minLength, 20 | }; 21 | 22 | /// 23 | /// Define the maximum length. 24 | /// 25 | public long? MaxLength => maxLength switch 26 | { 27 | -1 => null, 28 | _ => maxLength, 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /test/KubeOps.KubernetesClient.Test/LabelSelector.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | namespace KubeOps.KubernetesClient.Test; 8 | 9 | public class LabelSelectorTest : IntegrationTestBase 10 | { 11 | [Fact] 12 | public void Sould_Return_Correct_Expression() 13 | { 14 | var labelSelectors = new LabelSelector[] { 15 | new EqualsSelector("app", Enumerable.Range(0,3).Select(x=>$"app-{x}").ToArray()), 16 | new NotEqualsSelector("srv", Enumerable.Range(0,2).Select(x=>$"service-{x}").ToArray()) 17 | }; 18 | 19 | string expected = "app in (app-0,app-1,app-2),srv notin (service-0,service-1)"; 20 | var actual = labelSelectors.ToExpression(); 21 | Assert.Equal(expected, actual); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/Queue/IEntityRequeueFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Reconciliation.Queue; 9 | 10 | /// 11 | /// Represents a type used to create delegates of type for requeuing entities. 12 | /// 13 | public interface IEntityRequeueFactory 14 | { 15 | /// 16 | /// Creates a new for the given type. 17 | /// 18 | /// The entity type. 19 | /// A . 20 | EntityRequeue Create() 21 | where TEntity : IKubernetesObject; 22 | } 23 | -------------------------------------------------------------------------------- /examples/WebhookOperator/WebhookOperator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | enable 7 | false 8 | true 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/KubeOps.Templates.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | KubeOps Templates 5 | netstandard2.0 6 | 7 | true 8 | false 9 | content 10 | $(NoWarn);NU5128 11 | 12 | 13 | 14 | KubeOps.Templates 15 | dotnet-new templates Kubernetes Operator Sdk KubeOps 16 | Template 17 | dotnet new templates for KubeOps operator sdk. 18 | 19 | 20 | 21 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Kustomize/KustomizationSecretGenerator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Kustomize; 6 | 7 | /// 8 | /// Entitiy for config map generators in a kustomization.yaml file. 9 | /// 10 | public class KustomizationSecretGenerator 11 | { 12 | /// 13 | /// The name of the config map. 14 | /// 15 | public string Name { get; set; } = string.Empty; 16 | 17 | /// 18 | /// List of files that should be added to the generated config map. 19 | /// 20 | public IList? Files { get; set; } 21 | 22 | /// 23 | /// Config literals to add to the config map in the form of: 24 | /// - NAME=value. 25 | /// 26 | public IList? Literals { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Kustomize/KustomizationConfigMapGenerator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Kustomize; 6 | 7 | /// 8 | /// Entity for config map generators in a kustomization.yaml file. 9 | /// 10 | public class KustomizationConfigMapGenerator 11 | { 12 | /// 13 | /// The name of the config map. 14 | /// 15 | public string Name { get; set; } = string.Empty; 16 | 17 | /// 18 | /// List of files that should be added to the generated config map. 19 | /// 20 | public IList? Files { get; set; } 21 | 22 | /// 23 | /// Config literals to add to the config map in the form of: 24 | /// - NAME=value. 25 | /// 26 | public IList? Literals { get; set; } 27 | } 28 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/ConversionWebhookOperator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net9.0 5 | enable 6 | true 7 | enable 8 | false 9 | 10 | 11 | 12 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Kustomize/KustomizationCommonLabels.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Kustomize; 6 | 7 | /// 8 | /// Common labels for the resources. 9 | /// 10 | public class KustomizationCommonLabels 11 | { 12 | public KustomizationCommonLabels(IDictionary pairs) 13 | { 14 | foreach (var keyValuePair in pairs) 15 | { 16 | Pairs.Add(keyValuePair.Key, keyValuePair.Value); 17 | } 18 | } 19 | 20 | /// 21 | /// Include selectors. 22 | /// 23 | public bool IncludeSelectors { get; init; } = true; 24 | 25 | /// 26 | /// A list of common labels. 27 | /// 28 | public KustomizationCommonLabelsPair Pairs { get; set; } = new KustomizationCommonLabelsPair(); 29 | } 30 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/KubeOps.KubernetesClient.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;net9.0;net10.0 5 | 6 | 7 | 8 | KubeOps.KubernetesClient 9 | Kubernetes Dotnet Client API 10 | 11 | Kubernetes Client written in DotNet. Based on the implementation of 12 | Google (https://github.com/kubernetes-client/csharp) but with dotnet native language 13 | features like generics. Internally uses the "GenericClient" of the Google KubernetesClient. 14 | However, wraps the methods around with true generics. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Transpilation/BaseWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace KubeOps.Cli.Transpilation; 10 | 11 | internal abstract record BaseWebhook(TypeInfo Webhook, EntityMetadata Metadata) 12 | { 13 | public abstract string WebhookPath { get; } 14 | 15 | private bool HasCreate => Webhook.DeclaredMembers.Any(m => m.Name.StartsWith("Create")); 16 | 17 | private bool HasUpdate => Webhook.DeclaredMembers.Any(m => m.Name.StartsWith("Update")); 18 | 19 | private bool HasDelete => Webhook.DeclaredMembers.Any(m => m.Name.StartsWith("Delete")); 20 | 21 | public string[] GetOperations() => 22 | new[] { HasCreate ? "CREATE" : null, HasUpdate ? "UPDATE" : null, HasDelete ? "DELETE" : null, } 23 | .Where(o => o is not null).ToArray()!; 24 | } 25 | -------------------------------------------------------------------------------- /src/KubeOps.KubernetesClient/LabelSelectors/LabelSelector.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.KubernetesClient.LabelSelectors; 6 | 7 | /// 8 | /// Different label selectors for querying the Kubernetes API. 9 | /// 10 | public abstract record LabelSelector 11 | { 12 | /// 13 | /// Cast the label selector to a string. 14 | /// 15 | /// The selector. 16 | /// A string representation of the label selector. 17 | public static implicit operator string(LabelSelector selector) => selector.ToExpression(); 18 | 19 | /// 20 | /// Create an expression from the label selector. 21 | /// 22 | /// A string that represents the label selector. 23 | protected abstract string ToExpression(); 24 | } 25 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/ItemsAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Define minimum and maximum items count for an array property. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class ItemsAttribute(long minItems = -1, long maxItems = -1) : Attribute 12 | { 13 | /// 14 | /// Defines the minimal item count for the property. 15 | /// 16 | public long? MinItems => minItems switch 17 | { 18 | -1 => null, 19 | _ => minItems, 20 | }; 21 | 22 | /// 23 | /// Defines the maximal item count for the property. 24 | /// 25 | public long? MaxItems => maxItems switch 26 | { 27 | -1 => null, 28 | _ => maxItems, 29 | }; 30 | } 31 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Generators/CertificateGenerator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Cli.Output; 6 | using KubeOps.Operator.Web.Certificates; 7 | 8 | namespace KubeOps.Cli.Generators; 9 | 10 | internal class CertificateGenerator(string serverName, string namespaceName) : IConfigGenerator 11 | { 12 | public void Generate(ResultOutput output) 13 | { 14 | using Operator.Web.Certificates.CertificateGenerator generator = new(serverName, namespaceName); 15 | 16 | output.Add("ca.pem", generator.Root.Certificate.EncodeToPem(), OutputFormat.Plain); 17 | output.Add("ca-key.pem", generator.Root.Key.EncodeToPem(), OutputFormat.Plain); 18 | output.Add("svc.pem", generator.Server.Certificate.EncodeToPem(), OutputFormat.Plain); 19 | output.Add("svc-key.pem", generator.Server.Key.EncodeToPem(), OutputFormat.Plain); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/Operator/Operator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net9.0 6 | enable 7 | enable 8 | false 9 | false 10 | 11 | 12 | 13 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Crds/CrdInstallerSettings.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Crds; 6 | 7 | /// 8 | /// Settings for the CRD installer. 9 | /// 10 | public sealed class CrdInstallerSettings 11 | { 12 | /// 13 | /// Determines whether existing CRDs should be overwritten. 14 | /// This is useful for development purposes and should be used with caution. 15 | /// It is a destructive operation that may lead to data loss. 16 | /// 17 | public bool OverwriteExisting { get; set; } = false; 18 | 19 | /// 20 | /// Determines whether the installed CRDs should be deleted when the operator shuts down. 21 | /// This is a very destructive operation and should only be used in development environments. 22 | /// 23 | public bool DeleteOnShutdown { get; set; } = false; 24 | } 25 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Test/KubeOps.Operator.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Conversion/ConversionStatus.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Runtime.Versioning; 6 | using System.Text.Json.Serialization; 7 | 8 | namespace KubeOps.Operator.Web.Webhooks.Conversion; 9 | 10 | /// 11 | /// Status object for the conversion. Reports the success / failure of the conversion 12 | /// to the Kubernetes API. 13 | /// 14 | /// If set, reports the reason for the failure. Otherwise, the conversion is a success. 15 | [RequiresPreviewFeatures( 16 | "Conversion webhooks API is not yet stable, the way that conversion " + 17 | "webhooks are implemented may change in the future based on user feedback.")] 18 | public record ConversionStatus([property: JsonPropertyName("message")] 19 | string? Message = null) 20 | { 21 | [JsonPropertyName("status")] 22 | public string Status => string.IsNullOrWhiteSpace(Message) ? "Success" : "Failed"; 23 | } 24 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Builder/RequeueStrategy.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Builder; 6 | 7 | /// 8 | /// Defines the strategy for requeuing reconciliation events within the operator. 9 | /// 10 | public enum RequeueStrategy 11 | { 12 | /// 13 | /// Represents an in-memory requeue strategy where reconciliation events 14 | /// are managed and requeued without external persistence or reliance on third-party systems. 15 | /// Suitable for scenarios requiring lightweight or transient processing. 16 | /// 17 | InMemory, 18 | 19 | /// 20 | /// Represents a custom requeue strategy where the logic for managing and 21 | /// handling reconciliation events is fully defined and implemented by the user. 22 | /// This provides maximum flexibility for scenarios with specific or complex requirements. 23 | /// 24 | Custom, 25 | } 26 | -------------------------------------------------------------------------------- /docs/src/css/custom.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Any CSS included here will be global. The classic template 3 | * bundles Infima by default. Infima is a CSS framework designed to 4 | * work well for content-centric websites. 5 | */ 6 | 7 | /* You can override the default Infima variables here. */ 8 | :root { 9 | --ifm-color-primary: #2e8555; 10 | --ifm-color-primary-dark: #29784c; 11 | --ifm-color-primary-darker: #277148; 12 | --ifm-color-primary-darkest: #205d3b; 13 | --ifm-color-primary-light: #33925d; 14 | --ifm-color-primary-lighter: #359962; 15 | --ifm-color-primary-lightest: #3cad6e; 16 | --ifm-code-font-size: 95%; 17 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.1); 18 | } 19 | 20 | /* For readability concerns, you should choose a lighter palette in dark mode. */ 21 | [data-theme='dark'] { 22 | --ifm-color-primary: #25c2a0; 23 | --ifm-color-primary-dark: #21af90; 24 | --ifm-color-primary-darker: #1fa588; 25 | --ifm-color-primary-darkest: #1a8870; 26 | --ifm-color-primary-light: #29d5b0; 27 | --ifm-color-primary-lighter: #32d8b4; 28 | --ifm-color-primary-lightest: #4fddbf; 29 | --docusaurus-highlighted-code-line-bg: rgba(0, 0, 0, 0.3); 30 | } 31 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/CustomKubernetesEntity{TSpec,TStatus}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | namespace KubeOps.Abstractions.Entities; 8 | 9 | /// 10 | /// Defines a custom Kubernetes entity. 11 | /// This entity contains a spec (like ) 12 | /// and a status () which can be updated to reflect the state 13 | /// of the entity. 14 | /// 15 | /// The type of the specified data. 16 | /// The type of the status data. 17 | public abstract class CustomKubernetesEntity : CustomKubernetesEntity, IStatus 18 | where TSpec : new() 19 | where TStatus : new() 20 | { 21 | /// 22 | /// Status object for the entity. 23 | /// 24 | // [JsonPropertyName("status")] 25 | public TStatus Status { get; set; } = new(); 26 | } 27 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/LeaderElection/KubernetesLeaderElectorFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.LeaderElection; 7 | using k8s.LeaderElection.ResourceLock; 8 | 9 | using KubeOps.Abstractions.Builder; 10 | using KubeOps.KubernetesClient; 11 | 12 | namespace KubeOps.Operator.LeaderElection; 13 | 14 | internal sealed class KubernetesLeaderElectorFactory( 15 | IKubernetes kubernetes, 16 | IKubernetesClient client, 17 | OperatorSettings settings) 18 | : ILeaderElectorFactory 19 | { 20 | public LeaderElector CreateElector() => new(new LeaderElectionConfig(new LeaseLock( 21 | kubernetes, 22 | client.GetCurrentNamespace(), 23 | $"{settings.Name}-leader", 24 | Environment.MachineName)) 25 | { 26 | LeaseDuration = settings.LeaderElectionLeaseDuration, 27 | RenewDeadline = settings.LeaderElectionRenewDeadline, 28 | RetryPeriod = settings.LeaderElectionRetryPeriod, 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /examples/WebhookOperator/Webhooks/TestValidationWebhook.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Operator.Web.Webhooks.Admission.Validation; 6 | 7 | using WebhookOperator.Entities; 8 | 9 | namespace WebhookOperator.Webhooks; 10 | 11 | [ValidationWebhook(typeof(V1TestEntity))] 12 | public sealed class TestValidationWebhook : ValidationWebhook 13 | { 14 | public override ValidationResult Create(V1TestEntity entity, bool dryRun) 15 | { 16 | if (entity.Spec.Username == "forbidden") 17 | { 18 | return Fail("name may not be 'forbidden'.", 422); 19 | } 20 | 21 | return Success(); 22 | } 23 | 24 | public override ValidationResult Update(V1TestEntity oldEntity, V1TestEntity newEntity, bool dryRun) 25 | { 26 | if (newEntity.Spec.Username == "forbidden") 27 | { 28 | return Fail("name may not be 'forbidden'."); 29 | } 30 | 31 | return Success(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /docs/docs/packages/index.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | title: Packages 3 | description: Overview of all KubeOps packages and their features 4 | sidebar_position: 1 5 | --- 6 | 7 | # KubeOps Packages 8 | 9 | KubeOps is designed to be modular, allowing you to include only the packages you need for your specific use case. This section provides detailed information about each package and its features. 10 | 11 | ## Core Packages 12 | 13 | - [KubeOps.Abstractions](./abstractions) - Core interfaces and attributes 14 | - [KubeOps.Operator](./operator) - Main operator engine 15 | - [KubeOps.Operator.Web](./operator-web) - ASP.NET Core integration 16 | 17 | ## Development Tools 18 | 19 | - [KubeOps.Cli](./cli) - Command-line tools 20 | - [KubeOps.Generator](./generator) - Source generators 21 | - [KubeOps.Templates](./templates) - Project templates 22 | 23 | ## Utilities 24 | 25 | - [KubeOps.KubernetesClient](./kubernetes-client) - Enhanced Kubernetes client 26 | - [KubeOps.Transpiler](./transpiler) - YAML manifest generation 27 | 28 | Each package is designed to work together seamlessly while maintaining independence where possible. This modular approach allows you to pick and choose the components you need for your operator implementation. 29 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/Finalizer/IEntityFinalizer{TEntity}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Reconciliation.Finalizer; 9 | 10 | /// 11 | /// Finalizer for an entity. 12 | /// 13 | /// The type of the entity. 14 | public interface IEntityFinalizer 15 | where TEntity : IKubernetesObject 16 | { 17 | /// 18 | /// Finalize an entity that is pending for deletion. 19 | /// 20 | /// The kubernetes entity that needs to be finalized. 21 | /// The token to monitor for cancellation requests. 22 | /// A task that represents the asynchronous operation and contains the result of the reconcile process. 23 | Task> FinalizeAsync(TEntity entity, CancellationToken cancellationToken); 24 | } 25 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/Operator.CSharp/Controller/DemoController.cs: -------------------------------------------------------------------------------- 1 | using KubeOps.Abstractions.Reconciliation; 2 | using KubeOps.Abstractions.Reconciliation.Controller; 3 | using KubeOps.Abstractions.Rbac; 4 | 5 | using Microsoft.Extensions.Logging; 6 | 7 | using GeneratedOperatorProject.Entities; 8 | 9 | namespace GeneratedOperatorProject.Controller; 10 | 11 | [EntityRbac(typeof(V1DemoEntity), Verbs = RbacVerb.All)] 12 | public sealed class DemoController(ILogger logger) : IEntityController 13 | { 14 | public Task> ReconcileAsync(V1DemoEntity entity, CancellationToken cancellationToken) 15 | { 16 | logger.LogInformation("Reconcile entity {MetadataName}", entity.Metadata.Name); 17 | 18 | return Task.FromResult(ReconciliationResult.Success(entity)); 19 | } 20 | 21 | public Task> DeletedAsync(V1DemoEntity entity, CancellationToken cancellationToken) 22 | { 23 | logger.LogInformation("Deleted entity {Entity}.", entity); 24 | 25 | return Task.FromResult(ReconciliationResult.Success(entity)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/KubeOps.Generator.Test/TestHelperExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | using Microsoft.CodeAnalysis; 8 | using Microsoft.CodeAnalysis.CSharp; 9 | 10 | namespace KubeOps.Generator.Test; 11 | 12 | internal static class TestHelperExtensions 13 | { 14 | public static Compilation CreateCompilation(this string source) 15 | => CSharpCompilation.Create( 16 | "compilation", 17 | [ 18 | CSharpSyntaxTree.ParseText(source), 19 | ], 20 | [ 21 | MetadataReference.CreateFromFile(typeof(Binder).GetTypeInfo().Assembly.Location), 22 | MetadataReference.CreateFromFile(typeof(Abstractions.Reconciliation.Controller.IEntityController<>).GetTypeInfo().Assembly.Location), 23 | MetadataReference.CreateFromFile(typeof(k8s.IKubernetesObject<>).GetTypeInfo().Assembly.Location), 24 | ], 25 | new(OutputKind.DynamicallyLinkedLibrary)); 26 | } 27 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/Controller/DemoController.cs: -------------------------------------------------------------------------------- 1 | using KubeOps.Abstractions.Reconciliation; 2 | using KubeOps.Abstractions.Reconciliation.Controller; 3 | using KubeOps.Abstractions.Rbac; 4 | 5 | using Microsoft.Extensions.Logging; 6 | 7 | using GeneratedOperatorProject.Entities; 8 | 9 | namespace GeneratedOperatorProject.Controller; 10 | 11 | [EntityRbac(typeof(V1DemoEntity), Verbs = RbacVerb.All)] 12 | public sealed class DemoController(ILogger logger) : IEntityController 13 | { 14 | public Task> ReconcileAsync(V1DemoEntity entity, CancellationToken cancellationToken) 15 | { 16 | logger.LogInformation("Reconcile entity {MetadataName}", entity.Metadata.Name); 17 | 18 | return Task.FromResult(ReconciliationResult.Success(entity)); 19 | } 20 | 21 | public Task> DeletedAsync(V1DemoEntity entity, CancellationToken cancellationToken) 22 | { 23 | logger.LogInformation("Deleted entity {Entity}.", entity); 24 | 25 | return Task.FromResult(ReconciliationResult.Success(entity)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/Queue/EntityRequeue.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Reconciliation.Queue; 9 | 10 | /// 11 | /// Injectable delegate for scheduling an entity to be requeued after a specified amount of time. 12 | /// 13 | /// The type of the Kubernetes entity being requeued. 14 | /// The entity instance that should be requeued. 15 | /// The type of operation for which the reconcile behavior should be performed. 16 | /// The duration to wait before triggering the next reconcile process. 17 | /// A cancellation token to observe while waiting for the requeue duration. 18 | public delegate void EntityRequeue( 19 | TEntity entity, RequeueType type, TimeSpan requeueIn, CancellationToken cancellationToken) 20 | where TEntity : IKubernetesObject; 21 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Certificates/CertificateWebhookService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Abstractions.Certificates; 6 | using KubeOps.KubernetesClient; 7 | using KubeOps.Operator.Web.Webhooks; 8 | 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace KubeOps.Operator.Web.Certificates; 13 | 14 | internal class CertificateWebhookService(ILogger logger, IKubernetesClient client, WebhookLoader loader, WebhookConfig config, ICertificateProvider provider) 15 | : WebhookServiceBase(client, loader, config), IHostedService 16 | { 17 | public async Task StartAsync(CancellationToken cancellationToken) 18 | { 19 | CaBundle = provider.Server.Certificate.EncodeToPemBytes(); 20 | 21 | logger.LogDebug("Registering webhooks"); 22 | await RegisterAll(); 23 | } 24 | 25 | public Task StopAsync(CancellationToken cancellationToken) 26 | { 27 | provider.Dispose(); 28 | return Task.CompletedTask; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Admission/AdmissionResponse.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Text.Json.Serialization; 6 | 7 | namespace KubeOps.Operator.Web.Webhooks.Admission; 8 | 9 | internal sealed class AdmissionResponse : AdmissionReview 10 | { 11 | [JsonPropertyName("response")] 12 | public AdmissionResponseData Response { get; init; } = new(); 13 | 14 | internal sealed class AdmissionResponseData 15 | { 16 | [JsonPropertyName("uid")] 17 | public string Uid { get; init; } = string.Empty; 18 | 19 | [JsonPropertyName("allowed")] 20 | public bool Allowed { get; init; } 21 | 22 | [JsonPropertyName("status")] 23 | public AdmissionStatus? Status { get; init; } 24 | 25 | [JsonPropertyName("warnings")] 26 | public string[]? Warnings { get; init; } 27 | 28 | [JsonPropertyName("patch")] 29 | public string? Patch { get; init; } 30 | 31 | [JsonPropertyName("patchType")] 32 | public string? PatchType { get; init; } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/KubeOps.Operator.Web.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;net9.0;net10.0 5 | 6 | 7 | 8 | KubeOps.Operator.Web 9 | Kubernetes Operator SDK CustomResourceDefinition ASP.NET 10 | 11 | This is an operator sdk written in c#. 12 | It enables a developer to create a custom controller for CRDs 13 | (CustomResourceDefinitions) that runs on kubernetes. 14 | This operator uses ASP.net to support webhooks and external 15 | access to the operator. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /examples/WebhookOperator/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Net; 6 | 7 | using KubeOps.Operator; 8 | using KubeOps.Operator.Web.Builder; 9 | using KubeOps.Operator.Web.Certificates; 10 | 11 | var builder = WebApplication.CreateBuilder(args); 12 | var opBuilder = builder.Services 13 | .AddKubernetesOperator() 14 | .RegisterComponents(); 15 | 16 | #if DEBUG 17 | const string ip = "192.168.1.100"; 18 | const ushort port = 443; 19 | using var generator = new CertificateGenerator(ip); 20 | var cert = generator.Server.CopyServerCertWithPrivateKey(); 21 | 22 | builder.WebHost.ConfigureKestrel(serverOptions => 23 | { 24 | serverOptions.Listen(IPAddress.Any, port, listenOptions => 25 | { 26 | listenOptions.UseHttps(cert); 27 | }); 28 | }); 29 | 30 | opBuilder.UseCertificateProvider(port, ip, generator); 31 | #endif 32 | 33 | builder.Services 34 | .AddControllers(); 35 | 36 | var app = builder.Build(); 37 | 38 | app.UseRouting(); 39 | app.UseDeveloperExceptionPage(); 40 | app.MapControllers(); 41 | 42 | await app.RunAsync(); 43 | -------------------------------------------------------------------------------- /src/KubeOps.Generator/KubeOps.Generator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0 5 | true 6 | true 7 | false 8 | true 9 | 10 | 11 | 12 | KubeOps.Generator 13 | Kubernetes Operator SDK SourceGenerator 14 | 15 | Source Generator for the Kubernetes Operator SDK. 16 | Helps with EntityMetadata and registering them 17 | within the Operator. 18 | 19 | 20 | 21 | 22 | 25 | 26 | 27 | 28 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/LocalTunnel/TunnelWebhookService.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.KubernetesClient; 6 | using KubeOps.Operator.Web.Certificates; 7 | using KubeOps.Operator.Web.Webhooks; 8 | 9 | using Microsoft.Extensions.Hosting; 10 | using Microsoft.Extensions.Logging; 11 | 12 | namespace KubeOps.Operator.Web.LocalTunnel; 13 | 14 | internal class TunnelWebhookService( 15 | ILogger logger, 16 | IKubernetesClient client, 17 | WebhookLoader loader, 18 | WebhookConfig config, 19 | DevelopmentTunnel developmentTunnel) 20 | : WebhookServiceBase(client, loader, config), IHostedService 21 | { 22 | public async Task StartAsync(CancellationToken cancellationToken) 23 | { 24 | Uri = await developmentTunnel.StartAsync(cancellationToken); 25 | 26 | logger.LogDebug("Registering webhooks"); 27 | await RegisterAll(); 28 | } 29 | 30 | public Task StopAsync(CancellationToken cancellationToken) 31 | { 32 | developmentTunnel.Dispose(); 33 | return Task.CompletedTask; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Rbac/EntityRbacAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Rbac; 6 | 7 | /// 8 | /// Generate rbac information for a type. 9 | /// Attach this attribute to a controller with the type reference to 10 | /// a custom entity to define rbac needs for this given type(s). 11 | /// 12 | /// 13 | /// Allow the operator "ALL" access to the V1TestEntity. 14 | /// 15 | /// [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)] 16 | /// 17 | /// 18 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 19 | public class EntityRbacAttribute(params Type[] entities) : RbacAttribute 20 | { 21 | /// 22 | /// List of types that this rbac verbs are valid. 23 | /// 24 | public IEnumerable Entities => entities; 25 | 26 | /// 27 | /// Flags ("list") of allowed verbs. 28 | /// 29 | /// Yaml example: 30 | /// "verbs: ["get", "list", "watch"]". 31 | /// 32 | /// 33 | public RbacVerb Verbs { get; init; } 34 | } 35 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/ReconciliationTriggerSource.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Reconciliation; 6 | 7 | /// 8 | /// Defines the source that triggered the reconciliation process in the Kubernetes operator. 9 | /// Used to identify which component or mechanism initiated the reconciliation cycle. 10 | /// 11 | public enum ReconciliationTriggerSource 12 | { 13 | /// 14 | /// Represents a reconciliation trigger initiated by the Kubernetes API server. 15 | /// This source typically implies that the operator has been informed about 16 | /// a resource event (e.g., creation, modification, deletion) via API server 17 | /// notifications or resource watches. 18 | /// 19 | ApiServer, 20 | 21 | /// 22 | /// Represents a reconciliation trigger initiated directly by the operator. 23 | /// This source indicates that the reconciliation process was started internally 24 | /// by the operator, such as during a scheduled task or an operator-specific event. 25 | /// 26 | Operator, 27 | } 28 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "docs", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "docusaurus": "docusaurus", 7 | "start": "docusaurus start", 8 | "build": "docusaurus build", 9 | "swizzle": "docusaurus swizzle", 10 | "deploy": "docusaurus deploy", 11 | "clear": "docusaurus clear", 12 | "serve": "docusaurus serve", 13 | "write-translations": "docusaurus write-translations", 14 | "write-heading-ids": "docusaurus write-heading-ids" 15 | }, 16 | "dependencies": { 17 | "@docusaurus/core": "3.9.2", 18 | "@docusaurus/preset-classic": "3.9.2", 19 | "@docusaurus/theme-mermaid": "3.9.2", 20 | "@easyops-cn/docusaurus-search-local": "^0.52.0", 21 | "@mdx-js/react": "^3.0.0", 22 | "clsx": "^2.0.0", 23 | "prism-react-renderer": "^2.3.0", 24 | "react": "^19.0.0", 25 | "react-dom": "^19.0.0" 26 | }, 27 | "devDependencies": { 28 | "@docusaurus/module-type-aliases": "3.9.2", 29 | "@docusaurus/types": "3.9.2" 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.5%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 3 chrome version", 39 | "last 3 firefox version", 40 | "last 5 safari version" 41 | ] 42 | }, 43 | "engines": { 44 | "node": ">=18.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Generators/DockerfileGenerator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Cli.Output; 6 | 7 | namespace KubeOps.Cli.Generators; 8 | 9 | internal class DockerfileGenerator(bool hasWebhooks) : IConfigGenerator 10 | { 11 | public void Generate(ResultOutput output) 12 | { 13 | output.Add( 14 | "Dockerfile", 15 | $""" 16 | FROM mcr.microsoft.com/dotnet/sdk:latest as build 17 | WORKDIR /operator 18 | 19 | COPY ./ ./ 20 | RUN dotnet publish -c Release /p:AssemblyName=operator -o out 21 | 22 | # The runner for the application 23 | FROM mcr.microsoft.com/dotnet/{(hasWebhooks ? "aspnet" : "runtime")}:latest as final 24 | 25 | RUN addgroup k8s-operator && useradd -G k8s-operator operator-user 26 | 27 | WORKDIR /operator 28 | COPY --chown=operator-user:k8s-operator --from=build /operator/out/ ./ 29 | 30 | USER operator-user 31 | 32 | ENTRYPOINT [ "dotnet", "operator.dll" ] 33 | """, 34 | OutputFormat.Plain); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/Queue/KubeOpsEntityRequeueFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | using KubeOps.Abstractions.Reconciliation.Queue; 9 | 10 | using Microsoft.Extensions.DependencyInjection; 11 | using Microsoft.Extensions.Logging; 12 | 13 | namespace KubeOps.Operator.Queue; 14 | 15 | internal sealed class KubeOpsEntityRequeueFactory(IServiceProvider services) 16 | : IEntityRequeueFactory 17 | { 18 | public EntityRequeue Create() 19 | where TEntity : IKubernetesObject => 20 | (entity, type, timeSpan, cancellationToken) => 21 | { 22 | var logger = services.GetService>>(); 23 | var queue = services.GetRequiredService>(); 24 | 25 | logger?.LogTrace( 26 | """Requeue entity "{Kind}/{Name}" in {Milliseconds}ms.""", 27 | entity.Kind, 28 | entity.Name(), 29 | timeSpan.TotalMilliseconds); 30 | 31 | queue.Enqueue(entity, type, timeSpan, cancellationToken); 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yaml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: "Create a report to help fix a problem." 3 | title: "[bug]: " 4 | labels: ["bug"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thanks for taking the time to fill out this bug report! 10 | - type: textarea 11 | id: description 12 | attributes: 13 | label: Describe the bug 14 | description: A clear and concise description of what the bug is. 15 | validations: 16 | required: true 17 | - type: textarea 18 | id: reproduce 19 | attributes: 20 | label: To reproduce 21 | description: Steps to reproduce the behaviour 22 | placeholder: | 23 | Steps to reproduce the behavior: 24 | 1. Go to '...' 25 | 2. Click on '....' 26 | 3. Scroll down to '....' 27 | 4. See error 28 | validations: 29 | required: true 30 | - type: textarea 31 | id: expected 32 | attributes: 33 | label: Expected behavior 34 | description: A clear and concise description of what you expected to happen. 35 | - type: textarea 36 | id: screenshots 37 | attributes: 38 | label: Screenshots 39 | description: If applicable, add screenshots to help explain your problem. 40 | - type: textarea 41 | id: additional 42 | attributes: 43 | label: Additional Context 44 | description: Please add any other infos that could be useful. 45 | -------------------------------------------------------------------------------- /.github/workflows/gh-pages.yml: -------------------------------------------------------------------------------- 1 | name: Deploy GitHub Pages 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - "docs/**" 9 | - ".github/workflows/gh-pages.yml" 10 | 11 | workflow_dispatch: 12 | 13 | permissions: 14 | contents: read 15 | pages: write 16 | id-token: write 17 | 18 | concurrency: 19 | group: pages 20 | cancel-in-progress: true 21 | 22 | jobs: 23 | build: 24 | runs-on: ubuntu-latest 25 | environment: 26 | name: github-pages 27 | url: ${{ steps.deployment.outputs.page_url }} 28 | steps: 29 | - uses: actions/checkout@v6 30 | 31 | - name: Setup Node.js 32 | uses: actions/setup-node@v6 33 | with: 34 | node-version: "24" 35 | cache: "npm" 36 | cache-dependency-path: docs/package-lock.json 37 | 38 | - name: Install Dependencies 39 | working-directory: docs 40 | run: npm ci 41 | 42 | - name: Build Docusaurus 43 | working-directory: docs 44 | run: npm run build 45 | 46 | - name: Setup Pages 47 | uses: actions/configure-pages@v5 48 | 49 | - name: Upload artifact 50 | uses: actions/upload-pages-artifact@v4 51 | with: 52 | path: docs/build 53 | 54 | - name: Deploy to GitHub Pages 55 | id: deployment 56 | uses: actions/deploy-pages@v4 57 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Abstractions.Builder; 6 | using KubeOps.Operator.Builder; 7 | 8 | using Microsoft.Extensions.DependencyInjection; 9 | 10 | namespace KubeOps.Operator; 11 | 12 | /// 13 | /// Method extensions for the . 14 | /// 15 | public static class ServiceCollectionExtensions 16 | { 17 | /// 18 | /// Add the Kubernetes operator to the dependency injection. 19 | /// 20 | /// . 21 | /// An optional configure action for adjusting settings in the operator. 22 | /// An for further configuration and chaining. 23 | public static IOperatorBuilder AddKubernetesOperator( 24 | this IServiceCollection services, 25 | Action? configure = null) 26 | { 27 | var settings = new OperatorSettings(); 28 | configure?.Invoke(settings); 29 | return new OperatorBuilder(services, settings); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Program.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Net; 6 | 7 | using KubeOps.Operator; 8 | using KubeOps.Operator.Web.Builder; 9 | using KubeOps.Operator.Web.Certificates; 10 | 11 | var builder = WebApplication.CreateBuilder(args); 12 | var opBuilder = builder.Services 13 | .AddKubernetesOperator() 14 | .RegisterComponents(); 15 | 16 | #if DEBUG 17 | const string ip = "192.168.1.100"; 18 | const ushort port = 443; 19 | using var generator = new CertificateGenerator(ip); 20 | var cert = generator.Server.CopyServerCertWithPrivateKey(); 21 | 22 | // Configure Kestrel to listen on IPv4, use port 443, and use the server certificate 23 | builder.WebHost.ConfigureKestrel(serverOptions => 24 | { 25 | serverOptions.Listen(IPAddress.Any, port, listenOptions => 26 | { 27 | listenOptions.UseHttps(cert); 28 | }); 29 | }); 30 | 31 | opBuilder.UseCertificateProvider(port, ip, generator); 32 | #endif 33 | 34 | builder.Services 35 | .AddControllers(); 36 | 37 | var app = builder.Build(); 38 | 39 | app.UseRouting(); 40 | app.UseDeveloperExceptionPage(); 41 | app.MapControllers(); 42 | 43 | await app.RunAsync(); 44 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/launchsettings.json", 3 | "profiles": { 4 | "CLI Generate Operator": { 5 | "commandName": "Project", 6 | "workingDirectory": "$(ProjectDir)", 7 | "commandLineArgs": "g op kub-op ../../examples/Operator/Operator.csproj" 8 | }, 9 | "CLI Generate Webhook Operator": { 10 | "commandName": "Project", 11 | "workingDirectory": "$(ProjectDir)", 12 | "commandLineArgs": "g op kub-wh-op ../../examples/WebhookOperator/WebhookOperator.csproj" 13 | }, 14 | "CLI Generate Conversion Operator": { 15 | "commandName": "Project", 16 | "workingDirectory": "$(ProjectDir)", 17 | "commandLineArgs": "g op kub-conv-op ../../examples/ConversionWebhookOperator/ConversionWebhookOperator.csproj" 18 | }, 19 | "CLI Install": { 20 | "commandName": "Project", 21 | "workingDirectory": "$(ProjectDir)", 22 | "commandLineArgs": "i ../../examples/Operator/Operator.csproj -f" 23 | }, 24 | "CLI Uninstall": { 25 | "commandName": "Project", 26 | "workingDirectory": "$(ProjectDir)", 27 | "commandLineArgs": "u ../../examples/Operator/Operator.csproj -f" 28 | }, 29 | "CLI Version": { 30 | "commandName": "Project", 31 | "workingDirectory": "$(ProjectDir)", 32 | "commandLineArgs": "av" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/WebOperator.CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | using KubeOps.Operator; 4 | using KubeOps.Operator.Web.Builder; 5 | using KubeOps.Operator.Web.Certificates; 6 | 7 | var builder = WebApplication.CreateBuilder(args); 8 | var opBuilder = builder.Services 9 | .AddKubernetesOperator() 10 | //-:cnd:noEmit 11 | #if DEBUG 12 | .AddCrdInstaller(c => 13 | { 14 | // Careful, this can be very destructive. 15 | // c.OverwriteExisting = true; 16 | // c.DeleteOnShutdown = true; 17 | }) 18 | #endif 19 | //+:cnd:noEmit 20 | .RegisterComponents(); 21 | 22 | //-:cnd:noEmit 23 | #if DEBUG 24 | const string ip = "192.168.1.100"; 25 | const ushort port = 443; 26 | using var generator = new CertificateGenerator(ip); 27 | var cert = generator.Server.CopyServerCertWithPrivateKey(); 28 | 29 | builder.WebHost.ConfigureKestrel(serverOptions => 30 | { 31 | serverOptions.Listen(IPAddress.Any, port, listenOptions => 32 | { 33 | listenOptions.UseHttps(cert); 34 | }); 35 | }); 36 | 37 | opBuilder.UseCertificateProvider(port, ip, generator); 38 | #endif 39 | //+:cnd:noEmit 40 | 41 | builder.Services 42 | .AddControllers(); 43 | 44 | var app = builder.Build(); 45 | 46 | app.UseRouting(); 47 | app.UseDeveloperExceptionPage(); 48 | app.MapControllers(); 49 | 50 | await app.RunAsync(); 51 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/EmptyWebOperator.CSharp/Program.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | using KubeOps.Operator; 4 | using KubeOps.Operator.Web.Builder; 5 | using KubeOps.Operator.Web.Certificates; 6 | 7 | var builder = WebApplication.CreateBuilder(args); 8 | var opBuilder = builder.Services 9 | .AddKubernetesOperator() 10 | //-:cnd:noEmit 11 | #if DEBUG 12 | .AddCrdInstaller(c => 13 | { 14 | // Careful, this can be very destructive. 15 | // c.OverwriteExisting = true; 16 | // c.DeleteOnShutdown = true; 17 | }) 18 | #endif 19 | //+:cnd:noEmit 20 | .RegisterComponents(); 21 | 22 | //-:cnd:noEmit 23 | #if DEBUG 24 | const string ip = "192.168.1.100"; 25 | const ushort port = 443; 26 | using var generator = new CertificateGenerator(ip); 27 | var cert = generator.Server.CopyServerCertWithPrivateKey(); 28 | 29 | builder.WebHost.ConfigureKestrel(serverOptions => 30 | { 31 | serverOptions.Listen(IPAddress.Any, port, listenOptions => 32 | { 33 | listenOptions.UseHttps(cert); 34 | }); 35 | }); 36 | 37 | opBuilder.UseCertificateProvider(port, ip, generator); 38 | #endif 39 | //+:cnd:noEmit 40 | 41 | builder.Services 42 | .AddControllers(); 43 | 44 | var app = builder.Build(); 45 | 46 | app.UseRouting(); 47 | app.UseDeveloperExceptionPage(); 48 | app.MapControllers(); 49 | 50 | await app.RunAsync(); 51 | -------------------------------------------------------------------------------- /src/KubeOps.Generator/Generators/AutoGeneratedSyntaxTrivia.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using Microsoft.CodeAnalysis; 6 | using Microsoft.CodeAnalysis.CSharp; 7 | using Microsoft.CodeAnalysis.CSharp.Syntax; 8 | 9 | namespace KubeOps.Generator.Generators; 10 | 11 | public static class AutoGeneratedSyntaxTrivia 12 | { 13 | public static readonly SyntaxTriviaList Instance = 14 | new( 15 | SyntaxFactory.Comment( 16 | """ 17 | // 18 | // This code was generated by a tool. 19 | // Changes to this file may cause incorrect behavior and will be lost if the code is regenerated. 20 | // 21 | """), 22 | SyntaxFactory.CarriageReturnLineFeed, 23 | SyntaxFactory.Trivia( 24 | SyntaxFactory.PragmaWarningDirectiveTrivia( 25 | SyntaxFactory.Token(SyntaxKind.DisableKeyword), 26 | SyntaxFactory.SeparatedList(new SyntaxNodeOrTokenList(SyntaxFactory.IdentifierName("CS1591"))), 27 | false)), 28 | SyntaxFactory.CarriageReturnLineFeed); 29 | } 30 | -------------------------------------------------------------------------------- /test/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | net10.0 4 | enable 5 | enable 6 | false 7 | true 8 | true 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-pre-release.yml: -------------------------------------------------------------------------------- 1 | name: .NET Pre-Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | permissions: 10 | contents: read 11 | 12 | jobs: 13 | semantic-release: 14 | runs-on: ubuntu-latest 15 | permissions: 16 | contents: write 17 | issues: write 18 | steps: 19 | - uses: actions/checkout@v6 20 | with: 21 | fetch-depth: 1 22 | 23 | - name: Setup .NET 24 | uses: actions/setup-dotnet@v5 25 | with: 26 | dotnet-version: 9.x 27 | 28 | - name: Setup Semantic Release 29 | run: | 30 | npm install https://github.com/buehler/semantic-release#feat/force-pre-release 31 | npm install \ 32 | @semantic-release/github \ 33 | @semantic-release/commit-analyzer \ 34 | @semantic-release/release-notes-generator \ 35 | conventional-changelog-conventionalcommits \ 36 | semantic-release-net 37 | 38 | - name: Semantic Release 39 | run: npx https://github.com/buehler/semantic-release#feat/force-pre-release -e ./.release/release.base.mjs -e ./.release/release.main-pre.mjs 40 | env: 41 | DEBUG: semantic-release:* 42 | NUGET_API_KEY: ${{ secrets.NUGET_KEY }} 43 | GH_NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | -------------------------------------------------------------------------------- /.github/workflows/dotnet-release.yml: -------------------------------------------------------------------------------- 1 | name: .NET Release 2 | 3 | on: 4 | workflow_dispatch: 5 | schedule: 6 | - cron: "0 12 * * 2" # Every Tuesday at 12:00 UTC 7 | 8 | permissions: 9 | contents: read 10 | 11 | jobs: 12 | semantic-release: 13 | runs-on: ubuntu-latest 14 | permissions: 15 | contents: write 16 | issues: write 17 | steps: 18 | - uses: actions/checkout@v6 19 | with: 20 | fetch-depth: 1 21 | 22 | - name: Setup .NET 23 | uses: actions/setup-dotnet@v5 24 | with: 25 | dotnet-version: 9.x 26 | 27 | - name: Setup Semantic Release 28 | run: | 29 | npm install https://github.com/buehler/semantic-release#feat/force-pre-release 30 | npm install \ 31 | @semantic-release/github \ 32 | @semantic-release/commit-analyzer \ 33 | @semantic-release/release-notes-generator \ 34 | conventional-changelog-conventionalcommits \ 35 | semantic-release-net 36 | 37 | - name: Semantic Release 38 | run: npx https://github.com/buehler/semantic-release#feat/force-pre-release -e ./.release/release.base.mjs -e ./.release/release.main.mjs 39 | env: 40 | DEBUG: semantic-release:* 41 | NUGET_API_KEY: ${{ secrets.NUGET_KEY }} 42 | GH_NUGET_API_KEY: ${{ secrets.GITHUB_TOKEN }} 43 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 44 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/EntityMetadata.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities; 6 | 7 | /// 8 | /// Metadata for a given entity. 9 | /// 10 | /// The kind of the entity (e.g. deployment). 11 | /// Version (e.g. v1 or v2-alpha). 12 | /// The group in Kubernetes (e.g. "testing.dev"). 13 | /// An optional plural name. Defaults to the singular name with an added "s". 14 | public record EntityMetadata(string Kind, string Version, string? Group = null, string? Plural = null) 15 | { 16 | /// 17 | /// Kind of the entity when used in a list. 18 | /// 19 | public string ListKind => $"{Kind}List"; 20 | 21 | /// 22 | /// Name of the singular entity. 23 | /// 24 | public string SingularName => Kind.ToLowerInvariant(); 25 | 26 | /// 27 | /// Name of the plural entity. 28 | /// 29 | public string PluralName => (Plural ?? $"{Kind}s").ToLowerInvariant(); 30 | 31 | public string GroupWithVersion => $"{Group ?? string.Empty}/{Version}".TrimStart('/'); 32 | } 33 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/Finalizer/IEventFinalizerAttacherFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Reconciliation.Finalizer; 9 | 10 | /// 11 | /// Represents a type used to create for controllers. 12 | /// 13 | public interface IEventFinalizerAttacherFactory 14 | { 15 | /// 16 | /// Creates a new , which attaches the finalizer of 17 | /// type to . 18 | /// 19 | /// The finalizer identifier. 20 | /// The finalizer. 21 | /// The entity. 22 | /// A delegate to attach the finalizer implementation to the entity. 23 | EntityFinalizerAttacher Create(string identifier) 24 | where TImplementation : class, IEntityFinalizer 25 | where TEntity : IKubernetesObject; 26 | } 27 | -------------------------------------------------------------------------------- /examples/WebhookOperator/Controller/V1TestEntityController.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Rbac; 8 | using KubeOps.Abstractions.Reconciliation; 9 | using KubeOps.Abstractions.Reconciliation.Controller; 10 | 11 | using WebhookOperator.Entities; 12 | 13 | namespace WebhookOperator.Controller; 14 | 15 | [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)] 16 | public sealed class V1TestEntityController(ILogger logger) : IEntityController 17 | { 18 | public Task> ReconcileAsync(V1TestEntity entity, CancellationToken cancellationToken) 19 | { 20 | logger.LogInformation("Reconciling entity {Namespace}/{Name}.", entity.Namespace(), entity.Name()); 21 | return Task.FromResult(ReconciliationResult.Success(entity)); 22 | } 23 | 24 | public Task> DeletedAsync(V1TestEntity entity, CancellationToken cancellationToken) 25 | { 26 | logger.LogInformation("Deleted entity {Namespace}/{Name}.", entity.Namespace(), entity.Name()); 27 | return Task.FromResult(ReconciliationResult.Success(entity)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/WebhookLoader.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | using KubeOps.Operator.Web.Webhooks.Admission.Mutation; 8 | using KubeOps.Operator.Web.Webhooks.Admission.Validation; 9 | using KubeOps.Operator.Web.Webhooks.Conversion; 10 | 11 | namespace KubeOps.Operator.Web.Webhooks; 12 | 13 | internal record WebhookLoader(Assembly Entry) 14 | { 15 | public IEnumerable ValidationWebhooks => Entry 16 | .DefinedTypes 17 | .Where(t => t.BaseType?.IsGenericType == true && 18 | t.BaseType?.GetGenericTypeDefinition() == typeof(ValidationWebhook<>)); 19 | 20 | public IEnumerable MutationWebhooks => Entry 21 | .DefinedTypes 22 | .Where(t => t.BaseType?.IsGenericType == true && 23 | t.BaseType?.GetGenericTypeDefinition() == typeof(MutationWebhook<>)); 24 | 25 | public IEnumerable ConversionWebhooks => Entry 26 | .DefinedTypes 27 | .Where(t => t.BaseType?.IsGenericType == true && 28 | #pragma warning disable CA2252 // This is internal only. 29 | t.BaseType?.GetGenericTypeDefinition() == typeof(ConversionWebhook<>)); 30 | #pragma warning restore CA2252 31 | } 32 | -------------------------------------------------------------------------------- /examples/ConversionWebhookOperator/Controller/V1TestEntityController.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using ConversionWebhookOperator.Entities; 6 | 7 | using k8s.Models; 8 | 9 | using KubeOps.Abstractions.Rbac; 10 | using KubeOps.Abstractions.Reconciliation; 11 | using KubeOps.Abstractions.Reconciliation.Controller; 12 | 13 | namespace ConversionWebhookOperator.Controller; 14 | 15 | [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)] 16 | public sealed class V1TestEntityController(ILogger logger) : IEntityController 17 | { 18 | public Task> ReconcileAsync(V1TestEntity entity, CancellationToken cancellationToken) 19 | { 20 | logger.LogInformation("Reconciling entity {Namespace}/{Name}.", entity.Namespace(), entity.Name()); 21 | return Task.FromResult(ReconciliationResult.Success(entity)); 22 | } 23 | 24 | public Task> DeletedAsync(V1TestEntity entity, CancellationToken cancellationToken) 25 | { 26 | logger.LogInformation("Deleted entity {Namespace}/{Name}.", entity.Namespace(), entity.Name()); 27 | return Task.FromResult(ReconciliationResult.Success(entity)); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Test/TestEntities/V1OperatorIntegrationTestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace KubeOps.Operator.Test.TestEntities; 10 | 11 | [KubernetesEntity(Group = "operator.test", ApiVersion = "v1", Kind = "OperatorIntegrationTest")] 12 | public sealed class V1OperatorIntegrationTestEntity : CustomKubernetesEntity 14 | { 15 | public V1OperatorIntegrationTestEntity() 16 | { 17 | ApiVersion = "operator.test/v1"; 18 | Kind = "OperatorIntegrationTest"; 19 | } 20 | 21 | public V1OperatorIntegrationTestEntity(string name, string username, string ns) : this() 22 | { 23 | Metadata.Name = name; 24 | Spec.Username = username; 25 | Metadata.NamespaceProperty = ns; 26 | } 27 | 28 | public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username}"; 29 | 30 | public sealed class EntitySpec 31 | { 32 | public string Username { get; set; } = string.Empty; 33 | } 34 | 35 | public sealed class EntityStatus 36 | { 37 | public string Status { get; set; } = string.Empty; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:recommended", ":disableDependencyDashboard"], 4 | "labels": ["dependencies"], 5 | "vulnerabilityAlerts": { 6 | "labels": ["dependencies-security"] 7 | }, 8 | "branchConcurrentLimit": 0, 9 | "prHourlyLimit": 0, 10 | "prConcurrentLimit": 0, 11 | "automerge": true, 12 | "timezone": "Europe/Zurich", 13 | "schedule": ["after 9pm", "before 6am"], 14 | "packageRules": [ 15 | { 16 | "matchFileNames": ["examples/**/*"], 17 | "enabled": false 18 | }, 19 | { 20 | "matchManagers": ["dotnet"], 21 | "matchDepNames": ["Microsoft.CodeAnalysis.CSharp"], 22 | "matchFileNames": ["KubeOps.Generator.csproj"], 23 | "enabled": false 24 | }, 25 | { 26 | "matchFileNames": ["**/*"], 27 | "semanticCommitType": "deps", 28 | "semanticCommitScope": "general" 29 | }, 30 | { 31 | "matchFileNames": [".github/**/*", ".release/**/*"], 32 | "semanticCommitScope": "ci" 33 | }, 34 | { 35 | "matchFileNames": [".config/dotnet-tools.json"], 36 | "semanticCommitScope": "tools" 37 | }, 38 | { 39 | "matchFileNames": ["src/**/*"], 40 | "semanticCommitScope": "core" 41 | }, 42 | { 43 | "matchFileNames": ["test/**/*"], 44 | "semanticCommitScope": "test" 45 | }, 46 | { 47 | "matchFileNames": ["docs/**/*"], 48 | "semanticCommitScope": "docs" 49 | } 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/TestApp/V1OperatorWebIntegrationTestEntity.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Entities; 8 | 9 | namespace KubeOps.Operator.Web.Test.TestApp; 10 | 11 | [KubernetesEntity(Group = "weboperator.test", ApiVersion = "v1", Kind = "WebOperatorIntegrationTest")] 12 | public class V1OperatorWebIntegrationTestEntity : CustomKubernetesEntity 14 | { 15 | public V1OperatorWebIntegrationTestEntity() 16 | { 17 | ApiVersion = "weboperator.test/v1"; 18 | Kind = "WebOperatorIntegrationTest"; 19 | } 20 | 21 | public V1OperatorWebIntegrationTestEntity(string name, string username) : this() 22 | { 23 | Metadata.Name = name; 24 | Metadata.NamespaceProperty = "default"; 25 | Spec.Username = username; 26 | } 27 | 28 | public override string ToString() => $"Test Entity ({Metadata.Name}): {Spec.Username}"; 29 | 30 | public class EntitySpec 31 | { 32 | public string Username { get; set; } = string.Empty; 33 | } 34 | 35 | public class EntityStatus 36 | { 37 | public string Status { get; set; } = string.Empty; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /examples/Operator/Controller/V1TestEntityController.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.Models; 6 | 7 | using KubeOps.Abstractions.Rbac; 8 | using KubeOps.Abstractions.Reconciliation; 9 | using KubeOps.Abstractions.Reconciliation.Controller; 10 | 11 | using Microsoft.Extensions.Logging; 12 | 13 | using Operator.Entities; 14 | 15 | namespace Operator.Controller; 16 | 17 | [EntityRbac(typeof(V1TestEntity), Verbs = RbacVerb.All)] 18 | public sealed class V1TestEntityController(ILogger logger) 19 | : IEntityController 20 | { 21 | public Task> ReconcileAsync( 22 | V1TestEntity entity, CancellationToken cancellationToken) 23 | { 24 | logger.LogInformation("Reconciling entity {Namespace}/{Name}.", entity.Namespace(), entity.Name()); 25 | return Task.FromResult(ReconciliationResult.Success(entity)); 26 | } 27 | 28 | public Task> DeletedAsync( 29 | V1TestEntity entity, CancellationToken cancellationToken) 30 | { 31 | logger.LogInformation("Deleted entity {Namespace}/{Name}.", entity.Namespace(), entity.Name()); 32 | return Task.FromResult(ReconciliationResult.Success(entity)); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Builder/LeaderElectionType.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Builder; 6 | 7 | /// 8 | /// Specifies the types of leader election mechanisms to be used in distributed systems or workloads. 9 | /// 10 | public enum LeaderElectionType 11 | { 12 | /// 13 | /// Represents the absence of a leader election mechanism. 14 | /// This option is used when no leader election is required, and all instances 15 | /// are expected to operate without coordination or exclusivity. 16 | /// 17 | None = 0, 18 | 19 | /// 20 | /// Represents the leader election mechanism where only a single instance of the application 21 | /// assumes the leader role at any given time. This is used to coordinate operations 22 | /// that require exclusivity or to manage shared resources in distributed systems. 23 | /// 24 | Single = 1, 25 | 26 | /// 27 | /// Represents a custom leader election mechanism determined by the user. 28 | /// This option allows the integration of user-defined logic for handling 29 | /// leader election, enabling tailored coordination strategies beyond the 30 | /// provided defaults. 31 | /// 32 | Custom = 2, 33 | } 34 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Test/InvocationCounter.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Collections.Concurrent; 6 | using System.Runtime.CompilerServices; 7 | 8 | using k8s; 9 | using k8s.Models; 10 | 11 | namespace KubeOps.Operator.Test; 12 | 13 | public class InvocationCounter 14 | where TEntity : IKubernetesObject 15 | { 16 | private TaskCompletionSource _task = new(); 17 | private readonly ConcurrentQueue<(string Method, TEntity Entity)> _invocations = new(); 18 | public IReadOnlyList<(string Method, TEntity Entity)> Invocations => _invocations.ToList(); 19 | 20 | #if DEBUG 21 | public Task WaitForInvocations => _task.Task; 22 | #else 23 | public Task WaitForInvocations => _task.Task.WaitAsync(TimeSpan.FromSeconds(30)); 24 | #endif 25 | 26 | public int TargetInvocationCount { get; set; } = 1; 27 | 28 | public void Invocation(TEntity entity, [CallerMemberName] string name = "Invocation") 29 | { 30 | _invocations.Enqueue((name, entity)); 31 | if (Invocations.Count >= TargetInvocationCount) 32 | { 33 | _task.TrySetResult(); 34 | } 35 | } 36 | 37 | public void Clear() 38 | { 39 | _invocations.Clear(); 40 | _task = new TaskCompletionSource(); 41 | TargetInvocationCount = 1; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/Webhooks/MutationWebhook.Integration.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using FluentAssertions; 6 | 7 | using KubeOps.KubernetesClient; 8 | using KubeOps.Operator.Web.Test.TestApp; 9 | 10 | namespace KubeOps.Operator.Web.Test.Webhooks; 11 | 12 | public class MutationWebhookIntegrationTest : IntegrationTestBase 13 | { 14 | [Fact(Skip = "This test is flakey since localtunnel is not always available. Need an alternative.")] 15 | public async Task Should_Allow_Creation_Of_Entity() 16 | { 17 | using var client = new KubernetesClient.KubernetesClient() as IKubernetesClient; 18 | var e = await client.CreateAsync(new V1OperatorWebIntegrationTestEntity("test-entity", "foobar")); 19 | e.Spec.Username.Should().Be("foobar"); 20 | await client.DeleteAsync(e); 21 | } 22 | 23 | [Fact(Skip = "This test is flakey since localtunnel is not always available. Need an alternative.")] 24 | public async Task Should_Mutate_Entity_According_To_Code() 25 | { 26 | using var client = new KubernetesClient.KubernetesClient() as IKubernetesClient; 27 | var e = await client.CreateAsync(new V1OperatorWebIntegrationTestEntity("test-entity", "overwrite")); 28 | e.Spec.Username.Should().Be("overwritten"); 29 | await client.DeleteAsync(e); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/KubeOps.Cli/Commands/Utilities/Version.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.CommandLine; 6 | 7 | using k8s; 8 | 9 | using Spectre.Console; 10 | 11 | namespace KubeOps.Cli.Commands.Utilities; 12 | 13 | internal static class Version 14 | { 15 | public static Command Command 16 | { 17 | get 18 | { 19 | var cmd = new Command( 20 | "api-version", 21 | "Prints the actual server version of the connected kubernetes cluster."); 22 | cmd.Aliases.Add("av"); 23 | cmd.SetAction(_ => 24 | Handler(AnsiConsole.Console, new Kubernetes(KubernetesClientConfiguration.BuildDefaultConfig()))); 25 | 26 | return cmd; 27 | } 28 | } 29 | 30 | internal static async Task Handler(IAnsiConsole console, IKubernetes client) 31 | { 32 | var version = await client.Version.GetCodeAsync(); 33 | console.Write(new Table() 34 | .Title("Kubernetes API Version") 35 | .HideHeaders() 36 | .AddColumns("Info", "Value") 37 | .AddRow("Git-Version", version.GitVersion) 38 | .AddRow("Major", version.Major) 39 | .AddRow("Minor", version.Minor) 40 | .AddRow("Platform", version.Platform)); 41 | 42 | return ExitCodes.Success; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/Webhooks/ValidationWebhook.Integration.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using FluentAssertions; 6 | 7 | using k8s.Autorest; 8 | 9 | using KubeOps.KubernetesClient; 10 | using KubeOps.Operator.Web.Test.TestApp; 11 | 12 | namespace KubeOps.Operator.Web.Test.Webhooks; 13 | 14 | public class ValidationWebhookIntegrationTest : IntegrationTestBase 15 | { 16 | [Fact(Skip = "This test is flakey since localtunnel is not always available. Need an alternative.")] 17 | public async Task Should_Allow_Creation_Of_Entity() 18 | { 19 | using var client = new KubernetesClient.KubernetesClient() as IKubernetesClient; 20 | var e = await client.CreateAsync(new V1OperatorWebIntegrationTestEntity("test-entity", "foobar")); 21 | await client.DeleteAsync(e); 22 | } 23 | 24 | [Fact(Skip = "This test is flakey since localtunnel is not always available. Need an alternative.")] 25 | public async Task Should_Disallow_Creation_When_Validation_Fails() 26 | { 27 | using var client = new KubernetesClient.KubernetesClient() as IKubernetesClient; 28 | var ex = await Assert.ThrowsAsync(async () => 29 | await client.CreateAsync(new V1OperatorWebIntegrationTestEntity("test-entity", "forbidden"))); 30 | ex.Message.Should().Contain("name may not be 'forbidden'"); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/Webhooks/Conversion/ConversionWebhookAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Runtime.Versioning; 6 | 7 | using KubeOps.Transpiler; 8 | 9 | using Microsoft.AspNetCore.Mvc; 10 | 11 | namespace KubeOps.Operator.Web.Webhooks.Conversion; 12 | 13 | /// 14 | /// Defines (marks) an MVC controller as "conversion webhook". The route is automatically set to 15 | /// /convert/[group]/[plural-name]. 16 | /// This must be used in conjunction with the class. 17 | /// 18 | [AttributeUsage(AttributeTargets.Class)] 19 | [RequiresPreviewFeatures( 20 | "Conversion webhooks API is not yet stable, the way that conversion " + 21 | "webhooks are implemented may change in the future based on user feedback.")] 22 | public class ConversionWebhookAttribute : RouteAttribute 23 | { 24 | public ConversionWebhookAttribute(Type entityType) 25 | : base(GetRouteTemplate(entityType)) 26 | { 27 | } 28 | 29 | public ConversionWebhookAttribute(string group, string pluralName) 30 | : base($"/convert/{group}/{pluralName}") 31 | { 32 | } 33 | 34 | private static string GetRouteTemplate(Type entityType) 35 | { 36 | var meta = Entities.ToEntityMetadata(entityType).Metadata; 37 | return $"/convert/{meta.Group}/{meta.PluralName}"; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Reconciliation/IReconciler{TEntity}.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Abstractions.Reconciliation; 9 | 10 | /// 11 | /// Defines methods for handling reconciliation processes related to Kubernetes resources. 12 | /// This interface provides the necessary functionality for handling the lifecycle events 13 | /// of a resource, such as creation, modification, and deletion. 14 | /// 15 | /// 16 | /// The type of the Kubernetes resource, which must implement . 17 | /// 18 | public interface IReconciler 19 | where TEntity : IKubernetesObject 20 | { 21 | /// 22 | /// Handles the reconciliation process for a Kubernetes entity. 23 | /// 24 | /// The context containing details of the entity to reconcile. 25 | /// A token to monitor for cancellation requests during the reconciliation process. 26 | /// A task that represents the asynchronous reconciliation operation, returning the result of the reconciliation process. 27 | Task> Reconcile(ReconciliationContext reconciliationContext, CancellationToken cancellationToken); 28 | } 29 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/LeaderElection/ServiceCollectionExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s.LeaderElection; 6 | 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.DependencyInjection.Extensions; 9 | 10 | namespace KubeOps.Operator.LeaderElection; 11 | 12 | internal static class ServiceCollectionExtensions 13 | { 14 | /// 15 | /// Adds support for leader election. 16 | /// 17 | /// The service collection. 18 | public static void AddLeaderElection(this IServiceCollection services) 19 | { 20 | // In order to enable leader election, two things need to be done: 21 | // First, we need to setup the LeaderElector, which is done by the factory. This is done in order to allow the 22 | // injection of other services (like the k8s.IKubernetes) into the creation of the elector. 23 | services.TryAddSingleton(); 24 | services.TryAddSingleton(provider => 25 | provider.GetRequiredService().CreateElector()); 26 | 27 | // The second thing to do is the addition of the LeaderElectionBackgroundService which is responsible for managing 28 | // the leader election itself. 29 | services.AddHostedService(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Entities/Attributes/AdditionalPrinterColumnAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Entities.Attributes; 6 | 7 | /// 8 | /// Defines a property as an additional printer column. 9 | /// 10 | [AttributeUsage(AttributeTargets.Property)] 11 | public class AdditionalPrinterColumnAttribute(PrinterColumnPriority priority = default, string? name = null) 12 | : Attribute 13 | { 14 | /// 15 | /// The name of the column. Defaults to the property-name. 16 | /// 17 | public string? Name => name; 18 | 19 | /// 20 | /// The priority of the additional printer column. 21 | /// As documented in 22 | /// https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#priority 23 | /// the following rules apply to priority: 24 | /// 25 | /// 26 | /// Columns with priority `0` are shown in standard view 27 | /// 28 | /// 29 | /// Columns with priority greater than `0` are shown only in wide view 30 | /// 31 | /// 32 | /// 33 | public PrinterColumnPriority Priority => priority; 34 | } 35 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/KubeOps.Operator.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net8.0;net9.0;net10.0 5 | 6 | 7 | 8 | KubeOps.Operator 9 | Kubernetes Operator SDK CustomResourceDefinition 10 | 11 | This is an operator sdk written in c#. 12 | It enables a developer to create a custom controller for CRDs 13 | (CustomResourceDefinitions) that runs on kubernetes. This operator 14 | may run without ASP.net but needs the IHost of dotnet to run. 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | build/ 31 | 32 | 33 | 34 | 35 | 36 | <_Parameter1>$(MSBuildProjectName).Web.Test 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Rbac/RbacVerbs.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Rbac; 6 | 7 | /// 8 | /// List of possible rbac verbs. 9 | /// 10 | [Flags] 11 | public enum RbacVerb 12 | { 13 | /// 14 | /// No permissions on the resource. 15 | /// 16 | None = 0, 17 | 18 | /// 19 | /// All possible permissions. 20 | /// 21 | All = 1 << 0, 22 | 23 | /// 24 | /// Retrieve the resource from the api. 25 | /// 26 | Get = 1 << 1, 27 | 28 | /// 29 | /// List resources on the api. 30 | /// 31 | List = 1 << 2, 32 | 33 | /// 34 | /// Watch for events on resources. 35 | /// 36 | Watch = 1 << 3, 37 | 38 | /// 39 | /// Create new instances of the resource. 40 | /// 41 | Create = 1 << 4, 42 | 43 | /// 44 | /// Update existing resources. 45 | /// 46 | Update = 1 << 5, 47 | 48 | /// 49 | /// Patch resources. 50 | /// 51 | Patch = 1 << 6, 52 | 53 | /// 54 | /// Delete resources on the api. 55 | /// 56 | Delete = 1 << 7, 57 | 58 | /// 59 | /// All possible permissions (defined explicitly). 60 | /// 61 | AllExplicit = 1 << 8, 62 | } 63 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/KubeOps.Operator.Web.Test.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | CS0618 29 | 30 | -------------------------------------------------------------------------------- /src/KubeOps.Operator.Web/LocalTunnel/DevelopmentTunnel.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Operator.Web.Webhooks; 6 | 7 | using Localtunnel; 8 | using Localtunnel.Endpoints.Http; 9 | using Localtunnel.Handlers.Kestrel; 10 | using Localtunnel.Processors; 11 | using Localtunnel.Tunnels; 12 | 13 | using Microsoft.Extensions.Logging; 14 | 15 | namespace KubeOps.Operator.Web.LocalTunnel; 16 | 17 | internal class DevelopmentTunnel(ILoggerFactory loggerFactory, WebhookConfig config) : IDisposable 18 | { 19 | private readonly LocaltunnelClient _tunnelClient = new(loggerFactory); 20 | private Tunnel? _tunnel; 21 | 22 | public async Task StartAsync(CancellationToken cancellationToken) 23 | { 24 | _tunnel = await _tunnelClient.OpenAsync( 25 | new KestrelTunnelConnectionHandler( 26 | new HttpRequestProcessingPipelineBuilder() 27 | .Append(new HttpHostHeaderRewritingRequestProcessor(config.Hostname)).Build(), 28 | new HttpTunnelEndpointFactory(config.Hostname, config.Port)), 29 | cancellationToken: cancellationToken); 30 | await _tunnel.StartAsync(cancellationToken: cancellationToken); 31 | return _tunnel.Information.Url; 32 | } 33 | 34 | public void Dispose() 35 | { 36 | Dispose(true); 37 | GC.SuppressFinalize(this); 38 | } 39 | 40 | protected virtual void Dispose(bool disposing) 41 | { 42 | _tunnel?.Dispose(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Test/HostedServices/LeaderResourceWatcher.Integration.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using KubeOps.Abstractions.Builder; 6 | using KubeOps.Abstractions.Reconciliation; 7 | using KubeOps.Abstractions.Reconciliation.Controller; 8 | using KubeOps.Operator.Test.TestEntities; 9 | 10 | using Microsoft.Extensions.Hosting; 11 | 12 | namespace KubeOps.Operator.Test.HostedServices; 13 | 14 | public sealed class LeaderAwareHostedServiceDisposeIntegrationTest : HostedServiceDisposeIntegrationTest 15 | { 16 | protected override void ConfigureHost(HostApplicationBuilder builder) 17 | { 18 | builder.Services 19 | .AddKubernetesOperator(op => op.LeaderElectionType = LeaderElectionType.Single) 20 | .AddController(); 21 | } 22 | 23 | private sealed class TestController : IEntityController 24 | { 25 | public Task> ReconcileAsync(V1OperatorIntegrationTestEntity entity, CancellationToken cancellationToken) 26 | => Task.FromResult(ReconciliationResult.Success(entity)); 27 | 28 | public Task> DeletedAsync(V1OperatorIntegrationTestEntity entity, CancellationToken cancellationToken) 29 | => Task.FromResult(ReconciliationResult.Success(entity)); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/Operator.CSharp/.template.config/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/template", 3 | "author": "Christoph Bühler", 4 | "classifications": [ 5 | "Kubernetes", 6 | "Operator" 7 | ], 8 | "identity": "KubeOps.Templates.Operator.CSharp", 9 | "groupIdentity": "KubeOps.Templates.Operator", 10 | "name": "Kubernetes Operator", 11 | "description": "Create a Kubernetes operator with demo implementation", 12 | "shortName": "operator", 13 | "tags": { 14 | "language": "C#", 15 | "type": "project" 16 | }, 17 | "sourceName": "GeneratedOperatorProject", 18 | "defaultName": "Operator", 19 | "preferNameDirectory": true, 20 | "postActions": [ 21 | { 22 | "actionId": "B17581D1-C5C9-4489-8F0A-004BE667B814", 23 | "description": "Add KubeOps Operator reference", 24 | "continueOnError": false, 25 | "manualInstructions": [ 26 | { 27 | "text": "Add the KubeOps package to your project via nuget" 28 | } 29 | ], 30 | "args": { 31 | "referenceType": "package", 32 | "reference": "KubeOps.Operator", 33 | "projectFileExtensions": ".csproj" 34 | } 35 | }, 36 | { 37 | "actionId": "B17581D1-C5C9-4489-8F0A-004BE667B814", 38 | "description": "Add KubeOps Generator reference", 39 | "continueOnError": false, 40 | "manualInstructions": [ 41 | { 42 | "text": "Add the KubeOps Generator package to your project via nuget" 43 | } 44 | ], 45 | "args": { 46 | "referenceType": "package", 47 | "reference": "KubeOps.Generator", 48 | "projectFileExtensions": ".csproj" 49 | } 50 | } 51 | ] 52 | } 53 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/Rbac/GenericRbacAttribute.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | namespace KubeOps.Abstractions.Rbac; 6 | 7 | /// 8 | /// 9 | /// Generic attribute to define rbac needs for the operator. 10 | /// This needs get generated into rbac - yaml style resources 11 | /// for installation on a cluster. 12 | /// 13 | /// The attribute essentially defines the role definition of kubernetes. 14 | /// 15 | [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] 16 | public class GenericRbacAttribute : RbacAttribute 17 | { 18 | /// 19 | /// List of groups. 20 | /// 21 | /// Yaml example: 22 | /// "apiGroups: ...". 23 | /// 24 | /// 25 | public string[] Groups { get; init; } = Array.Empty(); 26 | 27 | /// 28 | /// List of resources. 29 | /// 30 | /// Yaml example: 31 | /// "resources: ["pods"]". 32 | /// 33 | /// 34 | public string[] Resources { get; init; } = Array.Empty(); 35 | 36 | /// 37 | /// List of urls. 38 | /// 39 | public string[] Urls { get; init; } = Array.Empty(); 40 | 41 | /// 42 | /// Flags ("list") of allowed verbs. 43 | /// 44 | /// Yaml example: 45 | /// "verbs: ["get", "list", "watch"]". 46 | /// 47 | /// 48 | public RbacVerb Verbs { get; init; } 49 | } 50 | -------------------------------------------------------------------------------- /docs/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import useDocusaurusContext from "@docusaurus/useDocusaurusContext"; 2 | import Layout from "@theme/Layout"; 3 | import clsx from "clsx"; 4 | 5 | import Heading from "@theme/Heading"; 6 | import styles from "./index.module.css"; 7 | 8 | import LogoUrl from "@site/static/img/logo.png"; 9 | 10 | function HomepageHeader() { 11 | const { siteConfig } = useDocusaurusContext(); 12 | return ( 13 |
14 |
15 | 16 | {siteConfig.title} 17 | 18 |

{siteConfig.tagline}

19 |
20 | KubeOps Logo 21 |
22 |
23 |
24 | ); 25 | } 26 | 27 | export default function Home() { 28 | const { siteConfig } = useDocusaurusContext(); 29 | return ( 30 | 31 | 32 |
33 |
34 |

Join our Discord

35 | 43 |
44 |
45 |
46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/KubeOps.Templates/Templates/EmptyOperator.CSharp/.template.config/template.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/template", 3 | "author": "Christoph Bühler", 4 | "classifications": ["Kubernetes", "Operator", "Empty"], 5 | "identity": "KubeOps.Templates.EmptyOperator.CSharp", 6 | "groupIdentity": "KubeOps.Templates.EmptyOperator", 7 | "name": "Kubernetes Empty Operator", 8 | "description": "Create an empty Kubernetes operator with the KubeOps SDK. The default empty operator does not contain any web capabilities.", 9 | "shortName": "operator-empty", 10 | "tags": { 11 | "language": "C#", 12 | "type": "project" 13 | }, 14 | "sourceName": "GeneratedOperatorProject", 15 | "defaultName": "Operator", 16 | "preferNameDirectory": true, 17 | "postActions": [ 18 | { 19 | "actionId": "B17581D1-C5C9-4489-8F0A-004BE667B814", 20 | "description": "Add KubeOps Operator SDK reference", 21 | "continueOnError": false, 22 | "manualInstructions": [ 23 | { 24 | "text": "Add the KubeOps Operator package to your project via nuget" 25 | } 26 | ], 27 | "args": { 28 | "referenceType": "package", 29 | "reference": "KubeOps.Operator", 30 | "projectFileExtensions": ".csproj" 31 | } 32 | }, 33 | { 34 | "actionId": "B17581D1-C5C9-4489-8F0A-004BE667B814", 35 | "description": "Add KubeOps Generator reference", 36 | "continueOnError": false, 37 | "manualInstructions": [ 38 | { 39 | "text": "Add the KubeOps Generator package to your project via nuget" 40 | } 41 | ], 42 | "args": { 43 | "referenceType": "package", 44 | "reference": "KubeOps.Generator", 45 | "projectFileExtensions": ".csproj" 46 | } 47 | } 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/KubeOps.Transpiler.Test/Entities.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using FluentAssertions; 6 | 7 | using k8s.Models; 8 | 9 | using KubeOps.Abstractions.Entities; 10 | using KubeOps.Abstractions.Entities.Attributes; 11 | 12 | namespace KubeOps.Transpiler.Test; 13 | 14 | public class EntitiesTest 15 | { 16 | [Theory] 17 | [InlineData(typeof(NamespaceEntity), "Namespaced", "namespaceentity", "namespaceentities", "testing.dev/v1")] 18 | [InlineData(typeof(ClusterEntity), "Cluster", "clusterentity", "clusterentities", "testing.dev/v1")] 19 | public void Should_Correctly_Parse_Metadata( 20 | Type entityType, 21 | string expectedScope, 22 | string singular, 23 | string plural, 24 | string groupVersion) 25 | { 26 | var (meta, scope) = Entities.ToEntityMetadata(entityType); 27 | 28 | scope.Should().Be(expectedScope); 29 | meta.SingularName.Should().Be(singular); 30 | meta.PluralName.Should().Be(plural); 31 | meta.GroupWithVersion.Should().Be(groupVersion); 32 | } 33 | 34 | #region Test Entity Classes 35 | 36 | [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "NamespaceEntity", 37 | PluralName = "NamespaceEntities")] 38 | public class NamespaceEntity : CustomKubernetesEntity; 39 | 40 | [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "ClusterEntity", 41 | PluralName = "clusterentities")] 42 | [EntityScope(EntityScope.Cluster)] 43 | public class ClusterEntity : CustomKubernetesEntity; 44 | 45 | #endregion 46 | } 47 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/Finalizer/KubeOpsEventFinalizerAttacherFactory.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | using KubeOps.Abstractions.Reconciliation.Finalizer; 9 | using KubeOps.KubernetesClient; 10 | 11 | using Microsoft.Extensions.Logging; 12 | 13 | namespace KubeOps.Operator.Finalizer; 14 | 15 | internal sealed class KubeOpsEventFinalizerAttacherFactory(ILoggerFactory loggerFactory, IKubernetesClient client) 16 | : IEventFinalizerAttacherFactory 17 | { 18 | public EntityFinalizerAttacher Create(string identifier) 19 | where TImplementation : class, IEntityFinalizer 20 | where TEntity : IKubernetesObject 21 | { 22 | var logger = loggerFactory.CreateLogger>(); 23 | return (entity, token) => 24 | { 25 | logger.LogTrace( 26 | """Try to add finalizer "{Finalizer}" on entity "{Kind}/{Name}".""", 27 | identifier, 28 | entity.Kind, 29 | entity.Name()); 30 | 31 | if (!entity.AddFinalizer(identifier)) 32 | { 33 | return Task.FromResult(entity); 34 | } 35 | 36 | logger.LogInformation( 37 | """Added finalizer "{Finalizer}" on entity "{Kind}/{Name}".""", 38 | identifier, 39 | entity.Kind, 40 | entity.Name()); 41 | return client.UpdateAsync(entity, token); 42 | }; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/KubeOps.Abstractions/README.md: -------------------------------------------------------------------------------- 1 | # KubeOps Abstractions 2 | 3 | [![Nuget](https://img.shields.io/nuget/v/KubeOps.Abstractions?label=NuGet&logo=nuget)](https://www.nuget.org/packages/KubeOps.Abstractions) 4 | [![NuGet Pre-Release](https://img.shields.io/nuget/vpre/KubeOps.Abstractions?label=NuGet&logo=nuget)](https://www.nuget.org/packages/KubeOps.Abstractions) 5 | 6 | This package provides the fundamental building blocks for the KubeOps SDK. It defines the core interfaces, abstract base classes, and [.NET attributes](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/reflection-and-attributes/) used throughout the operator framework. 7 | 8 | Think of this package as the contract definition for key KubeOps components. 9 | 10 | ## General Description 11 | 12 | The `KubeOps.Abstractions` package is designed to provide a robust foundation for building Kubernetes operators using the KubeOps SDK. It offers a set of abstractions that allow developers to define custom resources, implement controllers, manage finalizers, and handle webhooks. By leveraging these abstractions, developers can create scalable and maintainable operator applications that interact seamlessly with Kubernetes. 13 | 14 | ## When to Use 15 | 16 | Most projects building a KubeOps operator will reference the main `KubeOps.Operator` package, which includes this abstractions package as a dependency. 17 | 18 | By depending only on this package, you can define your entities and interfaces without pulling in the full operator runtime or Kubernetes client logic, promoting better separation of concerns. This is primarily useful if you want to: 19 | 20 | - Define your CRD entity classes in a separate library, shared between your operator and potentially other applications. 21 | - Build tools that need to understand KubeOps entity definitions without needing the operator runtime. 22 | -------------------------------------------------------------------------------- /test/KubeOps.Transpiler.Test/Entities.Mlc.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using FluentAssertions; 6 | 7 | using k8s.Models; 8 | 9 | using KubeOps.Abstractions.Entities; 10 | using KubeOps.Abstractions.Entities.Attributes; 11 | 12 | namespace KubeOps.Transpiler.Test; 13 | 14 | public class EntitiesMlcTest(MlcProvider provider) : TranspilerTestBase(provider) 15 | { 16 | [Theory] 17 | [InlineData(typeof(NamespaceEntity), "Namespaced", "namespaceentity", "namespaceentities", "testing.dev/v1")] 18 | [InlineData(typeof(ClusterEntity), "Cluster", "clusterentity", "clusterentities", "testing.dev/v1")] 19 | public void Should_Correctly_Parse_Metadata( 20 | Type entityType, 21 | string expectedScope, 22 | string singular, 23 | string plural, 24 | string groupVersion) 25 | { 26 | var (meta, scope) = _mlc.ToEntityMetadata(entityType); 27 | 28 | scope.Should().Be(expectedScope); 29 | meta.SingularName.Should().Be(singular); 30 | meta.PluralName.Should().Be(plural); 31 | meta.GroupWithVersion.Should().Be(groupVersion); 32 | } 33 | 34 | #region Test Entity Classes 35 | 36 | [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "NamespaceEntity", 37 | PluralName = "NamespaceEntities")] 38 | public class NamespaceEntity : CustomKubernetesEntity; 39 | 40 | [KubernetesEntity(Group = "testing.dev", ApiVersion = "v1", Kind = "ClusterEntity", 41 | PluralName = "clusterentities")] 42 | [EntityScope(EntityScope.Cluster)] 43 | public class ClusterEntity : CustomKubernetesEntity; 44 | 45 | #endregion 46 | } 47 | -------------------------------------------------------------------------------- /.release/release.base.mjs: -------------------------------------------------------------------------------- 1 | export default { 2 | debug: true, 3 | plugins: [ 4 | [ 5 | "@semantic-release/commit-analyzer", 6 | { 7 | preset: "angular", 8 | releaseRules: [{ type: "deps", scope: "core", release: "patch" }], 9 | }, 10 | ], 11 | [ 12 | "@semantic-release/release-notes-generator", 13 | { 14 | preset: "conventionalcommits", 15 | presetConfig: { 16 | types: [ 17 | { type: "feat", section: "Features" }, 18 | { type: "fix", section: "Bug Fixes" }, 19 | { type: "perf", section: "Performance Improvements" }, 20 | { type: "deps", section: "Dependencies" }, 21 | { type: "revert", section: "Reverts" }, 22 | { type: "docs", section: "Documentation" }, 23 | { type: "style", section: "Styles", hidden: true }, 24 | { type: "chore", section: "Miscellaneous Chores", hidden: true }, 25 | { type: "refactor", section: "Code Refactoring", hidden: true }, 26 | { type: "test", section: "Tests", hidden: true }, 27 | { type: "build", section: "Build System", hidden: true }, 28 | { type: "ci", section: "Continuous Integration", hidden: true }, 29 | ], 30 | }, 31 | }, 32 | ], 33 | [ 34 | "semantic-release-net", 35 | { 36 | sources: [ 37 | { 38 | url: "https://api.nuget.org/v3/index.json", 39 | apiKeyEnvVar: "NUGET_API_KEY", 40 | }, 41 | ], 42 | }, 43 | ], 44 | [ 45 | "@semantic-release/github", 46 | { 47 | successComment: false, 48 | failComment: false, 49 | releasedLabels: false, 50 | assets: [ 51 | { 52 | path: "src/**/bin/Release/**/*.nupkg", 53 | }, 54 | ], 55 | }, 56 | ], 57 | ], 58 | }; 59 | -------------------------------------------------------------------------------- /test/KubeOps.Transpiler.Test/MlcProvider.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | using System.Runtime.InteropServices; 7 | 8 | using Microsoft.Build.Locator; 9 | using Microsoft.CodeAnalysis.MSBuild; 10 | 11 | namespace KubeOps.Transpiler.Test; 12 | 13 | public class MlcProvider : IAsyncLifetime 14 | { 15 | static MlcProvider() 16 | { 17 | MSBuildLocator.RegisterDefaults(); 18 | } 19 | 20 | public MetadataLoadContext Mlc { get; private set; } = null!; 21 | 22 | public async Task InitializeAsync() 23 | { 24 | var assemblyConfigurationAttribute = 25 | typeof(MlcProvider).Assembly.GetCustomAttribute(); 26 | var buildConfigurationName = assemblyConfigurationAttribute?.Configuration ?? "Debug"; 27 | 28 | using var workspace = MSBuildWorkspace.Create(new Dictionary 29 | { 30 | { "Configuration", buildConfigurationName }, 31 | }); 32 | 33 | workspace.SkipUnrecognizedProjects = true; 34 | workspace.LoadMetadataForReferencedProjects = true; 35 | var project = await workspace.OpenProjectAsync("../../../KubeOps.Transpiler.Test.csproj"); 36 | 37 | Mlc = ContextCreator.Create(Directory 38 | .GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll") 39 | .Concat(Directory.GetFiles(Path.GetDirectoryName(project.OutputFilePath)!, "*.dll")) 40 | .Distinct(), coreAssemblyName: typeof(object).Assembly.GetName().Name); 41 | } 42 | 43 | public Task DisposeAsync() 44 | { 45 | Mlc.Dispose(); 46 | return Task.CompletedTask; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /test/KubeOps.Operator.Web.Test/Certificates/CertificateGenerator.Test.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Security.Cryptography.X509Certificates; 6 | 7 | using FluentAssertions; 8 | 9 | using KubeOps.Operator.Web.Certificates; 10 | 11 | namespace KubeOps.Operator.Web.Test.Certificates; 12 | 13 | public class CertificateGeneratorTest : IDisposable 14 | { 15 | private readonly CertificateGenerator _certificateGenerator = new(Environment.MachineName); 16 | 17 | [Fact] 18 | public void Root_Should_Be_Valid() 19 | { 20 | var (certificate, key) = _certificateGenerator.Root; 21 | 22 | certificate.Should().NotBeNull(); 23 | DateTime.Parse(certificate.GetEffectiveDateString()).Should().BeOnOrBefore(DateTime.UtcNow); 24 | certificate.Extensions.Any(e => e is X509BasicConstraintsExtension basic && basic.CertificateAuthority).Should().BeTrue(); 25 | certificate.HasPrivateKey.Should().BeTrue(); 26 | 27 | key.Should().NotBeNull(); 28 | } 29 | 30 | [Fact] 31 | public void Server_Should_Be_Valid() 32 | { 33 | var (certificate, key) = _certificateGenerator.Server; 34 | 35 | certificate.Should().NotBeNull(); 36 | DateTime.Parse(certificate.GetEffectiveDateString()).Should().BeOnOrBefore(DateTime.UtcNow); 37 | certificate.Extensions.Any(e => e is X509BasicConstraintsExtension basic && basic.CertificateAuthority).Should().BeFalse(); 38 | certificate.HasPrivateKey.Should().BeFalse(); 39 | 40 | key.Should().NotBeNull(); 41 | } 42 | 43 | public void Dispose() 44 | { 45 | _certificateGenerator.Dispose(); 46 | GC.SuppressFinalize(this); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/KubeOps.Transpiler/ContextCreator.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using System.Reflection; 6 | 7 | namespace KubeOps.Transpiler; 8 | 9 | /// 10 | /// Helper to create s. 11 | /// 12 | public static class ContextCreator 13 | { 14 | /// 15 | /// Create a new with the given 16 | /// and directly load an assembly into it. 17 | /// 18 | /// A list of paths. 19 | /// The byte array that contains the assembly to load. 20 | /// Optional core assembly name. 21 | /// The configured . 22 | public static MetadataLoadContext Create( 23 | IEnumerable assemblyPaths, 24 | byte[] assembly, 25 | string? coreAssemblyName = null) 26 | { 27 | var mlc = Create(assemblyPaths, coreAssemblyName); 28 | mlc.LoadFromByteArray(assembly); 29 | return mlc; 30 | } 31 | 32 | /// 33 | /// Create a new with the given . 34 | /// 35 | /// A list of paths. 36 | /// Optional core assembly name. 37 | /// The configured . 38 | public static MetadataLoadContext Create(IEnumerable assemblyPaths, string? coreAssemblyName = null) => 39 | new(new PathAssemblyResolver(assemblyPaths), coreAssemblyName: coreAssemblyName); 40 | } 41 | -------------------------------------------------------------------------------- /src/KubeOps.Operator/Queue/TimedEntityQueueExtensions.cs: -------------------------------------------------------------------------------- 1 | // Licensed to the .NET Foundation under one or more agreements. 2 | // The .NET Foundation licenses this file to you under the Apache 2.0 License. 3 | // See the LICENSE file in the project root for more information. 4 | 5 | using k8s; 6 | using k8s.Models; 7 | 8 | namespace KubeOps.Operator.Queue; 9 | 10 | /// 11 | /// Provides extension methods for the interface. 12 | /// 13 | public static class TimedEntityQueueExtensions 14 | { 15 | /// 16 | /// Retrieves a unique key for the specified Kubernetes entity. The key is constructed 17 | /// using the entity's namespace and name, if available. If the entity does not have 18 | /// a valid name, the method returns null. 19 | /// 20 | /// 21 | /// The type of the Kubernetes entity. Must implement . 22 | /// 23 | /// 24 | /// The timed entity queue from which the key should be derived. 25 | /// 26 | /// 27 | /// The Kubernetes entity for which the key will be retrieved. 28 | /// 29 | /// 30 | /// A string representing the unique key for the entity, or null if the entity does not have a valid name. 31 | /// 32 | // ReSharper disable once UnusedParameter.Global 33 | public static string? GetKey(this ITimedEntityQueue queue, TEntity entity) 34 | where TEntity : IKubernetesObject 35 | { 36 | if (string.IsNullOrWhiteSpace(entity.Name())) 37 | { 38 | return null; 39 | } 40 | 41 | return string.IsNullOrWhiteSpace(entity.Namespace()) 42 | ? entity.Name() 43 | : $"{entity.Namespace()}/{entity.Name()}"; 44 | } 45 | } 46 | --------------------------------------------------------------------------------