Skip to content

Commit af160de

Browse files
authored
feat: handling custom resources with SPI from fabric8 client (#87)
1 parent 5671e07 commit af160de

File tree

7 files changed

+32
-79
lines changed

7 files changed

+32
-79
lines changed

core/src/main/java/io/javaoperatorsdk/webhook/conversion/AsyncConversionController.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,6 @@ public void registerMapper(AsyncMapper<?, ?> mapper) {
2929
throw new IllegalStateException(MAPPER_ALREADY_REGISTERED_FOR_VERSION_MESSAGE + version);
3030
}
3131
mappers.put(version, mapper);
32-
Utils.registerCustomKind(
33-
Utils.getFirstTypeArgumentFromInterface(mapper.getClass(), AsyncMapper.class));
3432
}
3533

3634
@Override

core/src/main/java/io/javaoperatorsdk/webhook/conversion/ConversionController.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@ public void registerMapper(Mapper<?, ?> mapper) {
2626
throw new IllegalStateException(MAPPER_ALREADY_REGISTERED_FOR_VERSION_MESSAGE + version);
2727
}
2828
mappers.put(version, mapper);
29-
Utils.registerCustomKind(
30-
Utils.getFirstTypeArgumentFromInterface(mapper.getClass(), Mapper.class));
3129
}
3230

3331
@Override
Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,5 @@
11
package io.javaoperatorsdk.webhook.conversion;
22

3-
import java.lang.reflect.ParameterizedType;
4-
import java.util.Arrays;
5-
6-
import io.fabric8.kubernetes.api.model.HasMetadata;
7-
import io.fabric8.kubernetes.internal.KubernetesDeserializer;
8-
93
public class Utils {
104

115
private Utils() {}
@@ -19,24 +13,4 @@ public static String versionOfApiVersion(String apiVersion) {
1913
return apiVersion.substring(lastDelimiter + 1);
2014
}
2115

22-
public static void registerCustomKind(Class<? extends HasMetadata> clazz) {
23-
KubernetesDeserializer.registerCustomKind(HasMetadata.getApiVersion(clazz),
24-
HasMetadata.getKind(clazz), clazz);
25-
}
26-
27-
@SuppressWarnings("unchecked")
28-
public static Class<? extends HasMetadata> getFirstTypeArgumentFromInterface(Class<?> clazz,
29-
Class<?> expectedImplementedInterface) {
30-
return (Class<? extends HasMetadata>) Arrays.stream(clazz.getGenericInterfaces())
31-
.filter(type -> type.getTypeName().startsWith(expectedImplementedInterface.getName())
32-
&& type instanceof ParameterizedType)
33-
.map(ParameterizedType.class::cast)
34-
.findFirst()
35-
.map(t -> (Class<?>) t.getActualTypeArguments()[0])
36-
.orElseThrow(() -> new RuntimeException(
37-
"Couldn't retrieve generic parameter type from " + clazz.getSimpleName()
38-
+ " because it doesn't implement "
39-
+ expectedImplementedInterface.getSimpleName()
40-
+ " directly"));
41-
}
4216
}

core/src/test/java/io/javaoperatorsdk/webhook/conversion/UtilsTest.java

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@
22

33
import org.junit.jupiter.api.Test;
44

5-
import io.javaoperatorsdk.webhook.conversion.crd.CustomResourceV1;
6-
import io.javaoperatorsdk.webhook.conversion.crd.CustomResourceV2;
7-
import io.javaoperatorsdk.webhook.conversion.mapper.AsyncV1Mapper;
8-
import io.javaoperatorsdk.webhook.conversion.mapper.AsyncV2Mapper;
9-
import io.javaoperatorsdk.webhook.conversion.mapper.CustomResourceV1Mapper;
10-
import io.javaoperatorsdk.webhook.conversion.mapper.CustomResourceV2Mapper;
11-
125
import static org.assertj.core.api.Assertions.assertThat;
136
import static org.junit.jupiter.api.Assertions.*;
147

@@ -19,17 +12,4 @@ void getsVersionFromApiVersion() {
1912
assertThat(Utils.versionOfApiVersion("apiextensions.k8s.io/v1")).isEqualTo("v1");
2013
assertThat(Utils.versionOfApiVersion("extensions/v1beta1")).isEqualTo("v1beta1");
2114
}
22-
23-
@Test
24-
void getMapperResourceType() {
25-
assertThat(Utils.getFirstTypeArgumentFromInterface(CustomResourceV1Mapper.class, Mapper.class))
26-
.isEqualTo(CustomResourceV1.class);
27-
assertThat(Utils.getFirstTypeArgumentFromInterface(CustomResourceV2Mapper.class, Mapper.class))
28-
.isEqualTo(CustomResourceV2.class);
29-
assertThat(Utils.getFirstTypeArgumentFromInterface(AsyncV1Mapper.class, AsyncMapper.class))
30-
.isEqualTo(CustomResourceV1.class);
31-
assertThat(Utils.getFirstTypeArgumentFromInterface(AsyncV2Mapper.class, AsyncMapper.class))
32-
.isEqualTo(CustomResourceV2.class);
33-
}
34-
3515
}

