diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5006fb4de9..dced018208 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -23,7 +23,7 @@ jobs: httpclient-tests: strategy: matrix: - httpclient: [ 'vertx', 'jdk', 'jetty' ] + httpclient: [ 'vertx-5', 'jdk', 'jetty' ] uses: ./.github/workflows/integration-tests.yml with: java-version: 25 diff --git a/docs/content/en/docs/migration/v5-7-migration.md b/docs/content/en/docs/migration/v5-7-migration.md new file mode 100644 index 0000000000..a5fb257fd3 --- /dev/null +++ b/docs/content/en/docs/migration/v5-7-migration.md @@ -0,0 +1,43 @@ +--- +title: Migrating from v5.6 to v5.7 +description: Migrating from v5.6 to v5.7 +--- + +## Fabric8 Kubernetes Client upgraded to v8.0.0 + +v5.7 uses version 8.0.0 of the +[Fabric8 Kubernetes Client](https://github.com/fabric8io/kubernetes-client). Please consult the +client's own release notes and migration guide for the complete list of changes. The ones that are +most likely to affect operators built with JOSDK are described below. + +### Jackson 3 + +The client moved from Jackson 2 to Jackson 3. The packages changed from `com.fasterxml.jackson` to +`tools.jackson` (the annotations from `jackson-annotations` are the exception and remain in +`com.fasterxml.jackson.annotation`). If your operator uses Jackson types directly, for example +`JsonNode` obtained from the client's `KubernetesSerialization`, update the imports and the +dependencies accordingly: + +```java +// before +import com.fasterxml.jackson.databind.JsonNode; + +// after +import tools.jackson.databind.JsonNode; +``` + +Other notable Jackson 3 differences: + +- `JsonProcessingException` was replaced by `JacksonException`, which is unchecked. +- `JsonNode.fields()` was replaced by `properties()`, `fieldNames()` by `propertyNames()` and + `asText()` by `asString()`. +- `ObjectMapper` instances are created using builders, for example `YAMLMapper.builder().build()`. + +### Ambiguous `withShardSelector(null)` + +`Filterable` now has an additional `withShardSelector(ShardSelector)` overload next to +`withShardSelector(String)`. Calls passing a bare `null` no longer compile, cast the argument: + +```java +client.pods().withShardSelector((String) null); +``` diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml index e0c6031670..ffb717f4cf 100644 --- a/operator-framework-core/pom.xml +++ b/operator-framework-core/pom.xml @@ -46,7 +46,7 @@ io.fabric8 - kubernetes-httpclient-okhttp + kubernetes-httpclient-vertx-5 @@ -54,6 +54,11 @@ org.slf4j slf4j-api + + io.fabric8 + kubernetes-httpclient-${fabric8-httpclient-impl.name} + test + org.apache.logging.log4j log4j-slf4j2-impl diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java index 35f46e5019..336af9e2a8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java @@ -147,11 +147,11 @@ public R clone(R object) { * you do not need to worry about setting the namespace information in most cases). * *

