From 5e40e851e714c4e1a07b0002048a6a5d8dc957af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20B=C3=BChler?= Date: Fri, 18 Sep 2020 10:30:02 +0200 Subject: [PATCH] docs: add finalized documentation to ghpages --- .github/workflows/github-pages.yml | 8 +- docs/docs/commands.md | 37 ++++++++- docs/docs/controller.md | 18 ++++- docs/docs/entities.md | 119 ++++++++++++++++++++++++++++- docs/docs/features.md | 3 + docs/docs/finalizer.md | 41 ++++++++-- docs/docs/getting_started.md | 9 ++- docs/docs/ms_build.md | 13 +++- docs/docs/settings.md | 14 +++- docs/docs/testing.md | 80 ++++++++++++++++++- docs/docs/utilities.md | 40 +++++++++- 11 files changed, 351 insertions(+), 31 deletions(-) diff --git a/.github/workflows/github-pages.yml b/.github/workflows/github-pages.yml index 4fd73149..d9d526f0 100644 --- a/.github/workflows/github-pages.yml +++ b/.github/workflows/github-pages.yml @@ -19,10 +19,9 @@ jobs: uses: crazy-max/ghaction-chocolatey@v1 with: args: install docfx -y - - name: Restore Solution - run: dotnet restore - name: Build Solution - run: dotnet build + run: .\build.ps1 --target Compile --no-logo + shell: powershell - name: Build Documentation run: docfx docs/docfx.json - name: Upload docs artifact @@ -34,6 +33,7 @@ jobs: runs-on: ubuntu-latest needs: build-gh-pages steps: + - uses: actions/checkout@v2 - name: Download docs artifact id: download uses: actions/download-artifact@v2 @@ -45,4 +45,4 @@ jobs: with: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} BRANCH: gh-pages - FOLDER: ${{ steps.download.outputs.download-path }} + FOLDER: public diff --git a/docs/docs/commands.md b/docs/docs/commands.md index 6c116c8c..d8dc9b1d 100644 --- a/docs/docs/commands.md +++ b/docs/docs/commands.md @@ -1,12 +1,40 @@ -TODO +# Commands -## Commands +For convenience, there are multiple commands added to the executable +of your operator (through the KubeOps package). -There are default command line commands which you can see when using +Those are implemented with the [CommandLineUtils by NateMcMaster](https://github.com/natemcmaster/CommandLineUtils). + +you can see the help and overview when using `dotnet run -- --help` in your project. As you can see, you can run multiple commands. Some of them do install / uninstall your crds in your currently selected kubernetes cluster or can generate code. +> [!NOTE] +> For the normal "dotnet run" command exists a `--namespaced` +> option that starts the operator in namespaced mode. This means +> that only the given namespace is watched for entities. + +## Available Commands + +Here is a brief overview over the available commands: + +> [!NOTE] +> all commands assume either the compiled dll or you using +> `dotnet run -- ` as prepended command. + +- ` ` (empty): runs the operator (normal `dotnet run`) +- `version`: prints the version information for the actual connected kubernetes cluster +- `install`: install the CRDs for the solution into the cluster +- `uninstall`: uninstall the CRDs for the solution from the cluster +- `generator`: entry command for generator commands (i.e. has subcommands), all commands + output their result to the stdout or the given output path + - `crd`: Generate the CRDs + - `docker`: Generate the dockerfile + - `installer`: Generate the installer files (i.e. kustomization yaml) for the operator + - `operator`: Generate the deployment for the operator + - `rbac`: Generate the needed rbac roles / role bindings for the operator + ## Code Generation When installing this package, you also reference the default Targets and Props @@ -22,3 +50,6 @@ The dockerfile will not be overwritten in case you have custom elements in there The installation files won't be overwritten as well if you have custom elements in there. To regenerate those two elements, just delete them and rebuild your code. + +For the customization on those build targets, header over to the +[ms build extensions](./ms_build.md). diff --git a/docs/docs/controller.md b/docs/docs/controller.md index 9642faf1..46743b1f 100644 --- a/docs/docs/controller.md +++ b/docs/docs/controller.md @@ -7,6 +7,10 @@ resources on kubernetes and queueing of the events. When you want to create a controller for your (or any) entity, read the following instructions. +When you have controllers, don't forget to register them with + +to the DI system. + ## Controller instance After you created a custom entity (like described in [Entities](./entities.md)) @@ -14,7 +18,8 @@ or you want to reconcile a given entity (from the `k8s.Models` namespace, e.g. `V1ConfigMap`) you need to create a controller class as you would do for a MVC or API controller in asp.net. -Make sure you have the correct baseclass (`ResourceControllerBase`) +Make sure you have the correct baseclass +() inherited. ```csharp @@ -32,6 +37,17 @@ public class FooCtrl: ResourceControllerBase } ``` +## Namespaced controller + +To limit the operator (and therefore all controllers) to a specific +namespace in kubernetes, use the @"KubeOps.Operator.OperatorSettings" +and configure a specific namespace when it is predefined. + +To use namespacing dynamically, run the application with the `--namespaced` +option. When given a name (i.e. `--namespaced=foobar`) the defined +namespace is used. When only the option is provided (i.e. `--namespaced`) +then the actual namespace is used that the pod runs in. + ## RBAC The entity rbac attribute does provide the information needed about diff --git a/docs/docs/entities.md b/docs/docs/entities.md index 958fed3f..8fbfb652 100644 --- a/docs/docs/entities.md +++ b/docs/docs/entities.md @@ -84,9 +84,10 @@ You can use the various validator attributes to customize your crd: - `Required`: The field is listed in the required fields - `PreserveUnknownFields`: Set the `X-Kubernetes-Preserve-Unknown-Fields` to `true` -_NOTE on `Description`_: if your project generates the XML documentation files -for the result, the crd generator also searches for those files and a possible -`` tag in the xml documentation. The attribute will take precedence though. +> [!NOTE] +> For `Description`: if your project generates the XML documentation files +> for the result, the crd generator also searches for those files and a possible +> `` tag in the xml documentation. The attribute will take precedence though. ```csharp public class MappingSpec @@ -101,4 +102,114 @@ In the example above, the text of the attribute will be used. ## Multi-Version Entities -TODO +You can manage multiple versions of a CRD. To do this, you can +specify multiple classes as the "same" entity, but with different +versions. + +To mark multiple entity classes as the same, use exactly the same +`Kind`, `Group` and `PluralName` and differ in the `ApiVersion` +field. + +### Version priority + +Sorting of the versions - and therefore determine which version should be +the `storage version` if no attribute is provided - is done by the kubernetes +rules of version sorting: + +Priority is as follows: + +1. General Availablility (i.e. `V1Foobar`, `V2Foobar`) +2. Beta Versions (i.e. `V11Beta13Foobar`, `V2Beta1Foobar`) +3. Alpha Versions (i.e. `V16Alpha13Foobar`, `V2Alpha10Foobar`) + +The parsed version numbers are sorted by the highest first, this leads +to the following version priority: + +``` +- v10 +- v2 +- v1 +- v11beta2 +- v10beta3 +- v3beta1 +- v12alpha1 +- v11alpha2 +``` + +This can also be seen over at the +[kubernetes documentation](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#version-priority). + +### Storage Version + +To determine the storage version (of which one, and exactly one must exist) +the system uses the previously mentioned version priority to sort the versions +and picking the first one. To overwrite this behaviour, use the +. + +> [!WARNING] +> when multiple +> are used, the system will thrown an error. + +To overwrite a version, annotate the entity class with the attribute. + +### Example + +#### Normal multiversion entity + +Note that the `Kind` + +```csharp +[KubernetesEntity( + ApiVersion = "v1", + Kind = "VersionedEntity", + Group = "kubeops.test.dev", + PluralName = "versionedentities")] +public class V1VersionedEntity : CustomKubernetesEntity +{ +} + +[KubernetesEntity( + ApiVersion = "v1beta1", + Kind = "VersionedEntity", + Group = "kubeops.test.dev", + PluralName = "versionedentities")] +public class V1Beta1VersionedEntity : CustomKubernetesEntity +{ +} + +[KubernetesEntity( + ApiVersion = "v1alpha1", + Kind = "VersionedEntity", + Group = "kubeops.test.dev", + PluralName = "versionedentities")] +public class V1Alpha1VersionedEntity : CustomKubernetesEntity +{ +} +``` + +The resulting storage version would be `V1VersionedEntity`. + +#### Overwritten storage version multiversion entity + +```csharp +[KubernetesEntity( + ApiVersion = "v1", + Kind = "AttributeVersionedEntity", + Group = "kubeops.test.dev", + PluralName = "attributeversionedentities")] +[StorageVersion] +public class V1AttributeVersionedEntity : CustomKubernetesEntity +{ +} + +[KubernetesEntity( + ApiVersion = "v2", + Kind = "AttributeVersionedEntity", + Group = "kubeops.test.dev", + PluralName = "attributeversionedentities")] +public class V2AttributeVersionedEntity : CustomKubernetesEntity +{ +} +``` + +The resulting storage version would be `V1AttributeVersionedEntity`. diff --git a/docs/docs/features.md b/docs/docs/features.md index b3d346d9..f6936559 100644 --- a/docs/docs/features.md +++ b/docs/docs/features.md @@ -5,6 +5,9 @@ at the sections. As of now, the operator sdk supports - roughly - the following features: +- Entities + - Normal entities + - Multi version entities - Controller with all operations of an entity - Created - Updated diff --git a/docs/docs/finalizer.md b/docs/docs/finalizer.md index 4b6cff5e..77a87995 100644 --- a/docs/docs/finalizer.md +++ b/docs/docs/finalizer.md @@ -1,6 +1,19 @@ -TODO +# Finalizers -### Write Finalizers +A finalizer is a special type of software that can asynchronously +cleanup stuff for an entity that is beeing deleted. + +A finalizer is registered as an identifier in the kubernetes +resource (i.e. in the yaml / json structure) and an object +wont be removed from the api until all finalizers are removed. + +If you write finalizer, don't forget to register them with + +to the DI system. + +## Write a finalizer + +Use the correct base class (). A finalizer can be as simple as: @@ -10,16 +23,28 @@ public class FooFinalizer : ResourceFinalizerBase public override async Task Finalize(Foo resource) { // do something with the resource. + // like deleting a database. } } ``` -And can be added to a resource with: +When the finalizer successfully completed his job, it is automatically removed +from the finalizers list of the resource. The finalizers are registered +as transient resources in DI. + +## Register a finalizer + +To attach a finalizer for a resource, call the + +method in the controller during reconciliation. ```csharp -// in a resource controller -await AttachFinalizer(resource); +public class TestController : ResourceControllerBase +{ + protected override async Task Created(V1TestEntity resource) + { + await AttachFinalizer(resource); + return await base.Created(resource); + } +} ``` - -After the finalizer ran successfully on a resource, it is unregistered -on the resource. diff --git a/docs/docs/getting_started.md b/docs/docs/getting_started.md index 56d1aa1b..2944e531 100644 --- a/docs/docs/getting_started.md +++ b/docs/docs/getting_started.md @@ -62,10 +62,11 @@ public static class Program This adds the default commands (like run and the code generators) to your app. The commands are documentated under the [CLI Commands](./commands.md) section. -_NOTE_: Technically you don't need to replace the function, -but if you don't, the other commands like yaml generation -are not available to your application. Also namespaceing is not -possible via run flag. +> [!NOTE] +> Technically you don't need to replace the function, +> but if you don't, the other commands like yaml generation +> are not available to your application. Also namespaceing is not +> possible via run flag. ### Add to Startup.cs diff --git a/docs/docs/ms_build.md b/docs/docs/ms_build.md index 7521a2fe..330806cb 100644 --- a/docs/docs/ms_build.md +++ b/docs/docs/ms_build.md @@ -1,4 +1,15 @@ -TODO +# MS Build extensions + +This project extends the default build process of dotnet with some +code generation targets before the build. + +You'll find those two files here: + +- [KubeOps.props](https://github.com/buehler/dotnet-operator-sdk/blob/master/src/KubeOps/Build/KubeOps.props): defines the build properties +- [KubeOps.targets](https://github.com/buehler/dotnet-operator-sdk/blob/master/src/KubeOps/Build/KubeOps.targets): defines the additional build targets + +They can be configured with the prop settings described below. +The props file just defines the defaults. ## Prop Settings diff --git a/docs/docs/settings.md b/docs/docs/settings.md index 1333ed77..f0dfaae7 100644 --- a/docs/docs/settings.md +++ b/docs/docs/settings.md @@ -1 +1,13 @@ -TODO +# Settings + +To configure the operator, use the @"KubeOps.Operator.OperatorSettings" +that are configurable during the generic host extension method +@"KubeOps.Operator.ServiceCollectionExtensions.AddKubernetesOperator(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{KubeOps.Operator.OperatorSettings})" +or @"KubeOps.Operator.ServiceCollectionExtensions.AddKubernetesOperator(Microsoft.Extensions.DependencyInjection.IServiceCollection,KubeOps.Operator.OperatorSettings)". + +You can configure things like the name of the operator, +if it should use namespacing, and other elements like the +urls of metrics and lease durations for the leader election. + +Please look at the documention over at: @"KubeOps.Operator.OperatorSettings" +to know what the fields mean. diff --git a/docs/docs/testing.md b/docs/docs/testing.md index c3fce093..9a037657 100644 --- a/docs/docs/testing.md +++ b/docs/docs/testing.md @@ -1,2 +1,78 @@ -testing shit -TODO +# Test your operator + +The `KubeOps.Testing` package provides you with some tools +to aid with the testing of the custom operator. + +To have an example for integration testing a custom operator +have a look at the test code in the repository: +[Integration Tests](https://github.com/buehler/dotnet-operator-sdk/tree/master/tests/KubeOps.TestOperator) + +> [!NOTE] +> The tools provided aid with Integration testing. +> For normal unit testing, you can just mock all the things. + +The main entry point for testing your custom operator is the +@"KubeOps.Testing.KubernetesOperatorFactory`1". It is ment to be +injected into your test. + +> [!NOTE] +> The following documentation assumes you are using xUnit as a +> testing framework. The techniques used should be present +> with other testing frameworks as well. + +The following steps are needed for integration testing the controller: + +- Create a `TestStartup.cs` file (or any other name you want) +- Inject the @"KubeOps.Testing.KubernetesOperatorFactory`1" +- Use the mocked client / queues to test your operator + +## Test Startup + +This file is very similar to a "normal" `Startup.cs` file of an +asp.net application. Either you subclass it and replace your test mocked +services, or you create a new one. + +[!code-csharp[TestStartup.cs](../../tests/KubeOps.TestOperator.Test/TestStartup.cs?highlight=20-21)] + +## Mocked elements + +The main part of this operator factory does mock the used kubernetes client +and the resource event queues. + +Both mocked elements can be retrieved via the operator factory with: + +- @"KubeOps.Testing.KubernetesOperatorFactory`1.GetMockedEventQueue``1" +- @"KubeOps.Testing.KubernetesOperatorFactory`1.MockedKubernetesClient" + +### Mocked event queue + +The event queue does not use a channel to read and write event but directly fire +the given events. + +Also, the @"KubeOps.Operator.Queue.IResourceEventQueue`1.Enqueue(`0,System.Nullable{System.TimeSpan})" +method does not actually enqueue anything but just adds the resource to a list +of resources. + +### Mocked IKubernetesClient + +The client is essentially an empty implementation of the interface. +All methods do return `null` or the object that was passed +into the mocked class. There are five different object +references that can be set to return certain results upon calling the client. + +[!code-csharp[TestStartup.cs](../../src/KubeOps.Testing/MockKubernetesClient.cs?range=13-14,17-25,88&highlight=3-11)] + +> [!WARNING] +> The mocked client is injected as singleton, this means +> the used "result" references can vary. Be aware of that +> and don't run your tests in parallel if you rely +> on those results. + +## Writting a test + +Now with all parts in place, we can write a test. +You probably need to set the solution relative content root for +asp.net testing. But then you can run the factory +and create a test. + +[!code-csharp[TestStartup.cs](../../tests/KubeOps.TestOperator.Test/TestController.Test.cs?range=10-31,84&highlight=7,13)] diff --git a/docs/docs/utilities.md b/docs/docs/utilities.md index 086080d6..6b3774b4 100644 --- a/docs/docs/utilities.md +++ b/docs/docs/utilities.md @@ -1,3 +1,37 @@ -healthchekcs -metrics -TODO +# Operator utils + +There are two basic utilities that should be mentioned: + +- Healthchecks +- Metrics + +## Healthchecks + +This is a basic feature of asp.net. The operator sdk makes use of +it and splits them up into `Liveness` and `Readiness` checks. + +With the appropriate methods, you can add an `IHealthCheck` interface +to either `/ready`, `/health` or both. + +The urls can be configured via @"KubeOps.Operator.OperatorSettings". + +- @"KubeOps.Operator.Builder.IOperatorBuilder.AddHealthCheck``1(System.String)": + adds a healthcheck to ready and liveness +- @"KubeOps.Operator.Builder.IOperatorBuilder.AddLivenessCheck``1(System.String)": + adds a healthcheck to the liveness route only +- @"KubeOps.Operator.Builder.IOperatorBuilder.AddReadinessCheck``1(System.String)": + adds a healthcheck to the readiness route only + +## Metrics + +By default, the operator lists some interessting metrics on the +`/metrics` route. The url can be configured via @"KubeOps.Operator.OperatorSettings". + +There are many counters on how many elements have been reconciled, if the +controllers and queues are up and how many elements are in timed requeue state. + +Please have a look at the metrics if you run your operator locally or online +to see which metrics are available. + +Of course you can also have a look at the used metrics classes to see the +implementation: [Metrics Implementations](https://github.com/buehler/dotnet-operator-sdk/tree/master/src/KubeOps/Operator/DevOps).