|
| 1 | +# Kubernetes Webhooks Framework Documentation |
| 2 | + |
| 3 | +## Intro |
| 4 | + |
| 5 | +Kubernetes Webhooks Framework makes it simple to |
| 6 | +implementing [admission controllers](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/) |
| 7 | +and [conversion hooks](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#webhook-conversion) |
| 8 | +for Kubernetes in Java. |
| 9 | + |
| 10 | +## Getting Started |
| 11 | + |
| 12 | +Before you start make sure you understand these concepts in Kubernetes, reading the docs mentioned above. |
| 13 | + |
| 14 | +### Samples |
| 15 | + |
| 16 | +There are samples both |
| 17 | +for [Spring Boot](https://github.com/java-operator-sdk/admission-controller-framework/tree/main/samples/spring-boot) |
| 18 | +and [Quarkus](https://github.com/java-operator-sdk/kubernetes-webooks-framework/tree/main/samples/quarkus), both of them |
| 19 | +implements the same logic. Both sync and async APIs |
| 20 | +are showcased. This documentation describes the Quarkus version, however Spring Boot version is almost identical. |
| 21 | + |
| 22 | +There are two endpoint, one |
| 23 | +for [admission controllers](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/samples/quarkus/src/main/java/io/javaoperatorsdk/webhook/sample/admission/AdmissionEndpoint.java) |
| 24 | +(a validating and a mutating) and one for the |
| 25 | +sample [conversion hook](https://github.com/java-operator-sdk/admission-controller-framework/blob/76fd9c4b9fef6738310a7dd97b159c3668ced9f1/samples/quarkus/src/main/java/io/javaoperatorsdk/webhook/sample/conversion/ConversionEndpoint.java) |
| 26 | +. |
| 27 | + |
| 28 | +Starting from those endpoints, it should be trivial to understand how the underlying logic works. |
| 29 | + |
| 30 | +### End-To-End Tests |
| 31 | + |
| 32 | +The goal of the end-to-end tests is to test the framework in a production like environment, but also works as an |
| 33 | +executable documentation to guide developers how to deploy and configure the target service. |
| 34 | + |
| 35 | +The [end-to-end tests](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/samples/quarkus/src/test/java/io/javaoperatorsdk/webhook/sample/QuarkusWebhooksE2E.java) |
| 36 | +are using the [same test cases](https://github.com/java-operator-sdk/admission-controller-framework/blob/de2b0da7f592aa166049ef7ad65bcebf8d45e358/samples/commons/src/test/java/io/javaoperatorsdk/webhook/sample/EndToEndTestBase.java) and are based on the samples (See Spring Boot |
| 37 | +version [here](https://github.com/java-operator-sdk/admission-controller-framework/blob/e2637a90152bebfca2983ba17268c1f7ec7e9602/samples/spring-boot/src/test/java/io/javaoperatorsdk/webhook/sample/springboot/SpringBootWebhooksE2E.java)). |
| 38 | +To see how those tests are executed during a pull request check |
| 39 | +the [related GitHub Action](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/.github/workflows/pr.yml#L66-L66) |
| 40 | + |
| 41 | +The samples are first built, then [deployed](https://github.com/java-operator-sdk/admission-controller-framework/blob/6959de06c0de1c8e04fc241ea5f4196219002e53/samples/quarkus/src/test/java/io/javaoperatorsdk/webhook/sample/QuarkusWebhooksE2E.java#L23-L30) to a local Kubernetes cluster (in our case minikube is used). |
| 42 | +For Quarkus most of the deployment artifacts is generated using extensions (works similarly for Spring Boot, |
| 43 | +using [dekorate](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/samples/spring-boot/pom.xml#L52-L63)): |
| 44 | + |
| 45 | +```xml |
| 46 | + |
| 47 | +<dependency> |
| 48 | + <groupId>io.quarkus</groupId> |
| 49 | + <artifactId>quarkus-kubernetes</artifactId> |
| 50 | +</dependency> |
| 51 | +<dependency> |
| 52 | + <groupId>io.quarkiverse.certmanager</groupId> |
| 53 | + <artifactId>quarkus-certmanager</artifactId> |
| 54 | +</dependency> |
| 55 | +``` |
| 56 | + |
| 57 | +Only additional resources used for admission hooks, are present in |
| 58 | +the [k8s](https://github.com/java-operator-sdk/admission-controller-framework/tree/main/samples/quarkus/k8s) |
| 59 | +directory. These are the configuration files to configure the admission hooks. For example the configuration for |
| 60 | +validation look like: |
| 61 | + |
| 62 | +```yaml |
| 63 | + |
| 64 | +apiVersion: admissionregistration.k8s.io/v1 |
| 65 | +kind: ValidatingWebhookConfiguration |
| 66 | +metadata: |
| 67 | + name: "validating.quarkus.example.com" |
| 68 | + annotations: |
| 69 | + # Cert Manager annotation to inject CA |
| 70 | + cert-manager.io/inject-ca-from: default/quarkus-sample |
| 71 | +webhooks: |
| 72 | + - name: "validating.quarkus.example.com" |
| 73 | + rules: |
| 74 | + - apiGroups: [ "networking.k8s.io" ] |
| 75 | + apiVersions: [ "v1" ] |
| 76 | + operations: [ "*" ] |
| 77 | + resources: [ "ingresses" ] |
| 78 | + scope: "Namespaced" |
| 79 | + clientConfig: |
| 80 | + service: |
| 81 | + namespace: "default" |
| 82 | + name: "quarkus-sample" |
| 83 | + path: "/validate" |
| 84 | + port: 443 |
| 85 | + admissionReviewVersions: [ "v1" ] |
| 86 | + sideEffects: None |
| 87 | + timeoutSeconds: 5 |
| 88 | +``` |
| 89 | +
|
| 90 | +The conversion hook is configured within the `CustomResourceDefinition`, see |
| 91 | +related [Kubernetes docs](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#configure-customresourcedefinition-to-use-conversion-webhooks). |
| 92 | +Since this is [not yet supported](https://github.com/fabric8io/kubernetes-client/issues/4692) by the fabric8 client CRD |
| 93 | +generator, the hook definition is |
| 94 | +[added before](https://github.com/java-operator-sdk/admission-controller-framework/blob/e2637a90152bebfca2983ba17268c1f7ec7e9602/samples/commons/src/test/java/io/javaoperatorsdk/webhook/sample/EndToEndTestBase.java#L97-L124) CRD is applied. |
| 95 | + |
| 96 | +Note |
| 97 | +that [cert manager](https://github.com/java-operator-sdk/admission-controller-framework/blob/e2637a90152bebfca2983ba17268c1f7ec7e9602/samples/quarkus/src/test/java/io/javaoperatorsdk/webhook/sample/QuarkusWebhooksE2E.java#L19-L23) |
| 98 | +is used to generate certificates for the application and for configurations. |
| 99 | + |
| 100 | +## Admission Controllers API |
| 101 | + |
| 102 | +There are two types of admission controllers: validation and mutation. Both should be extremely simple to use. |
| 103 | + |
| 104 | +To create the related controller simply pass a lambda to the constructor |
| 105 | +of [AdmissionController](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/admission/AdmissionController.java#L104-L104) |
| 106 | +that validates the resource. (See also |
| 107 | +the [async version](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/admission/AsyncAdmissionController.java#L104-L104) |
| 108 | +of admission controller implementation.) |
| 109 | + |
| 110 | +```java |
| 111 | +new AdmissionController<>((resource,operation) -> { |
| 112 | + if(resource.getMetadata().getLabels() == null || resource.getMetadata().getLabels().get(APP_NAME_LABEL_KEY) == null){ |
| 113 | + throw new NotAllowedException("Missing label: "+APP_NAME_LABEL_KEY); |
| 114 | + } |
| 115 | +}); |
| 116 | +``` |
| 117 | + |
| 118 | +respectively mutates it: |
| 119 | + |
| 120 | +```java |
| 121 | +new AdmissionController<>((resource,operation) -> { |
| 122 | + if(resource.getMetadata().getLabels() == null){ |
| 123 | + resource.getMetadata().setLabels(new HashMap<>()); |
| 124 | + } |
| 125 | + resource.getMetadata().getLabels().putIfAbsent(APP_NAME_LABEL_KEY,"mutation-test"); |
| 126 | + return resource; |
| 127 | +}); |
| 128 | +``` |
| 129 | + |
| 130 | +All changes made to the resource are reflected in the response created by the admission controller. |
| 131 | + |
| 132 | +## Conversion Hooks API |
| 133 | + |
| 134 | +[ConversionController](https://github.com/java-operator-sdk/admission-controller-framework/blob/core/src/main/java/io/javaoperatorsdk/webhook/conversion/ConversionController.java) ( |
| 135 | +respectively [AsyncConversionController](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/conversion/AsyncConversionController.java)) |
| 136 | +handles conversion between different versions of custom resources |
| 137 | +using [mappers](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/conversion/Mapper.java) |
| 138 | +( respectively [async mappers](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/conversion/AsyncMapper.java)). |
| 139 | + |
| 140 | +The mapper interface is simple: |
| 141 | + |
| 142 | +```java |
| 143 | +public interface Mapper<R extends HasMetadata, HUB> { |
| 144 | +
|
| 145 | + HUB toHub(R resource); |
| 146 | +
|
| 147 | + R fromHub(HUB hub); |
| 148 | +
|
| 149 | +} |
| 150 | +``` |
| 151 | + |
| 152 | +It handles mapping to and from a Hub. Hub is an intermediate representation in a conversion. Thus, the conversion |
| 153 | +steps from v1 to v2 happens in the following way: v1 -> HUB -> v2. Using the provided v1 and v2 mappers implementations. |
| 154 | +Having this approach is useful mainly in case there are more than two version of resources on the cluster, so there is |
| 155 | +no need for a mapper for every combination. See also related docs in |
| 156 | +[Kubebuilder](https://book.kubebuilder.io/multiversion-tutorial/conversion-concepts.html). |
0 commit comments