Previous versions of this class provided direct access to the serialization mechanism (via - * {@link com.fasterxml.jackson.databind.ObjectMapper}) or the client's configuration. This was - * somewhat confusing, in particular with respect to changes made in the Fabric8 client - * serialization architecture made in 6.7. The proper way to configure these aspects is now to - * configure the Kubernetes client accordingly and the SDK will extract the information it needs - * from this instance. The recommended way to do so is to create your operator with {@link + * {@link tools.jackson.databind.ObjectMapper}) or the client's configuration. This was somewhat + * confusing, in particular with respect to changes made in the Fabric8 client serialization + * architecture made in 6.7. The proper way to configure these aspects is now to configure the + * Kubernetes client accordingly and the SDK will extract the information it needs from this + * instance. The recommended way to do so is to create your operator with {@link * io.javaoperatorsdk.operator.Operator#Operator(Consumer)}, passing your custom instance with * {@link ConfigurationServiceOverrider#withKubernetesClient(KubernetesClient)}. * diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java index 23fb29151f..d1d220bf67 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcher.java @@ -25,7 +25,7 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.Matcher; -import com.fasterxml.jackson.databind.JsonNode; +import tools.jackson.databind.JsonNode; public class GenericKubernetesResourceMatcher { @@ -262,7 +262,7 @@ private static boolean match(boolean equality, JsonNode diff, final List if (!ignoreList.isEmpty()) { return nodeIsChildOf(diff, ignoreList); } - return ADD.equals(diff.get(OP).asText()); + return ADD.equals(diff.get(OP).asString()); } static boolean nodeIsChildOf(JsonNode n, List prefixes) { @@ -276,6 +276,6 @@ static boolean nodeIsChildOf(JsonNode n, List prefixes) { } static String getPath(JsonNode n) { - return n.get(PATH).asText(); + return n.get(PATH).asString(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/MatcherUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/MatcherUtils.java index 413a1c4acc..9bd6994fb2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/MatcherUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/matcher/MatcherUtils.java @@ -20,10 +20,10 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.matcher.Matcher; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.node.JsonNodeFactory; -import com.fasterxml.jackson.databind.node.NullNode; -import com.fasterxml.jackson.databind.node.ObjectNode; +import tools.jackson.databind.JsonNode; +import tools.jackson.databind.node.JsonNodeFactory; +import tools.jackson.databind.node.NullNode; +import tools.jackson.databind.node.ObjectNode; /** Shared helpers for the client-side patch based {@link Matcher} implementations. */ final class MatcherUtils { @@ -66,9 +66,7 @@ private static JsonNode applyMergePatch(JsonNode target, JsonNode patch) { } var targetObject = target.isObject() ? (ObjectNode) target : JsonNodeFactory.instance.objectNode(); - var fields = patch.fields(); - while (fields.hasNext()) { - var entry = fields.next(); + for (var entry : patch.properties()) { if (entry.getValue().isNull()) { targetObject.remove(entry.getKey()); } else { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceNamespaceChangeTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceNamespaceChangeTest.java index a1ee558036..12bb8cf8d1 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceNamespaceChangeTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceNamespaceChangeTest.java @@ -110,7 +110,7 @@ private void stubInformerStore() { .resources(Deployment.class) .inNamespace("ns1") .withLabelSelector((String) null) - .withShardSelector(null) + .withShardSelector((String) null) .runnableInformer(0); when(informer.getStore()).thenReturn(mock(Cache.class)); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourcePoolIdentityTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourcePoolIdentityTest.java index a2d0759fbc..5ee911de1f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourcePoolIdentityTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourcePoolIdentityTest.java @@ -70,7 +70,7 @@ void setup() { .resources(Deployment.class) .inNamespace(NAMESPACE) .withLabelSelector((String) null) - .withShardSelector(null) + .withShardSelector((String) null) .runnableInformer(0); when(informer.getStore()).thenReturn(mock(Cache.class)); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceStartFailureTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceStartFailureTest.java index a4d78ace74..17ff0f38b6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceStartFailureTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSourceStartFailureTest.java @@ -88,7 +88,7 @@ private void stubInformerStore() { .resources(Deployment.class) .inNamespace("ns1") .withLabelSelector((String) null) - .withShardSelector(null) + .withShardSelector((String) null) .runnableInformer(0); when(informer.getStore()).thenReturn(mock(Cache.class)); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/provider/YamlConfigProvider.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/provider/YamlConfigProvider.java index fc80e46a43..872391fd45 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/provider/YamlConfigProvider.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/loader/provider/YamlConfigProvider.java @@ -28,8 +28,10 @@ import io.javaoperatorsdk.operator.config.loader.ConfigProvider; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import tools.jackson.core.JacksonException; +import tools.jackson.core.JsonParser; +import tools.jackson.databind.ObjectMapper; +import tools.jackson.dataformat.yaml.YAMLMapper; /** * A {@link ConfigProvider} that resolves configuration values from a YAML file. @@ -44,7 +46,7 @@ public class YamlConfigProvider implements ConfigProvider { private static final Logger log = LoggerFactory.getLogger(YamlConfigProvider.class); - private static final ObjectMapper MAPPER = new ObjectMapper(new YAMLFactory()); + private static final ObjectMapper MAPPER = YAMLMapper.builder().build(); private final Map data; @@ -101,7 +103,7 @@ private static Map load(Path path) { } try (InputStream in = Files.newInputStream(path); - com.fasterxml.jackson.core.JsonParser parser = MAPPER.getFactory().createParser(in)) { + JsonParser parser = MAPPER.createParser(in)) { if (parser.nextToken() == null) { log.debug( "YAML file contains no mappings (possibly empty or comments only). " @@ -113,6 +115,8 @@ private static Map load(Path path) { return result != null ? result : Map.of(); } catch (IOException e) { throw new UncheckedIOException("Failed to load config YAML from " + path, e); + } catch (JacksonException e) { + throw new UncheckedIOException("Failed to load config YAML from " + path, new IOException(e)); } } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java index b2606ad740..c4f2ca8df3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDIT.java @@ -32,7 +32,7 @@ import io.javaoperatorsdk.operator.api.config.InformerStoppedHandler; import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; -import com.fasterxml.jackson.core.JsonProcessingException; +import tools.jackson.core.JacksonException; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; @@ -93,7 +93,7 @@ public void onStop(SharedIndexInformer informer, Throwable ex) { final var object = unmarshal.get("object"); resourceCreateAsVersion = acceptOnlyIfUnsetOrEqualToAlreadySet( - resourceCreateAsVersion, object.get("apiVersion").asText()); + resourceCreateAsVersion, object.get("apiVersion").asString()); // extract the asked resource version failedResourceVersion = acceptOnlyIfUnsetOrEqualToAlreadySet( @@ -103,8 +103,8 @@ public void onStop(SharedIndexInformer informer, Throwable ex) { .get("managedFields") .get(0) .get("apiVersion") - .asText()); - } catch (JsonProcessingException e) { + .asString()); + } catch (JacksonException e) { throw new RuntimeException(e); } }); diff --git a/pom.xml b/pom.xml index c88d2f4588..61a906f3f4 100644 --- a/pom.xml +++ b/pom.xml @@ -71,7 +71,7 @@ https://sonarcloud.io jdk 6.1.3 - 7.9.0 + 8.0.0 2.0.18 2.26.1 5.23.0 @@ -236,6 +236,11 @@ kubernetes-httpclient-vertx ${fabric8-client.version} + + io.fabric8 + kubernetes-httpclient-vertx-5 + ${fabric8-client.version} + io.fabric8 kubernetes-httpclient-jdk