docs/README.md

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,16 @@ The goal of the end-to-end tests is to test the framework in a production-like e
3333
executable documentation to guide developers how to deploy and configure the target service.
3434

3535
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
36+
are using
37+
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)
38+
and are based on the samples (See Spring Boot
3739
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)).
3840
To see how those tests are executed during a pull request check
3941
the [related GitHub Action](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/.github/workflows/pr.yml#L66-L66)
4042

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).
43+
The samples are first built,
44+
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)
45+
to a local Kubernetes cluster (in our case minikube is used).
4246
For Quarkus most of the deployment artifacts is generated using extensions (works similarly for Spring Boot,
4347
using [dekorate](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/samples/spring-boot/pom.xml#L52-L63)):
4448

@@ -91,7 +95,8 @@ The conversion hook is configured within the `CustomResourceDefinition`, see
9195
related [Kubernetes docs](https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definition-versioning/#configure-customresourcedefinition-to-use-conversion-webhooks).
9296
Since this is [not yet supported](https://github.com/fabric8io/kubernetes-client/issues/4692) by the fabric8 client CRD
9397
generator, the hook definition is
94-
[added before](https://github.com/java-operator-sdk/admission-controller-framework/blob/57a889ea1c0cb42b5a703a3cc8053f51c3982f74/samples/commons/src/main/java/io/javaoperatorsdk/webhook/sample/commons/Utils.java#L83-L110) CRD is applied.
98+
[added before](https://github.com/java-operator-sdk/admission-controller-framework/blob/57a889ea1c0cb42b5a703a3cc8053f51c3982f74/samples/commons/src/main/java/io/javaoperatorsdk/webhook/sample/commons/Utils.java#L83-L110)
99+
CRD is applied.
95100

96101
Note
97102
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)
@@ -135,7 +140,8 @@ All changes made to the resource are reflected in the response created by the ad
135140
respectively [AsyncConversionController](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/conversion/AsyncConversionController.java))
136141
handles conversion between different versions of custom resources
137142
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)).
143+
(
144+
respectively [async mappers](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/core/src/main/java/io/javaoperatorsdk/webhook/conversion/AsyncMapper.java)).
139145

140146
The mapper interface is simple:
141147

@@ -149,8 +155,26 @@ public interface Mapper<R extends HasMetadata, HUB> {
149155
}
150156
```
151157

152-
It handles mapping to and from a Hub. Hub is an intermediate representation in a conversion. Thus, the conversion
158+
It handles mapping to and from a Hub. Hub is an intermediate representation in a conversion. Thus, the conversion
153159
steps from v1 to v2 happen 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 versions of resources on the cluster, so there is
160+
Having this approach is useful mainly in case there are more than two versions of resources on the cluster, so there is
155161
no need for a mapper for every combination. See also related docs in
156162
[Kubebuilder](https://book.kubebuilder.io/multiversion-tutorial/conversion-concepts.html).
163+
164+
## Using Custom Resources in the API
165+
166+
In order to properly register your own custom types (custom resources) for deserialization it needs to be added to
167+
`META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource` file.
168+
169+
See in the [samples](https://github.com/java-operator-sdk/admission-controller-framework/blob/main/samples/commons/src/main/resources/META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource#L164-L164).
170+
171+
Related release not in fabric8 client:
172+
```text
173+
Fix #4579: the implicit registration of resource and list types that happens when using the resource(class) methods
174+
has been removed. This makes the behavior of the client more predictable as that was an undocumented side-effect.
175+
If you expect to see instances of a custom type from an untyped api call - typically KubernetesClient.load,
176+
KubernetesClient.resourceList, KubernetesClient.resource(InputStream|String), then you must either create a
177+
META-INF/services/io.fabric8.kubernetes.api.model.KubernetesResource file (see above #3923), or make calls to
178+
KubernetesDeserializer.registerCustomKind - however since KubernetesDeserializer is an internal class that mechanism
179+
is not preferred.
180+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
io.javaoperatorsdk.webhook.sample.commons.customresource.MultiVersionCustomResource
2+
io.javaoperatorsdk.webhook.sample.commons.customresource.MultiVersionCustomResourceV2

samples/quarkus/src/main/java/io/javaoperatorsdk/webhook/sample/conversion/CustomResourceDeserializationCustomizer.java

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)