diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97555ae1b5..b81fa41b6d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,73 +5,35 @@ env: on: workflow_call: - inputs: - kube-version: - type: string - required: true jobs: - set_up_kubernetes: - name: Set up Kubernetes ${{ inputs.kube-version }} - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - - name: Set up Minikube - uses: manusa/actions-setup-minikube@v2.13.1 - with: - minikube version: 'v1.34.0' - kubernetes version: '${{ inputs.kube-version }}' - driver: 'docker' - github token: ${{ secrets.GITHUB_TOKEN }} - - - name: Save minikube directory - id: minikube - run: | - echo "minikube-dir=$MINIKUBE_HOME" >> $GITHUB_OUTPUT - - - name: Upload minikube - uses: actions/upload-artifact@v4 - with: - name: minikube-${{ inputs.kube-version }} - path: ${{ steps.minikube.outputs.minikube-dir }} - include-hidden-files: true - integration_tests: - name: "JDK: ${{ matrix.java }}, IT category: ${{ matrix.it-category }}" - needs: set_up_kubernetes strategy: matrix: - java: [ 17, 21 ] - it-category: [ 'baseapi', 'dependent', 'workflow' ] + java: [ 17, 21, 24 ] + kubernetes: [ '1.30.12', '1.31.8', '1.32.4','1.33.0' ] uses: ./.github/workflows/integration-tests.yml with: - kube-version: ${{ inputs.kube-version }} java-version: ${{ matrix.java }} - it-category: ${{ matrix.it-category }} + kube-version: ${{ matrix.kubernetes }} - http_client_tests: - name: "JDK: ${{ matrix.java }}, IT category: ${{ matrix.it-category }}, HTTP client: ${{ matrix.httpclient }}" - needs: set_up_kubernetes + httpclient-tests: strategy: matrix: - java: [ 17, 21 ] - it-category: [ 'baseapi' ] httpclient: [ 'vertx', 'jdk', 'jetty' ] uses: ./.github/workflows/integration-tests.yml with: - kube-version: ${{ inputs.kube-version }} - java-version: ${{ matrix.java }} - it-category: ${{ matrix.it-category }} + java-version: 24 + kube-version: '1.32.0' http-client: ${{ matrix.httpclient }} + experimental: true special_integration_tests: name: "Special integration tests (${{ matrix.java }})" - needs: set_up_kubernetes runs-on: ubuntu-latest strategy: matrix: - java: [ 17, 21 ] + java: [ 17, 21, 24 ] steps: - uses: actions/checkout@v4 - name: Set up Java and Maven @@ -81,13 +43,3 @@ jobs: java-version: ${{ matrix.java }} - name: Run Special Integration Tests run: ./mvnw ${MAVEN_ARGS} -B package -P minimal-watch-timeout-dependent-it --file pom.xml - - delete_kubernetes: - needs: [ integration_tests, http_client_tests, special_integration_tests ] - if: always() - name: Delete Kubernetes ${{ inputs.kube-version }} artifact - runs-on: ubuntu-latest - steps: - - uses: geekyeggo/delete-artifact@v5 - with: - name: minikube-${{ inputs.kube-version }} diff --git a/.github/workflows/e2e-test.yml b/.github/workflows/e2e-test.yml index 4ac58ab062..e06b427960 100644 --- a/.github/workflows/e2e-test.yml +++ b/.github/workflows/e2e-test.yml @@ -30,10 +30,10 @@ jobs: uses: actions/checkout@v4 - name: Setup Minikube-Kubernetes - uses: manusa/actions-setup-minikube@v2.13.1 + uses: manusa/actions-setup-minikube@v2.14.0 with: minikube version: v1.34.0 - kubernetes version: v1.32.0 + kubernetes version: v1.33.0 github token: ${{ secrets.GITHUB_TOKEN }} driver: docker diff --git a/.github/workflows/fabric8-next-version-schedule.yml b/.github/workflows/fabric8-next-version-schedule.yml deleted file mode 100644 index 64d2042135..0000000000 --- a/.github/workflows/fabric8-next-version-schedule.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Fabric8 Client Snapshot Build - -env: - MAVEN_ARGS: -V -ntp -e - -concurrency: - group: ${{ github.ref }}-${{ github.workflow }} - cancel-in-progress: true -on: - schedule: - # Run on end of the day - - cron: '0 0 * * *' - workflow_dispatch: -jobs: - check_format_and_unit_tests: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - ref: 'fabric8-next-version' - - name: Set up Java and Maven - uses: actions/setup-java@v4 - with: - distribution: temurin - java-version: 17 - - name: Run unit tests - run: ./mvnw ${MAVEN_ARGS} clean install --file pom.xml - - build: - uses: ./.github/workflows/build.yml \ No newline at end of file diff --git a/.github/workflows/hugo.yaml b/.github/workflows/hugo.yaml index 1d91c85ef3..511f10a8e0 100644 --- a/.github/workflows/hugo.yaml +++ b/.github/workflows/hugo.yaml @@ -32,7 +32,7 @@ jobs: build: runs-on: ubuntu-latest env: - HUGO_VERSION: 0.125.4 + HUGO_VERSION: 0.145.0 steps: - name: Install Hugo CLI run: | diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index dff59bfe7c..75a6093371 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -21,20 +21,14 @@ on: type: string required: false default: '' - it-category: - type: string - required: false - default: '' jobs: integration_tests: - name: "Experimental: ${{ inputs.experimental }}, Checkout ref: ${{ inputs.checkout-ref }}" + name: Integration tests (${{ inputs.java-version }}, ${{ inputs.kube-version }}, ${{ inputs.http-client }}) runs-on: ubuntu-latest continue-on-error: ${{ inputs.experimental }} timeout-minutes: 40 steps: - - name: Output test information - run: echo "Running ITs with ${{ inputs.http-client }}, ${{ inputs.kube-version }}, ${{ inputs.java-version }}" - uses: actions/checkout@v4 with: ref: ${{ inputs.checkout-ref }} @@ -44,18 +38,13 @@ jobs: distribution: temurin java-version: ${{ inputs.java-version }} cache: 'maven' - - name: Download minikube artifact for Kubernetes ${{ inputs.kube-version }} - uses: actions/download-artifact@v4 + - name: Set up Minikube + uses: manusa/actions-setup-minikube@v2.14.0 with: - name: minikube-${{inputs.kube-version}} - path: minikube - - name: Start minikube with Kubernetes ${{ inputs.kube-version }} - run: | - # wait for docker - docker version -f '{{.Server.Version}} - {{.Client.Version}}' - export MINIKUBE_HOME=$PWD/minikube - minikube start --driver=docker - kubectl version + minikube version: 'v1.34.0' + kubernetes version: '${{ inputs.kube-version }}' + driver: 'docker' + github token: ${{ secrets.GITHUB_TOKEN }} - name: "${{inputs.it-category}} integration tests (kube: ${{ inputs.kube-version }} / java: ${{ inputs.java-version }} / client: ${{ inputs.http-client }})" run: | if [ -z "${{inputs.it-category}}" ]; then diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index df0d2eee2b..27742bf7c2 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -22,7 +22,7 @@ jobs: uses: actions/setup-java@v4 with: distribution: temurin - java-version: 17 + java-version: 21 cache: 'maven' - name: Check code format run: | @@ -31,10 +31,4 @@ jobs: run: ./mvnw ${MAVEN_ARGS} clean install -Pno-apt --file pom.xml build: - name: Integration tests with Kubernetes ${{ matrix.kubernetes }} - strategy: - matrix: - kubernetes: [ 'v1.29.12','1.30.8', '1.31.4', '1.32.0' ] uses: ./.github/workflows/build.yml - with: - kube-version: ${{ matrix.kubernetes }} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 81afad78b8..797938cf3c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,5 @@ { - "java.format.settings.url": "contributing/eclipse-google-style.xml", + "java.format.settings.url": "https://raw.githubusercontent.com/google/styleguide/gh-pages/eclipse-java-google-style.xml", "java.completion.importOrder": [ "java", "javax", diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index facbbd7df9..c3a9e63545 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -62,10 +62,10 @@ The SDK modules and samples are formatted to follow the Java Google code style. On every `compile` the code gets formatted automatically, however, to make things simpler (i.e. avoid getting a PR rejected simply because of code style issues), you can import one of the following code style schemes based on the IDE you use: -- for *IntelliJ IDEA*: - - Install the [Eclipse Code Formatter plugin](https://github.com/krasa/EclipseCodeFormatter#instructions) - - Use [contributing/eclipse-google-style.xml](contributing/eclipse-google-style.xml) for the Eclipse formatter config file -- for *Eclipse* import [contributing/eclipse-google-style.xml](contributing/eclipse-google-style.xml) +- for *Intellij IDEA* + install [google-java-format](https://plugins.jetbrains.com/plugin/8527-google-java-format) plugin +- for *Eclipse* + follow [these intructions](https://github.com/google/google-java-format?tab=readme-ov-file#eclipse) ## Thanks diff --git a/bootstrapper-maven-plugin/pom.xml b/bootstrapper-maven-plugin/pom.xml index 535945760f..7487daa6fd 100644 --- a/bootstrapper-maven-plugin/pom.xml +++ b/bootstrapper-maven-plugin/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT bootstrapper @@ -58,7 +58,7 @@ commons-io commons-io - 2.18.0 + 2.19.0 com.github.spullara.mustache.java diff --git a/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/Bootstrapper.java b/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/Bootstrapper.java index ed12e7619d..7339d7e9aa 100644 --- a/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/Bootstrapper.java +++ b/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/Bootstrapper.java @@ -27,8 +27,7 @@ public class Bootstrapper { private static final Map TOP_LEVEL_STATIC_FILES = Map.of("_.gitignore", ".gitignore", "README.md", "README.md"); private static final List JAVA_FILES = - List.of("CustomResource.java", "Reconciler.java", - "Spec.java", "Status.java"); + List.of("CustomResource.java", "Reconciler.java", "Spec.java", "Status.java"); public void create(File targetDir, String groupId, String artifactId) { try { @@ -61,19 +60,24 @@ private void addJavaFiles(File projectDir, String groupId, String artifactId) { var targetTestDir = new File(projectDir, "src/test/java/" + packages); FileUtils.forceMkdir(targetDir); var classFileNamePrefix = artifactClassId(artifactId); - JAVA_FILES.forEach(f -> addTemplatedFile(projectDir, f, groupId, artifactId, targetDir, - classFileNamePrefix + f)); + JAVA_FILES.forEach( + f -> + addTemplatedFile( + projectDir, f, groupId, artifactId, targetDir, classFileNamePrefix + f)); addTemplatedFile(projectDir, "Runner.java", groupId, artifactId, targetDir, null); - addTemplatedFile(projectDir, "ConfigMapDependentResource.java", groupId, artifactId, - targetDir, null); - addTemplatedFile(projectDir, "ReconcilerIntegrationTest.java", groupId, + addTemplatedFile( + projectDir, "ConfigMapDependentResource.java", groupId, artifactId, targetDir, null); + addTemplatedFile( + projectDir, + "ReconcilerIntegrationTest.java", + groupId, artifactId, - targetTestDir, artifactClassId(artifactId) + "ReconcilerIntegrationTest.java"); + targetTestDir, + artifactClassId(artifactId) + "ReconcilerIntegrationTest.java"); } catch (IOException e) { throw new RuntimeException(e); } - } private void addTemplatedFiles(File projectDir, String groupId, String artifactId) { @@ -81,22 +85,37 @@ private void addTemplatedFiles(File projectDir, String groupId, String artifactI addTemplatedFile(projectDir, "k8s/test-resource.yaml", groupId, artifactId); } - private void addTemplatedFile(File projectDir, String fileName, String groupId, - String artifactId) { + private void addTemplatedFile( + File projectDir, String fileName, String groupId, String artifactId) { addTemplatedFile(projectDir, fileName, groupId, artifactId, null, null); } - private void addTemplatedFile(File projectDir, String fileName, String groupId, String artifactId, - File targetDir, String targetFileName) { + private void addTemplatedFile( + File projectDir, + String fileName, + String groupId, + String artifactId, + File targetDir, + String targetFileName) { try { - var values = Map.of("groupId", groupId, "artifactId", artifactId, - "artifactClassId", artifactClassId(artifactId), - "josdkVersion", Versions.JOSDK, - "fabric8Version", Versions.KUBERNETES_CLIENT); + var values = + Map.of( + "groupId", + groupId, + "artifactId", + artifactId, + "artifactClassId", + artifactClassId(artifactId), + "josdkVersion", + Versions.JOSDK, + "fabric8Version", + Versions.KUBERNETES_CLIENT); var mustache = mustacheFactory.compile("templates/" + fileName); - var targetFile = new File(targetDir == null ? projectDir : targetDir, - targetFileName == null ? fileName : targetFileName); + var targetFile = + new File( + targetDir == null ? projectDir : targetDir, + targetFileName == null ? fileName : targetFileName); FileUtils.forceMkdir(targetFile.getParentFile()); var writer = new FileWriter(targetFile); mustache.execute(writer, values); @@ -114,8 +133,8 @@ private void addStaticFile(File targetDir, String fileName, String targetFileNam addStaticFile(targetDir, fileName, targetFileName, null); } - private void addStaticFile(File targetDir, String fileName, String targetFilename, - String subDir) { + private void addStaticFile( + File targetDir, String fileName, String targetFilename, String subDir) { String sourcePath = subDir == null ? "/static/" : "/static/" + subDir; String path = sourcePath + fileName; try (var is = Bootstrapper.class.getResourceAsStream(path)) { @@ -127,14 +146,12 @@ private void addStaticFile(File targetDir, String fileName, String targetFilenam } catch (IOException e) { throw new RuntimeException("File path: " + path, e); } - } public static String artifactClassId(String artifactId) { var parts = artifactId.split("-"); - return Arrays.stream(parts).map(p -> p.substring(0, 1) - .toUpperCase() + p.substring(1)) + return Arrays.stream(parts) + .map(p -> p.substring(0, 1).toUpperCase() + p.substring(1)) .collect(Collectors.joining("")); } - } diff --git a/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/BootstrapperMojo.java b/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/BootstrapperMojo.java index 0d87a152e2..cb470f7e87 100644 --- a/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/BootstrapperMojo.java +++ b/bootstrapper-maven-plugin/src/main/java/io/javaoperatorsdk/boostrapper/BootstrapperMojo.java @@ -8,8 +8,7 @@ import org.apache.maven.plugins.annotations.Parameter; @Mojo(name = "create", requiresProject = false) -public class BootstrapperMojo - extends AbstractMojo { +public class BootstrapperMojo extends AbstractMojo { @Parameter(defaultValue = "${projectGroupId}") protected String projectGroupId; @@ -17,8 +16,7 @@ public class BootstrapperMojo @Parameter(defaultValue = "${projectArtifactId}") protected String projectArtifactId; - public void execute() - throws MojoExecutionException { + public void execute() throws MojoExecutionException { String userDir = System.getProperty("user.dir"); new Bootstrapper().create(new File(userDir), projectGroupId, projectArtifactId); } diff --git a/bootstrapper-maven-plugin/src/main/resources/templates/ConfigMapDependentResource.java b/bootstrapper-maven-plugin/src/main/resources/templates/ConfigMapDependentResource.java index a8d43c60db..59eae8b01c 100644 --- a/bootstrapper-maven-plugin/src/main/resources/templates/ConfigMapDependentResource.java +++ b/bootstrapper-maven-plugin/src/main/resources/templates/ConfigMapDependentResource.java @@ -17,10 +17,6 @@ public class ConfigMapDependentResource public static final String KEY = "key"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override protected ConfigMap desired({{artifactClassId}}CustomResource primary, Context<{{artifactClassId}}CustomResource> context) { diff --git a/bootstrapper-maven-plugin/src/test/java/io/javaoperatorsdk/bootstrapper/BootstrapperTest.java b/bootstrapper-maven-plugin/src/test/java/io/javaoperatorsdk/bootstrapper/BootstrapperTest.java index 0fde63059a..f7840c1585 100644 --- a/bootstrapper-maven-plugin/src/test/java/io/javaoperatorsdk/bootstrapper/BootstrapperTest.java +++ b/bootstrapper-maven-plugin/src/test/java/io/javaoperatorsdk/bootstrapper/BootstrapperTest.java @@ -30,9 +30,11 @@ void copiesFilesToTarget() { private void assertProjectCompiles() { try { - var process = Runtime.getRuntime() - .exec( - "mvn clean install -f target/test-project/pom.xml -DskipTests -Dspotless.apply.skip"); + var process = + Runtime.getRuntime() + .exec( + "mvn clean install -f target/test-project/pom.xml -DskipTests" + + " -Dspotless.apply.skip"); BufferedReader stdOut = new BufferedReader(new InputStreamReader(process.getInputStream())); diff --git a/caffeine-bounded-cache-support/pom.xml b/caffeine-bounded-cache-support/pom.xml index bd51e0af11..a421b3cd9f 100644 --- a/caffeine-bounded-cache-support/pom.xml +++ b/caffeine-bounded-cache-support/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT caffeine-bounded-cache-support diff --git a/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java index af4a17e2c2..c7ac96cb20 100644 --- a/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java +++ b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCache.java @@ -2,9 +2,7 @@ import com.github.benmanes.caffeine.cache.Cache; -/** - * Caffeine cache wrapper to be used in a {@link BoundedItemStore} - */ +/** Caffeine cache wrapper to be used in a {@link BoundedItemStore} */ public class CaffeineBoundedCache implements BoundedCache { private final Cache cache; diff --git a/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java index a58d58bd2a..89fbcef70f 100644 --- a/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java +++ b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java @@ -9,9 +9,9 @@ import com.github.benmanes.caffeine.cache.Caffeine; /** - * A factory for Caffeine-backed - * {@link BoundedItemStore}. The implementation uses a {@link CaffeineBoundedCache} to store - * resources and progressively evict them if they haven't been used in a while. The idea about + * A factory for Caffeine-backed {@link + * BoundedItemStore}. The implementation uses a {@link CaffeineBoundedCache} to store resources and + * progressively evict them if they haven't been used in a while. The idea about * CaffeinBoundedItemStore-s is that, caffeine will cache the resources which were recently used, * and will evict resource, which are not used for a while. This is ideal for startup performance * and efficiency when all resources should be cached to avoid undue load on the API server. This is @@ -20,11 +20,11 @@ * happen that some / many of these resources are then seldom or even reconciled anymore. In that * situation, large amounts of memory might be consumed to cache resources that are never used * again. - *

- * Note that if a resource is reconciled and is not present anymore in cache, it will transparently - * be fetched again from the API server. Similarly, since associated secondary resources are usually - * reconciled too, they might need to be fetched and populated to the cache, and will remain there - * for some time, for subsequent reconciliations. + * + *

Note that if a resource is reconciled and is not present anymore in cache, it will + * transparently be fetched again from the API server. Similarly, since associated secondary + * resources are usually reconciled too, they might need to be fetched and populated to the cache, + * and will remain there for some time, for subsequent reconciliations. */ public class CaffeineBoundedItemStores { @@ -39,11 +39,8 @@ private CaffeineBoundedItemStores() {} */ @SuppressWarnings("unused") public static BoundedItemStore boundedItemStore( - KubernetesClient client, Class rClass, - Duration accessExpireDuration) { - Cache cache = Caffeine.newBuilder() - .expireAfterAccess(accessExpireDuration) - .build(); + KubernetesClient client, Class rClass, Duration accessExpireDuration) { + Cache cache = Caffeine.newBuilder().expireAfterAccess(accessExpireDuration).build(); return boundedItemStore(client, rClass, cache); } @@ -51,5 +48,4 @@ public static BoundedItemStore boundedItemStore( KubernetesClient client, Class rClass, Cache cache) { return new BoundedItemStore<>(new CaffeineBoundedCache<>(cache), rClass, client); } - } diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java index 21adf81cc0..532e5237f8 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCacheTestBase.java @@ -17,7 +17,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -public abstract class BoundedCacheTestBase

> { +public abstract class BoundedCacheTestBase< + P extends CustomResource> { private static final Logger log = LoggerFactory.getLogger(BoundedCacheTestBase.class); @@ -42,34 +43,46 @@ void reconciliationWorksWithLimitedCache() { } private void assertConfigMapsDeleted() { - await().atMost(Duration.ofSeconds(30)) - .untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); - assertThat(cm).isNull(); - })); + await() + .atMost(Duration.ofSeconds(120)) + .untilAsserted( + () -> + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) + .forEach( + i -> { + var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + assertThat(cm).isNull(); + })); } private void deleteTestResources() { - IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension().get(customResourceClass(), RESOURCE_NAME_PREFIX + i); - var deleted = extension().delete(cm); - if (!deleted) { - log.warn("Custom resource might not be deleted: {}", cm); - } - }); + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) + .forEach( + i -> { + var cm = extension().get(customResourceClass(), RESOURCE_NAME_PREFIX + i); + var deleted = extension().delete(cm); + if (!deleted) { + log.warn("Custom resource might not be deleted: {}", cm); + } + }); } private void updateTestResources() { - IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); - cm.getData().put(DATA_KEY, UPDATED_PREFIX + i); - extension().replace(cm); - }); + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) + .forEach( + i -> { + var cm = extension().get(ConfigMap.class, RESOURCE_NAME_PREFIX + i); + cm.getData().put(DATA_KEY, UPDATED_PREFIX + i); + extension().replace(cm); + }); } void assertConfigMapData(String dataPrefix) { - await().untilAsserted(() -> IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) - .forEach(i -> assertConfigMap(i, dataPrefix))); + await() + .untilAsserted( + () -> + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) + .forEach(i -> assertConfigMap(i, dataPrefix))); } private void assertConfigMap(int i, String prefix) { @@ -79,9 +92,11 @@ private void assertConfigMap(int i, String prefix) { } private void createTestResources() { - IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST).forEach(i -> { - extension().create(createTestResource(i)); - }); + IntStream.range(0, NUMBER_OF_RESOURCE_TO_TEST) + .forEach( + i -> { + extension().create(createTestResource(i)); + }); } abstract P createTestResource(int index); @@ -89,7 +104,4 @@ private void createTestResources() { abstract Class

customResourceClass(); abstract LocallyRunOperatorExtension extension(); - - - } diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java index 252b20f4a4..0c16c1227b 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheClusterScopeIT.java @@ -19,21 +19,22 @@ public class CaffeineBoundedCacheClusterScopeIT @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() - .withReconciler(new BoundedCacheClusterScopeTestReconciler(), o -> { - o.withItemStore(boundedItemStore( - new KubernetesClientBuilder().build(), - BoundedCacheClusterScopeTestCustomResource.class, - Duration.ofMinutes(1), - 1)); - }) + .withReconciler( + new BoundedCacheClusterScopeTestReconciler(), + o -> { + o.withItemStore( + boundedItemStore( + new KubernetesClientBuilder().build(), + BoundedCacheClusterScopeTestCustomResource.class, + Duration.ofMinutes(1), + 1)); + }) .build(); @Override BoundedCacheClusterScopeTestCustomResource createTestResource(int index) { var res = new BoundedCacheClusterScopeTestCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME_PREFIX + index) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME_PREFIX + index).build()); res.setSpec(new BoundedCacheTestSpec()); res.getSpec().setData(INITIAL_DATA_PREFIX + index); res.getSpec().setTargetNamespace(extension.getNamespace()); diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java index ae7f8f5873..534d7b2027 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedCacheNamespacedIT.java @@ -18,19 +18,22 @@ class CaffeineBoundedCacheNamespacedIT @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new BoundedCacheTestReconciler(), o -> { - o.withItemStore(boundedItemStore( - new KubernetesClientBuilder().build(), BoundedCacheTestCustomResource.class, - Duration.ofMinutes(1), - 1)); - }) + LocallyRunOperatorExtension.builder() + .withReconciler( + new BoundedCacheTestReconciler(), + o -> { + o.withItemStore( + boundedItemStore( + new KubernetesClientBuilder().build(), + BoundedCacheTestCustomResource.class, + Duration.ofMinutes(1), + 1)); + }) .build(); BoundedCacheTestCustomResource createTestResource(int index) { var res = new BoundedCacheTestCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME_PREFIX + index) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME_PREFIX + index).build()); res.setSpec(new BoundedCacheTestSpec()); res.getSpec().setData(INITIAL_DATA_PREFIX + index); res.getSpec().setTargetNamespace(extension.getNamespace()); @@ -46,5 +49,4 @@ Class customResourceClass() { LocallyRunOperatorExtension extension() { return extension; } - } diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java index 10ab50138a..b059ac033b 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java @@ -28,7 +28,8 @@ import com.github.benmanes.caffeine.cache.Cache; import com.github.benmanes.caffeine.cache.Caffeine; -public abstract class AbstractTestReconciler

> +public abstract class AbstractTestReconciler< + P extends CustomResource> implements Reconciler

{ private static final Logger log = @@ -37,9 +38,7 @@ public abstract class AbstractTestReconciler

reconcile( - P resource, - Context

context) { + public UpdateControl

reconcile(P resource, Context

context) { var maybeConfigMap = context.getSecondaryResource(ConfigMap.class); maybeConfigMap.ifPresentOrElse( cm -> updateConfigMapIfNeeded(cm, resource, context), @@ -58,33 +57,39 @@ protected void updateConfigMapIfNeeded(ConfigMap cm, P resource, Context

cont } protected void createConfigMap(P resource, Context

context) { - var cm = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getSpec().getTargetNamespace()) - .build()) - .withData(Map.of(DATA_KEY, resource.getSpec().getData())) - .build(); + var cm = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getSpec().getTargetNamespace()) + .build()) + .withData(Map.of(DATA_KEY, resource.getSpec().getData())) + .build(); cm.addOwnerReference(resource); context.getClient().configMaps().resource(cm).create(); } @Override - public List> prepareEventSources( - EventSourceContext

context) { + public List> prepareEventSources(EventSourceContext

context) { var boundedItemStore = - boundedItemStore(new KubernetesClientBuilder().build(), - ConfigMap.class, Duration.ofMinutes(1), 1); // setting max size for testing purposes - - var es = new InformerEventSource<>( - InformerEventSourceConfiguration.from(ConfigMap.class, primaryClass()) - .withItemStore(boundedItemStore) - .withSecondaryToPrimaryMapper( - Mappers.fromOwnerReferences(context.getPrimaryResourceClass(), - this instanceof BoundedCacheClusterScopeTestReconciler)) - .build(), - context); + boundedItemStore( + new KubernetesClientBuilder().build(), + ConfigMap.class, + Duration.ofMinutes(1), + 1); // setting max size for testing purposes + + var es = + new InformerEventSource<>( + InformerEventSourceConfiguration.from(ConfigMap.class, primaryClass()) + .withItemStore(boundedItemStore) + .withSecondaryToPrimaryMapper( + Mappers.fromOwnerReferences( + context.getPrimaryResourceClass(), + this instanceof BoundedCacheClusterScopeTestReconciler)) + .build(), + context); return List.of(es); } @@ -96,17 +101,18 @@ private void ensureStatus(P resource) { } public static BoundedItemStore boundedItemStore( - KubernetesClient client, Class rClass, + KubernetesClient client, + Class rClass, Duration accessExpireDuration, // max size is only for testing purposes long cacheMaxSize) { - Cache cache = Caffeine.newBuilder() - .expireAfterAccess(accessExpireDuration) - .maximumSize(cacheMaxSize) - .build(); + Cache cache = + Caffeine.newBuilder() + .expireAfterAccess(accessExpireDuration) + .maximumSize(cacheMaxSize) + .build(); return CaffeineBoundedItemStores.boundedItemStore(client, rClass, cache); } protected abstract Class

primaryClass(); - } diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java index a77416715e..6fc9a5babc 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestCustomResource.java @@ -11,5 +11,4 @@ @Version("v1") @ShortNames("bccs") public class BoundedCacheClusterScopeTestCustomResource - extends CustomResource { -} + extends CustomResource {} diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java index 84448fc9d8..93f103cbf2 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/clusterscope/BoundedCacheClusterScopeTestReconciler.java @@ -4,8 +4,8 @@ import io.javaoperatorsdk.operator.processing.event.source.cache.sample.AbstractTestReconciler; @ControllerConfiguration -public class BoundedCacheClusterScopeTestReconciler extends - AbstractTestReconciler { +public class BoundedCacheClusterScopeTestReconciler + extends AbstractTestReconciler { @Override protected Class primaryClass() { diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java index a5e37917ba..9b77aa7bf8 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestCustomResource.java @@ -10,5 +10,4 @@ @Version("v1") @ShortNames("bct") public class BoundedCacheTestCustomResource - extends CustomResource implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java index 2bdd434d23..5aa5ca2258 100644 --- a/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java +++ b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/namespacescope/BoundedCacheTestStatus.java @@ -1,4 +1,3 @@ package io.javaoperatorsdk.operator.processing.event.source.cache.sample.namespacescope; -public class BoundedCacheTestStatus { -} +public class BoundedCacheTestStatus {} diff --git a/contributing/eclipse-google-style.xml b/contributing/eclipse-google-style.xml deleted file mode 100644 index 64340b1054..0000000000 --- a/contributing/eclipse-google-style.xml +++ /dev/null @@ -1,337 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/contributing/eclipse.importorder b/contributing/eclipse.importorder deleted file mode 100644 index 8a156041e9..0000000000 --- a/contributing/eclipse.importorder +++ /dev/null @@ -1,7 +0,0 @@ -0=java -1=javax -2=org -3=io -4=com -5= -6=\# diff --git a/docs/content/en/_index.md b/docs/content/en/_index.md index f2124a21a2..f375ebfb97 100644 --- a/docs/content/en/_index.md +++ b/docs/content/en/_index.md @@ -33,7 +33,7 @@ We do a [Pull Request](https://github.com/operator-framework/java-operator-sdk/p {{% /blocks/feature %}} -{{% blocks/feature icon="fab fa-twitter" title="Follow us on Twitter!" url="https://twitter.com/javaoperatorsdk" %}} +{{% blocks/feature icon="fa-brands fa-bluesky" title="Follow us on BlueSky!" url="https://bsky.app/profile/javaoperatorsdk.bsky.social" %}} For announcement of latest features etc. {{% /blocks/feature %}} diff --git a/docs/content/en/blog/_index.md b/docs/content/en/blog/_index.md index c8219f7994..e792e415fe 100644 --- a/docs/content/en/blog/_index.md +++ b/docs/content/en/blog/_index.md @@ -1,6 +1,6 @@ --- title: Blog -menu: {main: {weight: 30}} +menu: {main: {weight: 2}} --- This is the **blog** section. It has two categories: News and Releases. diff --git a/docs/content/en/blog/news/_index.md b/docs/content/en/blog/news/_index.md index 646c97f954..aaf1c2adcd 100644 --- a/docs/content/en/blog/news/_index.md +++ b/docs/content/en/blog/news/_index.md @@ -1,4 +1,4 @@ --- title: Posts -weight: 20 +weight: 220 --- diff --git a/docs/content/en/blog/releases/_index.md b/docs/content/en/blog/releases/_index.md index 9143a23148..dbf2ee1729 100644 --- a/docs/content/en/blog/releases/_index.md +++ b/docs/content/en/blog/releases/_index.md @@ -1,4 +1,4 @@ --- title: Releases -weight: 20 +weight: 230 --- diff --git a/docs/content/en/community/_index.md b/docs/content/en/community/_index.md index 3f237b8a79..fa42c2d974 100644 --- a/docs/content/en/community/_index.md +++ b/docs/content/en/community/_index.md @@ -1,6 +1,6 @@ --- title: Community -menu: {main: {weight: 40}} +menu: {main: {weight: 3}} --- diff --git a/docs/content/en/docs/_index.md b/docs/content/en/docs/_index.md index 76486e22f7..7118464154 100755 --- a/docs/content/en/docs/_index.md +++ b/docs/content/en/docs/_index.md @@ -1,8 +1,8 @@ --- title: Documentation linkTitle: Docs -menu: {main: {weight: 20}} -weight: 20 +menu: {main: {weight: 1}} +weight: 1 --- diff --git a/docs/content/en/docs/configuration/_index.md b/docs/content/en/docs/configuration/_index.md deleted file mode 100644 index 11929e3358..0000000000 --- a/docs/content/en/docs/configuration/_index.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Configuring JOSDK -layout: docs -permalink: /docs/configuration ---- - -# Configuration options - -The Java Operator SDK (JOSDK) provides several abstractions that work great out of the -box. However, while we strive to cover the most common cases with the default behavior, we also -recognize that that default behavior is not always what any given user might want for their -operator. Numerous configuration options are therefore provided to help people tailor the -framework to their needs. - -Configuration options act at several levels, depending on which behavior you wish to act upon: -- `Operator`-level using `ConfigurationService` -- `Reconciler`-level using `ControllerConfiguration` -- `DependentResouce`-level using the `DependentResourceConfigurator` interface -- `EventSource`-level: some event sources, such as `InformerEventSource`, might need to be - fine-tuned to properly identify which events will trigger the associated reconciler. - -## Operator-level configuration - -Configuration that impacts the whole operator is performed via the `ConfigurationService` class. -`ConfigurationService` is an abstract class, and the implementation can be different based -on which flavor of the framework is used. For example Quarkus Operator SDK replaces the -default implementation. Configurations are initialized with sensible defaults, but can -be changed during initialization. - -For instance, if you wish to not validate that the CRDs are present on your cluster when the -operator starts and configure leader election, you would do something similar to: - -```java -Operator operator = new Operator( override -> override - .checkingCRDAndValidateLocalModel(false) - .withLeaderElectionConfiguration(new LeaderElectionConfiguration("bar", "barNS"))); -``` - -## Reconciler-level configuration - -While reconcilers are typically configured using the `@ControllerConfiguration` annotation, it -is also possible to override the configuration at runtime, when the reconciler is registered -with the operator instance, either by passing it a completely new `ControllerConfiguration` -instance or by preferably overriding some aspects of the current configuration using a -`ControllerConfigurationOverrider` `Consumer`: - -```java -Operator operator; -Reconciler reconciler; -... -operator.register(reconciler, configOverrider -> - configOverrider.withFinalizer("my-nifty-operator/finalizer").withLabelSelector("foo=bar")); -``` - -## DependentResource-level configuration - -`DependentResource` implementations can implement the `DependentResourceConfigurator` interface -to pass information to the implementation. For example, the SDK -provides specific support for the `KubernetesDependentResource`, which can be configured via the -`@KubernetesDependent` annotation. This annotation is, in turn, converted into a -`KubernetesDependentResourceConfig` instance, which is then passed to the `configureWith` method -implementation. - -TODO: still subject to change / uniformization - -## EventSource-level configuration - -TODO diff --git a/docs/content/en/docs/contributing/_index.md b/docs/content/en/docs/contributing/_index.md index e67c45ebb6..4cea1f0e5d 100644 --- a/docs/content/en/docs/contributing/_index.md +++ b/docs/content/en/docs/contributing/_index.md @@ -1,6 +1,6 @@ --- title: Contributing To Java Operator SDK -weight: 100 +weight: 110 --- First of all, we'd like to thank you for considering contributing to the project! We really @@ -72,9 +72,9 @@ avoid getting a PR rejected simply because of code style issues), you can import following code style schemes based on the IDE you use: - for *Intellij IDEA* - import [contributing/intellij-google-style.xml](contributing/intellij-google-style.xml) + install [google-java-format](https://plugins.jetbrains.com/plugin/8527-google-java-format) plugin - for *Eclipse* - import [contributing/eclipse-google-style.xml](contributing/eclipse-google-style.xml) + follow [these intructions](https://github.com/google/google-java-format?tab=readme-ov-file#eclipse) ## Thanks diff --git a/docs/content/en/docs/documentation/_index.md b/docs/content/en/docs/documentation/_index.md new file mode 100644 index 0000000000..cc7fc50a57 --- /dev/null +++ b/docs/content/en/docs/documentation/_index.md @@ -0,0 +1,4 @@ +--- +title: Documentation +weight: 40 +--- \ No newline at end of file diff --git a/docs/content/en/docs/architecture/_index.md b/docs/content/en/docs/documentation/architecture.md similarity index 99% rename from docs/content/en/docs/architecture/_index.md rename to docs/content/en/docs/documentation/architecture.md index a29f70c4f6..8663b64d67 100644 --- a/docs/content/en/docs/architecture/_index.md +++ b/docs/content/en/docs/documentation/architecture.md @@ -1,9 +1,8 @@ --- title: Architecture and Internals -weight: 90 +weight: 85 --- - This document gives an overview of the internal structure and components of Java Operator SDK core, in order to make it easier for developers to understand and contribute to it. This document is not intended to be a comprehensive reference, rather an introduction to the core concepts and we diff --git a/docs/content/en/docs/documentation/configuration.md b/docs/content/en/docs/documentation/configuration.md new file mode 100644 index 0000000000..06eda5f2a2 --- /dev/null +++ b/docs/content/en/docs/documentation/configuration.md @@ -0,0 +1,166 @@ +--- +title: Configurations +weight: 55 +--- + +The Java Operator SDK (JOSDK) provides several abstractions that work great out of the +box. However, while we strive to cover the most common cases with the default behavior, we also +recognize that that default behavior is not always what any given user might want for their +operator. Numerous configuration options are therefore provided to help people tailor the +framework to their needs. + +Configuration options act at several levels, depending on which behavior you wish to act upon: +- `Operator`-level using `ConfigurationService` +- `Reconciler`-level using `ControllerConfiguration` +- `DependentResouce`-level using the `DependentResourceConfigurator` interface +- `EventSource`-level: some event sources, such as `InformerEventSource`, might need to be + fine-tuned to properly identify which events will trigger the associated reconciler. + +## Operator-level configuration + +Configuration that impacts the whole operator is performed via the `ConfigurationService` class. +`ConfigurationService` is an abstract class, and the implementation can be different based +on which flavor of the framework is used. For example Quarkus Operator SDK replaces the +default implementation. Configurations are initialized with sensible defaults, but can +be changed during initialization. + +For instance, if you wish to not validate that the CRDs are present on your cluster when the +operator starts and configure leader election, you would do something similar to: + +```java +Operator operator = new Operator( override -> override + .checkingCRDAndValidateLocalModel(false) + .withLeaderElectionConfiguration(new LeaderElectionConfiguration("bar", "barNS"))); +``` + +## Reconciler-level configuration + +While reconcilers are typically configured using the `@ControllerConfiguration` annotation, it +is also possible to override the configuration at runtime, when the reconciler is registered +with the operator instance, either by passing it a completely new `ControllerConfiguration` +instance or by preferably overriding some aspects of the current configuration using a +`ControllerConfigurationOverrider` `Consumer`: + +```java +Operator operator; +Reconciler reconciler; +... +operator.register(reconciler, configOverrider -> + configOverrider.withFinalizer("my-nifty-operator/finalizer").withLabelSelector("foo=bar")); +``` + +## Dynamically Changing Target Namespaces + +A controller can be configured to watch a specific set of namespaces in addition of the +namespace in which it is currently deployed or the whole cluster. The framework supports +dynamically changing the list of these namespaces while the operator is running. +When a reconciler is registered, an instance of +[`RegisteredController`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ec37025a15046d8f409c77616110024bf32c3416/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java#L5) +is returned, providing access to the methods allowing users to change watched namespaces as the +operator is running. + +A typical scenario would probably involve extracting the list of target namespaces from a +`ConfigMap` or some other input but this part is out of the scope of the framework since this is +use-case specific. For example, reacting to changes to a `ConfigMap` would probably involve +registering an associated `Informer` and then calling the `changeNamespaces` method on +`RegisteredController`. + +```java + +public static void main(String[] args) { + KubernetesClient client = new DefaultKubernetesClient(); + Operator operator = new Operator(client); + RegisteredController registeredController = operator.register(new WebPageReconciler(client)); + operator.installShutdownHook(); + operator.start(); + + // call registeredController further while operator is running +} + +``` + +If watched namespaces change for a controller, it might be desirable to propagate these changes to +`InformerEventSources` associated with the controller. In order to express this, +`InformerEventSource` implementations interested in following such changes need to be +configured appropriately so that the `followControllerNamespaceChanges` method returns `true`: + +```java + +@ControllerConfiguration +public class MyReconciler implements Reconciler { + + @Override + public Map prepareEventSources( + EventSourceContext context) { + + InformerEventSource configMapES = + new InformerEventSource<>(InformerEventSourceConfiguration.from(ConfigMap.class, TestCustomResource.class) + .withNamespacesInheritedFromController(context) + .build(), context); + + return EventSourceUtils.nameEventSources(configMapES); + } + +} +``` + +As seen in the above code snippet, the informer will have the initial namespaces inherited from +controller, but also will adjust the target namespaces if it changes for the controller. + +See also +the [integration test](https://github.com/operator-framework/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace) +for this feature. + +## DependentResource-level configuration + +It is possible to define custom annotations to configure custom `DependentResource` implementations. In order to provide +such a configuration mechanism for your own `DependentResource` implementations, they must be annotated with the +`@Configured` annotation. This annotation defines 3 fields that tie everything together: + +- `by`, which specifies which annotation class will be used to configure your dependents, +- `with`, which specifies the class holding the configuration object for your dependents and +- `converter`, which specifies the `ConfigurationConverter` implementation in charge of converting the annotation + specified by the `by` field into objects of the class specified by the `with` field. + +`ConfigurationConverter` instances implement a single `configFrom` method, which will receive, as expected, the +annotation instance annotating the dependent resource instance to be configured, but it can also extract information +from the `DependentResourceSpec` instance associated with the `DependentResource` class so that metadata from it can be +used in the configuration, as well as the parent `ControllerConfiguration`, if needed. The role of +`ConfigurationConverter` implementations is to extract the annotation information, augment it with metadata from the +`DependentResourceSpec` and the configuration from the parent controller on which the dependent is defined, to finally +create the configuration object that the `DependentResource` instances will use. + +However, one last element is required to finish the configuration process: the target `DependentResource` class must +implement the `ConfiguredDependentResource` interface, parameterized with the annotation class defined by the +`@Configured` annotation `by` field. This interface is called by the framework to inject the configuration at the +appropriate time and retrieve the configuration, if it's available. + +For example, `KubernetesDependentResource`, a core implementation that the framework provides, can be configured via the +`@KubernetesDependent` annotation. This set up is configured as follows: + +```java + +@Configured( + by = KubernetesDependent.class, + with = KubernetesDependentResourceConfig.class, + converter = KubernetesDependentConverter.class) +public abstract class KubernetesDependentResource + extends AbstractEventSourceHolderDependentResource> + implements ConfiguredDependentResource> { + // code omitted +} +``` + +The `@Configured` annotation specifies that `KubernetesDependentResource` instances can be configured by using the +`@KubernetesDependent` annotation, which gets converted into a `KubernetesDependentResourceConfig` object by a +`KubernetesDependentConverter`. That configuration object is then injected by the framework in the +`KubernetesDependentResource` instance, after it's been created, because the class implements the +`ConfiguredDependentResource` interface, properly parameterized. + +For more information on how to use this feature, we recommend looking at how this mechanism is implemented for +`KubernetesDependentResource` in the core framework, `SchemaDependentResource` in the samples or `CustomAnnotationDep` +in the `BaseConfigurationServiceTest` test class. + +## EventSource-level configuration + +TODO diff --git a/docs/content/en/docs/documentation/dependent-resource-and-workflows/_index.md b/docs/content/en/docs/documentation/dependent-resource-and-workflows/_index.md new file mode 100644 index 0000000000..9446f7ceca --- /dev/null +++ b/docs/content/en/docs/documentation/dependent-resource-and-workflows/_index.md @@ -0,0 +1,9 @@ +--- +title: Dependent resources and workflows +weight: 70 +--- + +Dependent resources and workflows are features sometimes referenced as higher +level abstractions. These two related concepts provides an abstraction +over reconciliation of a single resource (Dependent resource) and the +orchestration of such resources (Workflows). \ No newline at end of file diff --git a/docs/content/en/docs/dependent-resources/_index.md b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md similarity index 99% rename from docs/content/en/docs/dependent-resources/_index.md rename to docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md index f79443de74..304e20bafe 100644 --- a/docs/content/en/docs/dependent-resources/_index.md +++ b/docs/content/en/docs/documentation/dependent-resource-and-workflows/dependent-resources.md @@ -1,6 +1,6 @@ --- -title: Dependent Resources -weight: 60 +title: Dependent resources +weight: 75 --- ## Motivations and Goals @@ -136,10 +136,6 @@ Deleted (or set to be garbage collected). The following example shows how to cre @KubernetesDependent(labelSelector = WebPageManagedDependentsReconciler.SELECTOR) class DeploymentDependentResource extends CRUDKubernetesDependentResource { - public DeploymentDependentResource() { - super(Deployment.class); - } - @Override protected Deployment desired(WebPage webPage, Context context) { var deploymentName = deploymentName(webPage); diff --git a/docs/content/en/docs/workflows/_index.md b/docs/content/en/docs/documentation/dependent-resource-and-workflows/workflows.md similarity index 99% rename from docs/content/en/docs/workflows/_index.md rename to docs/content/en/docs/documentation/dependent-resource-and-workflows/workflows.md index 620f8c5436..4b1bea6790 100644 --- a/docs/content/en/docs/workflows/_index.md +++ b/docs/content/en/docs/documentation/dependent-resource-and-workflows/workflows.md @@ -1,6 +1,6 @@ --- title: Workflows -weight: 70 +weight: 80 --- ## Overview diff --git a/docs/content/en/docs/documentation/error-handling-retries.md b/docs/content/en/docs/documentation/error-handling-retries.md new file mode 100644 index 0000000000..a36c46f08e --- /dev/null +++ b/docs/content/en/docs/documentation/error-handling-retries.md @@ -0,0 +1,115 @@ +--- +title: Error handling and retries +weight: 46 +--- + +## Automatic Retries on Error + +JOSDK will schedule an automatic retry of the reconciliation whenever an exception is thrown by +your `Reconciler`. The retry behavior is configurable, but a default implementation is provided +covering most of the typical use-cases, see +[GenericRetry](https://github.com/java-operator-sdk/java-operator-sdk/blob/master/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java) +. + +```java + GenericRetry.defaultLimitedExponentialRetry() + .setInitialInterval(5000) + .setIntervalMultiplier(1.5D) + .setMaxAttempts(5); +``` + +You can also configure the default retry behavior using the `@GradualRetry` annotation. + +It is possible to provide a custom implementation using the `retry` field of the +`@ControllerConfiguration` annotation and specifying the class of your custom implementation. +Note that this class must provide an accessible no-arg constructor for automated +instantiation. Additionally, your implementation can be automatically configured from an +annotation that you can provide by having your `Retry` implementation implement the +`AnnotationConfigurable` interface, parameterized with your annotation type. See the +`GenericRetry` implementation for more details. + +Information about the current retry state is accessible from +the [Context](https://github.com/java-operator-sdk/java-operator-sdk/blob/master/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/Context.java) +object. Of note, particularly interesting is the `isLastAttempt` method, which could allow your +`Reconciler` to implement a different behavior based on this status, by setting an error message +in your resource status, for example, when attempting a last retry. + +Note, though, that reaching the retry limit won't prevent new events to be processed. New +reconciliations will happen for new events as usual. However, if an error also occurs that +would trigger a retry, the SDK won't schedule one at this point since the retry limit +has already been reached. + +A successful execution resets the retry state. + +### Reconciler Error Handler + +In order to facilitate error reporting you can override [`updateErrorStatus`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java#L52) +method in `Reconciler`: + +```java +public class MyReconciler implements Reconciler { + + @Override + public ErrorStatusUpdateControl updateErrorStatus( + WebPage resource, Context context, Exception e) { + return handleError(resource, e); + } + +} +``` + +The `updateErrorStatus` method is called in case an exception is thrown from the `Reconciler`. It is +also called even if no retry policy is configured, just after the reconciler execution. +`RetryInfo.getAttemptCount()` is zero after the first reconciliation attempt, since it is not a +result of a retry (regardless of whether a retry policy is configured). + +`ErrorStatusUpdateControl` tells the SDK what to do and how to perform the status +update on the primary resource, which is always performed as a status sub-resource request. Note that +this update request will also produce an event and result in a reconciliation if the +controller is not generation-aware. + +This feature is only available for the `reconcile` method of the `Reconciler` interface, since +there should not be updates to resources that have been marked for deletion. + +Retry can be skipped in cases of unrecoverable errors: + +```java + ErrorStatusUpdateControl.patchStatus(customResource).withNoRetry(); +``` + +### Correctness and Automatic Retries + +While it is possible to deactivate automatic retries, this is not desirable unless there is a particular reason. +Errors naturally occur, whether it be transient network errors or conflicts +when a given resource is handled by a `Reconciler` but modified simultaneously by a user in +a different process. Automatic retries handle these cases nicely and will eventually result in a +successful reconciliation. + +## Retry, Rescheduling and Event Handling Common Behavior + +Retry, reschedule, and standard event processing form a relatively complex system, each of these +functionalities interacting with the others. In the following, we describe the interplay of +these features: + +1. A successful execution resets a retry and the rescheduled executions that were present before + the reconciliation. However, the reconciliation outcome can instruct a new rescheduling (`UpdateControl` or `DeleteControl`). + + For example, if a reconciliation had previously been rescheduled for after some amount of time, but an event triggered + the reconciliation (or cleanup) in the meantime, the scheduled execution would be automatically cancelled, i.e. + rescheduling a reconciliation does not guarantee that one will occur precisely at that time; it simply guarantees that a reconciliation will occur at the latest. + Of course, it's always possible to reschedule a new reconciliation at the end of that "automatic" reconciliation. + + Similarly, if a retry was scheduled, any event from the cluster triggering a successful execution in the meantime + would cancel the scheduled retry (because there's now no point in retrying something that already succeeded) + +2. In case an exception is thrown, a retry is initiated. However, if an event is received + meanwhile, it will be reconciled instantly, and this execution won't count as a retry attempt. +3. If the retry limit is reached (so no more automatic retry would happen), but a new event + received, the reconciliation will still happen, but won't reset the retry, and will still be + marked as the last attempt in the retry info. The point (1) still holds - thus successful reconciliation will reset the retry - but no retry will happen in case of an error. + +The thing to remember when it comes to retrying or rescheduling is that JOSDK tries to avoid unnecessary work. When +you reschedule an operation, you instruct JOSDK to perform that operation by the end of the rescheduling +delay at the latest. If something occurred on the cluster that triggers that particular operation (reconciliation or cleanup), then +JOSDK considers that there's no point in attempting that operation again at the end of the specified delay since there +is no point in doing so anymore. The same idea also applies to retries. diff --git a/docs/content/en/docs/documentation/eventing.md b/docs/content/en/docs/documentation/eventing.md new file mode 100644 index 0000000000..2591ab19c9 --- /dev/null +++ b/docs/content/en/docs/documentation/eventing.md @@ -0,0 +1,327 @@ +--- +title: Event sources and related topics +weight: 47 +--- + +## Handling Related Events with Event Sources + +See also +this [blog post](https://csviri.medium.com/java-operator-sdk-introduction-to-event-sources-a1aab5af4b7b) +. + +Event sources are a relatively simple yet powerful and extensible concept to trigger controller +executions, usually based on changes to dependent resources. You typically need an event source +when you want your `Reconciler` to be triggered when something occurs to secondary resources +that might affect the state of your primary resource. This is needed because a given +`Reconciler` will only listen by default to events affecting the primary resource type it is +configured for. Event sources act as listen to events affecting these secondary resources so +that a reconciliation of the associated primary resource can be triggered when needed. Note that +these secondary resources need not be Kubernetes resources. Typically, when dealing with +non-Kubernetes objects or services, we can extend our operator to handle webhooks or websockets +or to react to any event coming from a service we interact with. This allows for very efficient +controller implementations because reconciliations are then only triggered when something occurs +on resources affecting our primary resources thus doing away with the need to periodically +reschedule reconciliations. + +![Event Sources architecture diagram](/images/event-sources.png) + +There are few interesting points here: + +The `CustomResourceEventSource` event source is a special one, responsible for handling events +pertaining to changes affecting our primary resources. This `EventSource` is always registered +for every controller automatically by the SDK. It is important to note that events always relate +to a given primary resource. Concurrency is still handled for you, even in the presence of +`EventSource` implementations, and the SDK still guarantees that there is no concurrent execution of +the controller for any given primary resource (though, of course, concurrent/parallel executions +of events pertaining to other primary resources still occur as expected). + +### Caching and Event Sources + +Kubernetes resources are handled in a declarative manner. The same also holds true for event +sources. For example, if we define an event source to watch for changes of a Kubernetes Deployment +object using an `InformerEventSource`, we always receive the whole associated object from the +Kubernetes API. This object might be needed at any point during our reconciliation process and +it's best to retrieve it from the event source directly when possible instead of fetching it +from the Kubernetes API since the event source guarantees that it will provide the latest +version. Not only that, but many event source implementations also cache resources they handle +so that it's possible to retrieve the latest version of resources without needing to make any +calls to the Kubernetes API, thus allowing for very efficient controller implementations. + +Note after an operator starts, caches are already populated by the time the first reconciliation +is processed for the `InformerEventSource` implementation. However, this does not necessarily +hold true for all event source implementations (`PerResourceEventSource` for example). The SDK +provides methods to handle this situation elegantly, allowing you to check if an object is +cached, retrieving it from a provided supplier if not. See +related [method](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java#L146) +. + +### Registering Event Sources + +To register event sources, your `Reconciler` has to override the `prepareEventSources` and return +list of event sources to register. One way to see this in action is +to look at the +[WebPage example](https://github.com/operator-framework/java-operator-sdk/blob/main/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java) +(irrelevant details omitted): + +```java + +import java.util.List; + +@ControllerConfiguration +public class WebappReconciler + implements Reconciler, Cleaner, EventSourceInitializer { + // ommitted code + + @Override + public List> prepareEventSources(EventSourceContext context) { + InformerEventSourceConfiguration configuration = + InformerEventSourceConfiguration.from(Deployment.class, Webapp.class) + .withLabelSelector(SELECTOR) + .build(); + return List.of(new InformerEventSource<>(configuration, context)); + } +} +``` + +In the example above an `InformerEventSource` is configured and registered. +`InformerEventSource` is one of the bundled `EventSource` implementations that JOSDK provides to +cover common use cases. + +### Managing Relation between Primary and Secondary Resources + +Event sources let your operator know when a secondary resource has changed and that your +operator might need to reconcile this new information. However, in order to do so, the SDK needs +to somehow retrieve the primary resource associated with which ever secondary resource triggered +the event. In the `Webapp` example above, when an event occurs on a tracked `Deployment`, the +SDK needs to be able to identify which `Webapp` resource is impacted by that change. + +Seasoned Kubernetes users already know one way to track this parent-child kind of relationship: +using owner references. Indeed, that's how the SDK deals with this situation by default as well, +that is, if your controller properly set owner references on your secondary resources, the SDK +will be able to follow that reference back to your primary resource automatically without you +having to worry about it. + +However, owner references cannot always be used as they are restricted to operating within a +single namespace (i.e. you cannot have an owner reference to a resource in a different namespace) +and are, by essence, limited to Kubernetes resources so you're out of luck if your secondary +resources live outside of a cluster. + +This is why JOSDK provides the `SecondayToPrimaryMapper` interface so that you can provide +alternative ways for the SDK to identify which primary resource needs to be reconciled when +something occurs to your secondary resources. We even provide some of these alternatives in the +[Mappers](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java) +class. + +Note that, while a set of `ResourceID` is returned, this set usually consists only of one +element. It is however possible to return multiple values or even no value at all to cover some +rare corner cases. Returning an empty set means that the mapper considered the secondary +resource event as irrelevant and the SDK will thus not trigger a reconciliation of the primary +resource in that situation. + +Adding a `SecondaryToPrimaryMapper` is typically sufficient when there is a one-to-many relationship +between primary and secondary resources. The secondary resources can be mapped to its primary +owner, and this is enough information to also get these secondary resources from the `Context` +object that's passed to your `Reconciler`. + +There are however cases when this isn't sufficient and you need to provide an explicit mapping +between a primary resource and its associated secondary resources using an implementation of the +`PrimaryToSecondaryMapper` interface. This is typically needed when there are many-to-one or +many-to-many relationships between primary and secondary resources, e.g. when the primary resource +is referencing secondary resources. +See [PrimaryToSecondaryIT](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java) +integration test for a sample. + +### Built-in EventSources + +There are multiple event-sources provided out of the box, the following are some more central ones: + +#### `InformerEventSource` + +[InformerEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java) +is probably the most important `EventSource` implementation to know about. When you create an +`InformerEventSource`, JOSDK will automatically create and register a `SharedIndexInformer`, a +fabric8 Kubernetes client class, that will listen for events associated with the resource type +you configured your `InformerEventSource` with. If you want to listen to Kubernetes resource +events, `InformerEventSource` is probably the only thing you need to use. It's highly +configurable so you can tune it to your needs. Take a look at +[InformerEventSourceConfiguration](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java) +and associated classes for more details but some interesting features we can mention here is the +ability to filter events so that you can only get notified for events you care about. A +particularly interesting feature of the `InformerEventSource`, as opposed to using your own +informer-based listening mechanism is that caches are particularly well optimized preventing +reconciliations from being triggered when not needed and allowing efficient operators to be written. + +#### `PerResourcePollingEventSource` + +[PerResourcePollingEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java) +is used to poll external APIs, which don't support webhooks or other event notifications. It +extends the abstract +[ExternalResourceCachingEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java) +to support caching. +See [MySQL Schema sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java) +for usage. + +#### `PollingEventSource` + +[PollingEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java) +is similar to `PerResourceCachingEventSource` except that, contrary to that event source, it +doesn't poll a specific API separately per resource, but periodically and independently of +actually observed primary resources. + +#### Inbound event sources + +[SimpleInboundEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/SimpleInboundEventSource.java) +and +[CachingInboundEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java) +are used to handle incoming events from webhooks and messaging systems. + +#### `ControllerResourceEventSource` + +[ControllerResourceEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java) +is a special `EventSource` implementation that you will never have to deal with directly. It is, +however, at the core of the SDK is automatically added for you: this is the main event source +that listens for changes to your primary resources and triggers your `Reconciler` when needed. +It features smart caching and is really optimized to minimize Kubernetes API accesses and avoid +triggering unduly your `Reconciler`. + +More on the philosophy of the non Kubernetes API related event source see in +issue [#729](https://github.com/java-operator-sdk/java-operator-sdk/issues/729). + + +## InformerEventSource Multi-Cluster Support + +It is possible to handle resources for remote cluster with `InformerEventSource`. To do so, +simply set a client that connects to a remote cluster: + +```java + +InformerEventSourceConfiguration configuration = + InformerEventSourceConfiguration.from(SecondaryResource.class, PrimaryResource.class) + .withKubernetesClient(remoteClusterClient) + .withSecondaryToPrimaryMapper(Mappers.fromDefaultAnnotations()); + +``` + +You will also need to specify a `SecondaryToPrimaryMapper`, since the default one +is based on owner references and won't work across cluster instances. You could, for example, use the provided implementation that relies on annotations added to the secondary resources to identify the associated primary resource. + +See related [integration test](https://github.com/operator-framework/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster). + + +## Generation Awareness and Event Filtering + +A best practice when an operator starts up is to reconcile all the associated resources because +changes might have occurred to the resources while the operator was not running. + +When this first reconciliation is done successfully, the next reconciliation is triggered if either +dependent resources are changed or the primary resource `.spec` field is changed. If other fields +like `.metadata` are changed on the primary resource, the reconciliation could be skipped. This +behavior is supported out of the box and reconciliation is by default not triggered if +changes to the primary resource do not increase the `.metadata.generation` field. +Note that changes to `.metada.generation` are automatically handled by Kubernetes. + +To turn off this feature, set `generationAwareEventProcessing` to `false` for the `Reconciler`. + + +## Max Interval Between Reconciliations + +When informers / event sources are properly set up, and the `Reconciler` implementation is +correct, no additional reconciliation triggers should be needed. However, it's +a [common practice](https://github.com/java-operator-sdk/java-operator-sdk/issues/848#issuecomment-1016419966) +to have a failsafe periodic trigger in place, just to make sure resources are nevertheless +reconciled after a certain amount of time. This functionality is in place by default, with a +rather high time interval (currently 10 hours) after which a reconciliation will be +automatically triggered even in the absence of other events. See how to override this using the +standard annotation: + +```java +@ControllerConfiguration(maxReconciliationInterval = @MaxReconciliationInterval( + interval = 50, + timeUnit = TimeUnit.MILLISECONDS)) +public class MyReconciler implements Reconciler {} +``` + +The event is not propagated at a fixed rate, rather it's scheduled after each reconciliation. So the +next reconciliation will occur at most within the specified interval after the last reconciliation. + +This feature can be turned off by setting `maxReconciliationInterval` +to [`Constants.NO_MAX_RECONCILIATION_INTERVAL`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Constants.java#L20-L20) +or any non-positive number. + +The automatic retries are not affected by this feature so a reconciliation will be re-triggered +on error, according to the specified retry policy, regardless of this maximum interval setting. + +## Rate Limiting + +It is possible to rate limit reconciliation on a per-resource basis. The rate limit also takes +precedence over retry/re-schedule configurations: for example, even if a retry was scheduled for +the next second but this request would make the resource go over its rate limit, the next +reconciliation will be postponed according to the rate limiting rules. Note that the +reconciliation is never cancelled, it will just be executed as early as possible based on rate +limitations. + +Rate limiting is by default turned **off**, since correct configuration depends on the reconciler +implementation, in particular, on how long a typical reconciliation takes. +(The parallelism of reconciliation itself can be +limited [`ConfigurationService`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ce4d996ee073ebef5715737995fc3d33f4751275/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L120-L120) +by configuring the `ExecutorService` appropriately.) + +A default rate limiter implementation is provided, see: +[`PeriodRateLimiter`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ce4d996ee073ebef5715737995fc3d33f4751275/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/PeriodRateLimiter.java#L14-L14) +. +Users can override it by implementing their own +[`RateLimiter`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ce4d996ee073ebef5715737995fc3d33f4751275/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/RateLimiter.java) +and specifying this custom implementation using the `rateLimiter` field of the +`@ControllerConfiguration` annotation. Similarly to the `Retry` implementations, +`RateLimiter` implementations must provide an accessible, no-arg constructor for instantiation +purposes and can further be automatically configured from your own, provided annotation provided +your `RateLimiter` implementation also implements the `AnnotationConfigurable` interface, +parameterized by your custom annotation type. + +To configure the default rate limiter use the `@RateLimited` annotation on your +`Reconciler` class. The following configuration limits each resource to reconcile at most twice +within a 3 second interval: + +```java + +@RateLimited(maxReconciliations = 2, within = 3, unit = TimeUnit.SECONDS) +@ControllerConfiguration +public class MyReconciler implements Reconciler { + +} +``` + +Thus, if a given resource was reconciled twice in one second, no further reconciliation for this +resource will happen before two seconds have elapsed. Note that, since rate is limited on a +per-resource basis, other resources can still be reconciled at the same time, as long, of course, +that they stay within their own rate limits. + +## Optimizing Caches + +One of the ideas around the operator pattern is that all the relevant resources are cached, thus reconciliation is +usually very fast (especially if no resources are updated in the process) since the operator is then mostly working with +in-memory state. However for large clusters, caching huge amount of primary and secondary resources might consume lots +of memory. JOSDK provides ways to mitigate this issue and optimize the memory usage of controllers. While these features +are working and tested, we need feedback from real production usage. + +### Bounded Caches for Informers + +Limiting caches for informers - thus for Kubernetes resources - is supported by ensuring that resources are in the cache +for a limited time, via a cache eviction of least recently used resources. This means that when resources are created +and frequently reconciled, they stay "hot" in the cache. However, if, over time, a given resource "cools" down, i.e. it +becomes less and less used to the point that it might not be reconciled anymore, it will eventually get evicted from the +cache to free up memory. If such an evicted resource were to become reconciled again, the bounded cache implementation +would then fetch it from the API server and the "hot/cold" cycle would start anew. + +Since all resources need to be reconciled when a controller start, it is not practical to set a maximal cache size as +it's desirable that all resources be cached as soon as possible to make the initial reconciliation process on start as +fast and efficient as possible, avoiding undue load on the API server. It's therefore more interesting to gradually +evict cold resources than try to limit cache sizes. + +See usage of the related implementation using [Caffeine](https://github.com/ben-manes/caffeine) cache in integration +tests +for [primary resources](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java). + +See +also [CaffeineBoundedItemStores](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java) +for more details. \ No newline at end of file diff --git a/docs/content/en/docs/documentation/features.md b/docs/content/en/docs/documentation/features.md new file mode 100644 index 0000000000..c39dece4e3 --- /dev/null +++ b/docs/content/en/docs/documentation/features.md @@ -0,0 +1,73 @@ +--- +title: Other Features +weight: 57 +--- + +The Java Operator SDK (JOSDK) is a high level framework and related tooling aimed at +facilitating the implementation of Kubernetes operators. The features are by default following +the best practices in an opinionated way. However, feature flags and other configuration options +are provided to fine tune or turn off these features. + +## Support for Well Known (non-custom) Kubernetes Resources + +A Controller can be registered for a non-custom resource, so well known Kubernetes resources like ( +`Ingress`, `Deployment`,...). + +See +the [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment) +for reconciling deployments. + +```java +public class DeploymentReconciler + implements Reconciler, TestExecutionInfoProvider { + + @Override + public UpdateControl reconcile( + Deployment resource, Context context) { + // omitted code + } +} +``` + +## Leader Election + +Operators are generally deployed with a single running or active instance. However, it is +possible to deploy multiple instances in such a way that only one, called the "leader", processes the +events. This is achieved via a mechanism called "leader election". While all the instances are +running, and even start their event sources to populate the caches, only the leader will process +the events. This means that should the leader change for any reason, for example because it +crashed, the other instances are already warmed up and ready to pick up where the previous +leader left off should one of them become elected leader. + +See sample configuration in +the [E2E test](https://github.com/java-operator-sdk/java-operator-sdk/blob/8865302ac0346ee31f2d7b348997ec2913d5922b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java#L21-L23) +. + +## Automatic Generation of CRDs + +Note that this feature is provided by the +[Fabric8 Kubernetes Client](https://github.com/fabric8io/kubernetes-client), not JOSDK itself. + +To automatically generate CRD manifests from your annotated Custom Resource classes, you only need +to add the following dependencies to your project: + +```xml + + + io.fabric8 + crd-generator-apt + provided + +``` + +The CRD will be generated in `target/classes/META-INF/fabric8` (or +in `target/test-classes/META-INF/fabric8`, if you use the `test` scope) with the CRD name +suffixed by the generated spec version. For example, a CR using the `java-operator-sdk.io` group +with a `mycrs` plural form will result in 2 files: + +- `mycrs.java-operator-sdk.io-v1.yml` +- `mycrs.java-operator-sdk.io-v1beta1.yml` + +**NOTE:** +> Quarkus users using the `quarkus-operator-sdk` extension do not need to add any extra dependency +> to get their CRD generated as this is handled by the extension itself. diff --git a/docs/content/en/docs/documentation/observability.md b/docs/content/en/docs/documentation/observability.md new file mode 100644 index 0000000000..27a68086d5 --- /dev/null +++ b/docs/content/en/docs/documentation/observability.md @@ -0,0 +1,112 @@ +--- +title: Observability +weight: 55 +--- + +## Runtime Info + +[RuntimeInfo](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RuntimeInfo.java#L16-L16) +is used mainly to check the actual health of event sources. Based on this information it is easy to implement custom +liveness probes. + +[stopOnInformerErrorDuringStartup](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L168-L168) +setting, where this flag usually needs to be set to false, in order to control the exact liveness properties. + +See also an example implementation in the +[WebPage sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/3e2e7c4c834ef1c409d636156b988125744ca911/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java#L38-L43) + +## Contextual Info for Logging with MDC + +Logging is enhanced with additional contextual information using +[MDC](http://www.slf4j.org/manual.html#mdc). The following attributes are available in most +parts of reconciliation logic and during the execution of the controller: + +| MDC Key | Value added from primary resource | +|:---------------------------|:----------------------------------| +| `resource.apiVersion` | `.apiVersion` | +| `resource.kind` | `.kind` | +| `resource.name` | `.metadata.name` | +| `resource.namespace` | `.metadata.namespace` | +| `resource.resourceVersion` | `.metadata.resourceVersion` | +| `resource.generation` | `.metadata.generation` | +| `resource.uid` | `.metadata.uid` | + +For more information about MDC see this [link](https://www.baeldung.com/mdc-in-log4j-2-logback). + +## Metrics + +JOSDK provides built-in support for metrics reporting on what is happening with your reconcilers in the form of +the `Metrics` interface which can be implemented to connect to your metrics provider of choice, JOSDK calling the +methods as it goes about reconciling resources. By default, a no-operation implementation is provided thus providing a +no-cost sane default. A [micrometer](https://micrometer.io)-based implementation is also provided. + +You can use a different implementation by overriding the default one provided by the default `ConfigurationService`, as +follows: + +```java +Metrics metrics; // initialize your metrics implementation +Operator operator = new Operator(client, o -> o.withMetrics(metrics)); +``` + +### Micrometer implementation + +The micrometer implementation is typically created using one of the provided factory methods which, depending on which +is used, will return either a ready to use instance or a builder allowing users to customized how the implementation +behaves, in particular when it comes to the granularity of collected metrics. It is, for example, possible to collect +metrics on a per-resource basis via tags that are associated with meters. This is the default, historical behavior but +this will change in a future version of JOSDK because this dramatically increases the cardinality of metrics, which +could lead to performance issues. + +To create a `MicrometerMetrics` implementation that behaves how it has historically behaved, you can just create an +instance via: + +```java +MeterRegistry registry; // initialize your registry implementation +Metrics metrics = new MicrometerMetrics(registry); +``` + +Note, however, that this constructor is deprecated and we encourage you to use the factory methods instead, which either +return a fully pre-configured instance or a builder object that will allow you to configure more easily how the instance +will behave. You can, for example, configure whether or not the implementation should collect metrics on a per-resource +basis, whether or not associated meters should be removed when a resource is deleted and how the clean-up is performed. +See the relevant classes documentation for more details. + +For example, the following will create a `MicrometerMetrics` instance configured to collect metrics on a per-resource +basis, deleting the associated meters after 5 seconds when a resource is deleted, using up to 2 threads to do so. + +```java +MicrometerMetrics.newPerResourceCollectingMicrometerMetricsBuilder(registry) + .withCleanUpDelayInSeconds(5) + .withCleaningThreadNumber(2) + .build(); +``` + +### Operator SDK metrics + +The micrometer implementation records the following metrics: + +| Meter name | Type | Tag names | Description | +|-------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------| +| operator.sdk.reconciliations.executions.`` | gauge | group, version, kind | Number of executions of the named reconciler | +| operator.sdk.reconciliations.queue.size.`` | gauge | group, version, kind | How many resources are queued to get reconciled by named reconciler | +| operator.sdk.``.size | gauge map size | | Gauge tracking the size of a specified map (currently unused but could be used to monitor caches size) | +| operator.sdk.events.received | counter | ``, event, action | Number of received Kubernetes events | +| operator.sdk.events.delete | counter | `` | Number of received Kubernetes delete events | +| operator.sdk.reconciliations.started | counter | ``, reconciliations.retries.last, reconciliations.retries.number | Number of started reconciliations per resource type | +| operator.sdk.reconciliations.failed | counter | ``, exception | Number of failed reconciliations per resource type | +| operator.sdk.reconciliations.success | counter | `` | Number of successful reconciliations per resource type | +| operator.sdk.controllers.execution.reconcile | timer | ``, controller | Time taken for reconciliations per controller | +| operator.sdk.controllers.execution.cleanup | timer | ``, controller | Time taken for cleanups per controller | +| operator.sdk.controllers.execution.reconcile.success | counter | controller, type | Number of successful reconciliations per controller | +| operator.sdk.controllers.execution.reconcile.failure | counter | controller, exception | Number of failed reconciliations per controller | +| operator.sdk.controllers.execution.cleanup.success | counter | controller, type | Number of successful cleanups per controller | +| operator.sdk.controllers.execution.cleanup.failure | counter | controller, exception | Number of failed cleanups per controller | + +As you can see all the recorded metrics start with the `operator.sdk` prefix. ``, in the table above, +refers to resource-specific metadata and depends on the considered metric and how the implementation is configured and +could be summed up as follows: `group?, version, kind, [name, namespace?], scope` where the tags in square +brackets (`[]`) won't be present when per-resource collection is disabled and tags followed by a question mark are +omitted if the associated value is empty. Of note, when in the context of controllers' execution metrics, these tag +names are prefixed with `resource.`. This prefix might be removed in a future version for greater consistency. + + diff --git a/docs/content/en/docs/documentation/reconciler.md b/docs/content/en/docs/documentation/reconciler.md new file mode 100644 index 0000000000..fa51399de7 --- /dev/null +++ b/docs/content/en/docs/documentation/reconciler.md @@ -0,0 +1,217 @@ +--- +title: Implementing a reconciler +weight: 45 +--- + +## Reconciliation Execution in a Nutshell + +An event always triggers reconciliation execution. Events typically come from a +primary resource, usually a custom resource, triggered by changes made to that resource +on the server (e.g. a resource is created, updated, or deleted) or from secondary resources for which there is a registered event source. +Reconciler implementations are associated with a given resource type and listen for such events from the Kubernetes API server +so that they can appropriately react to them. It is, however, possible for secondary sources to +trigger the reconciliation process. This occurs via +the [event source](#handling-related-events-with-event-sources) mechanism. + +When we receive an event, it triggers the reconciliation unless a reconciliation is already +underway for this particular resource. In other words, the framework guarantees that no concurrent reconciliation happens for a resource. + +Once the reconciliation is done, the framework checks if: + +- an exception was thrown during execution, and if yes, schedules a retry. +- new events were received during the controller execution; if yes, schedule a new reconciliation. +- the reconciler results explicitly re-scheduled (`UpdateControl.rescheduleAfter(..)`) a reconciliation with a time delay, if yes, + schedules a timer event with the specific delay. +- if none of the above applies, the reconciliation is finished. + +In summary, the core of the SDK is implemented as an eventing system where events trigger +reconciliation requests. + +## Implementing a Reconciler and Cleaner interfaces + +To implement a reconciler, you always have to implement the [`Reconciler`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java) interface. + +The lifecycle of a Kubernetes resource can be separated into two phases depending on whether the resource has already been marked for deletion or not. + +The framework out of the box supports this logic, it will always +call the `reconcile` method unless the custom resource is +[marked from deletion](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/#how-finalizers-work). + +On the other hand, if the resource is marked from deletion and if the `Reconciler` implements the +[`Cleaner`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java) interface, only the `cleanup` method is called. By implementing this interface +the framework will automatically handle (add/remove) the finalizers for you. + +In short, if you need to provide explicit cleanup logic, you always want to use finalizers; for a more detailed explanation, see [Finalizer support](#finalizer-support) for more details. + +### Using `UpdateControl` and `DeleteControl` + +These two classes control the outcome or the desired behavior after the reconciliation. + +The [`UpdateControl`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java) +can instruct the framework to update the status sub-resource of the resource +and/or re-schedule a reconciliation with a desired time delay: + +```java + @Override + public UpdateControl reconcile( + EventSourceTestCustomResource resource, Context context) { + // omitted code + + return UpdateControl.patchStatus(resource).rescheduleAfter(10, TimeUnit.SECONDS); + } +``` + +without an update: + +```java + @Override + public UpdateControl reconcile( + EventSourceTestCustomResource resource, Context context) { + // omitted code + + return UpdateControl.noUpdate().rescheduleAfter(10, TimeUnit.SECONDS); + } +``` + +Note, though, that using `EventSources` is the preferred way of scheduling since the +reconciliation is triggered only when a resource is changed, not on a timely basis. + +At the end of the reconciliation, you typically update the status sub-resources. +It is also possible to update both the status and the resource with the `patchResourceAndStatus` method. In this case, +the resource is updated first followed by the status, using two separate requests to the Kubernetes API. + +From v5 `UpdateControl` only supports patching the resources, by default +using [Server Side Apply (SSA)](https://kubernetes.io/docs/reference/using-api/server-side-apply/). +It is important to understand how SSA works in Kubernetes. Mainly, resources applied using SSA +should contain only the fields identifying the resource and those the user is interested in (a 'fully specified intent' +in Kubernetes parlance), thus usually using a resource created from scratch, see +[sample](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa). +To contrast, see the same sample, this time [without SSA](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAReconciler.java). + +Non-SSA based patch is still supported. +You can control whether or not to use SSA +using [`ConfigurationServcice.useSSAToPatchPrimaryResource()`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L385-L385) +and the related `ConfigurationServiceOverrider.withUseSSAToPatchPrimaryResource` method. +Related integration test can be +found [here](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa). + +Handling resources directly using the client, instead of delegating these updates operations to JOSDK by returning +an `UpdateControl` at the end of your reconciliation, should work appropriately. However, we do recommend to +use `UpdateControl` instead since JOSDK makes sure that the operations are handled properly, since there are subtleties +to be aware of. For example, if you are using a finalizer, JOSDK makes sure to include it in your fully specified intent +so that it is not unintentionally removed from the resource (which would happen if you omit it, since your controller is +the designated manager for that field and Kubernetes interprets the finalizer being gone from the specified intent as a +request for removal). + +[`DeleteControl`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DeleteControl.java) +typically instructs the framework to remove the finalizer after the dependent +resource are cleaned up in `cleanup` implementation. + +```java + +public DeleteControl cleanup(MyCustomResource customResource,Context context){ + // omitted code + + return DeleteControl.defaultDelete(); + } + +``` + +However, it is possible to instruct the SDK to not remove the finalizer, this allows to clean up +the resources in a more asynchronous way, mostly for cases when there is a long waiting period +after a delete operation is initiated. Note that in this case you might want to either schedule +a timed event to make sure `cleanup` is executed again or use event sources to get notified +about the state changes of the deleted resource. + +### Finalizer Support + +[Kubernetes finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) +make sure that your `Reconciler` gets a chance to act before a resource is actually deleted +after it's been marked for deletion. Without finalizers, the resource would be deleted directly +by the Kubernetes server. + +Depending on your use case, you might or might not need to use finalizers. In particular, if +your operator doesn't need to clean any state that would not be automatically managed by the +Kubernetes cluster (e.g. external resources), you might not need to use finalizers. You should +use the +Kubernetes [garbage collection](https://kubernetes.io/docs/concepts/architecture/garbage-collection/#owners-dependents) +mechanism as much as possible by setting owner references for your secondary resources so that +the cluster can automatically delete them for you whenever the associated primary resource is +deleted. Note that setting owner references is the responsibility of the `Reconciler` +implementation, though [dependent resources](https://javaoperatorsdk.io/docs/dependent-resources) +make that process easier. + +If you do need to clean such a state, you need to use finalizers so that their +presence will prevent the Kubernetes server from deleting the resource before your operator is +ready to allow it. This allows for clean-up even if your operator was down when the resource was marked for deletion. + +JOSDK makes cleaning resources in this fashion easier by taking care of managing finalizers +automatically for you when needed. The only thing you need to do is let the SDK know that your +operator is interested in cleaning the state associated with your primary resources by having it +implement +the [`Cleaner

`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java) +interface. If your `Reconciler` doesn't implement the `Cleaner` interface, the SDK will consider +that you don't need to perform any clean-up when resources are deleted and will, therefore, not activate finalizer support. +In other words, finalizer support is added only if your `Reconciler` implements the `Cleaner` interface. + +The framework automatically adds finalizers as the first step, thus after a resource +is created but before the first reconciliation. The finalizer is added via a separate +Kubernetes API call. As a result of this update, the finalizer will then be present on the +resource. The reconciliation can then proceed as normal. + +The automatically added finalizer will also be removed after the `cleanup` is executed on +the reconciler. This behavior is customizable as explained +[above](#using-updatecontrol-and-deletecontrol) when we addressed the use of +`DeleteControl`. + +You can specify the name of the finalizer to use for your `Reconciler` using the +[`@ControllerConfiguration`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java) +annotation. If you do not specify a finalizer name, one will be automatically generated for you. + +From v5, by default, the finalizer is added using Server Side Apply. See also `UpdateControl` in docs. + +### Making sure the primary resource is up to date for the next reconciliation + +It is typical to want to update the status subresource with the information that is available during the reconciliation. +This is sometimes referred to as the last observed state. When the primary resource is updated, though, the framework +does not cache the resource directly, relying instead on the propagation of the update to the underlying informer's +cache. It can, therefore, happen that, if other events trigger other reconciliations, before the informer cache gets +updated, your reconciler does not see the latest version of the primary resource. While this might not typically be a +problem in most cases, as caches eventually become consistent, depending on your reconciliation logic, you might still +require the latest status version possible, for example, if the status subresource is used to store allocated values. +See [Representing Allocated Values](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#representing-allocated-values) +from the Kubernetes docs for more details. + +The framework provides the +[`PrimaryUpdateAndCacheUtils`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java) utility class +to help with these use cases. + +This class' methods use internal caches in combination with update methods that leveraging +optimistic locking. If the update method fails on optimistic locking, it will retry +using a fresh resource from the server as base for modification. + +```java +@Override +public UpdateControl reconcile( + StatusPatchCacheCustomResource resource, Context context) { + + // omitted logic + + // update with SSA requires a fresh copy + var freshCopy = createFreshCopy(primary); + freshCopy.getStatus().setValue(statusWithState()); + + var updatedResource = PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResource(resource, freshCopy, context); + + return UpdateControl.noUpdate(); + } +``` + +After the update `PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResource` puts the result of the update into an internal +cache and the framework will make sure that the next reconciliation contains the most recent version of the resource. +Note that it is not necessarily the same version returned as response from the update, it can be a newer version since other parties +can do additional updates meanwhile. However, unless it has been explicitly modified, that +resource will contain the up-to-date status. + + +See related integration test [here](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache). diff --git a/docs/content/en/docs/faq/_index.md b/docs/content/en/docs/faq/_index.md index 5e4975a385..9308ce4cfa 100644 --- a/docs/content/en/docs/faq/_index.md +++ b/docs/content/en/docs/faq/_index.md @@ -1,9 +1,9 @@ --- title: FAQ -weight: 80 +weight: 90 --- -### Q: How can I access the events which triggered the Reconciliation? +### How can I access the events which triggered the Reconciliation? In the v1.* version events were exposed to `Reconciler` (which was called `ResourceController` then). This included events (Create, Update) of the custom resource, but also events produced by @@ -16,7 +16,7 @@ sound agreement between the developers that this is the way to go. Note that this is also consistent with Kubernetes [level based](https://cloud.redhat.com/blog/kubernetes-operators-best-practices) reconciliation approach. -### Q: Can I re-schedule a reconciliation, possibly with a specific delay? +### Can I re-schedule a reconciliation, possibly with a specific delay? Yes, this can be done using [`UpdateControl`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java) @@ -46,7 +46,7 @@ without an update: Although you might consider using `EventSources`, to handle reconciliation triggering in a smarter way. -### Q: How can I run an operator without cluster scope rights? +### How can I run an operator without cluster scope rights? By default, JOSDK requires access to CRs at cluster scope. You may not be granted such rights and you will see some error at startup that looks like: @@ -74,7 +74,7 @@ is `true` (`false` by default). To disable, set it to `false` at [Operator-level Operator operator = new Operator( override -> override.checkingCRDAndValidateLocalModel(false)); ``` -### Q: I'm managing an external resource that has a generated ID, where should I store that? +### I'm managing an external resource that has a generated ID, where should I store that? It is common that a non-Kubernetes or external resource is managed from a controller. Those external resources might have a generated ID, so are not simply addressable based on the spec of a custom resources. Therefore, the @@ -91,8 +91,39 @@ it is not guaranteed that during the next reconciliation you will see the fresh which do this, usually cache the updated status in memory to make sure it is present for next reconciliation. Dependent Resources feature supports the [first approach](../dependent-resources/_index.md#external-state-tracking-dependent-resources). + +### How can I skip the reconciliation of a dependent resource? -### Q: How to fix `sun.security.provider.certpath.SunCertPathBuilderException` on Rancher Desktop and k3d/k3s Kubernetes +Skipping workflow reconciliation altogether is possible with the explicit invocation feature since v5. +You can read more about this in [v5 release notes](https://javaoperatorsdk.io/blog/2025/01/06/version-5-released/#explicit-workflow-invocation). + +However, what if you want to avoid reconciling a single dependent resource based on some state? +First of all, remember that the dependent resource won't be modified if the desired state and the actual state match. +Moreover, it is generally a good practice to reconcile all your resources, JOSDK taking care of only processing the +resources which state doesn't match the desired one. +However, in some corner cases (for example, if it is expensive to compute the desired state or compare it to the actual +state), it is somtimes useful to be able to only skip the reconcilation of some resources but not all, if it is known +that they don't need to be processed based for example on the status of the custom resource. + +A common mistake is to use `ReconcilePrecondition`, if the condition does not hold it will delete the resources. +This is by design (although it's true that the name of this condition might be misleading), but not what we want in this +case. + +The way to go is to override the matcher in the dependent resource: + +```java +public Result match(R actualResource, R desired, P primary, Context

context) { + if (alreadyIsCertainState(primary.getStatus())) { + return true; + } else { + return super.match(actual, desired, primary, context); + } +} +``` + +This will make sure that the dependent resource is not updated if the primary resource is in certain state. + +### How to fix `sun.security.provider.certpath.SunCertPathBuilderException` on Rancher Desktop and k3d/k3s Kubernetes It's a common issue when using k3d and the fabric8 client tries to connect to the cluster an exception is thrown: @@ -111,4 +142,4 @@ the following dependency on the classpath: org.bouncycastle bcpkix-jdk15on -``` \ No newline at end of file +``` diff --git a/docs/content/en/docs/features/_index.md b/docs/content/en/docs/features/_index.md deleted file mode 100644 index de49abe2b5..0000000000 --- a/docs/content/en/docs/features/_index.md +++ /dev/null @@ -1,853 +0,0 @@ ---- -title: Features -weight: 50 ---- - -# Features - -The Java Operator SDK (JOSDK) is a high level framework and related tooling aimed at -facilitating the implementation of Kubernetes operators. The features are by default following -the best practices in an opinionated way. However, feature flags and other configuration options -are provided to fine tune or turn off these features. - -## Reconciliation Execution in a Nutshell - -Reconciliation execution is always triggered by an event. Events typically come from a -primary resource, most of the time a custom resource, triggered by changes made to that resource -on the server (e.g. a resource is created, updated or deleted). Reconciler implementations are -associated with a given resource type and listens for such events from the Kubernetes API server -so that they can appropriately react to them. It is, however, possible for secondary sources to -trigger the reconciliation process. This usually occurs via -the [event source](#handling-related-events-with-event-sources) mechanism. - -When an event is received reconciliation is executed, unless a reconciliation is already -underway for this particular resource. In other words, the framework guarantees that no -concurrent reconciliation happens for any given resource. - -Once the reconciliation is done, the framework checks if: - -- an exception was thrown during execution and if yes schedules a retry. -- new events were received during the controller execution, if yes schedule a new reconciliation. -- the reconcilier instructed the SDK to re-schedule a reconciliation at a later date, if yes - schedules a timer event with the specified delay. -- none of the above, the reconciliation is finished. - -In summary, the core of the SDK is implemented as an eventing system, where events trigger -reconciliation requests. - -## Implementing a [`Reconciler`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java) and/or [`Cleaner`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java) - -The lifecycle of a Kubernetes resource can be clearly separated into two phases from the -perspective of an operator depending on whether a resource is created or updated, or on the -other hand if it is marked for deletion. - -This separation-related logic is automatically handled by the framework. The framework will always -call the `reconcile` method, unless the custom resource is -[marked from deletion](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/#how-finalizers-work) -. On the other, if the resource is marked from deletion and if the `Reconciler` implements the -`Cleaner` interface, only the `cleanup` method will be called. Implementing the `Cleaner` -interface allows developers to let the SDK know that they are interested in cleaning related -state (e.g. out-of-cluster resources). The SDK will therefore automatically add a finalizer -associated with your `Reconciler` so that the Kubernetes server doesn't delete your resources -before your `Reconciler` gets a chance to clean things up. -See [Finalizer support](#finalizer-support) for more details. - -### Using `UpdateControl` and `DeleteControl` - -These two classes are used to control the outcome or the desired behaviour after the reconciliation. - -The [`UpdateControl`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java) -can instruct the framework to update the status sub-resource of the resource -and/or re-schedule a reconciliation with a desired time delay: - -```java - @Override - public UpdateControl reconcile( - EventSourceTestCustomResource resource, Context context) { - // omitted code - - return UpdateControl.patchStatus(resource).rescheduleAfter(10, TimeUnit.SECONDS); - } -``` - -without an update: - -```java - @Override - public UpdateControl reconcile( - EventSourceTestCustomResource resource, Context context) { - // omitted code - - return UpdateControl.noUpdate().rescheduleAfter(10, TimeUnit.SECONDS); - } -``` - -Note, though, that using `EventSources` should be preferred to rescheduling since the -reconciliation will then be triggered only when needed instead than on a timely basis. - -Those are the typical use cases of resource updates, however in some cases there it can happen that -the controller wants to update the resource itself (for example to add annotations) or not perform -any updates, which is also supported. - -It is also possible to update both the status and the resource with the `patchResourceAndStatus` method. In this case, -the resource is updated first followed by the status, using two separate requests to the Kubernetes API. - -From v5 `UpdateControl` only supports patching the resources, by default -using [Server Side Apply (SSA)](https://kubernetes.io/docs/reference/using-api/server-side-apply/). -It is important to understand how SSA works in Kubernetes. Mainly, resources applied using SSA -should contain only the fields identifying the resource and those the user is interested in (a 'fully specified intent' -in Kubernetes parlance), thus usually using a resource created from scratch, see -[sample](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa). -To contrast, see the same sample, this time [without SSA](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAReconciler.java). - -Non-SSA based patch is still supported. -You can control whether or not to use SSA -using [`ConfigurationServcice.useSSAToPatchPrimaryResource()`](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L385-L385) -and the related `ConfigurationServiceOverrider.withUseSSAToPatchPrimaryResource` method. -Related integration test can be -found [here](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa). - -Handling resources directly using the client, instead of delegating these updates operations to JOSDK by returning -an `UpdateControl` at the end of your reconciliation, should work appropriately. However, we do recommend to -use `UpdateControl` instead since JOSDK makes sure that the operations are handled properly, since there are subtleties -to be aware of. For example, if you are using a finalizer, JOSDK makes sure to include it in your fully specified intent -so that it is not unintentionally removed from the resource (which would happen if you omit it, since your controller is -the designated manager for that field and Kubernetes interprets the finalizer being gone from the specified intent as a -request for removal). - -[`DeleteControl`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DeleteControl.java) -typically instructs the framework to remove the finalizer after the dependent -resource are cleaned up in `cleanup` implementation. - -```java - -public DeleteControl cleanup(MyCustomResource customResource,Context context){ - // omitted code - - return DeleteControl.defaultDelete(); - } - -``` - -However, it is possible to instruct the SDK to not remove the finalizer, this allows to clean up -the resources in a more asynchronous way, mostly for cases when there is a long waiting period -after a delete operation is initiated. Note that in this case you might want to either schedule -a timed event to make sure `cleanup` is executed again or use event sources to get notified -about the state changes of the deleted resource. - -### Finalizer Support - -[Kubernetes finalizers](https://kubernetes.io/docs/concepts/overview/working-with-objects/finalizers/) -make sure that your `Reconciler` gets a chance to act before a resource is actually deleted -after it's been marked for deletion. Without finalizers, the resource would be deleted directly -by the Kubernetes server. - -Depending on your use case, you might or might not need to use finalizers. In particular, if -your operator doesn't need to clean any state that would not be automatically managed by the -Kubernetes cluster (e.g. external resources), you might not need to use finalizers. You should -use the -Kubernetes [garbage collection](https://kubernetes.io/docs/concepts/architecture/garbage-collection/#owners-dependents) -mechanism as much as possible by setting owner references for your secondary resources so that -the cluster can automatically deleted them for you whenever the associated primary resource is -deleted. Note that setting owner references is the responsibility of the `Reconciler` -implementation, though [dependent resources](https://javaoperatorsdk.io/docs/dependent-resources) -make that process easier. - -If you do need to clean such state, you need to use finalizers so that their -presence will prevent the Kubernetes server from deleting the resource before your operator is -ready to allow it. This allows for clean up to still occur even if your operator was down when -the resources was "deleted" by a user. - -JOSDK makes cleaning resources in this fashion easier by taking care of managing finalizers -automatically for you when needed. The only thing you need to do is let the SDK know that your -operator is interested in cleaning state associated with your primary resources by having it -implement -the [`Cleaner

`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java) -interface. If your `Reconciler` doesn't implement the `Cleaner` interface, the SDK will consider -that you don't need to perform any clean-up when resources are deleted and will therefore not -activate finalizer support. In other words, finalizer support is added only if your `Reconciler` -implements the `Cleaner` interface. - -Finalizers are automatically added by the framework as the first step, thus after a resource -is created, but before the first reconciliation. The finalizer is added via a separate -Kubernetes API call. As a result of this update, the finalizer will then be present on the -resource. The reconciliation can then proceed as normal. - -The finalizer that is automatically added will be also removed after the `cleanup` is executed on -the reconciler. This behavior is customizable as explained -[above](#using-updatecontrol-and-deletecontrol) when we addressed the use of -`DeleteControl`. - -You can specify the name of the finalizer to use for your `Reconciler` using the -[`@ControllerConfiguration`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java) -annotation. If you do not specify a finalizer name, one will be automatically generated for you. - -From v5 by default finalizer is added using Served Side Apply. See also UpdateControl in docs. - -## Generation Awareness and Event Filtering - -A best practice when an operator starts up is to reconcile all the associated resources because -changes might have occurred to the resources while the operator was not running. - -When this first reconciliation is done successfully, the next reconciliation is triggered if either -dependent resources are changed or the primary resource `.spec` field is changed. If other fields -like `.metadata` are changed on the primary resource, the reconciliation could be skipped. This -behavior is supported out of the box and reconciliation is by default not triggered if -changes to the primary resource do not increase the `.metadata.generation` field. -Note that changes to `.metada.generation` are automatically handled by Kubernetes. - -To turn off this feature, set `generationAwareEventProcessing` to `false` for the `Reconciler`. - -## Support for Well Known (non-custom) Kubernetes Resources - -A Controller can be registered for a non-custom resource, so well known Kubernetes resources like ( -`Ingress`, `Deployment`,...). - -See -the [integration test](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment) -for reconciling deployments. - -```java -public class DeploymentReconciler - implements Reconciler, TestExecutionInfoProvider { - - @Override - public UpdateControl reconcile( - Deployment resource, Context context) { - // omitted code - } -} -``` - -## Max Interval Between Reconciliations - -When informers / event sources are properly set up, and the `Reconciler` implementation is -correct, no additional reconciliation triggers should be needed. However, it's -a [common practice](https://github.com/java-operator-sdk/java-operator-sdk/issues/848#issuecomment-1016419966) -to have a failsafe periodic trigger in place, just to make sure resources are nevertheless -reconciled after a certain amount of time. This functionality is in place by default, with a -rather high time interval (currently 10 hours) after which a reconciliation will be -automatically triggered even in the absence of other events. See how to override this using the -standard annotation: - -```java -@ControllerConfiguration(maxReconciliationInterval = @MaxReconciliationInterval( - interval = 50, - timeUnit = TimeUnit.MILLISECONDS)) -public class MyReconciler implements Reconciler {} -``` - -The event is not propagated at a fixed rate, rather it's scheduled after each reconciliation. So the -next reconciliation will occur at most within the specified interval after the last reconciliation. - -This feature can be turned off by setting `maxReconciliationInterval` -to [`Constants.NO_MAX_RECONCILIATION_INTERVAL`](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Constants.java#L20-L20) -or any non-positive number. - -The automatic retries are not affected by this feature so a reconciliation will be re-triggered -on error, according to the specified retry policy, regardless of this maximum interval setting. - -## Automatic Retries on Error - -JOSDK will schedule an automatic retry of the reconciliation whenever an exception is thrown by -your `Reconciler`. The retry is behavior is configurable but a default implementation is provided -covering most of the typical use-cases, see -[GenericRetry](https://github.com/java-operator-sdk/java-operator-sdk/blob/master/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java) -. - -```java - GenericRetry.defaultLimitedExponentialRetry() - .setInitialInterval(5000) - .setIntervalMultiplier(1.5D) - .setMaxAttempts(5); -``` - -You can also configure the default retry behavior using the `@GradualRetry` annotation. - -It is possible to provide a custom implementation using the `retry` field of the -`@ControllerConfiguration` annotation and specifying the class of your custom implementation. -Note that this class will need to provide an accessible no-arg constructor for automated -instantiation. Additionally, your implementation can be automatically configured from an -annotation that you can provide by having your `Retry` implementation implement the -`AnnotationConfigurable` interface, parameterized with your annotation type. See the -`GenericRetry` implementation for more details. - -Information about the current retry state is accessible from -the [Context](https://github.com/java-operator-sdk/java-operator-sdk/blob/master/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/Context.java) -object. Of note, particularly interesting is the `isLastAttempt` method, which could allow your -`Reconciler` to implement a different behavior based on this status, by setting an error message -in your resource' status, for example, when attempting a last retry. - -Note, though, that reaching the retry limit won't prevent new events to be processed. New -reconciliations will happen for new events as usual. However, if an error also occurs that -would normally trigger a retry, the SDK won't schedule one at this point since the retry limit -is already reached. - -A successful execution resets the retry state. - -### Setting Error Status After Last Retry Attempt - -In order to facilitate error reporting, `Reconciler` can implement the -[ErrorStatusHandler](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ErrorStatusHandler.java) -interface: - -```java -public interface ErrorStatusHandler

{ - - ErrorStatusUpdateControl

updateErrorStatus(P resource, Context

context, Exception e); - -} -``` - -The `updateErrorStatus` method is called in case an exception is thrown from the `Reconciler`. It is -also called even if no retry policy is configured, just after the reconciler execution. -`RetryInfo.getAttemptCount()` is zero after the first reconciliation attempt, since it is not a -result of a retry (regardless of whether a retry policy is configured or not). - -`ErrorStatusUpdateControl` is used to tell the SDK what to do and how to perform the status -update on the primary resource, always performed as a status sub-resource request. Note that -this update request will also produce an event, and will result in a reconciliation if the -controller is not generation aware. - -This feature is only available for the `reconcile` method of the `Reconciler` interface, since -there should not be updates to resource that have been marked for deletion. - -Retry can be skipped in cases of unrecoverable errors: - -```java - ErrorStatusUpdateControl.patchStatus(customResource).withNoRetry(); -``` - -### Correctness and Automatic Retries - -While it is possible to deactivate automatic retries, this is not desirable, unless for very -specific reasons. Errors naturally occur, whether it be transient network errors or conflicts -when a given resource is handled by a `Reconciler` but is modified at the same time by a user in -a different process. Automatic retries handle these cases nicely and will usually result in a -successful reconciliation. - -## Retry and Rescheduling and Event Handling Common Behavior - -Retry, reschedule and standard event processing form a relatively complex system, each of these -functionalities interacting with the others. In the following, we describe the interplay of -these features: - -1. A successful execution resets a retry and the rescheduled executions which were present before - the reconciliation. However, a new rescheduling can be instructed from the reconciliation - outcome (`UpdateControl` or `DeleteControl`). - - For example, if a reconciliation had previously been re-scheduled after some amount of time, but an event triggered - the reconciliation (or cleanup) in the mean time, the scheduled execution would be automatically cancelled, i.e. - re-scheduling a reconciliation does not guarantee that one will occur exactly at that time, it simply guarantees that - one reconciliation will occur at that time at the latest, triggering one if no event from the cluster triggered one. - Of course, it's always possible to re-schedule a new reconciliation at the end of that "automatic" reconciliation. - - Similarly, if a retry was scheduled, any event from the cluster triggering a successful execution in the mean time - would cancel the scheduled retry (because there's now no point in retrying something that already succeeded) - -2. In case an exception happened, a retry is initiated. However, if an event is received - meanwhile, it will be reconciled instantly, and this execution won't count as a retry attempt. -3. If the retry limit is reached (so no more automatic retry would happen), but a new event - received, the reconciliation will still happen, but won't reset the retry, and will still be - marked as the last attempt in the retry info. The point (1) still holds, but in case of an - error, no retry will happen. - -The thing to keep in mind when it comes to retrying or rescheduling is that JOSDK tries to avoid unnecessary work. When -you reschedule an operation, you instruct JOSDK to perform that operation at the latest by the end of the rescheduling -delay. If something occurred on the cluster that triggers that particular operation (reconciliation or cleanup), then -JOSDK considers that there's no point in attempting that operation again at the end of the specified delay since there -is now no point to do so anymore. The same idea also applies to retries. - -## Rate Limiting - -It is possible to rate limit reconciliation on a per-resource basis. The rate limit also takes -precedence over retry/re-schedule configurations: for example, even if a retry was scheduled for -the next second but this request would make the resource go over its rate limit, the next -reconciliation will be postponed according to the rate limiting rules. Note that the -reconciliation is never cancelled, it will just be executed as early as possible based on rate -limitations. - -Rate limiting is by default turned **off**, since correct configuration depends on the reconciler -implementation, in particular, on how long a typical reconciliation takes. -(The parallelism of reconciliation itself can be -limited [`ConfigurationService`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ce4d996ee073ebef5715737995fc3d33f4751275/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L120-L120) -by configuring the `ExecutorService` appropriately.) - -A default rate limiter implementation is provided, see: -[`PeriodRateLimiter`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ce4d996ee073ebef5715737995fc3d33f4751275/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/PeriodRateLimiter.java#L14-L14) -. -Users can override it by implementing their own -[`RateLimiter`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ce4d996ee073ebef5715737995fc3d33f4751275/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/RateLimiter.java) -and specifying this custom implementation using the `rateLimiter` field of the -`@ControllerConfiguration` annotation. Similarly to the `Retry` implementations, -`RateLimiter` implementations must provide an accessible, no-arg constructor for instantiation -purposes and can further be automatically configured from your own, provided annotation provided -your `RateLimiter` implementation also implements the `AnnotationConfigurable` interface, -parameterized by your custom annotation type. - -To configure the default rate limiter use the `@RateLimited` annotation on your -`Reconciler` class. The following configuration limits each resource to reconcile at most twice -within a 3 second interval: - -```java - -@RateLimited(maxReconciliations = 2, within = 3, unit = TimeUnit.SECONDS) -@ControllerConfiguration -public class MyReconciler implements Reconciler { - -} -``` - -Thus, if a given resource was reconciled twice in one second, no further reconciliation for this -resource will happen before two seconds have elapsed. Note that, since rate is limited on a -per-resource basis, other resources can still be reconciled at the same time, as long, of course, -that they stay within their own rate limits. - -## Handling Related Events with Event Sources - -See also -this [blog post](https://csviri.medium.com/java-operator-sdk-introduction-to-event-sources-a1aab5af4b7b) -. - -Event sources are a relatively simple yet powerful and extensible concept to trigger controller -executions, usually based on changes to dependent resources. You typically need an event source -when you want your `Reconciler` to be triggered when something occurs to secondary resources -that might affect the state of your primary resource. This is needed because a given -`Reconciler` will only listen by default to events affecting the primary resource type it is -configured for. Event sources act as listen to events affecting these secondary resources so -that a reconciliation of the associated primary resource can be triggered when needed. Note that -these secondary resources need not be Kubernetes resources. Typically, when dealing with -non-Kubernetes objects or services, we can extend our operator to handle webhooks or websockets -or to react to any event coming from a service we interact with. This allows for very efficient -controller implementations because reconciliations are then only triggered when something occurs -on resources affecting our primary resources thus doing away with the need to periodically -reschedule reconciliations. - -![Event Sources architecture diagram](../assets/images/event-sources.png) - -There are few interesting points here: - -The `CustomResourceEventSource` event source is a special one, responsible for handling events -pertaining to changes affecting our primary resources. This `EventSource` is always registered -for every controller automatically by the SDK. It is important to note that events always relate -to a given primary resource. Concurrency is still handled for you, even in the presence of -`EventSource` implementations, and the SDK still guarantees that there is no concurrent execution of -the controller for any given primary resource (though, of course, concurrent/parallel executions -of events pertaining to other primary resources still occur as expected). - -### Caching and Event Sources - -Kubernetes resources are handled in a declarative manner. The same also holds true for event -sources. For example, if we define an event source to watch for changes of a Kubernetes Deployment -object using an `InformerEventSource`, we always receive the whole associated object from the -Kubernetes API. This object might be needed at any point during our reconciliation process and -it's best to retrieve it from the event source directly when possible instead of fetching it -from the Kubernetes API since the event source guarantees that it will provide the latest -version. Not only that, but many event source implementations also cache resources they handle -so that it's possible to retrieve the latest version of resources without needing to make any -calls to the Kubernetes API, thus allowing for very efficient controller implementations. - -Note after an operator starts, caches are already populated by the time the first reconciliation -is processed for the `InformerEventSource` implementation. However, this does not necessarily -hold true for all event source implementations (`PerResourceEventSource` for example). The SDK -provides methods to handle this situation elegantly, allowing you to check if an object is -cached, retrieving it from a provided supplier if not. See -related [method](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java#L146) -. - -### Registering Event Sources - -To register event sources, your `Reconciler` has to override the `prepareEventSources` and return -list of event sources to register. One way to see this in action is -to look at the -[tomcat example](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java) -(irrelevant details omitted): - -```java - -@ControllerConfiguration -public class WebappReconciler - implements Reconciler, Cleaner, EventSourceInitializer { - // ommitted code - - @Override - public Map prepareEventSources(EventSourceContext context) { - InformerEventSourceConfiguration configuration = - InformerEventSourceConfiguration.from(Tomcat.class, Tomcat.class) - .withSecondaryToPrimaryMapper(webappsMatchingTomcatName) - .withPrimaryToSecondaryMapper( - (Webapp primary) -> Set.of(new ResourceID(primary.getSpec().getTomcat(), - primary.getMetadata().getNamespace()))) - .build(); - return EventSourceInitializer - .nameEventSources(new InformerEventSource<>(configuration, context)); - } - -} -``` - -In the example above an `InformerEventSource` is configured and registered. -`InformerEventSource` is one of the bundled `EventSource` implementations that JOSDK provides to -cover common use cases. - -### Managing Relation between Primary and Secondary Resources - -Event sources let your operator know when a secondary resource has changed and that your -operator might need to reconcile this new information. However, in order to do so, the SDK needs -to somehow retrieve the primary resource associated with which ever secondary resource triggered -the event. In the `Tomcat` example above, when an event occurs on a tracked `Deployment`, the -SDK needs to be able to identify which `Tomcat` resource is impacted by that change. - -Seasoned Kubernetes users already know one way to track this parent-child kind of relationship: -using owner references. Indeed, that's how the SDK deals with this situation by default as well, -that is, if your controller properly set owner references on your secondary resources, the SDK -will be able to follow that reference back to your primary resource automatically without you -having to worry about it. - -However, owner references cannot always be used as they are restricted to operating within a -single namespace (i.e. you cannot have an owner reference to a resource in a different namespace) -and are, by essence, limited to Kubernetes resources so you're out of luck if your secondary -resources live outside of a cluster. - -This is why JOSDK provides the `SecondayToPrimaryMapper` interface so that you can provide -alternative ways for the SDK to identify which primary resource needs to be reconciled when -something occurs to your secondary resources. We even provide some of these alternatives in the -[Mappers](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java) -class. - -Note that, while a set of `ResourceID` is returned, this set usually consists only of one -element. It is however possible to return multiple values or even no value at all to cover some -rare corner cases. Returning an empty set means that the mapper considered the secondary -resource event as irrelevant and the SDK will thus not trigger a reconciliation of the primary -resource in that situation. - -Adding a `SecondaryToPrimaryMapper` is typically sufficient when there is a one-to-many relationship -between primary and secondary resources. The secondary resources can be mapped to its primary -owner, and this is enough information to also get these secondary resources from the `Context` -object that's passed to your `Reconciler`. - -There are however cases when this isn't sufficient and you need to provide an explicit mapping -between a primary resource and its associated secondary resources using an implementation of the -`PrimaryToSecondaryMapper` interface. This is typically needed when there are many-to-one or -many-to-many relationships between primary and secondary resources, e.g. when the primary resource -is referencing secondary resources. -See [PrimaryToSecondaryIT](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java) -integration test for a sample. - -### Built-in EventSources - -There are multiple event-sources provided out of the box, the following are some more central ones: - -#### `InformerEventSource` - -[InformerEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java) -is probably the most important `EventSource` implementation to know about. When you create an -`InformerEventSource`, JOSDK will automatically create and register a `SharedIndexInformer`, a -fabric8 Kubernetes client class, that will listen for events associated with the resource type -you configured your `InformerEventSource` with. If you want to listen to Kubernetes resource -events, `InformerEventSource` is probably the only thing you need to use. It's highly -configurable so you can tune it to your needs. Take a look at -[InformerEventSourceConfiguration](https://github.com/operator-framework/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java) -and associated classes for more details but some interesting features we can mention here is the -ability to filter events so that you can only get notified for events you care about. A -particularly interesting feature of the `InformerEventSource`, as opposed to using your own -informer-based listening mechanism is that caches are particularly well optimized preventing -reconciliations from being triggered when not needed and allowing efficient operators to be written. - -#### `PerResourcePollingEventSource` - -[PerResourcePollingEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java) -is used to poll external APIs, which don't support webhooks or other event notifications. It -extends the abstract -[ExternalResourceCachingEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java) -to support caching. -See [MySQL Schema sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java) -for usage. - -#### `PollingEventSource` - -[PollingEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java) -is similar to `PerResourceCachingEventSource` except that, contrary to that event source, it -doesn't poll a specific API separately per resource, but periodically and independently of -actually observed primary resources. - -#### Inbound event sources - -[SimpleInboundEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/SimpleInboundEventSource.java) -and -[CachingInboundEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java) -are used to handle incoming events from webhooks and messaging systems. - -#### `ControllerResourceEventSource` - -[ControllerResourceEventSource](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerResourceEventSource.java) -is a special `EventSource` implementation that you will never have to deal with directly. It is, -however, at the core of the SDK is automatically added for you: this is the main event source -that listens for changes to your primary resources and triggers your `Reconciler` when needed. -It features smart caching and is really optimized to minimize Kubernetes API accesses and avoid -triggering unduly your `Reconciler`. - -More on the philosophy of the non Kubernetes API related event source see in -issue [#729](https://github.com/java-operator-sdk/java-operator-sdk/issues/729). - -## Contextual Info for Logging with MDC - -Logging is enhanced with additional contextual information using -[MDC](http://www.slf4j.org/manual.html#mdc). The following attributes are available in most -parts of reconciliation logic and during the execution of the controller: - -| MDC Key | Value added from primary resource | -|:---------------------------|:----------------------------------| -| `resource.apiVersion` | `.apiVersion` | -| `resource.kind` | `.kind` | -| `resource.name` | `.metadata.name` | -| `resource.namespace` | `.metadata.namespace` | -| `resource.resourceVersion` | `.metadata.resourceVersion` | -| `resource.generation` | `.metadata.generation` | -| `resource.uid` | `.metadata.uid` | - -For more information about MDC see this [link](https://www.baeldung.com/mdc-in-log4j-2-logback). - -## InformerEventSource Multi-Cluster Support - -It is possible to handle resources for remote cluster with `InformerEventSource`. To do so, -simply set a client that connects to a remote cluster: - -```java - -InformerEventSourceConfiguration configuration = - InformerEventSourceConfiguration.from(SecondaryResource.class, PrimaryResource.class) - .withKubernetesClient(remoteClusterClient) - .withSecondaryToPrimaryMapper(Mappers.fromDefaultAnnotations()); - -``` - -You will also need to specify a `SecondaryToPrimaryMapper`, since the default one -is based on owner references and won't work across cluster instances. You could, for example, use the provided implementation that relies on annotations added to the secondary resources to identify the associated primary resource. - -See related [integration test](https://github.com/operator-framework/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster). - -## Dynamically Changing Target Namespaces - -A controller can be configured to watch a specific set of namespaces in addition of the -namespace in which it is currently deployed or the whole cluster. The framework supports -dynamically changing the list of these namespaces while the operator is running. -When a reconciler is registered, an instance of -[`RegisteredController`](https://github.com/java-operator-sdk/java-operator-sdk/blob/ec37025a15046d8f409c77616110024bf32c3416/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java#L5) -is returned, providing access to the methods allowing users to change watched namespaces as the -operator is running. - -A typical scenario would probably involve extracting the list of target namespaces from a -`ConfigMap` or some other input but this part is out of the scope of the framework since this is -use-case specific. For example, reacting to changes to a `ConfigMap` would probably involve -registering an associated `Informer` and then calling the `changeNamespaces` method on -`RegisteredController`. - -```java - -public static void main(String[] args) { - KubernetesClient client = new DefaultKubernetesClient(); - Operator operator = new Operator(client); - RegisteredController registeredController = operator.register(new WebPageReconciler(client)); - operator.installShutdownHook(); - operator.start(); - - // call registeredController further while operator is running -} - -``` - -If watched namespaces change for a controller, it might be desirable to propagate these changes to -`InformerEventSources` associated with the controller. In order to express this, -`InformerEventSource` implementations interested in following such changes need to be -configured appropriately so that the `followControllerNamespaceChanges` method returns `true`: - -```java - -@ControllerConfiguration -public class MyReconciler implements Reconciler { - - @Override - public Map prepareEventSources( - EventSourceContext context) { - - InformerEventSource configMapES = - new InformerEventSource<>(InformerEventSourceConfiguration.from(ConfigMap.class, TestCustomResource.class) - .withNamespacesInheritedFromController(context) - .build(), context); - - return EventSourceUtils.nameEventSources(configMapES); - } - -} -``` - -As seen in the above code snippet, the informer will have the initial namespaces inherited from -controller, but also will adjust the target namespaces if it changes for the controller. - -See also -the [integration test](https://github.com/operator-framework/java-operator-sdk/tree/main/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace) -for this feature. - -## Leader Election - -Operators are generally deployed with a single running or active instance. However, it is -possible to deploy multiple instances in such a way that only one, called the "leader", processes the -events. This is achieved via a mechanism called "leader election". While all the instances are -running, and even start their event sources to populate the caches, only the leader will process -the events. This means that should the leader change for any reason, for example because it -crashed, the other instances are already warmed up and ready to pick up where the previous -leader left off should one of them become elected leader. - -See sample configuration in -the [E2E test](https://github.com/java-operator-sdk/java-operator-sdk/blob/8865302ac0346ee31f2d7b348997ec2913d5922b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestOperator.java#L21-L23) -. - -## Runtime Info - -[RuntimeInfo](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RuntimeInfo.java#L16-L16) -is used mainly to check the actual health of event sources. Based on this information it is easy to implement custom -liveness probes. - -[stopOnInformerErrorDuringStartup](https://github.com/java-operator-sdk/java-operator-sdk/blob/main/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationService.java#L168-L168) -setting, where this flag usually needs to be set to false, in order to control the exact liveness properties. - -See also an example implementation in the -[WebPage sample](https://github.com/java-operator-sdk/java-operator-sdk/blob/3e2e7c4c834ef1c409d636156b988125744ca911/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java#L38-L43) - -## Automatic Generation of CRDs - -Note that this feature is provided by the -[Fabric8 Kubernetes Client](https://github.com/fabric8io/kubernetes-client), not JOSDK itself. - -To automatically generate CRD manifests from your annotated Custom Resource classes, you only need -to add the following dependencies to your project: - -```xml - - - io.fabric8 - crd-generator-apt - provided - -``` - -The CRD will be generated in `target/classes/META-INF/fabric8` (or -in `target/test-classes/META-INF/fabric8`, if you use the `test` scope) with the CRD name -suffixed by the generated spec version. For example, a CR using the `java-operator-sdk.io` group -with a `mycrs` plural form will result in 2 files: - -- `mycrs.java-operator-sdk.io-v1.yml` -- `mycrs.java-operator-sdk.io-v1beta1.yml` - -**NOTE:** -> Quarkus users using the `quarkus-operator-sdk` extension do not need to add any extra dependency -> to get their CRD generated as this is handled by the extension itself. - -## Metrics - -JOSDK provides built-in support for metrics reporting on what is happening with your reconcilers in the form of -the `Metrics` interface which can be implemented to connect to your metrics provider of choice, JOSDK calling the -methods as it goes about reconciling resources. By default, a no-operation implementation is provided thus providing a -no-cost sane default. A [micrometer](https://micrometer.io)-based implementation is also provided. - -You can use a different implementation by overriding the default one provided by the default `ConfigurationService`, as -follows: - -```java -Metrics metrics; // initialize your metrics implementation -Operator operator = new Operator(client, o -> o.withMetrics(metrics)); -``` - -### Micrometer implementation - -The micrometer implementation is typically created using one of the provided factory methods which, depending on which -is used, will return either a ready to use instance or a builder allowing users to customized how the implementation -behaves, in particular when it comes to the granularity of collected metrics. It is, for example, possible to collect -metrics on a per-resource basis via tags that are associated with meters. This is the default, historical behavior but -this will change in a future version of JOSDK because this dramatically increases the cardinality of metrics, which -could lead to performance issues. - -To create a `MicrometerMetrics` implementation that behaves how it has historically behaved, you can just create an -instance via: - -```java -MeterRegistry registry; // initialize your registry implementation -Metrics metrics = new MicrometerMetrics(registry); -``` - -Note, however, that this constructor is deprecated and we encourage you to use the factory methods instead, which either -return a fully pre-configured instance or a builder object that will allow you to configure more easily how the instance -will behave. You can, for example, configure whether or not the implementation should collect metrics on a per-resource -basis, whether or not associated meters should be removed when a resource is deleted and how the clean-up is performed. -See the relevant classes documentation for more details. - -For example, the following will create a `MicrometerMetrics` instance configured to collect metrics on a per-resource -basis, deleting the associated meters after 5 seconds when a resource is deleted, using up to 2 threads to do so. - -```java -MicrometerMetrics.newPerResourceCollectingMicrometerMetricsBuilder(registry) - .withCleanUpDelayInSeconds(5) - .withCleaningThreadNumber(2) - .build(); -``` - -### Operator SDK metrics - -The micrometer implementation records the following metrics: - -| Meter name | Type | Tag names | Description | -|-------------------------------------------------------------|----------------|-------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------| -| operator.sdk.reconciliations.executions.`` | gauge | group, version, kind | Number of executions of the named reconciler | -| operator.sdk.reconciliations.queue.size.`` | gauge | group, version, kind | How many resources are queued to get reconciled by named reconciler | -| operator.sdk.``.size | gauge map size | | Gauge tracking the size of a specified map (currently unused but could be used to monitor caches size) | -| operator.sdk.events.received | counter | ``, event, action | Number of received Kubernetes events | -| operator.sdk.events.delete | counter | `` | Number of received Kubernetes delete events | -| operator.sdk.reconciliations.started | counter | ``, reconciliations.retries.last, reconciliations.retries.number | Number of started reconciliations per resource type | -| operator.sdk.reconciliations.failed | counter | ``, exception | Number of failed reconciliations per resource type | -| operator.sdk.reconciliations.success | counter | `` | Number of successful reconciliations per resource type | -| operator.sdk.controllers.execution.reconcile | timer | ``, controller | Time taken for reconciliations per controller | -| operator.sdk.controllers.execution.cleanup | timer | ``, controller | Time taken for cleanups per controller | -| operator.sdk.controllers.execution.reconcile.success | counter | controller, type | Number of successful reconciliations per controller | -| operator.sdk.controllers.execution.reconcile.failure | counter | controller, exception | Number of failed reconciliations per controller | -| operator.sdk.controllers.execution.cleanup.success | counter | controller, type | Number of successful cleanups per controller | -| operator.sdk.controllers.execution.cleanup.failure | counter | controller, exception | Number of failed cleanups per controller | - -As you can see all the recorded metrics start with the `operator.sdk` prefix. ``, in the table above, -refers to resource-specific metadata and depends on the considered metric and how the implementation is configured and -could be summed up as follows: `group?, version, kind, [name, namespace?], scope` where the tags in square -brackets (`[]`) won't be present when per-resource collection is disabled and tags followed by a question mark are -omitted if the associated value is empty. Of note, when in the context of controllers' execution metrics, these tag -names are prefixed with `resource.`. This prefix might be removed in a future version for greater consistency. - -## Optimizing Caches - -One of the ideas around the operator pattern is that all the relevant resources are cached, thus reconciliation is -usually very fast (especially if no resources are updated in the process) since the operator is then mostly working with -in-memory state. However for large clusters, caching huge amount of primary and secondary resources might consume lots -of memory. JOSDK provides ways to mitigate this issue and optimize the memory usage of controllers. While these features -are working and tested, we need feedback from real production usage. - -### Bounded Caches for Informers - -Limiting caches for informers - thus for Kubernetes resources - is supported by ensuring that resources are in the cache -for a limited time, via a cache eviction of least recently used resources. This means that when resources are created -and frequently reconciled, they stay "hot" in the cache. However, if, over time, a given resource "cools" down, i.e. it -becomes less and less used to the point that it might not be reconciled anymore, it will eventually get evicted from the -cache to free up memory. If such an evicted resource were to become reconciled again, the bounded cache implementation -would then fetch it from the API server and the "hot/cold" cycle would start anew. - -Since all resources need to be reconciled when a controller start, it is not practical to set a maximal cache size as -it's desirable that all resources be cached as soon as possible to make the initial reconciliation process on start as -fast and efficient as possible, avoiding undue load on the API server. It's therefore more interesting to gradually -evict cold resources than try to limit cache sizes. - -See usage of the related implementation using [Caffeine](https://github.com/ben-manes/caffeine) cache in integration -tests -for [primary resources](https://github.com/java-operator-sdk/java-operator-sdk/blob/902c8a562dfd7f8993a52e03473a7ad4b00f378b/caffeine-bounded-cache-support/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/sample/AbstractTestReconciler.java#L29-L29). - -See -also [CaffeineBoundedItemStores](https://github.com/java-operator-sdk/java-operator-sdk/blob/902c8a562dfd7f8993a52e03473a7ad4b00f378b/caffeine-bounded-cache-support/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/CaffeineBoundedItemStores.java) -for more details. - - diff --git a/docs/content/en/docs/getting-started/_index.md b/docs/content/en/docs/getting-started/_index.md index e3a3f95788..df8a4b77fe 100644 --- a/docs/content/en/docs/getting-started/_index.md +++ b/docs/content/en/docs/getting-started/_index.md @@ -1,58 +1,4 @@ --- -title: Getting Started - -weight: 20 ---- - -## Introduction & Resources on Operators - -Operators manage both cluster and non-cluster resources on behalf of Kubernetes. This Java -Operator SDK (JOSDK) aims at making it as easy as possible to write Kubernetes operators in Java -using an API that should feel natural to Java developers and without having to worry about many -low-level details that the SDK handles automatically. - -For an introduction on operators, please see this -[blog post](https://blog.container-solutions.com/kubernetes-operators-explained). -or [this talk](https://www.youtube.com/watch?v=CvftaV-xrB4) - -You can read about the common problems JOSDK is solving for you -[here](https://blog.container-solutions.com/a-deep-dive-into-the-java-operator-sdk). - -You can also refer to the -[Writing Kubernetes operators using JOSDK blog series](https://developers.redhat.com/articles/2022/02/15/write-kubernetes-java-java-operator-sdk) -. - -## Generating Project Skeleton - -Project includes a maven plugin to generate a skeleton project: - -```shell -mvn io.javaoperatorsdk:bootstrapper:[version]:create -DprojectGroupId=org.acme -DprojectArtifactId=getting-started -``` - -## Getting Started - -The easiest way to get started with SDK is to start -[minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) and -execute one of our [examples](https://github.com/java-operator-sdk/java-operator-sdk/tree/main/sample-operators). -There is a dedicated page to describe how to [use the samples](/docs/using-samples). - -Here are the main steps to develop the code and deploy the operator to a Kubernetes cluster. -A more detailed and specific version can be found under `samples/mysql-schema/README.md`. - -1. Setup `kubectl` to work with your Kubernetes cluster of choice. -1. Apply Custom Resource Definition -1. Compile the whole project (framework + samples) using `mvn install` in the root directory -1. Run the main class of the sample you picked and check out the sample's README to see what it - does. When run locally the framework will use your Kubernetes client configuration (in `~/. - kube/config`) to establish a connection to the cluster. This is why it was important to set - up `kubectl` up front. -1. You can work in this local development mode to play with the code. -1. Build the Docker image and push it to the registry -1. Apply RBAC configuration -1. Apply deployment configuration -1. Verify if the operator is up and running. Don't run it locally anymore to avoid conflicts in - processing events from the cluster's API server. - - - +title: Getting started +weight: 10 +--- \ No newline at end of file diff --git a/docs/content/en/docs/getting-started/bootstrap-and-samples.md b/docs/content/en/docs/getting-started/bootstrap-and-samples.md new file mode 100644 index 0000000000..597ed3477e --- /dev/null +++ b/docs/content/en/docs/getting-started/bootstrap-and-samples.md @@ -0,0 +1,38 @@ +--- +title: Bootstrapping and samples +weight: 20 +--- + +## Generating Project Skeleton + +Project includes a maven plugin to generate a skeleton project: + +```shell +mvn io.javaoperatorsdk:bootstrapper:[version]:create -DprojectGroupId=org.acme -DprojectArtifactId=getting-started +``` + +You can build this project with maven, +the build will generate also the [CustomResourceDefinition](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) +for you. + +## Getting started with samples + +You can find examples under [sample-operators](https://github.com/java-operator-sdk/java-operator-sdk/tree/master/sample-operators) +directory which are intended to demonstrate the usage of different components in different scenarios, but mainly are more real world +examples: + +* *webpage*: Simple example creating an NGINX webserver from a Custom Resource containing HTML code. We provide more + flavors of implementation, both with the low level APIs and higher level abstractions. +* *mysql-schema*: Operator managing schemas in a MySQL database. Shows how to manage non Kubernetes resources. +* *tomcat*: Operator with two controllers, managing Tomcat instances and Webapps running in Tomcat. The intention + with this example to show how to manage multiple related custom resources and/or more controllers. + +The easiest way to run / try out is to run one of the samples on +[minikube](https://kubernetes.io/docs/tasks/tools/install-minikube/) or [kind](https://kind.sigs.k8s.io/). +After applying the generated CRD, you can simply run your main class. The controller will automatically +start communicate with you local Kubernetes cluster and reconcile custom resource after you create one. + +See also detailed instructions under [`samples/mysql-schema/README.md`](https://github.com/operator-framework/java-operator-sdk/blob/main/sample-operators/mysql-schema/README.md). + + + diff --git a/docs/content/en/docs/getting-started/intro-to-operators.md b/docs/content/en/docs/getting-started/intro-to-operators.md new file mode 100644 index 0000000000..6247bef288 --- /dev/null +++ b/docs/content/en/docs/getting-started/intro-to-operators.md @@ -0,0 +1,32 @@ +--- +title: Introduction to Kubernetes operators +weight: 15 +--- + +## Introduction & Resources + +Operators manage both cluster and non-cluster resources on behalf of Kubernetes. Java +Operator SDK (JOSDK) aims to make it as easy as possible to implement a Kubernetes operators in Java. +The APIs are designed to feel natural to Java developers. In addition the framework tries to +handle common problem out of the box, so you don't have to worry about generic sub-problems. + +For an introduction on operators, please see this +[blog post](https://blog.container-solutions.com/kubernetes-operators-explained). + +For introductions to JOSDK see [this talk](https://www.youtube.com/watch?v=CvftaV-xrB4). + +You can read about the common problems JOSDK is solving for you +[here](https://blog.container-solutions.com/a-deep-dive-into-the-java-operator-sdk). + +You can also refer to the +[Writing Kubernetes operators using JOSDK blog series](https://developers.redhat.com/articles/2022/02/15/write-kubernetes-java-java-operator-sdk). + + +## Operators in General + - [Implementing Kubernetes Operators in Java talk](https://www.youtube.com/watch?v=CvftaV-xrB4) + - [Introduction of the concept of Kubernetes Operators](https://blog.container-solutions.com/kubernetes-operators-explained) + - [Operator pattern explained in Kubernetes documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) + - [An explanation why Java Operators makes sense](https://blog.container-solutions.com/cloud-native-java-infrastructure-automation-with-kubernetes-operators) + - [What are the problems an operator framework is solving](https://csviri.medium.com/deep-dive-building-a-kubernetes-operator-sdk-for-java-developers-5008218822cb) + - [Writing Kubernetes operators using JOSDK blog series](https://developers.redhat.com/articles/2022/02/15/write-kubernetes-java-java-operator-sdk) + diff --git a/docs/content/en/docs/patterns-and-best-practices/_index.md b/docs/content/en/docs/getting-started/patterns-best-practices.md similarity index 99% rename from docs/content/en/docs/patterns-and-best-practices/_index.md rename to docs/content/en/docs/getting-started/patterns-best-practices.md index a2b3b716b6..575c82af72 100644 --- a/docs/content/en/docs/patterns-and-best-practices/_index.md +++ b/docs/content/en/docs/getting-started/patterns-best-practices.md @@ -1,9 +1,8 @@ --- -title: Patterns and Best Practices +title: Patterns and best practices weight: 25 --- - This document describes patterns and best practices, to build and run operators, and how to implement them in terms of the Java Operator SDK (JOSDK). diff --git a/docs/content/en/docs/glossary/_index.md b/docs/content/en/docs/glossary/_index.md index 187a0bfb4a..1523f68a23 100644 --- a/docs/content/en/docs/glossary/_index.md +++ b/docs/content/en/docs/glossary/_index.md @@ -1,6 +1,6 @@ --- title: Glossary -weight: 40 +weight: 100 --- - **Primary Resource** - the resource that represents the desired state that the controller is diff --git a/docs/content/en/docs/intro-to-operators/_index.md b/docs/content/en/docs/intro-to-operators/_index.md deleted file mode 100644 index 54fc7d82a8..0000000000 --- a/docs/content/en/docs/intro-to-operators/_index.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Introduction to Operators - -weight: 10 ---- - -This page provides a selection of articles that gives an introduction to Kubernetes operators. - -## Operators in General - - [Implementing Kubernetes Operators in Java talk](https://www.youtube.com/watch?v=CvftaV-xrB4) - - [Introduction of the concept of Kubernetes Operators](https://blog.container-solutions.com/kubernetes-operators-explained) - - [Operator pattern explained in Kubernetes documentation](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/) - - [An explanation why Java Operators makes sense](https://blog.container-solutions.com/cloud-native-java-infrastructure-automation-with-kubernetes-operators) - - [What are the problems an operator framework is solving](https://csviri.medium.com/deep-dive-building-a-kubernetes-operator-sdk-for-java-developers-5008218822cb) - - [Writing Kubernetes operators using JOSDK blog series](https://developers.redhat.com/articles/2022/02/15/write-kubernetes-java-java-operator-sdk) - diff --git a/docs/content/en/docs/migration/_index.md b/docs/content/en/docs/migration/_index.md index 9d7ca4d7f2..115adab35d 100644 --- a/docs/content/en/docs/migration/_index.md +++ b/docs/content/en/docs/migration/_index.md @@ -1,4 +1,5 @@ --- title: Migrations +weight: 150 --- diff --git a/docs/content/en/docs/using-samples/_index.md b/docs/content/en/docs/using-samples/_index.md deleted file mode 100644 index 62319860ac..0000000000 --- a/docs/content/en/docs/using-samples/_index.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -title: Using sample Operators -weight: 30 ---- - -We have examples under [sample-operators](https://github.com/java-operator-sdk/java-operator-sdk/tree/master/sample-operators) -directory which are intended to demonstrate the usage of different components in different scenarios, but mainly are more real world -examples: - -* *webpage*: Simple example creating an NGINX webserver from a Custom Resource containing HTML code. -* *mysql-schema*: Operator managing schemas in a MySQL database. Shows how to manage non Kubernetes resources. -* *tomcat*: Operator with two controllers, managing Tomcat instances and Webapps running in Tomcat. The intention - with this example to show how to manage multiple related custom resources and/or more controllers. - -# Implementing a Sample Operator - -Add [dependency](https://search.maven.org/search?q=a:operator-framework%20AND%20g:io.javaoperatorsdk) to your project with Maven: - -```xml - - - io.javaoperatorsdk - operator-framework - {see https://search.maven.org/search?q=a:operator-framework%20AND%20g:io.javaoperatorsdk for latest version} - -``` - -Or alternatively with Gradle, which also requires declaring the SDK as an annotation processor to generate the mappings -between controllers and custom resource classes: - -```groovy -dependencies { - implementation "io.javaoperatorsdk:operator-framework:${javaOperatorVersion}" - annotationProcessor "io.javaoperatorsdk:operator-framework:${javaOperatorVersion}" -} -``` - -Once you've added the dependency, define a main method initializing the Operator and registering a controller. - -```java -public class Runner { - - public static void main(String[] args) { - Operator operator = new Operator(); - operator.register(new WebPageReconciler()); - operator.start(); - } -} -``` - -The Controller implements the business logic and describes all the classes needed to handle the CRD. - -```java - -@ControllerConfiguration -public class WebPageReconciler implements Reconciler { - - // Return the changed resource, so it gets updated. See javadoc for details. - @Override - public UpdateControl reconcile(CustomService resource, - Context context) { - // ... your logic ... - return UpdateControl.patchStatus(resource); - } -} -``` - -A sample custom resource POJO representation - -```java - -@Group("sample.javaoperatorsdk") -@Version("v1") -public class WebPage extends CustomResource implements - Namespaced { -} - -public class WebServerSpec { - - private String html; - - public String getHtml() { - return html; - } - - public void setHtml(String html) { - this.html = html; - } -} -``` - -### Deactivating CustomResource implementations validation - -The operator will, by default, query the deployed CRDs to check that the `CustomResource` -implementations match what is known to the cluster. This requires an additional query to the cluster and, sometimes, -elevated privileges for the operator to be able to read the CRDs from the cluster. This validation is mostly meant to -help users new to operator development get started and avoid common mistakes. Advanced users or production deployments -might want to skip this step. This is done by setting the `CHECK_CRD_ENV_KEY` environment variable to `false`. - -### Automatic generation of CRDs - -To automatically generate CRD manifests from your annotated Custom Resource classes, you only need to add the following -dependencies to your project (in the background an annotation processor is used), with Maven: - -```xml - - - io.fabric8 - crd-generator-apt - provided - -``` - -or with Gradle: - -```groovy -dependencies { - annotationProcessor 'io.fabric8:crd-generator-apt:' - ... -} -``` - -The CRD will be generated in `target/classes/META-INF/fabric8` (or in `target/test-classes/META-INF/fabric8`, if you use -the `test` scope) with the CRD name suffixed by the generated spec version. For example, a CR using -the `java-operator-sdk.io` group with a `mycrs` plural form will result in 2 files: - -- `mycrs.java-operator-sdk.io-v1.yml` -- `mycrs.java-operator-sdk.io-v1beta1.yml` - -**NOTE:** -> Quarkus users using the `quarkus-operator-sdk` extension do not need to add any extra dependency to get their CRD generated as this is handled by the extension itself. - -### Quarkus - -A [Quarkus](https://quarkus.io) extension is also provided to ease the development of Quarkus-based operators. - -Add [this dependency](https://search.maven.org/search?q=a:quarkus-operator-sdk) -to your project: - -```xml - - - io.quarkiverse.operatorsdk - quarkus-operator-sdk - {see https://search.maven.org/search?q=a:quarkus-operator-sdk for latest version} - - -``` - -Create an Application, Quarkus will automatically create and inject a `KubernetesClient` ( -or `OpenShiftClient`), `Operator`, `ConfigurationService` and `ResourceController` instances that your application can -use. Below, you can see the minimal code you need to write to get your operator and controllers up and running: - -```java - -@QuarkusMain -public class QuarkusOperator implements QuarkusApplication { - - @Inject - Operator operator; - - public static void main(String... args) { - Quarkus.run(QuarkusOperator.class, args); - } - - @Override - public int run(String... args) throws Exception { - operator.start(); - Quarkus.waitForExit(); - return 0; - } -} -``` - -### Spring Boot - -You can also let Spring Boot wire your application together and automatically register the controllers. - -Add [this dependency](https://search.maven.org/search?q=a:operator-framework-spring-boot-starter%20AND%20g:io.javaoperatorsdk) to your project: - -```xml - - - io.javaoperatorsdk - operator-framework-spring-boot-starter - {see https://search.maven.org/search?q=a:operator-framework-spring-boot-starter%20AND%20g:io.javaoperatorsdk for - latest version} - - -``` - -Create an Application - -```java - -@SpringBootApplication -public class Application { - - public static void main(String[] args) { - SpringApplication.run(Application.class, args); - } -} -``` - -You will also need a `@Configuration` to make sure that your reconciler is registered: - -```java - -@Configuration -public class Config { - - @Bean - public WebPageReconciler customServiceController() { - return new WebPageReconciler(); - } - - @Bean(initMethod = "start", destroyMethod = "stop") - @SuppressWarnings("rawtypes") - public Operator operator(List controllers) { - Operator operator = new Operator(); - controllers.forEach(operator::register); - return operator; - } -} -``` - -#### Spring Boot test support - -Adding the following dependency would let you mock the operator for the tests where loading the spring container is -necessary, but it doesn't need real access to a Kubernetes cluster. - -```xml - - - io.javaoperatorsdk - operator-framework-spring-boot-starter-test - {see https://search.maven.org/search?q=a:operator-framework-spring-boot-starter%20AND%20g:io.javaoperatorsdk for - latest version} - - -``` - -Mock the operator: - -```java - -@SpringBootTest -@EnableMockOperator -public class SpringBootStarterSampleApplicationTest { - - @Test - void contextLoads() { - } -} -``` diff --git a/docs/go.mod b/docs/go.mod index 00890a6408..65768398bb 100644 --- a/docs/go.mod +++ b/docs/go.mod @@ -2,4 +2,4 @@ module github.com/google/docsy-example go 1.12 -require github.com/google/docsy v0.10.0 // indirect +require github.com/google/docsy v0.11.0 // indirect diff --git a/docs/go.sum b/docs/go.sum index 1b3ed24b03..c7a05f45f1 100644 --- a/docs/go.sum +++ b/docs/go.sum @@ -1,9 +1,12 @@ github.com/FortAwesome/Font-Awesome v0.0.0-20240108205627-a1232e345536/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= github.com/FortAwesome/Font-Awesome v0.0.0-20240402185447-c0f460dca7f7/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= +github.com/FortAwesome/Font-Awesome v0.0.0-20240716171331-37eff7fa00de/go.mod h1:IUgezN/MFpCDIlFezw3L8j83oeiIuYoj28Miwr/KUYo= github.com/google/docsy v0.9.1 h1:+jqges1YCd+yHeuZ1BUvD8V8mEGVtPxULg5j/vaJ984= github.com/google/docsy v0.9.1/go.mod h1:saOqKEUOn07Bc0orM/JdIF3VkOanHta9LU5Y53bwN2U= github.com/google/docsy v0.10.0 h1:6tMDacPwAyRWNCfvsn/9qGOZDQ8b0aRzjRZvnZPY5dg= github.com/google/docsy v0.10.0/go.mod h1:c0nIAqmRTOuJ01F85U/wJPQtc3Zj9N58Kea9bOT2AJc= +github.com/google/docsy v0.11.0 h1:QnV40cc28QwS++kP9qINtrIv4hlASruhC/K3FqkHAmM= +github.com/google/docsy v0.11.0/go.mod h1:hGGW0OjNuG5ZbH5JRtALY3yvN8ybbEP/v2iaK4bwOUI= github.com/twbs/bootstrap v5.2.3+incompatible h1:lOmsJx587qfF7/gE7Vv4FxEofegyJlEACeVV+Mt7cgc= github.com/twbs/bootstrap v5.2.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= github.com/twbs/bootstrap v5.3.3+incompatible/go.mod h1:fZTSrkpSf0/HkL0IIJzvVspTt1r9zuf7XlZau8kpcY0= diff --git a/docs/hugo.toml b/docs/hugo.toml index b4535c08af..435cad2451 100644 --- a/docs/hugo.toml +++ b/docs/hugo.toml @@ -160,20 +160,20 @@ enable = false [params.links] [[params.links.user]] - name ="Twitter" - url = "https://twitter.com/javaoperatorsdk" - icon = "fab fa-twitter" - desc = "Follow us on Twitter to get the latest news!" + name ="BlueSky" + url = "https://bsky.app/profile/javaoperatorsdk.bsky.social" + icon = "fa-brands fa-bluesky" + desc = "Follow us on BlueSky to get the latest news!" [[params.links.user]] name = "Slack" url = "https://kubernetes.slack.com/archives/CAW0GV7A5" icon = "fab fa-slack" -desc = "Chat with other project developers" +desc = "Chat with other project developers on Kubernetes slack" [[params.links.user]] name = "Discord" url = "https://discord.gg/DacEhAy" icon = "fab fa-discord" -desc = "Chat with others on Discord" +desc = "Chat with others on our dedicated Discord server" #[[params.links.user]] # name = "Stack Overflow" # url = "https://example.org/stack" diff --git a/docs/layouts/partials/scripts/mermaid.html b/docs/layouts/partials/scripts/mermaid.html new file mode 100644 index 0000000000..ac9290a10f --- /dev/null +++ b/docs/layouts/partials/scripts/mermaid.html @@ -0,0 +1,68 @@ + +{{ $version := .Site.Params.mermaid.version | default "latest" -}} + +{{ $cdnurl := printf "https://cdn.jsdelivr.net/npm/mermaid@%s/dist/mermaid.esm.min.mjs" $version -}} +{{ with try (resources.GetRemote $cdnurl) -}} +{{ with .Err -}} +{{ errorf "Could not retrieve mermaid script from CDN. Reason: %s." . -}} +{{ end -}} +{{ else -}} +{{ errorf "Invalid Mermaid version %s, could not retrieve this version from CDN." $version -}} +{{ end -}} + + \ No newline at end of file diff --git a/docs/package.json b/docs/package.json index 02cc7f496a..a5a57eaa4c 100644 --- a/docs/package.json +++ b/docs/package.json @@ -1,7 +1,7 @@ { - "name": "docsy-example-site", - "version": "0.9.1", - "version.next": "0.9.2-dev.0-unreleased", + "name": "java-operator-sdk", + "version": "0.10.0", + "version.next": "0.10.1-dev.0-unreleased", "description": "Example site that uses Docsy theme for technical documentation.", "repository": "github:google/docsy-example", "homepage": "https://javaoperatorsdk.io", @@ -34,7 +34,7 @@ "update:pkg:hugo": "npm install --save-dev --save-exact hugo-extended@latest" }, "devDependencies": { - "autoprefixer": "^10.4.19", + "autoprefixer": "^10.4.20", "cross-env": "^7.0.3", "hugo-extended": "0.125.4", "postcss-cli": "^11.0.0" diff --git a/docs/static/images/event-sources.png b/docs/static/images/event-sources.png new file mode 100644 index 0000000000..f37e1f72d9 Binary files /dev/null and b/docs/static/images/event-sources.png differ diff --git a/micrometer-support/pom.xml b/micrometer-support/pom.xml index 67fe0d4b4c..9d9a47464f 100644 --- a/micrometer-support/pom.xml +++ b/micrometer-support/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT micrometer-support diff --git a/micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java b/micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java index 07106d9b3c..26f149249b 100644 --- a/micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java +++ b/micrometer-support/src/main/java/io/javaoperatorsdk/operator/monitoring/micrometer/MicrometerMetrics.java @@ -89,8 +89,8 @@ public static MicrometerMetricsBuilder newMicrometerMetricsBuilder(MeterRegistry * @return a MicrometerMetrics instance configured to not collect per-resource metrics * @see PerResourceCollectingMicrometerMetricsBuilder */ - public static PerResourceCollectingMicrometerMetricsBuilder newPerResourceCollectingMicrometerMetricsBuilder( - MeterRegistry registry) { + public static PerResourceCollectingMicrometerMetricsBuilder + newPerResourceCollectingMicrometerMetricsBuilder(MeterRegistry registry) { return new PerResourceCollectingMicrometerMetricsBuilder(registry); } @@ -103,8 +103,8 @@ public static PerResourceCollectingMicrometerMetricsBuilder newPerResourceCollec * @param cleaner the {@link Cleaner} to use * @param collectingPerResourceMetrics whether to collect per resource metrics */ - private MicrometerMetrics(MeterRegistry registry, Cleaner cleaner, - boolean collectingPerResourceMetrics) { + private MicrometerMetrics( + MeterRegistry registry, Cleaner cleaner, boolean collectingPerResourceMetrics) { this.registry = registry; this.cleaner = cleaner; this.collectPerResourceMetrics = collectingPerResourceMetrics; @@ -144,17 +144,17 @@ public T timeControllerExecution(ControllerExecution execution) { .publishPercentileHistogram() .register(registry); try { - final var result = timer.record(() -> { - try { - return execution.execute(); - } catch (Exception e) { - throw new OperatorException(e); - } - }); + final var result = + timer.record( + () -> { + try { + return execution.execute(); + } catch (Exception e) { + throw new OperatorException(e); + } + }); final var successType = execution.successTypeName(result); - registry - .counter(execName + SUCCESS_SUFFIX, CONTROLLER, name, TYPE, successType) - .increment(); + registry.counter(execName + SUCCESS_SUFFIX, CONTROLLER, name, TYPE, successType).increment(); return result; } catch (Exception e) { final var exception = e.getClass().getSimpleName(); @@ -168,12 +168,16 @@ public T timeControllerExecution(ControllerExecution execution) { @Override public void receivedEvent(Event event, Map metadata) { if (event instanceof ResourceEvent) { - incrementCounter(event.getRelatedCustomResourceID(), EVENTS_RECEIVED, + incrementCounter( + event.getRelatedCustomResourceID(), + EVENTS_RECEIVED, metadata, Tag.of(EVENT, event.getClass().getSimpleName()), Tag.of(ACTION, ((ResourceEvent) event).getAction().toString())); } else { - incrementCounter(event.getRelatedCustomResourceID(), EVENTS_RECEIVED, + incrementCounter( + event.getRelatedCustomResourceID(), + EVENTS_RECEIVED, metadata, Tag.of(EVENT, event.getClass().getSimpleName())); } @@ -187,14 +191,18 @@ public void cleanupDoneFor(ResourceID resourceID, Map metadata) } @Override - public void reconcileCustomResource(HasMetadata resource, RetryInfo retryInfoNullable, - Map metadata) { + public void reconcileCustomResource( + HasMetadata resource, RetryInfo retryInfoNullable, Map metadata) { Optional retryInfo = Optional.ofNullable(retryInfoNullable); - incrementCounter(ResourceID.fromResource(resource), RECONCILIATIONS_STARTED, + incrementCounter( + ResourceID.fromResource(resource), + RECONCILIATIONS_STARTED, metadata, - Tag.of(RECONCILIATIONS_RETRIES_NUMBER, + Tag.of( + RECONCILIATIONS_RETRIES_NUMBER, String.valueOf(retryInfo.map(RetryInfo::getAttemptCount).orElse(0))), - Tag.of(RECONCILIATIONS_RETRIES_LAST, + Tag.of( + RECONCILIATIONS_RETRIES_LAST, String.valueOf(retryInfo.map(RetryInfo::isLastAttempt).orElse(true)))); var controllerQueueSize = @@ -226,15 +234,18 @@ public void reconciliationExecutionFinished(HasMetadata resource, Map metadata) { + public void failedReconciliation( + HasMetadata resource, Exception exception, Map metadata) { var cause = exception.getCause(); if (cause == null) { cause = exception; } else if (cause instanceof RuntimeException) { cause = cause.getCause() != null ? cause.getCause() : cause; } - incrementCounter(ResourceID.fromResource(resource), RECONCILIATIONS_FAILED, metadata, + incrementCounter( + ResourceID.fromResource(resource), + RECONCILIATIONS_FAILED, + metadata, Tag.of(EXCEPTION, cause.getClass().getSimpleName())); } @@ -243,9 +254,8 @@ public void failedReconciliation(HasMetadata resource, Exception exception, return registry.gaugeMapSize(PREFIX + name + SIZE_SUFFIX, Collections.emptyList(), map); } - - private void addMetadataTags(ResourceID resourceID, Map metadata, - List tags, boolean prefixed) { + private void addMetadataTags( + ResourceID resourceID, Map metadata, List tags, boolean prefixed) { if (collectPerResourceMetrics) { addTag(NAME, resourceID.getName(), tags, prefixed); addTagOmittingOnEmptyValue(NAMESPACE, resourceID.getNamespace().orElse(null), tags, prefixed); @@ -261,8 +271,8 @@ private static void addTag(String name, String value, List tags, boolean pr tags.add(Tag.of(getPrefixedMetadataTag(name, prefixed), value)); } - private static void addTagOmittingOnEmptyValue(String name, String value, List tags, - boolean prefixed) { + private static void addTagOmittingOnEmptyValue( + String name, String value, List tags, boolean prefixed) { if (value != null && !value.isBlank()) { addTag(name, value, tags, prefixed); } @@ -282,8 +292,8 @@ private static void addGVKTags(GroupVersionKind gvk, List tags, boolean pre addTag(KIND, gvk.getKind(), tags, prefixed); } - private void incrementCounter(ResourceID id, String counterName, Map metadata, - Tag... additionalTags) { + private void incrementCounter( + ResourceID id, String counterName, Map metadata, Tag... additionalTags) { final var additionalTagsNb = additionalTags != null && additionalTags.length > 0 ? additionalTags.length : 0; final var metadataNb = metadata != null ? metadata.size() : 0; @@ -314,8 +324,8 @@ private PerResourceCollectingMicrometerMetricsBuilder(MeterRegistry registry) { /** * @param cleaningThreadsNumber the maximal number of threads that can be assigned to the - * removal of {@link Meter}s associated with deleted resources, defaults to 1 if not - * specified or if the provided number is lesser or equal to 0 + * removal of {@link Meter}s associated with deleted resources, defaults to 1 if not + * specified or if the provided number is lesser or equal to 0 */ public PerResourceCollectingMicrometerMetricsBuilder withCleaningThreadNumber( int cleaningThreadsNumber) { @@ -325,11 +335,11 @@ public PerResourceCollectingMicrometerMetricsBuilder withCleaningThreadNumber( /** * @param cleanUpDelayInSeconds the number of seconds to wait before {@link Meter}s are removed - * for deleted resources, defaults to 1 (meaning meters will be removed one second after - * the associated resource is deleted) if not specified or if the provided number is - * lesser than 0. Threading and the general interaction model of interacting with the API - * server means that it's not possible to ensure that meters are immediately deleted in - * all cases so a minimal delay of one second is always enforced + * for deleted resources, defaults to 1 (meaning meters will be removed one second after the + * associated resource is deleted) if not specified or if the provided number is lesser than + * 0. Threading and the general interaction model of interacting with the API server means + * that it's not possible to ensure that meters are immediately deleted in all cases so a + * minimal delay of one second is always enforced */ public PerResourceCollectingMicrometerMetricsBuilder withCleanUpDelayInSeconds( int cleanUpDelayInSeconds) { @@ -344,6 +354,7 @@ public MicrometerMetrics build() { return new MicrometerMetrics(registry, cleaner, true); } } + public static class MicrometerMetricsBuilder { protected final MeterRegistry registry; private boolean collectingPerResourceMetrics = true; @@ -352,9 +363,7 @@ private MicrometerMetricsBuilder(MeterRegistry registry) { this.registry = registry; } - /** - * Configures the instance to collect metrics on a per-resource basis. - */ + /** Configures the instance to collect metrics on a per-resource basis. */ @SuppressWarnings("unused") public PerResourceCollectingMicrometerMetricsBuilder collectingMetricsPerResource() { collectingPerResourceMetrics = true; @@ -422,8 +431,8 @@ static class DelayedCleaner extends MicrometerMetrics.DefaultCleaner { private final ScheduledExecutorService metersCleaner; private final int cleanUpDelayInSeconds; - private DelayedCleaner(MeterRegistry registry, int cleanUpDelayInSeconds, - int cleaningThreadsNumber) { + private DelayedCleaner( + MeterRegistry registry, int cleanUpDelayInSeconds, int cleaningThreadsNumber) { super(registry); this.cleanUpDelayInSeconds = cleanUpDelayInSeconds; this.metersCleaner = Executors.newScheduledThreadPool(cleaningThreadsNumber); @@ -432,8 +441,8 @@ private DelayedCleaner(MeterRegistry registry, int cleanUpDelayInSeconds, @Override public void removeMetersFor(ResourceID resourceID) { // schedule deletion of meters associated with ResourceID - metersCleaner.schedule(() -> super.removeMetersFor(resourceID), - cleanUpDelayInSeconds, TimeUnit.SECONDS); + metersCleaner.schedule( + () -> super.removeMetersFor(resourceID), cleanUpDelayInSeconds, TimeUnit.SECONDS); } } } diff --git a/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/AbstractMicrometerMetricsTestFixture.java b/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/AbstractMicrometerMetricsTestFixture.java index 8575f07243..788253e5e8 100644 --- a/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/AbstractMicrometerMetricsTestFixture.java +++ b/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/AbstractMicrometerMetricsTestFixture.java @@ -25,7 +25,6 @@ public abstract class AbstractMicrometerMetricsTestFixture { protected final MicrometerMetrics metrics = getMetrics(); protected static final String testResourceName = "micrometer-metrics-cr"; - @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() @@ -33,21 +32,23 @@ public abstract class AbstractMicrometerMetricsTestFixture { .withReconciler(new MetricsCleaningTestReconciler()) .build(); - protected abstract MicrometerMetrics getMetrics(); @Test void properlyHandlesResourceDeletion() throws Exception { - var testResource = new ConfigMapBuilder() - .withNewMetadata() - .withName(testResourceName) - .endMetadata() - .build(); + var testResource = + new ConfigMapBuilder().withNewMetadata().withName(testResourceName).endMetadata().build(); final var created = operator.create(testResource); // make sure the resource is created - await().until(() -> !operator.get(ConfigMap.class, testResourceName) - .getMetadata().getFinalizers().isEmpty()); + await() + .until( + () -> + !operator + .get(ConfigMap.class, testResourceName) + .getMetadata() + .getFinalizers() + .isEmpty()); final var resourceID = ResourceID.fromResource(created); final var meters = preDeleteChecks(resourceID); diff --git a/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/DelayedMetricsCleaningOnDeleteIT.java b/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/DelayedMetricsCleaningOnDeleteIT.java index 26dfe59f84..92929d5ddb 100644 --- a/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/DelayedMetricsCleaningOnDeleteIT.java +++ b/micrometer-support/src/test/java/io/javaoperatorsdk/operator/monitoring/micrometer/DelayedMetricsCleaningOnDeleteIT.java @@ -15,7 +15,9 @@ public class DelayedMetricsCleaningOnDeleteIT extends AbstractMicrometerMetricsT @Override protected MicrometerMetrics getMetrics() { return MicrometerMetrics.newPerResourceCollectingMicrometerMetricsBuilder(registry) - .withCleanUpDelayInSeconds(testDelay).withCleaningThreadNumber(2).build(); + .withCleanUpDelayInSeconds(testDelay) + .withCleaningThreadNumber(2) + .build(); } @Override diff --git a/operator-framework-bom/pom.xml b/operator-framework-bom/pom.xml index 7f1617c457..123ae032c7 100644 --- a/operator-framework-bom/pom.xml +++ b/operator-framework-bom/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk operator-framework-bom - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT pom Operator SDK - Bill of Materials Java SDK for implementing Kubernetes operators @@ -78,25 +78,34 @@ com.diffplug.spotless spotless-maven-plugin - ${spotless.version} pom.xml ./**/pom.xml - + + false + - - contributing/eclipse-google-style.xml - + + true + - contributing/eclipse.importorder + java,javax,org,io,com,,\# - + + + + + apply + + compile + + diff --git a/operator-framework-core/pom.xml b/operator-framework-core/pom.xml index a4dc87a3d3..b4166a3761 100644 --- a/operator-framework-core/pom.xml +++ b/operator-framework-core/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT ../pom.xml diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/AggregatedOperatorException.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/AggregatedOperatorException.java index 2fdc80d606..611b92bf24 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/AggregatedOperatorException.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/AggregatedOperatorException.java @@ -24,9 +24,11 @@ public Map getAggregatedExceptions() { @Override public String getMessage() { - return super.getMessage() + " " + causes.entrySet().stream() - .map(entry -> entry.getKey() + " -> " + exceptionDescription(entry)) - .collect(Collectors.joining("\n - ", "Details:\n - ", "")); + return super.getMessage() + + " " + + causes.entrySet().stream() + .map(entry -> entry.getKey() + " -> " + exceptionDescription(entry)) + .collect(Collectors.joining("\n - ", "Details:\n - ", "")); } private static String exceptionDescription(Entry entry) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java index 8f33036b74..48e07e939d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/BuilderUtils.java @@ -1,4 +1,3 @@ - package io.javaoperatorsdk.operator; import java.lang.reflect.Constructor; @@ -15,8 +14,12 @@ public static B newBuilder(Class builderType, T item) { try { Constructor constructor = builderType.getDeclaredConstructor(builderTargetType); return constructor.newInstance(item); - } catch (NoSuchMethodException | SecurityException | InstantiationException - | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { + } catch (NoSuchMethodException + | SecurityException + | InstantiationException + | IllegalAccessException + | IllegalArgumentException + | InvocationTargetException e) { throw new OperatorException( "Failied to instantiate builder: " + builderType.getCanonicalName() + " using: " + item, e); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ControllerManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ControllerManager.java index a755e4db7e..2d6e4c91f0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ControllerManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ControllerManager.java @@ -23,6 +23,7 @@ class ControllerManager { @SuppressWarnings("rawtypes") private final Map controllers = new HashMap<>(); + private boolean started = false; private final ExecutorServiceManager executorServiceManager; @@ -30,7 +31,6 @@ public ControllerManager(ExecutorServiceManager executorServiceManager) { this.executorServiceManager = executorServiceManager; } - public synchronized void shouldStart() { if (started) { return; @@ -41,27 +41,36 @@ public synchronized void shouldStart() { } public synchronized void start(boolean startEventProcessor) { - executorServiceManager.boundedExecuteAndWaitForAllToComplete(controllers().stream(), c -> { - c.start(startEventProcessor); - return null; - }, c -> "Controller Starter for: " + c.getConfiguration().getName()); + executorServiceManager.boundedExecuteAndWaitForAllToComplete( + controllers().stream(), + c -> { + c.start(startEventProcessor); + return null; + }, + c -> "Controller Starter for: " + c.getConfiguration().getName()); started = true; } public synchronized void stop() { - executorServiceManager.boundedExecuteAndWaitForAllToComplete(controllers().stream(), c -> { - log.debug("closing {}", c); - c.stop(); - return null; - }, c -> "Controller Stopper for: " + c.getConfiguration().getName()); + executorServiceManager.boundedExecuteAndWaitForAllToComplete( + controllers().stream(), + c -> { + log.debug("closing {}", c); + c.stop(); + return null; + }, + c -> "Controller Stopper for: " + c.getConfiguration().getName()); started = false; } public synchronized void startEventProcessing() { - executorServiceManager.boundedExecuteAndWaitForAllToComplete(controllers().stream(), c -> { - c.startEventProcessing(); - return null; - }, c -> "Event processor starter for: " + c.getConfiguration().getName()); + executorServiceManager.boundedExecuteAndWaitForAllToComplete( + controllers().stream(), + c -> { + c.startEventProcessing(); + return null; + }, + c -> "Event processor starter for: " + c.getConfiguration().getName()); } @SuppressWarnings("rawtypes") diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java index d6ee17a383..72f23d628e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/LeaderElectionManager.java @@ -39,8 +39,8 @@ public class LeaderElectionManager { private String leaseNamespace; private String leaseName; - LeaderElectionManager(ControllerManager controllerManager, - ConfigurationService configurationService) { + LeaderElectionManager( + ControllerManager controllerManager, ConfigurationService configurationService) { this.controllerManager = controllerManager; this.configurationService = configurationService; } @@ -52,8 +52,10 @@ public boolean isLeaderElectionEnabled() { private void init(LeaderElectionConfiguration config) { this.identity = identity(config); leaseNamespace = - config.getLeaseNamespace().orElseGet( - () -> configurationService.getKubernetesClient().getConfiguration().getNamespace()); + config + .getLeaseNamespace() + .orElseGet( + () -> configurationService.getKubernetesClient().getConfiguration().getNamespace()); if (leaseNamespace == null) { final var message = "Lease namespace is not set and cannot be inferred. Leader election cannot continue."; @@ -62,25 +64,25 @@ private void init(LeaderElectionConfiguration config) { } leaseName = config.getLeaseName(); final var lock = new LeaseLock(leaseNamespace, leaseName, identity); - leaderElector = new LeaderElectorBuilder( - configurationService.getKubernetesClient(), - configurationService.getExecutorServiceManager().cachingExecutorService()) - .withConfig( - new LeaderElectionConfig( - lock, - config.getLeaseDuration(), - config.getRenewDeadline(), - config.getRetryPeriod(), - leaderCallbacks(config), - // this is required to be false to receive stop event in all cases, thus stopLeading - // is called always when leadership is lost/cancelled - false, - leaseName)) - .build(); + leaderElector = + new LeaderElectorBuilder( + configurationService.getKubernetesClient(), + configurationService.getExecutorServiceManager().cachingExecutorService()) + .withConfig( + new LeaderElectionConfig( + lock, + config.getLeaseDuration(), + config.getRenewDeadline(), + config.getRetryPeriod(), + leaderCallbacks(config), + // this is required to be false to receive stop event in all cases, thus + // stopLeading + // is called always when leadership is lost/cancelled + false, + leaseName)) + .build(); } - - private LeaderCallbacks leaderCallbacks(LeaderElectionConfiguration config) { return new LeaderCallbacks( () -> { @@ -141,25 +143,33 @@ private void checkLeaseAccess() { review.setSpec(new SelfSubjectRulesReviewSpecBuilder().withNamespace(leaseNamespace).build()); var reviewResult = configurationService.getKubernetesClient().resource(review).create(); log.debug("SelfSubjectRulesReview result: {}", reviewResult); - var verbsAllowed = reviewResult.getStatus().getResourceRules().stream() - .filter(rule -> matchesValue(rule.getApiGroups(), COORDINATION_GROUP)) - .filter(rule -> matchesValue(rule.getResources(), LEASES_RESOURCE)) - .filter(rule -> rule.getResourceNames().isEmpty() - || rule.getResourceNames().contains(leaseName)) - .map(ResourceRule::getVerbs) - .flatMap(Collection::stream) - .distinct() - .collect(Collectors.toList()); + var verbsAllowed = + reviewResult.getStatus().getResourceRules().stream() + .filter(rule -> matchesValue(rule.getApiGroups(), COORDINATION_GROUP)) + .filter(rule -> matchesValue(rule.getResources(), LEASES_RESOURCE)) + .filter( + rule -> + rule.getResourceNames().isEmpty() + || rule.getResourceNames().contains(leaseName)) + .map(ResourceRule::getVerbs) + .flatMap(Collection::stream) + .distinct() + .collect(Collectors.toList()); if (verbsAllowed.contains(UNIVERSAL_VALUE) || verbsAllowed.containsAll(verbsRequired)) { return; } - var missingVerbs = verbsRequired.stream() - .filter(Predicate.not(verbsAllowed::contains)) - .collect(Collectors.toList()); - - throw new OperatorException(NO_PERMISSION_TO_LEASE_RESOURCE_MESSAGE + - " in namespace: " + leaseNamespace + "; missing required verbs: " + missingVerbs); + var missingVerbs = + verbsRequired.stream() + .filter(Predicate.not(verbsAllowed::contains)) + .collect(Collectors.toList()); + + throw new OperatorException( + NO_PERMISSION_TO_LEASE_RESOURCE_MESSAGE + + " in namespace: " + + leaseNamespace + + "; missing required verbs: " + + missingVerbs); } private boolean matchesValue(Collection values, String match) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java index 9680bc7e8d..22072bb696 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/Operator.java @@ -29,7 +29,6 @@ public class Operator implements LifecycleAware { private final ConfigurationService configurationService; private volatile boolean started = false; - public Operator() { this((KubernetesClient) null); } @@ -39,12 +38,12 @@ public Operator() { } /** - * Creates an Operator based on the configuration provided by the specified - * {@link ConfigurationService}. If you intend to use different values than the default, you use - * {@link Operator#Operator(Consumer)} instead to override the default with your intended setup. + * Creates an Operator based on the configuration provided by the specified {@link + * ConfigurationService}. If you intend to use different values than the default, you use {@link + * Operator#Operator(Consumer)} instead to override the default with your intended setup. * * @param configurationService a {@link ConfigurationService} providing the configuration for the - * operator + * operator */ public Operator(ConfigurationService configurationService) { this.configurationService = configurationService; @@ -60,14 +59,14 @@ public Operator(ConfigurationService configurationService) { * specified {@link ConfigurationServiceOverrider}. * * @param overrider a {@link ConfigurationServiceOverrider} consumer used to override the default - * {@link ConfigurationService} values + * {@link ConfigurationService} values */ public Operator(Consumer overrider) { this(initConfigurationService(null, overrider)); } - private static ConfigurationService initConfigurationService(KubernetesClient client, - Consumer overrider) { + private static ConfigurationService initConfigurationService( + KubernetesClient client, Consumer overrider) { // initialize the client if the user didn't provide one if (client == null) { var configurationService = ConfigurationService.newOverriddenConfigurationService(overrider); @@ -90,12 +89,12 @@ private static ConfigurationService initConfigurationService(KubernetesClient cl * Adds a shutdown hook that automatically calls {@link #stop()} when the app shuts down. Note * that graceful shutdown is usually not needed, but some {@link Reconciler} implementations might * require it. - *

- * Note that you might want to tune "terminationGracePeriodSeconds" for the Pod running the + * + *

Note that you might want to tune "terminationGracePeriodSeconds" for the Pod running the * controller. * * @param gracefulShutdownTimeout timeout to wait for executor threads to complete actual - * reconciliations + * reconciliations */ @SuppressWarnings("unused") public void installShutdownHook(Duration gracefulShutdownTimeout) { @@ -152,8 +151,8 @@ public void stop() throws OperatorException { if (!started) { return; } - log.info("Operator SDK {} is shutting down...", - configurationService.getVersion().getSdkVersion()); + log.info( + "Operator SDK {} is shutting down...", configurationService.getVersion().getSdkVersion()); controllerManager.stop(); configurationService.getExecutorServiceManager().stop(reconciliationTerminationTimeout); @@ -182,8 +181,8 @@ public

RegisteredController

register(Reconciler

re /** * Add a registration requests for the specified reconciler with this operator, overriding its - * default configuration by the specified one (usually created via - * {@link io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider#override(ControllerConfiguration)}, + * default configuration by the specified one (usually created via {@link + * io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider#override(ControllerConfiguration)}, * passing it the reconciler's original configuration. The effective registration of the * reconciler is delayed till the operator is started. * @@ -193,19 +192,20 @@ public

RegisteredController

register(Reconciler

re * @return registered controller * @throws OperatorException if a problem occurred during the registration process */ - public

RegisteredController

register(Reconciler

reconciler, - ControllerConfiguration

configuration) - throws OperatorException { + public

RegisteredController

register( + Reconciler

reconciler, ControllerConfiguration

configuration) throws OperatorException { if (started) { throw new OperatorException("Operator already started. Register all the controllers before."); } if (configuration == null) { throw new OperatorException( - "Cannot register reconciler with name " + reconciler.getClass().getCanonicalName() + - " reconciler named " + ReconcilerUtils.getNameFor(reconciler) - + " because its configuration cannot be found.\n" + - " Known reconcilers are: " + "Cannot register reconciler with name " + + reconciler.getClass().getCanonicalName() + + " reconciler named " + + ReconcilerUtils.getNameFor(reconciler) + + " because its configuration cannot be found.\n" + + " Known reconcilers are: " + configurationService.getKnownReconcilerNames()); } @@ -214,8 +214,10 @@ public

RegisteredController

register(Reconciler

re controllerManager.add(controller); final var informerConfig = configuration.getInformerConfig(); - final var watchedNS = informerConfig.watchAllNamespaces() ? "[all namespaces]" - : informerConfig.getEffectiveNamespaces(configuration); + final var watchedNS = + informerConfig.watchAllNamespaces() + ? "[all namespaces]" + : informerConfig.getEffectiveNamespaces(configuration); log.info( "Registered reconciler: '{}' for resource: '{}' for namespace(s): {}", @@ -233,10 +235,9 @@ public

RegisteredController

register(Reconciler

re * @return registered controller * @param

the {@code HasMetadata} type associated with the reconciler */ - public

RegisteredController

register(Reconciler

reconciler, - Consumer> configOverrider) { - final var controllerConfiguration = - configurationService.getConfigurationFor(reconciler); + public

RegisteredController

register( + Reconciler

reconciler, Consumer> configOverrider) { + final var controllerConfiguration = configurationService.getConfigurationFor(reconciler); var configToOverride = ControllerConfigurationOverrider.override(controllerConfiguration); configOverrider.accept(configToOverride); return register(reconciler, configToOverride.build()); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java index c2241e4bbb..ea7c58acfb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/ReconcilerUtils.java @@ -81,20 +81,26 @@ public static void checkIfCanAddOwnerReference(HasMetadata owner, HasMetadata re if (owner instanceof Namespaced) { if (!(resource instanceof Namespaced)) { throw new OperatorException( - "Cannot add owner reference from a cluster scoped to a namespace scoped resource." + - resourcesIdentifierDescription(owner, resource)); - } else if (!Objects.equals(owner.getMetadata().getNamespace(), - resource.getMetadata().getNamespace())) { + "Cannot add owner reference from a cluster scoped to a namespace scoped resource." + + resourcesIdentifierDescription(owner, resource)); + } else if (!Objects.equals( + owner.getMetadata().getNamespace(), resource.getMetadata().getNamespace())) { throw new OperatorException( - "Cannot add owner reference between two resource in different namespaces." + - resourcesIdentifierDescription(owner, resource)); + "Cannot add owner reference between two resource in different namespaces." + + resourcesIdentifierDescription(owner, resource)); } } } private static String resourcesIdentifierDescription(HasMetadata owner, HasMetadata resource) { - return " Owner name: " + owner.getMetadata().getName() + " Kind: " + owner.getKind() - + ", Resource name: " + resource.getMetadata().getName() + " Kind: " + resource.getKind(); + return " Owner name: " + + owner.getMetadata().getName() + + " Kind: " + + owner.getKind() + + ", Resource name: " + + resource.getMetadata().getName() + + " Kind: " + + resource.getKind(); } public static String getNameFor(Reconciler reconciler) { @@ -153,10 +159,11 @@ public static Object setSpec(HasMetadata resource, Object spec) { if (spec != null) { setSpecMethod = resourceClass.getMethod(SET_SPEC, spec.getClass()); } else { - setSpecMethod = Arrays.stream(resourceClass.getMethods()) - .filter(method -> SET_SPEC.equals(method.getName())) - .findFirst() - .orElseThrow(() -> noSpecException(resource, null)); + setSpecMethod = + Arrays.stream(resourceClass.getMethods()) + .filter(method -> SET_SPEC.equals(method.getName())) + .findFirst() + .orElseThrow(() -> noSpecException(resource, null)); } return setSpecMethod.invoke(resource, spec); @@ -165,17 +172,17 @@ public static Object setSpec(HasMetadata resource, Object spec) { } } - private static IllegalStateException noSpecException(HasMetadata resource, - ReflectiveOperationException e) { - return new IllegalStateException("No spec found on resource " + resource.getClass().getName(), - e); + private static IllegalStateException noSpecException( + HasMetadata resource, ReflectiveOperationException e) { + return new IllegalStateException( + "No spec found on resource " + resource.getClass().getName(), e); } public static T loadYaml(Class clazz, Class loader, String yaml) { try (InputStream is = loader.getResourceAsStream(yaml)) { if (Builder.class.isAssignableFrom(clazz)) { - return BuilderUtils.newBuilder(clazz, - Serialization.unmarshal(is, BuilderUtils.builderTargetType(clazz))); + return BuilderUtils.newBuilder( + clazz, Serialization.unmarshal(is, BuilderUtils.builderTargetType(clazz))); } return Serialization.unmarshal(is, clazz); } catch (IOException ex) { @@ -190,16 +197,16 @@ public static void handleKubernetesClientException(Exception e, String resourceT if (e instanceof KubernetesClientException ke) { // only throw MissingCRDException if the 404 error occurs on the target CRD - if (404 == ke.getCode() && - (resourceTypeName.equals(ke.getFullResourceName()) + if (404 == ke.getCode() + && (resourceTypeName.equals(ke.getFullResourceName()) || matchesResourceType(resourceTypeName, ke))) { throw new MissingCRDException(resourceTypeName, ke.getVersion(), e.getMessage(), e); } } } - private static boolean matchesResourceType(String resourceTypeName, - KubernetesClientException exception) { + private static boolean matchesResourceType( + String resourceTypeName, KubernetesClientException exception) { final var fullResourceName = exception.getFullResourceName(); if (fullResourceName != null) { return resourceTypeName.equals(fullResourceName); @@ -212,8 +219,8 @@ private static boolean matchesResourceType(String resourceTypeName, if (group.endsWith(".")) { group = group.substring(0, group.length() - 1); } - final var segments = Arrays.stream(group.split("/")).filter(Predicate.not(String::isEmpty)) - .toList(); + final var segments = + Arrays.stream(group.split("/")).filter(Predicate.not(String::isEmpty)).toList(); if (segments.size() != 3) { return false; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java index 88cd0123b0..9db473260b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RegisteredController.java @@ -10,5 +10,4 @@ public interface RegisteredController

extends NamespaceCh ControllerConfiguration

getConfiguration(); ControllerHealthInfo getControllerHealthInfo(); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RuntimeInfo.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RuntimeInfo.java index ee2f4d447e..b7fbce7f07 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RuntimeInfo.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/RuntimeInfo.java @@ -38,7 +38,8 @@ public Set getRegisteredControllers() { private void checkIfStarted() { if (!isStarted()) { log.warn( - "Operator not started yet while accessing runtime info, this might lead to an unreliable behavior"); + "Operator not started yet while accessing runtime info, this might lead to an unreliable" + + " behavior"); } } @@ -46,34 +47,36 @@ public boolean allEventSourcesAreHealthy() { checkIfStarted(); return registeredControllers.stream() .filter(rc -> !rc.getControllerHealthInfo().unhealthyEventSources().isEmpty()) - .findFirst().isEmpty(); + .findFirst() + .isEmpty(); } /** * @return Aggregated Map with controller related event sources. */ - public Map> unhealthyEventSources() { checkIfStarted(); Map> res = new HashMap<>(); for (var rc : registeredControllers) { - res.put(rc.getConfiguration().getName(), - rc.getControllerHealthInfo().unhealthyEventSources()); + res.put( + rc.getConfiguration().getName(), rc.getControllerHealthInfo().unhealthyEventSources()); } return res; } /** * @return Aggregated Map with controller related event sources that wraps an informer. Thus, - * either a {@link ControllerEventSource} or an - * {@link io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}. + * either a {@link ControllerEventSource} or an {@link + * io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}. */ - public Map> unhealthyInformerWrappingEventSourceHealthIndicator() { + public Map> + unhealthyInformerWrappingEventSourceHealthIndicator() { checkIfStarted(); Map> res = new HashMap<>(); for (var rc : registeredControllers) { - res.put(rc.getConfiguration().getName(), rc.getControllerHealthInfo() - .unhealthyInformerEventSourceHealthIndicators()); + res.put( + rc.getConfiguration().getName(), + rc.getControllerHealthInfo().unhealthyInformerEventSourceHealthIndicators()); } return res; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java index f7ed42c577..5abe6a7d03 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/AbstractConfigurationService.java @@ -33,40 +33,43 @@ protected AbstractConfigurationService(Version version, Cloner cloner) { * Creates a new {@link AbstractConfigurationService} with the specified parameters. * * @param client the {@link KubernetesClient} instance to use to connect to the cluster, if let - * {@code null}, the client will be lazily instantiated with the default configuration - * provided by {@link ConfigurationService#getKubernetesClient()} the first time - * {@link #getKubernetesClient()} is called + * {@code null}, the client will be lazily instantiated with the default configuration + * provided by {@link ConfigurationService#getKubernetesClient()} the first time {@link + * #getKubernetesClient()} is called * @param version the version information - * @param cloner the {@link Cloner} to use, if {@code null} the default provided by - * {@link ConfigurationService#getResourceCloner()} will be used + * @param cloner the {@link Cloner} to use, if {@code null} the default provided by {@link + * ConfigurationService#getResourceCloner()} will be used * @param executorServiceManager the {@link ExecutorServiceManager} instance to be used, can be - * {@code null} to lazily initialize one by default when - * {@link #getExecutorServiceManager()} is called + * {@code null} to lazily initialize one by default when {@link #getExecutorServiceManager()} + * is called */ - public AbstractConfigurationService(Version version, Cloner cloner, - ExecutorServiceManager executorServiceManager, KubernetesClient client) { + public AbstractConfigurationService( + Version version, + Cloner cloner, + ExecutorServiceManager executorServiceManager, + KubernetesClient client) { this.version = version; init(cloner, executorServiceManager, client); } /** - * Subclasses can call this method to more easily initialize the {@link Cloner} and - * {@link ExecutorServiceManager} associated with this ConfigurationService implementation. This - * is useful in situations where the cloner depends on a mapper that might require additional + * Subclasses can call this method to more easily initialize the {@link Cloner} and {@link + * ExecutorServiceManager} associated with this ConfigurationService implementation. This is + * useful in situations where the cloner depends on a mapper that might require additional * configuration steps before it's ready to be used. * * @param cloner the {@link Cloner} instance to be used, if {@code null}, the default provided by - * {@link ConfigurationService#getResourceCloner()} will be used + * {@link ConfigurationService#getResourceCloner()} will be used * @param executorServiceManager the {@link ExecutorServiceManager} instance to be used, can be - * {@code null} to lazily initialize one by default when - * {@link #getExecutorServiceManager()} is called + * {@code null} to lazily initialize one by default when {@link #getExecutorServiceManager()} + * is called * @param client the {@link KubernetesClient} instance to use to connect to the cluster, if let - * {@code null}, the client will be lazily instantiated with the default configuration - * provided by {@link ConfigurationService#getKubernetesClient()} the first time - * {@link #getKubernetesClient()} is called + * {@code null}, the client will be lazily instantiated with the default configuration + * provided by {@link ConfigurationService#getKubernetesClient()} the first time {@link + * #getKubernetesClient()} is called */ - protected void init(Cloner cloner, ExecutorServiceManager executorServiceManager, - KubernetesClient client) { + protected void init( + Cloner cloner, ExecutorServiceManager executorServiceManager, KubernetesClient client) { this.client = client; this.cloner = cloner != null ? cloner : ConfigurationService.super.getResourceCloner(); this.executorServiceManager = executorServiceManager; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java index 4204cb6faf..438f7d91a9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/BaseConfigurationService.java @@ -54,11 +54,9 @@ public BaseConfigurationService() { @SuppressWarnings({"unchecked", "rawtypes"}) private static List dependentResources( - Workflow annotation, - ControllerConfiguration controllerConfiguration) { + Workflow annotation, ControllerConfiguration controllerConfiguration) { final var dependents = annotation.dependents(); - if (dependents == null || dependents.length == 0) { return Collections.emptyList(); } @@ -79,19 +77,21 @@ private static List dependentResources( var eventSourceName = dependent.useEventSourceWithName(); eventSourceName = Constants.NO_VALUE_SET.equals(eventSourceName) ? null : eventSourceName; final var context = Utils.contextFor(name, dependentType, null); - spec = new DependentResourceSpec(dependentType, dependentName, - Set.of(dependent.dependsOn()), - Utils.instantiate(dependent.readyPostcondition(), Condition.class, context), - Utils.instantiate(dependent.reconcilePrecondition(), Condition.class, context), - Utils.instantiate(dependent.deletePostcondition(), Condition.class, context), - Utils.instantiate(dependent.activationCondition(), Condition.class, context), - eventSourceName); + spec = + new DependentResourceSpec( + dependentType, + dependentName, + Set.of(dependent.dependsOn()), + Utils.instantiate(dependent.readyPostcondition(), Condition.class, context), + Utils.instantiate(dependent.reconcilePrecondition(), Condition.class, context), + Utils.instantiate(dependent.deletePostcondition(), Condition.class, context), + Utils.instantiate(dependent.activationCondition(), Condition.class, context), + eventSourceName); specsMap.put(dependentName, spec); // extract potential configuration - DependentResourceConfigurationResolver.configureSpecFromConfigured(spec, - controllerConfiguration, - dependentType); + DependentResourceConfigurationResolver.configureSpecFromConfigured( + spec, controllerConfiguration, dependentType); specsMap.put(dependentName, spec); } @@ -106,8 +106,10 @@ private static T valueOrDefaultFromAnnotation( String defaultMethodName) { try { if (controllerConfiguration == null) { - return (T) io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class - .getDeclaredMethod(defaultMethodName).getDefaultValue(); + return (T) + io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class + .getDeclaredMethod(defaultMethodName) + .getDefaultValue(); } else { return mapper.apply(controllerConfiguration); } @@ -125,18 +127,19 @@ private static String getName(String name, Class de } @SuppressWarnings("unused") - private static Configurator configuratorFor(Class instanceType, - Class> reconcilerClass) { + private static Configurator configuratorFor( + Class instanceType, Class> reconcilerClass) { return instance -> configureFromAnnotatedReconciler(instance, reconcilerClass); } @SuppressWarnings({"unchecked", "rawtypes"}) - private static void configureFromAnnotatedReconciler(Object instance, - Class> reconcilerClass) { + private static void configureFromAnnotatedReconciler( + Object instance, Class> reconcilerClass) { if (instance instanceof AnnotationConfigurable configurable) { final Class configurationClass = - (Class) Utils.getFirstTypeArgumentFromSuperClassOrInterface( - instance.getClass(), AnnotationConfigurable.class); + (Class) + Utils.getFirstTypeArgumentFromSuperClassOrInterface( + instance.getClass(), AnnotationConfigurable.class); final var configAnnotation = reconcilerClass.getAnnotation(configurationClass); if (configAnnotation != null) { configurable.initFrom(configAnnotation); @@ -146,7 +149,9 @@ private static void configureFromAnnotatedReconciler(Object instance, @Override protected void logMissingReconcilerWarning(String reconcilerKey, String reconcilersNameMessage) { - logger.warn("Configuration for reconciler '{}' was not found. {}", reconcilerKey, + logger.warn( + "Configuration for reconciler '{}' was not found. {}", + reconcilerKey, reconcilersNameMessage); } @@ -168,10 +173,11 @@ public ControllerConfiguration getConfigurationFor( // create the configuration on demand and register it config = configFor(reconciler); register(config); - getLogger().info( - "Created configuration for reconciler {} with name {}", - reconciler.getClass().getName(), - config.getName()); + getLogger() + .info( + "Created configuration for reconciler {} with name {}", + reconciler.getClass().getName(), + config.getName()); } } else { // check that we don't have a reconciler name collision @@ -196,33 +202,34 @@ protected ResourceClassResolver getResourceClassResolver() { protected

ControllerConfiguration

configFor(Reconciler

reconciler) { final Class> reconcilerClass = (Class>) reconciler.getClass(); - final var controllerAnnotation = reconcilerClass.getAnnotation( - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class); + final var controllerAnnotation = + reconcilerClass.getAnnotation( + io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class); ResolvedControllerConfiguration

config = controllerConfiguration(reconcilerClass, controllerAnnotation); - final var workflowAnnotation = reconcilerClass.getAnnotation( - io.javaoperatorsdk.operator.api.reconciler.Workflow.class); + final var workflowAnnotation = + reconcilerClass.getAnnotation(io.javaoperatorsdk.operator.api.reconciler.Workflow.class); if (workflowAnnotation != null) { final var specs = dependentResources(workflowAnnotation, config); - WorkflowSpec workflowSpec = new WorkflowSpec() { - @Override - public List getDependentResourceSpecs() { - return specs; - } - - @Override - public boolean isExplicitInvocation() { - return workflowAnnotation.explicitInvocation(); - } - - @Override - public boolean handleExceptionsInReconciler() { - return workflowAnnotation.handleExceptionsInReconciler(); - } - - }; + WorkflowSpec workflowSpec = + new WorkflowSpec() { + @Override + public List getDependentResourceSpecs() { + return specs; + } + + @Override + public boolean isExplicitInvocation() { + return workflowAnnotation.explicitInvocation(); + } + + @Override + public boolean handleExceptionsInReconciler() { + return workflowAnnotation.handleExceptionsInReconciler(); + } + }; config.setWorkflowSpec(workflowSpec); } @@ -236,31 +243,44 @@ private

ResolvedControllerConfiguration

controllerCon final var resourceClass = getResourceClassResolver().getPrimaryResourceClass(reconcilerClass); final var name = ReconcilerUtils.getNameFor(reconcilerClass); - final var generationAware = valueOrDefaultFromAnnotation( - annotation, - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::generationAwareEventProcessing, - "generationAwareEventProcessing"); + final var generationAware = + valueOrDefaultFromAnnotation( + annotation, + io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration + ::generationAwareEventProcessing, + "generationAwareEventProcessing"); final var associatedReconcilerClass = ResolvedControllerConfiguration.getAssociatedReconcilerClassName(reconcilerClass); final var context = Utils.contextFor(name); final Class retryClass = - valueOrDefaultFromAnnotation(annotation, + valueOrDefaultFromAnnotation( + annotation, io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::retry, "retry"); - final var retry = Utils.instantiateAndConfigureIfNeeded(retryClass, Retry.class, - context, configuratorFor(Retry.class, reconcilerClass)); + final var retry = + Utils.instantiateAndConfigureIfNeeded( + retryClass, Retry.class, context, configuratorFor(Retry.class, reconcilerClass)); @SuppressWarnings("rawtypes") - final Class rateLimiterClass = valueOrDefaultFromAnnotation(annotation, - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::rateLimiter, - "rateLimiter"); - final var rateLimiter = Utils.instantiateAndConfigureIfNeeded(rateLimiterClass, - RateLimiter.class, context, configuratorFor(RateLimiter.class, reconcilerClass)); - - final var reconciliationInterval = valueOrDefaultFromAnnotation(annotation, - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::maxReconciliationInterval, - "maxReconciliationInterval"); + final Class rateLimiterClass = + valueOrDefaultFromAnnotation( + annotation, + io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::rateLimiter, + "rateLimiter"); + final var rateLimiter = + Utils.instantiateAndConfigureIfNeeded( + rateLimiterClass, + RateLimiter.class, + context, + configuratorFor(RateLimiter.class, reconcilerClass)); + + final var reconciliationInterval = + valueOrDefaultFromAnnotation( + annotation, + io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration + ::maxReconciliationInterval, + "maxReconciliationInterval"); long interval = -1; TimeUnit timeUnit = null; if (reconciliationInterval != null && reconciliationInterval.interval() > 0) { @@ -268,30 +288,36 @@ private

ResolvedControllerConfiguration

controllerCon timeUnit = reconciliationInterval.timeUnit(); } - var fieldManager = valueOrDefaultFromAnnotation(annotation, - io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::fieldManager, - "fieldManager"); + var fieldManager = + valueOrDefaultFromAnnotation( + annotation, + io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::fieldManager, + "fieldManager"); final var dependentFieldManager = - fieldManager.equals(CONTROLLER_NAME_AS_FIELD_MANAGER) ? name - : fieldManager; + fieldManager.equals(CONTROLLER_NAME_AS_FIELD_MANAGER) ? name : fieldManager; - InformerConfiguration

informerConfig = InformerConfiguration.builder(resourceClass) - .initFromAnnotation(annotation != null ? annotation.informer() : null, context) - .buildForController(); + InformerConfiguration

informerConfig = + InformerConfiguration.builder(resourceClass) + .initFromAnnotation(annotation != null ? annotation.informer() : null, context) + .buildForController(); return new ResolvedControllerConfiguration

( - name, generationAware, - associatedReconcilerClass, retry, rateLimiter, + name, + generationAware, + associatedReconcilerClass, + retry, + rateLimiter, ResolvedControllerConfiguration.getMaxReconciliationInterval(interval, timeUnit), - valueOrDefaultFromAnnotation(annotation, + valueOrDefaultFromAnnotation( + annotation, io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration::finalizerName, "finalizerName"), null, dependentFieldManager, - this, informerConfig); + this, + informerConfig); } - protected boolean createIfNeeded() { return true; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java index 30b2cee0e9..08cccab6f7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Cloner.java @@ -5,5 +5,4 @@ public interface Cloner { R clone(R object); - } 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 14a13bf0b6..18e74d29a9 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 @@ -13,6 +13,8 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.HasMetadata; import io.fabric8.kubernetes.api.model.Secret; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.fabric8.kubernetes.client.Config; import io.fabric8.kubernetes.client.ConfigBuilder; import io.fabric8.kubernetes.client.CustomResource; @@ -36,37 +38,32 @@ public interface ConfigurationService { Logger log = LoggerFactory.getLogger(ConfigurationService.class); int DEFAULT_MAX_CONCURRENT_REQUEST = 512; - /** - * The default numbers of concurrent reconciliations - */ + + /** The default numbers of concurrent reconciliations */ int DEFAULT_RECONCILIATION_THREADS_NUMBER = 50; - /** - * The default number of threads used to process dependent workflows - */ + + /** The default number of threads used to process dependent workflows */ int DEFAULT_WORKFLOW_EXECUTOR_THREAD_NUMBER = DEFAULT_RECONCILIATION_THREADS_NUMBER; /** - * Creates a new {@link ConfigurationService} instance used to configure an - * {@link io.javaoperatorsdk.operator.Operator} instance, starting from the specified base - * configuration and overriding specific aspects according to the provided - * {@link ConfigurationServiceOverrider} instance. + * Creates a new {@link ConfigurationService} instance used to configure an {@link + * io.javaoperatorsdk.operator.Operator} instance, starting from the specified base configuration + * and overriding specific aspects according to the provided {@link ConfigurationServiceOverrider} + * instance. * - *

- * NOTE: This overriding mechanism should only be used before creating - * your Operator instance as the configuration service is set at creation time and cannot be - * subsequently changed. As a result, overriding values this way after the Operator has been + *

NOTE: This overriding mechanism should only be used before + * creating your Operator instance as the configuration service is set at creation time and cannot + * be subsequently changed. As a result, overriding values this way after the Operator has been * configured will not take effect. - *

* * @param baseConfiguration the {@link ConfigurationService} to start from * @param overrider the {@link ConfigurationServiceOverrider} used to change the values provided - * by the base configuration + * by the base configuration * @return a new {@link ConfigurationService} starting from the configuration provided as base but - * with overridden values. + * with overridden values. */ static ConfigurationService newOverriddenConfigurationService( - ConfigurationService baseConfiguration, - Consumer overrider) { + ConfigurationService baseConfiguration, Consumer overrider) { if (overrider != null) { final var toOverride = new ConfigurationServiceOverrider(baseConfiguration); overrider.accept(toOverride); @@ -76,22 +73,20 @@ static ConfigurationService newOverriddenConfigurationService( } /** - * Creates a new {@link ConfigurationService} instance used to configure an - * {@link io.javaoperatorsdk.operator.Operator} instance, starting from the default configuration - * and overriding specific aspects according to the provided {@link ConfigurationServiceOverrider} + * Creates a new {@link ConfigurationService} instance used to configure an {@link + * io.javaoperatorsdk.operator.Operator} instance, starting from the default configuration and + * overriding specific aspects according to the provided {@link ConfigurationServiceOverrider} * instance. * - *

- * NOTE: This overriding mechanism should only be used before creating - * your Operator instance as the configuration service is set at creation time and cannot be - * subsequently changed. As a result, overriding values this way after the Operator has been + *

NOTE: This overriding mechanism should only be used before + * creating your Operator instance as the configuration service is set at creation time and cannot + * be subsequently changed. As a result, overriding values this way after the Operator has been * configured will not take effect. - *

* * @param overrider the {@link ConfigurationServiceOverrider} used to change the values provided - * by the default configuration + * by the default configuration * @return a new {@link ConfigurationService} overriding the default values with the ones provided - * by the specified {@link ConfigurationServiceOverrider} + * by the specified {@link ConfigurationServiceOverrider} * @since 4.4.0 */ static ConfigurationService newOverriddenConfigurationService( @@ -104,18 +99,16 @@ static ConfigurationService newOverriddenConfigurationService( * * @param reconciler the reconciler we want the configuration of * @param the {@code CustomResource} type associated with the specified reconciler - * @return the {@link ControllerConfiguration} associated with the specified reconciler or - * {@code null} if no configuration exists for the reconciler + * @return the {@link ControllerConfiguration} associated with the specified reconciler or {@code + * null} if no configuration exists for the reconciler */ ControllerConfiguration getConfigurationFor(Reconciler reconciler); /** * Used to clone custom resources. * - *

- * NOTE: It is strongly suggested that implementors override this method since the + *

NOTE: It is strongly suggested that implementors override this method since the * default implementation creates a new {@link Cloner} instance each time this method is called. - *

* * @return the configured {@link Cloner} */ @@ -134,31 +127,28 @@ public R clone(R object) { * take care of creating the required connections to watch the target resources (in particular, * 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 + *

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 io.javaoperatorsdk.operator.Operator#Operator(Consumer)}, passing your custom instance - * with {@link ConfigurationServiceOverrider#withKubernetesClient(KubernetesClient)}. - *

+ * 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)}. * - *

- * NOTE: It is strongly suggested that implementors override this method since the + *

NOTE: It is strongly suggested that implementors override this method since the * default implementation creates a new {@link KubernetesClient} instance each time this method is * called. - *

* * @return the configured {@link KubernetesClient} * @since 4.4.0 */ default KubernetesClient getKubernetesClient() { return new KubernetesClientBuilder() - .withConfig(new ConfigBuilder(Config.autoConfigure(null)) - .withMaxConcurrentRequests(DEFAULT_MAX_CONCURRENT_REQUEST) - .build()) + .withConfig( + new ConfigBuilder(Config.autoConfigure(null)) + .withMaxConcurrentRequests(DEFAULT_MAX_CONCURRENT_REQUEST) + .build()) .withKubernetesSerialization(new KubernetesSerialization()) .build(); } @@ -178,13 +168,11 @@ default KubernetesClient getKubernetesClient() { Version getVersion(); /** - * Whether the operator should query the CRD to make sure it's deployed and validate - * {@link CustomResource} implementations before attempting to register the associated - * reconcilers. + * Whether the operator should query the CRD to make sure it's deployed and validate {@link + * CustomResource} implementations before attempting to register the associated reconcilers. * - *

- * Note that this might require elevating the privileges associated with the operator to gain read - * access on the CRD resources. + *

Note that this might require elevating the privileges associated with the operator to gain + * read access on the CRD resources. * * @return {@code true} if CRDs should be checked (default), {@code false} otherwise */ @@ -226,7 +214,7 @@ default Metrics getMetrics() { * handle concurrent reconciliations * * @return the {@link ExecutorService} implementation to use for concurrent reconciliation - * processing + * processing */ default ExecutorService getExecutorService() { return Executors.newFixedThreadPool(concurrentReconciliationThreads()); @@ -243,8 +231,8 @@ default ExecutorService getWorkflowExecutorService() { } /** - * Determines whether the associated Kubernetes client should be closed when the associated - * {@link io.javaoperatorsdk.operator.Operator} is stopped. + * Determines whether the associated Kubernetes client should be closed when the associated {@link + * io.javaoperatorsdk.operator.Operator} is stopped. * * @return {@code true} if the Kubernetes should be closed on stop, {@code false} otherwise */ @@ -264,9 +252,9 @@ default DependentResourceFactory dependentResourceFactory() { } /** - * Retrieves the optional {@link LeaderElectionConfiguration} to specify how the associated - * {@link io.javaoperatorsdk.operator.Operator} handles leader election to ensure only one - * instance of the operator runs on the cluster at any given time + * Retrieves the optional {@link LeaderElectionConfiguration} to specify how the associated {@link + * io.javaoperatorsdk.operator.Operator} handles leader election to ensure only one instance of + * the operator runs on the cluster at any given time * * @return the {@link LeaderElectionConfiguration} */ @@ -275,15 +263,12 @@ default Optional getLeaderElectionConfiguration() { } /** - *

- * if true, operator stops if there are some issues with informers - * {@link io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource} or - * {@link ControllerEventSource} on startup. Other event sources may also respect this flag. - *

- *

- * if false, the startup will ignore recoverable errors, caused for example by RBAC issues, and + * if true, operator stops if there are some issues with informers {@link + * io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource} or {@link + * ControllerEventSource} on startup. Other event sources may also respect this flag. + * + *

if false, the startup will ignore recoverable errors, caused for example by RBAC issues, and * will try to reconnect periodically in the background. - *

* * @return actual value described above */ @@ -320,26 +305,28 @@ default Duration reconciliationTerminationTimeout() { * @return an optional InformerStopHandler */ default Optional getInformerStoppedHandler() { - return Optional.of((informer, ex) -> { - // hasSynced is checked to verify that informer already started. If not started, in case - // of a fatal error the operator will stop, no need for explicit exit. - if (ex != null && informer.hasSynced()) { - log.error("Fatal error in informer: {}. Stopping the operator", informer, ex); - System.exit(1); - } else { - log.debug( - "Informer stopped: {}. Has synced: {}, Error: {}. This can happen as a result of " + - "stopping the controller, or due to an error on startup." + - "See also stopOnInformerErrorDuringStartup configuration.", - informer, informer.hasSynced(), ex); - } - }); + return Optional.of( + (informer, ex) -> { + // hasSynced is checked to verify that informer already started. If not started, in case + // of a fatal error the operator will stop, no need for explicit exit. + if (ex != null && informer.hasSynced()) { + log.error("Fatal error in informer: {}. Stopping the operator", informer, ex); + System.exit(1); + } else { + log.debug( + "Informer stopped: {}. Has synced: {}, Error: {}. This can happen as a result of " + + "stopping the controller, or due to an error on startup." + + "See also stopOnInformerErrorDuringStartup configuration.", + informer, + informer.hasSynced(), + ex); + } + }); } /** - * Override to provide a custom {@link ManagedWorkflowFactory} implementation to change how - * {@link io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflow} are - * instantiated + * Override to provide a custom {@link ManagedWorkflowFactory} implementation to change how {@link + * io.javaoperatorsdk.operator.processing.dependent.workflow.ManagedWorkflow} are instantiated * * @return the custom {@link ManagedWorkflowFactory} implementation */ @@ -360,12 +347,12 @@ default ExecutorServiceManager getExecutorServiceManager() { /** * Allows to revert to the 4.3 behavior when it comes to creating, updating and matching * Kubernetes Dependent Resources when set to {@code false}. The default approach how these - * resources are created/updated and match was change to use - * Server-Side - * Apply (SSA) by default. - *

- * SSA based create/update can be still used with the legacy matching, just overriding the match - * method of Kubernetes Dependent Resource. + * resources are created/updated and match was change to use Server-Side Apply + * (SSA) by default. + * + *

SSA based create/update can be still used with the legacy matching, just overriding the + * match method of Kubernetes Dependent Resource. * * @return {@code true} if SSA should be used for dependent resources, {@code false} otherwise * @since 4.4.0 @@ -387,7 +374,9 @@ default boolean ssaBasedCreateUpdateMatchForDependentResources() { */ default boolean shouldUseSSA( KubernetesDependentResource dependentResource) { - return shouldUseSSA(dependentResource.getClass(), dependentResource.resourceType(), + return shouldUseSSA( + dependentResource.getClass(), + dependentResource.resourceType(), dependentResource.configuration().orElse(null)); } @@ -399,19 +388,19 @@ default boolean shouldUseSSA( * @param dependentResourceType the {@link KubernetesDependentResource} type under consideration * @param resourceType the resource type associated with the considered dependent resource type * @return {@code true} if SSA should be used for specified dependent resource type, {@code false} - * otherwise + * otherwise * @since 4.9.5 */ @SuppressWarnings("rawtypes") - default boolean shouldUseSSA(Class dependentResourceType, + default boolean shouldUseSSA( + Class dependentResourceType, Class resourceType, KubernetesDependentResourceConfig config) { if (ResourceUpdaterMatcher.class.isAssignableFrom(dependentResourceType)) { return false; } - Boolean useSSAConfig = Optional.ofNullable(config) - .map(KubernetesDependentResourceConfig::useSSA) - .orElse(null); + Boolean useSSAConfig = + Optional.ofNullable(config).map(KubernetesDependentResourceConfig::useSSA).orElse(null); // don't use SSA for certain resources by default, only if explicitly overridden if (useSSAConfig == null) { if (defaultNonSSAResources().contains(resourceType)) { @@ -426,12 +415,12 @@ default boolean shouldUseSSA(Class depend /** * Returns the set of default resources for which Server-Side Apply (SSA) will not be used, even - * if it is the default behavior for dependent resources as specified by - * {@link #ssaBasedCreateUpdateMatchForDependentResources()}. The exception to this is in the case - * where the use of SSA is explicitly enabled on the dependent resource directly using - * {@link KubernetesDependent#useSSA()}. - *

- * By default, SSA is disabled for {@link ConfigMap} and {@link Secret} resources. + * if it is the default behavior for dependent resources as specified by {@link + * #ssaBasedCreateUpdateMatchForDependentResources()}. The exception to this is in the case where + * the use of SSA is explicitly enabled on the dependent resource directly using {@link + * KubernetesDependent#useSSA()}. + * + *

By default, SSA is disabled for {@link ConfigMap} and {@link Secret} resources. * * @return The set of resource types for which SSA will not be used */ @@ -450,30 +439,56 @@ default Set> defaultNonSSAResource() { /** * If a javaoperatorsdk.io/previous annotation should be used so that the operator sdk can detect * events from its own updates of dependent resources and then filter them. - *

- * Disable this if you want to react to your own dependent resource updates * - * @return if special annotation should be used for dependent resource to filter events - * @since 4.5.0 + *

Disable this if you want to react to your own dependent resource updates * * @return if special annotation should be used for dependent resource to filter events + * @since 4.5.0 */ default boolean previousAnnotationForDependentResourcesEventFiltering() { return true; } + /** + * For dependent resources, the framework can add an annotation to filter out events resulting + * directly from the framework's operation. There are, however, some resources that do not follow + * the Kubernetes API conventions that changes in metadata should not increase the generation of + * the resource (as recorded in the {@code generation} field of the resource's {@code metadata}). + * For these resources, this convention is not respected and results in a new event for the + * framework to process. If that particular case is not handled correctly in the resource matcher, + * the framework will consider that the resource doesn't match the desired state and therefore + * triggers an update, which in turn, will re-add the annotation, thus starting the loop again, + * infinitely. + * + *

As a workaround, we automatically skip adding previous annotation for those well-known + * resources. Note that if you are sure that the matcher works for your use case, and it should in + * most instances, you can remove the resource type from the blocklist. + * + *

The consequence of adding a resource type to the set is that the framework will not use + * event filtering to prevent events, initiated by changes made by the framework itself as a + * result of its processing of dependent resources, to trigger the associated reconciler again. + * + *

Note that this method only takes effect if annotating dependent resources to prevent + * dependent resources events from triggering the associated reconciler again is activated as + * controlled by {@link #previousAnnotationForDependentResourcesEventFiltering()} + * + * @return a Set of resource classes where the previous version annotation won't be used. + */ + default Set> withPreviousAnnotationForDependentResourcesBlocklist() { + return Set.of(Deployment.class, StatefulSet.class); + } + /** * If the event logic should parse the resourceVersion to determine the ordering of dependent * resource events. This is typically not needed. - *

- * Disabled by default as Kubernetes does not support, and discourages, this interpretation of + * + *

Disabled by default as Kubernetes does not support, and discourages, this interpretation of * resourceVersions. Enable only if your api server event processing seems to lag the operator * logic, and you want to further minimize the amount of work done / updates issued by the * operator. * * @return if resource version should be parsed (as integer) * @since 4.5.0 - * * @return if resource version should be parsed (as integer) */ default boolean parseResourceVersionsForEventFilteringAndCaching() { @@ -486,7 +501,7 @@ default boolean parseResourceVersionsForEventFilteringAndCaching() { * adding finalizers, patching resources and status. * * @return {@code true} if Server-Side Apply (SSA) should be used when patching the primary - * resources, {@code false} otherwise + * resources, {@code false} otherwise * @see ConfigurationServiceOverrider#withUseSSAToPatchPrimaryResource(boolean) * @since 5.0.0 */ @@ -495,25 +510,20 @@ default boolean useSSAToPatchPrimaryResource() { } /** - *

- * Determines whether resources retrieved from caches such as via calls to - * {@link Context#getSecondaryResource(Class)} should be defensively cloned first. - *

+ * Determines whether resources retrieved from caches such as via calls to {@link + * Context#getSecondaryResource(Class)} should be defensively cloned first. * - *

- * Defensive cloning to prevent problematic cache modifications (modifying the resource would + *

Defensive cloning to prevent problematic cache modifications (modifying the resource would * otherwise modify the stored copy in the cache) was transparently done in previous JOSDK * versions. This might have performance consequences and, with the more prevalent use of * Server-Side Apply, where you should create a new copy of your resource with only modified * fields, such modifications of these resources are less likely to occur. - *

* * @return {@code true} if resources should be defensively cloned before returning them from - * caches, {@code false} otherwise + * caches, {@code false} otherwise * @since 5.0.0 */ default boolean cloneSecondaryResourcesWhenGettingFromCache() { return false; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java index 7de9bcda43..be86cbe312 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverrider.java @@ -40,6 +40,8 @@ public class ConfigurationServiceOverrider { private Boolean parseResourceVersions; private Boolean useSSAToPatchPrimaryResource; private Boolean cloneSecondaryResourcesWhenGettingFromCache; + private Set> previousAnnotationUsageBlocklist; + @SuppressWarnings("rawtypes") private DependentResourceFactory dependentResourceFactory; @@ -98,8 +100,8 @@ public ConfigurationServiceOverrider withWorkflowExecutorService( /** * Replaces the default {@link KubernetesClient} instance by the specified one. This is the * preferred mechanism to configure which client will be used to access the cluster. - *

- * Note that when {@link Operator#stop()} is called, by default the client is closed even if + * + *

Note that when {@link Operator#stop()} is called, by default the client is closed even if * explicitly provided with this method. Use {@link #withCloseClientOnStop(boolean)} to change * this behavior. * @@ -151,8 +153,7 @@ public ConfigurationServiceOverrider withDefaultNonSSAResource( return this; } - public ConfigurationServiceOverrider withPreviousAnnotationForDependentResources( - boolean value) { + public ConfigurationServiceOverrider withPreviousAnnotationForDependentResources(boolean value) { this.previousAnnotationForDependentResources = value; return this; } @@ -161,8 +162,7 @@ public ConfigurationServiceOverrider withPreviousAnnotationForDependentResources * @param value true if internal algorithms can use metadata.resourceVersion as a numeric value. * @return this */ - public ConfigurationServiceOverrider withParseResourceVersions( - boolean value) { + public ConfigurationServiceOverrider withParseResourceVersions(boolean value) { this.parseResourceVersions = value; return this; } @@ -173,8 +173,7 @@ public ConfigurationServiceOverrider withParseResourceVersions( * @return this */ @Deprecated(forRemoval = true) - public ConfigurationServiceOverrider wihtParseResourceVersions( - boolean value) { + public ConfigurationServiceOverrider wihtParseResourceVersions(boolean value) { this.parseResourceVersions = value; return this; } @@ -190,6 +189,12 @@ public ConfigurationServiceOverrider withCloneSecondaryResourcesWhenGettingFromC return this; } + public ConfigurationServiceOverrider withPreviousAnnotationForDependentResourcesBlocklist( + Set> blocklist) { + this.previousAnnotationUsageBlocklist = blocklist; + return this; + } + public ConfigurationService build() { return new BaseConfigurationService(original.getVersion(), cloner, client) { @Override @@ -197,28 +202,29 @@ public Set getKnownReconcilerNames() { return original.getKnownReconcilerNames(); } - private T overriddenValueOrDefault(T value, - Function defaultValue) { + private T overriddenValueOrDefault( + T value, Function defaultValue) { return value != null ? value : defaultValue.apply(original); } @Override public boolean checkCRDAndValidateLocalModel() { - return overriddenValueOrDefault(checkCR, - ConfigurationService::checkCRDAndValidateLocalModel); + return overriddenValueOrDefault( + checkCR, ConfigurationService::checkCRDAndValidateLocalModel); } @SuppressWarnings("rawtypes") @Override public DependentResourceFactory dependentResourceFactory() { - return overriddenValueOrDefault(dependentResourceFactory, - ConfigurationService::dependentResourceFactory); + return overriddenValueOrDefault( + dependentResourceFactory, ConfigurationService::dependentResourceFactory); } @Override public int concurrentReconciliationThreads() { return Utils.ensureValid( - overriddenValueOrDefault(concurrentReconciliationThreads, + overriddenValueOrDefault( + concurrentReconciliationThreads, ConfigurationService::concurrentReconciliationThreads), "maximum reconciliation threads", 1, @@ -228,7 +234,8 @@ public int concurrentReconciliationThreads() { @Override public int concurrentWorkflowExecutorThreads() { return Utils.ensureValid( - overriddenValueOrDefault(concurrentWorkflowExecutorThreads, + overriddenValueOrDefault( + concurrentWorkflowExecutorThreads, ConfigurationService::concurrentWorkflowExecutorThreads), "maximum workflow execution threads", 1, @@ -247,30 +254,40 @@ public boolean closeClientOnStop() { @Override public ExecutorService getExecutorService() { - return overriddenValueOrDefault(executorService, ConfigurationService::getExecutorService); + if (executorService != null) { + return executorService; + } else { + return super.getExecutorService(); + } } @Override public ExecutorService getWorkflowExecutorService() { - return overriddenValueOrDefault(workflowExecutorService, - ConfigurationService::getWorkflowExecutorService); + if (workflowExecutorService != null) { + return workflowExecutorService; + } else { + return super.getWorkflowExecutorService(); + } } @Override public Optional getLeaderElectionConfiguration() { - return leaderElectionConfiguration != null ? Optional.of(leaderElectionConfiguration) + return leaderElectionConfiguration != null + ? Optional.of(leaderElectionConfiguration) : original.getLeaderElectionConfiguration(); } @Override public Optional getInformerStoppedHandler() { - return informerStoppedHandler != null ? Optional.of(informerStoppedHandler) + return informerStoppedHandler != null + ? Optional.of(informerStoppedHandler) : original.getInformerStoppedHandler(); } @Override public boolean stopOnInformerErrorDuringStartup() { - return overriddenValueOrDefault(stopOnInformerErrorDuringStartup, + return overriddenValueOrDefault( + stopOnInformerErrorDuringStartup, ConfigurationService::stopOnInformerErrorDuringStartup); } @@ -281,46 +298,58 @@ public Duration cacheSyncTimeout() { @Override public Duration reconciliationTerminationTimeout() { - return overriddenValueOrDefault(reconciliationTerminationTimeout, + return overriddenValueOrDefault( + reconciliationTerminationTimeout, ConfigurationService::reconciliationTerminationTimeout); } @Override public boolean ssaBasedCreateUpdateMatchForDependentResources() { - return overriddenValueOrDefault(ssaBasedCreateUpdateMatchForDependentResources, + return overriddenValueOrDefault( + ssaBasedCreateUpdateMatchForDependentResources, ConfigurationService::ssaBasedCreateUpdateMatchForDependentResources); } @Override public Set> defaultNonSSAResources() { - return overriddenValueOrDefault(defaultNonSSAResource, - ConfigurationService::defaultNonSSAResources); + return overriddenValueOrDefault( + defaultNonSSAResource, ConfigurationService::defaultNonSSAResources); } @Override public boolean previousAnnotationForDependentResourcesEventFiltering() { - return overriddenValueOrDefault(previousAnnotationForDependentResources, + return overriddenValueOrDefault( + previousAnnotationForDependentResources, ConfigurationService::previousAnnotationForDependentResourcesEventFiltering); } @Override public boolean parseResourceVersionsForEventFilteringAndCaching() { - return overriddenValueOrDefault(parseResourceVersions, + return overriddenValueOrDefault( + parseResourceVersions, ConfigurationService::parseResourceVersionsForEventFilteringAndCaching); } @Override public boolean useSSAToPatchPrimaryResource() { - return overriddenValueOrDefault(useSSAToPatchPrimaryResource, - ConfigurationService::useSSAToPatchPrimaryResource); + return overriddenValueOrDefault( + useSSAToPatchPrimaryResource, ConfigurationService::useSSAToPatchPrimaryResource); } @Override public boolean cloneSecondaryResourcesWhenGettingFromCache() { - return overriddenValueOrDefault(cloneSecondaryResourcesWhenGettingFromCache, + return overriddenValueOrDefault( + cloneSecondaryResourcesWhenGettingFromCache, ConfigurationService::cloneSecondaryResourcesWhenGettingFromCache); } + + @Override + public Set> + withPreviousAnnotationForDependentResourcesBlocklist() { + return overriddenValueOrDefault( + previousAnnotationUsageBlocklist, + ConfigurationService::withPreviousAnnotationForDependentResourcesBlocklist); + } }; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java index e03cf5626e..2c18fa55d3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfiguration.java @@ -18,9 +18,8 @@ public interface ControllerConfiguration

extends Informab @SuppressWarnings("rawtypes") RateLimiter DEFAULT_RATE_LIMITER = LinearRateLimiter.deactivatedRateLimiter(); - /** - * Will use the controller name as fieldManager if set. - */ + + /** Will use the controller name as fieldManager if set. */ String CONTROLLER_NAME_AS_FIELD_MANAGER = "use_controller_name"; default String getName() { @@ -42,7 +41,9 @@ static String ensureValidFinalizerName(String finalizer, String resourceTypeName } else { throw new IllegalArgumentException( finalizer - + " is not a valid finalizer. See https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers for details"); + + " is not a valid finalizer. See" + + " https://kubernetes.io/docs/tasks/extend-kubernetes/custom-resources/custom-resource-definitions/#finalizers" + + " for details"); } } else { return ReconcilerUtils.getDefaultFinalizerName(resourceTypeName); @@ -80,9 +81,9 @@ default Set getEffectiveNamespaces() { } /** - * Retrieves the name used to assign as field manager for - * Server-Side - * Apply (SSA) operations. If unset, the sanitized controller name will be used. + * Retrieves the name used to assign as field manager for Server-Side Apply + * (SSA) operations. If unset, the sanitized controller name will be used. * * @return the name used as field manager for SSA operations */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java index 3d3eef5990..d2e37a397d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverrider.java @@ -17,7 +17,6 @@ import io.javaoperatorsdk.operator.processing.event.source.filter.OnUpdateFilter; import io.javaoperatorsdk.operator.processing.retry.Retry; - @SuppressWarnings({"rawtypes", "unused", "UnusedReturnValue"}) public class ControllerConfigurationOverrider { @@ -150,13 +149,11 @@ public ControllerConfigurationOverrider withName(String name) { return this; } - public ControllerConfigurationOverrider withFieldManager( - String dependentFieldManager) { + public ControllerConfigurationOverrider withFieldManager(String dependentFieldManager) { this.fieldManager = dependentFieldManager; return this; } - /** * Sets a max page size limit when starting the informer. This will result in pagination while * populating the cache. This means that longer lists will take multiple requests to fetch. See @@ -164,20 +161,22 @@ public ControllerConfigurationOverrider withFieldManager( * * @param informerListLimit null (the default) results in no pagination */ - public ControllerConfigurationOverrider withInformerListLimit( - Long informerListLimit) { + public ControllerConfigurationOverrider withInformerListLimit(Long informerListLimit) { config.withInformerListLimit(informerListLimit); return this; } - public ControllerConfigurationOverrider replacingNamedDependentResourceConfig(String name, - Object dependentResourceConfig) { + public ControllerConfigurationOverrider replacingNamedDependentResourceConfig( + String name, Object dependentResourceConfig) { final var specs = original.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); - final var spec = specs.stream() - .filter(drs -> drs.getName().equals(name)).findFirst() - .orElseThrow( - () -> new IllegalArgumentException("Cannot find a DependentResource named: " + name)); + final var spec = + specs.stream() + .filter(drs -> drs.getName().equals(name)) + .findFirst() + .orElseThrow( + () -> + new IllegalArgumentException("Cannot find a DependentResource named: " + name)); if (configurations == null) { configurations = new HashMap<>(specs.size()); @@ -189,9 +188,14 @@ public ControllerConfigurationOverrider replacingNamedDependentResourceConfig public ControllerConfiguration build() { return new ResolvedControllerConfiguration<>( name, - generationAware, original.getAssociatedReconcilerClassName(), retry, rateLimiter, + generationAware, + original.getAssociatedReconcilerClassName(), + retry, + rateLimiter, reconciliationMaxInterval, - finalizer, configurations, fieldManager, + finalizer, + configurations, + fieldManager, original.getConfigurationService(), config.buildForController(), original.getWorkflowSpec().orElse(null)); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceClassResolver.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceClassResolver.java index cdd4c5540e..cf44b9890e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceClassResolver.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/DefaultResourceClassResolver.java @@ -9,7 +9,7 @@ public class DefaultResourceClassResolver implements ResourceClassResolver { @Override public Class getPrimaryResourceClass( Class> reconcilerClass) { - return (Class) Utils.getFirstTypeArgumentFromSuperClassOrInterface(reconcilerClass, - Reconciler.class); + return (Class) + Utils.getFirstTypeArgumentFromSuperClassOrInterface(reconcilerClass, Reconciler.class); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ExecutorServiceManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ExecutorServiceManager.java index c35281e822..3cbf68d8fe 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ExecutorServiceManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ExecutorServiceManager.java @@ -41,39 +41,50 @@ public class ExecutorServiceManager { * @param threadNamer for naming thread * @param type */ - public void boundedExecuteAndWaitForAllToComplete(Stream stream, - Function task, Function threadNamer) { + public void boundedExecuteAndWaitForAllToComplete( + Stream stream, Function task, Function threadNamer) { executeAndWaitForAllToComplete(stream, task, threadNamer, cachingExecutorService()); } - public static void executeAndWaitForAllToComplete(Stream stream, - Function task, Function threadNamer, + public static void executeAndWaitForAllToComplete( + Stream stream, + Function task, + Function threadNamer, ExecutorService executorService) { final var instrumented = new InstrumentedExecutorService(executorService); try { - instrumented.invokeAll(stream.map(item -> (Callable) () -> { - // change thread name for easier debugging - final var thread = Thread.currentThread(); - final var name = thread.getName(); - thread.setName(threadNamer.apply(item)); - try { - task.apply(item); - return null; - } finally { - // restore original name - thread.setName(name); - } - }).collect(Collectors.toList())).forEach(f -> { - try { - // to find out any exceptions - f.get(); - } catch (ExecutionException e) { - throw new OperatorException(e); - } catch (InterruptedException e) { - log.warn("Interrupted.", e); - Thread.currentThread().interrupt(); - } - }); + instrumented + .invokeAll( + stream + .map( + item -> + (Callable) + () -> { + // change thread name for easier debugging + final var thread = Thread.currentThread(); + final var name = thread.getName(); + thread.setName(threadNamer.apply(item)); + try { + task.apply(item); + return null; + } finally { + // restore original name + thread.setName(name); + } + }) + .collect(Collectors.toList())) + .forEach( + f -> { + try { + // to find out any exceptions + f.get(); + } catch (ExecutionException e) { + throw new OperatorException(e); + } catch (InterruptedException e) { + log.warn("Interrupted.", e); + Thread.currentThread().interrupt(); + } + }); } catch (InterruptedException e) { log.warn("Interrupted.", e); Thread.currentThread().interrupt(); @@ -113,9 +124,11 @@ public void stop(Duration gracefulShutdownTimeout) { try { log.debug("Closing executor"); var parallelExec = Executors.newFixedThreadPool(3); - parallelExec.invokeAll(List.of(shutdown(executor, gracefulShutdownTimeout), - shutdown(workflowExecutor, gracefulShutdownTimeout), - shutdown(cachingExecutorService, gracefulShutdownTimeout))); + parallelExec.invokeAll( + List.of( + shutdown(executor, gracefulShutdownTimeout), + shutdown(workflowExecutor, gracefulShutdownTimeout), + shutdown(cachingExecutorService, gracefulShutdownTimeout))); workflowExecutor = null; parallelExec.shutdownNow(); started = false; @@ -125,16 +138,16 @@ public void stop(Duration gracefulShutdownTimeout) { } } - private static Callable shutdown(ExecutorService executorService, - Duration gracefulShutdownTimeout) { + private static Callable shutdown( + ExecutorService executorService, Duration gracefulShutdownTimeout) { return () -> { // workflow executor can be null if (executorService == null) { return null; } executorService.shutdown(); - if (!executorService.awaitTermination(gracefulShutdownTimeout.toMillis(), - TimeUnit.MILLISECONDS)) { + if (!executorService.awaitTermination( + gracefulShutdownTimeout.toMillis(), TimeUnit.MILLISECONDS)) { executorService.shutdownNow(); // if we timed out, waiting, cancel everything } return null; @@ -203,8 +216,9 @@ public List> invokeAll(Collection> tasks) } @Override - public List> invokeAll(Collection> tasks, long timeout, - TimeUnit unit) throws InterruptedException { + public List> invokeAll( + Collection> tasks, long timeout, TimeUnit unit) + throws InterruptedException { return executor.invokeAll(tasks, timeout, unit); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Informable.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Informable.java index 5b58836483..39272b2083 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Informable.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Informable.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.api.config; - import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java index cfce453e14..49efa10b8d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfiguration.java @@ -28,7 +28,10 @@ public LeaderElectionConfiguration(String leaseName, String leaseNamespace, Stri leaseNamespace, LEASE_DURATION_DEFAULT_VALUE, RENEW_DEADLINE_DEFAULT_VALUE, - RETRY_PERIOD_DEFAULT_VALUE, identity, null, true); + RETRY_PERIOD_DEFAULT_VALUE, + identity, + null, + true); } public LeaderElectionConfiguration(String leaseName, String leaseNamespace) { @@ -37,7 +40,10 @@ public LeaderElectionConfiguration(String leaseName, String leaseNamespace) { leaseNamespace, LEASE_DURATION_DEFAULT_VALUE, RENEW_DEADLINE_DEFAULT_VALUE, - RETRY_PERIOD_DEFAULT_VALUE, null, null, true); + RETRY_PERIOD_DEFAULT_VALUE, + null, + null, + true); } public LeaderElectionConfiguration(String leaseName) { @@ -46,7 +52,10 @@ public LeaderElectionConfiguration(String leaseName) { null, LEASE_DURATION_DEFAULT_VALUE, RENEW_DEADLINE_DEFAULT_VALUE, - RETRY_PERIOD_DEFAULT_VALUE, null, null, true); + RETRY_PERIOD_DEFAULT_VALUE, + null, + null, + true); } public LeaderElectionConfiguration( diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfigurationBuilder.java index eda262f9eb..c4d4fc6190 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/LeaderElectionConfigurationBuilder.java @@ -62,7 +62,14 @@ public LeaderElectionConfigurationBuilder withExitOnStopLeading(boolean exitOnSt } public LeaderElectionConfiguration build() { - return new LeaderElectionConfiguration(leaseName, leaseNamespace, leaseDuration, renewDeadline, - retryPeriod, identity, leaderCallbacks, exitOnStopLeading); + return new LeaderElectionConfiguration( + leaseName, + leaseNamespace, + leaseDuration, + renewDeadline, + retryPeriod, + identity, + leaderCallbacks, + exitOnStopLeading); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/NamespaceChangeable.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/NamespaceChangeable.java index 426b179438..6e6d53f49f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/NamespaceChangeable.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/NamespaceChangeable.java @@ -7,10 +7,9 @@ public interface NamespaceChangeable { /** - * If the controller and possibly registered - * {@link io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource} - * watches a set of namespaces this set can be adjusted dynamically, this when the operator is - * running. + * If the controller and possibly registered {@link + * io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource} watches a set + * of namespaces this set can be adjusted dynamically, this when the operator is running. * * @param namespaces target namespaces to watch */ @@ -24,5 +23,4 @@ default void changeNamespaces(String... namespaces) { default boolean allowsNamespaceChanges() { return true; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java index 7e8415f584..3c26659ed2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResolvedControllerConfiguration.java @@ -32,37 +32,61 @@ public class ResolvedControllerConfiguration

private WorkflowSpec workflowSpec; public ResolvedControllerConfiguration(ControllerConfiguration

other) { - this(other.getName(), other.isGenerationAware(), - other.getAssociatedReconcilerClassName(), other.getRetry(), other.getRateLimiter(), + this( + other.getName(), + other.isGenerationAware(), + other.getAssociatedReconcilerClassName(), + other.getRetry(), + other.getRateLimiter(), other.maxReconciliationInterval().orElse(null), - other.getFinalizerName(), Collections.emptyMap(), + other.getFinalizerName(), + Collections.emptyMap(), other.fieldManager(), other.getConfigurationService(), other.getInformerConfig(), other.getWorkflowSpec().orElse(null)); } - public ResolvedControllerConfiguration(String name, - boolean generationAware, String associatedReconcilerClassName, Retry retry, - RateLimiter rateLimiter, Duration maxReconciliationInterval, + public ResolvedControllerConfiguration( + String name, + boolean generationAware, + String associatedReconcilerClassName, + Retry retry, + RateLimiter rateLimiter, + Duration maxReconciliationInterval, String finalizer, Map configurations, String fieldManager, ConfigurationService configurationService, InformerConfiguration

informerConfig, WorkflowSpec workflowSpec) { - this(name, generationAware, associatedReconcilerClassName, retry, rateLimiter, - maxReconciliationInterval, finalizer, configurations, fieldManager, - configurationService, informerConfig); + this( + name, + generationAware, + associatedReconcilerClassName, + retry, + rateLimiter, + maxReconciliationInterval, + finalizer, + configurations, + fieldManager, + configurationService, + informerConfig); setWorkflowSpec(workflowSpec); } - protected ResolvedControllerConfiguration(String name, - boolean generationAware, String associatedReconcilerClassName, Retry retry, - RateLimiter rateLimiter, Duration maxReconciliationInterval, String finalizer, + protected ResolvedControllerConfiguration( + String name, + boolean generationAware, + String associatedReconcilerClassName, + Retry retry, + RateLimiter rateLimiter, + Duration maxReconciliationInterval, + String finalizer, Map configurations, String fieldManager, - ConfigurationService configurationService, InformerConfiguration

informerConfig) { + ConfigurationService configurationService, + InformerConfiguration

informerConfig) { this.informerConfig = informerConfig; this.configurationService = configurationService; this.name = ControllerConfiguration.ensureValidName(name, associatedReconcilerClassName); @@ -77,10 +101,22 @@ protected ResolvedControllerConfiguration(String name, this.fieldManager = fieldManager; } - protected ResolvedControllerConfiguration(Class

resourceClass, String name, - Class reconcilerClas, ConfigurationService configurationService) { - this(name, false, getAssociatedReconcilerClassName(reconcilerClas), null, null, - null, null, null, null, configurationService, + protected ResolvedControllerConfiguration( + Class

resourceClass, + String name, + Class reconcilerClas, + ConfigurationService configurationService) { + this( + name, + false, + getAssociatedReconcilerClassName(reconcilerClas), + null, + null, + null, + null, + null, + null, + configurationService, InformerConfiguration.builder(resourceClass).buildForController()); } @@ -136,7 +172,6 @@ public RateLimiter getRateLimiter() { return rateLimiter; } - @Override public Optional getWorkflowSpec() { return Optional.ofNullable(workflowSpec); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceClassResolver.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceClassResolver.java index 001eb3e0de..b1d0af9263 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceClassResolver.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/ResourceClassResolver.java @@ -7,5 +7,4 @@ public interface ResourceClassResolver {

Class

getPrimaryResourceClass( Class> reconcilerClass); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java index 15ffe178e5..3b6f94a025 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Utils.java @@ -62,9 +62,7 @@ private static Version loadFromProperties() { log.debug("Couldn't parse git.build.time property", e); builtTime = Date.from(Instant.EPOCH); } - return new Version( - properties.getProperty("git.commit.id.abbrev", "unknown"), - builtTime); + return new Version(properties.getProperty("git.commit.id.abbrev", "unknown"), builtTime); } public static int ensureValid(int value, String description, int minValue) { @@ -77,8 +75,13 @@ public static int ensureValid(int value, String description, int minValue, int d throw new IllegalArgumentException( "Default value for " + description + " must be greater than " + minValue); } - log.warn("Requested {} should be greater than {}. Requested: {}, using {}{} instead", - description, minValue, value, defaultValue, defaultValue == minValue ? "" : " (default)"); + log.warn( + "Requested {} should be greater than {}. Requested: {}, using {}{} instead", + description, + minValue, + value, + defaultValue, + defaultValue == minValue ? "" : " (default)"); value = defaultValue; } return value; @@ -98,7 +101,8 @@ public static boolean debugThreadPool() { return getBooleanFromSystemPropsOrDefault(DEBUG_THREAD_POOL_ENV_KEY, false); } - public static boolean getBooleanFromSystemPropsOrDefault(String propertyName, boolean defaultValue) { + public static boolean getBooleanFromSystemPropsOrDefault( + String propertyName, boolean defaultValue) { var property = System.getProperty(propertyName); if (property == null) { return defaultValue; @@ -121,20 +125,55 @@ public static Class getTypeArgumentFromExtendedClassByIndex(Class clazz, i Type type = clazz.getGenericSuperclass(); return (Class) ((ParameterizedType) type).getActualTypeArguments()[index]; } catch (Exception e) { - throw new RuntimeException(GENERIC_PARAMETER_TYPE_ERROR_PREFIX - + clazz.getSimpleName() - + " because it doesn't extend a class that is parameterized with the type we want to retrieve", + throw new RuntimeException( + GENERIC_PARAMETER_TYPE_ERROR_PREFIX + + clazz.getSimpleName() + + " because it doesn't extend a class that is parameterized with the type we want to" + + " retrieve", e); } } - public static Class getFirstTypeArgumentFromInterface(Class clazz, - Class expectedImplementedInterface) { + public static Class getTypeArgumentFromHierarchyByIndex(Class clazz, int index) { + return getTypeArgumentFromHierarchyByIndex(clazz, null, index); + } + + public static Class getTypeArgumentFromHierarchyByIndex( + Class clazz, Class expectedImplementedInterface, int index) { + Class c = clazz; + while (!(c.getGenericSuperclass() instanceof ParameterizedType)) { + c = c.getSuperclass(); + } + Class actualTypeArgument = + (Class) ((ParameterizedType) c.getGenericSuperclass()).getActualTypeArguments()[index]; + if (expectedImplementedInterface != null + && !expectedImplementedInterface.isAssignableFrom(actualTypeArgument)) { + throw new IllegalArgumentException( + GENERIC_PARAMETER_TYPE_ERROR_PREFIX + + clazz.getName() + + "because it doesn't extend a class that is parametrized with the type that" + + " implements " + + expectedImplementedInterface.getSimpleName() + + ". Please provide the resource type in the constructor (e.g.," + + " super(Deployment.class)."); + } else if (expectedImplementedInterface == null && actualTypeArgument.equals(Object.class)) { + throw new IllegalArgumentException( + GENERIC_PARAMETER_TYPE_ERROR_PREFIX + + clazz.getName() + + " because it doesn't extend a class that is parametrized with the type we want to" + + " retrieve or because it's Object.class. Please provide the resource type in the " + + "constructor (e.g., super(Deployment.class)."); + } + return actualTypeArgument; + } + + public static Class getFirstTypeArgumentFromInterface( + Class clazz, Class expectedImplementedInterface) { return getTypeArgumentFromInterfaceByIndex(clazz, expectedImplementedInterface, 0); } - public static Class getTypeArgumentFromInterfaceByIndex(Class clazz, - Class expectedImplementedInterface, int index) { + public static Class getTypeArgumentFromInterfaceByIndex( + Class clazz, Class expectedImplementedInterface, int index) { if (expectedImplementedInterface.isAssignableFrom(clazz)) { final var genericInterfaces = clazz.getGenericInterfaces(); @@ -149,50 +188,60 @@ public static Class getTypeArgumentFromInterfaceByIndex(Class clazz, return getTypeArgumentFromInterfaceByIndex(parent, expectedImplementedInterface, index); } } - throw new IllegalArgumentException(GENERIC_PARAMETER_TYPE_ERROR_PREFIX - + clazz.getSimpleName() + " because it or its superclasses don't implement " - + expectedImplementedInterface.getSimpleName()); + throw new IllegalArgumentException( + GENERIC_PARAMETER_TYPE_ERROR_PREFIX + + clazz.getSimpleName() + + " because it or its superclasses don't implement " + + expectedImplementedInterface.getSimpleName()); } - private static Optional> extractType(Class clazz, - Class expectedImplementedInterface, int index, Type[] genericInterfaces) { + private static Optional> extractType( + Class clazz, Class expectedImplementedInterface, int index, Type[] genericInterfaces) { Optional> target = Optional.empty(); if (genericInterfaces.length > 0) { // try to find the target interface among them - target = Arrays.stream(genericInterfaces) - .filter(type -> type.getTypeName().startsWith(expectedImplementedInterface.getName()) - && type instanceof ParameterizedType) - .map(ParameterizedType.class::cast) - .findFirst() - .map(t -> { - final Type argument = t.getActualTypeArguments()[index]; - if (argument instanceof Class) { - return (Class) argument; - } - // account for the case where the argument itself has parameters, which we will ignore - // and just return the raw type - if (argument instanceof ParameterizedType) { - final var rawType = ((ParameterizedType) argument).getRawType(); - if (rawType instanceof Class) { - return (Class) rawType; - } - } - throw new IllegalArgumentException(clazz.getSimpleName() + " implements " - + expectedImplementedInterface.getSimpleName() - + " but indirectly. Java type erasure doesn't allow to retrieve the generic type from it. Retrieved type was: " - + argument); - }); + target = + Arrays.stream(genericInterfaces) + .filter( + type -> + type.getTypeName().startsWith(expectedImplementedInterface.getName()) + && type instanceof ParameterizedType) + .map(ParameterizedType.class::cast) + .findFirst() + .map( + t -> { + final Type argument = t.getActualTypeArguments()[index]; + if (argument instanceof Class) { + return (Class) argument; + } + // account for the case where the argument itself has parameters, which we will + // ignore + // and just return the raw type + if (argument instanceof ParameterizedType) { + final var rawType = ((ParameterizedType) argument).getRawType(); + if (rawType instanceof Class) { + return (Class) rawType; + } + } + throw new IllegalArgumentException( + clazz.getSimpleName() + + " implements " + + expectedImplementedInterface.getSimpleName() + + " but indirectly. Java type erasure doesn't allow to retrieve the" + + " generic type from it. Retrieved type was: " + + argument); + }); } return target; } - public static Class getFirstTypeArgumentFromSuperClassOrInterface(Class clazz, - Class expectedImplementedInterface) { + public static Class getFirstTypeArgumentFromSuperClassOrInterface( + Class clazz, Class expectedImplementedInterface) { return getTypeArgumentFromSuperClassOrInterfaceByIndex(clazz, expectedImplementedInterface, 0); } - public static Class getTypeArgumentFromSuperClassOrInterfaceByIndex(Class clazz, - Class expectedImplementedInterface, int index) { + public static Class getTypeArgumentFromSuperClassOrInterfaceByIndex( + Class clazz, Class expectedImplementedInterface, int index) { // first check super class if it exists try { final Class superclass = clazz.getSuperclass(); @@ -205,8 +254,8 @@ public static Class getTypeArgumentFromSuperClassOrInterfaceByIndex(Class return getTypeArgumentFromInterfaceByIndex(clazz, expectedImplementedInterface, index); } catch (Exception ex) { // try on the parent - return getTypeArgumentFromSuperClassOrInterfaceByIndex(superclass, - expectedImplementedInterface, index); + return getTypeArgumentFromSuperClassOrInterfaceByIndex( + superclass, expectedImplementedInterface, index); } } } @@ -216,8 +265,11 @@ public static Class getTypeArgumentFromSuperClassOrInterfaceByIndex(Class } } - public static T instantiateAndConfigureIfNeeded(Class targetClass, - Class expectedType, String context, Configurator configurator) { + public static T instantiateAndConfigureIfNeeded( + Class targetClass, + Class expectedType, + String context, + Configurator configurator) { // if class to instantiate equals the expected interface, we cannot instantiate it so just // return null as it means we passed on void-type default value if (expectedType.equals(targetClass)) { @@ -232,11 +284,18 @@ public static T instantiateAndConfigureIfNeeded(Class targetCla } return instance; - } catch (InstantiationException | IllegalAccessException | InvocationTargetException + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException | IllegalStateException e) { - throw new OperatorException("Couldn't instantiate " + expectedType.getSimpleName() + " '" - + targetClass.getName() + "'." - + (context != null ? " Context: " + context : ""), e); + throw new OperatorException( + "Couldn't instantiate " + + expectedType.getSimpleName() + + " '" + + targetClass.getName() + + "'." + + (context != null ? " Context: " + context : ""), + e); } } @@ -252,8 +311,8 @@ public static Constructor getConstructor(Class targetClass) { return constructor; } - public static T instantiate(Class toInstantiate, Class expectedType, - String context) { + public static T instantiate( + Class toInstantiate, Class expectedType, String context) { return instantiateAndConfigureIfNeeded(toInstantiate, expectedType, context, null); } @@ -263,7 +322,8 @@ public interface Configurator { } @SuppressWarnings("rawtypes") - public static String contextFor(ControllerConfiguration controllerConfiguration, + public static String contextFor( + ControllerConfiguration controllerConfiguration, Class dependentType, Class configurationAnnotation) { return contextFor(controllerConfiguration.getName(), dependentType, configurationAnnotation); @@ -274,11 +334,13 @@ public static String contextFor(String reconcilerName) { } @SuppressWarnings("rawtypes") - public static String contextFor(String reconcilerName, + public static String contextFor( + String reconcilerName, Class dependentType, Class configurationAnnotation) { final var annotationName = - configurationAnnotation != null ? configurationAnnotation.getSimpleName() + configurationAnnotation != null + ? configurationAnnotation.getSimpleName() : io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration.class .getSimpleName(); var context = "annotation: " + annotationName + ", "; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java index d43d8aa1cf..571e389ecc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/Version.java @@ -36,8 +36,8 @@ public String getCommit() { /** * Returns the date at which this SDK instance was built * - * @return the build time at which this SDK instance was built or the date corresponding to - * {@link java.time.Instant#EPOCH} if the built time couldn't be retrieved + * @return the build time at which this SDK instance was built or the date corresponding to {@link + * java.time.Instant#EPOCH} if the built time couldn't be retrieved */ public Date getBuiltTime() { return builtTime; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/ConfigurationConverter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/ConfigurationConverter.java index f0327514ae..beebc16239 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/ConfigurationConverter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/ConfigurationConverter.java @@ -6,6 +6,8 @@ public interface ConfigurationConverter { - C configFrom(A configAnnotation, DependentResourceSpec spec, + C configFrom( + A configAnnotation, + DependentResourceSpec spec, ControllerConfiguration parentConfiguration); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationProvider.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationProvider.java deleted file mode 100644 index a0c9dc67ae..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationProvider.java +++ /dev/null @@ -1,6 +0,0 @@ -package io.javaoperatorsdk.operator.api.config.dependent; - -public interface DependentResourceConfigurationProvider { - @SuppressWarnings("rawtypes") - Object getConfigurationFor(DependentResourceSpec spec); -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolver.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolver.java index 471b0f6a8e..837ff7fbb0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolver.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolver.java @@ -16,9 +16,8 @@ private DependentResourceConfigurationResolver() {} private static final Map, ConverterAnnotationPair> converters = new HashMap<>(); - private static final Map, ConfigurationConverter> knownConverters = - new HashMap<>(); - + private static final Map, ConfigurationConverter> + knownConverters = new HashMap<>(); public static > void configureSpecFromConfigured( DependentResourceSpec spec, @@ -38,7 +37,9 @@ public static > void configureSpecFromConfi if (converterAnnotationPair == null) { final var configured = configuredClassPair.configured; converterAnnotationPair = - getOrCreateConverter(dependentResourceClass, parentConfiguration, + getOrCreateConverter( + dependentResourceClass, + parentConfiguration, configured.converter(), configured.by()); } else { @@ -74,18 +75,22 @@ private static ConfiguredClassPair getConfigured( return result; } - private static > ConverterAnnotationPair getOrCreateConverter( - Class dependentResourceClass, C parentConfiguration, - Class converterClass, - Class annotationClass) { + private static > + ConverterAnnotationPair getOrCreateConverter( + Class dependentResourceClass, + C parentConfiguration, + Class converterClass, + Class annotationClass) { var converterPair = converters.get(dependentResourceClass); if (converterPair == null) { // only instantiate a new converter if we haven't done so already for this converter type var converter = knownConverters.get(converterClass); if (converter == null) { - converter = Utils.instantiate(converterClass, - ConfigurationConverter.class, - Utils.contextFor(parentConfiguration, dependentResourceClass, Configured.class)); + converter = + Utils.instantiate( + converterClass, + ConfigurationConverter.class, + Utils.contextFor(parentConfiguration, dependentResourceClass, Configured.class)); knownConverters.put(converterClass, converter); } // record dependent class - converter association for faster future retrieval @@ -102,13 +107,16 @@ static ConfigurationConverter getConverter( } @SuppressWarnings("unused") - public static void registerConverter(Class dependentResourceClass, - ConfigurationConverter converter) { + public static void registerConverter( + Class dependentResourceClass, ConfigurationConverter converter) { var configured = getConfigured(dependentResourceClass); if (configured == null) { - throw new IllegalArgumentException("There is no @" + Configured.class.getSimpleName() - + " annotation on " + dependentResourceClass.getName() - + " or its superclasses and thus doesn't need to be associated with a converter"); + throw new IllegalArgumentException( + "There is no @" + + Configured.class.getSimpleName() + + " annotation on " + + dependentResourceClass.getName() + + " or its superclasses and thus doesn't need to be associated with a converter"); } // find the associated configuration annotation @@ -134,8 +142,8 @@ private static class ConfiguredClassPair { private final Configured configured; private final Class annotatedClass; - private ConfiguredClassPair(Configured configured, - Class annotatedClass) { + private ConfiguredClassPair( + Configured configured, Class annotatedClass) { this.configured = configured; this.annotatedClass = annotatedClass; } @@ -150,8 +158,8 @@ private static class ConverterAnnotationPair { private final ConfigurationConverter converter; private final Class annotationClass; - private ConverterAnnotationPair(ConfigurationConverter converter, - Class annotationClass) { + private ConverterAnnotationPair( + ConfigurationConverter converter, Class annotationClass) { this.converter = converter; this.annotationClass = annotationClass; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java index accccb3f6e..8e79571e73 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceSpec.java @@ -20,10 +20,15 @@ public class DependentResourceSpec { private final String useEventSourceWithName; private C nullableConfiguration; - public DependentResourceSpec(Class> dependentResourceClass, - String name, Set dependsOn, Condition readyCondition, - Condition reconcileCondition, Condition deletePostCondition, - Condition activationCondition, String useEventSourceWithName) { + public DependentResourceSpec( + Class> dependentResourceClass, + String name, + Set dependsOn, + Condition readyCondition, + Condition reconcileCondition, + Condition deletePostCondition, + Condition activationCondition, + String useEventSourceWithName) { this.dependentResourceClass = dependentResourceClass; this.name = name; this.dependsOn = dependsOn; @@ -44,8 +49,11 @@ public String getName() { @Override public String toString() { - return "DependentResourceSpec{ name='" + name + - "', type=" + getDependentResourceClass().getCanonicalName() + '}'; + return "DependentResourceSpec{ name='" + + name + + "', type=" + + getDependentResourceClass().getCanonicalName() + + '}'; } @Override @@ -100,5 +108,4 @@ public Optional getConfiguration() { protected void setNullableConfiguration(C configuration) { this.nullableConfiguration = configuration; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/Informer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/Informer.java index cdbf07a5c1..80a025009d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/Informer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/Informer.java @@ -27,18 +27,20 @@ * Specified which namespaces the associated informer monitors for custom resources events. If no * namespace is specified then which namespaces the informer will monitor will depend on the * context in which the informer is configured: + * *

* * You can set a list of namespaces or use the following constants: + * *
    - *
  • {@link Constants#WATCH_ALL_NAMESPACES}
  • - *
  • {@link Constants#WATCH_CURRENT_NAMESPACE}
  • - *
  • {@link Constants#SAME_AS_CONTROLLER}
  • + *
  • {@link Constants#WATCH_ALL_NAMESPACES} + *
  • {@link Constants#WATCH_CURRENT_NAMESPACE} + *
  • {@link Constants#SAME_AS_CONTROLLER} *
- * + * * @return the array of namespaces the associated informer monitors */ String[] namespaces() default {Constants.SAME_AS_CONTROLLER}; @@ -56,7 +58,7 @@ * Optional {@link OnAddFilter} to filter add events sent to the associated informer * * @return the {@link OnAddFilter} filter implementation to use, defaulting to the interface - * itself if no value is set + * itself if no value is set */ Class onAddFilter() default OnAddFilter.class; @@ -64,7 +66,7 @@ * Optional {@link OnUpdateFilter} to filter update events sent to the associated informer * * @return the {@link OnUpdateFilter} filter implementation to use, defaulting to the interface - * itself if no value is set + * itself if no value is set */ Class onUpdateFilter() default OnUpdateFilter.class; @@ -72,7 +74,7 @@ * Optional {@link OnDeleteFilter} to filter delete events sent to the associated informer * * @return the {@link OnDeleteFilter} filter implementation to use, defaulting to the interface - * itself if no value is set + * itself if no value is set */ Class onDeleteFilter() default OnDeleteFilter.class; @@ -80,7 +82,7 @@ * Optional {@link GenericFilter} to filter events sent to the associated informer * * @return the {@link GenericFilter} filter implementation to use, defaulting to the interface - * itself if no value is set + * itself if no value is set */ Class genericFilter() default GenericFilter.class; @@ -96,14 +98,10 @@ * "https://github.com/fabric8io/kubernetes-client/blob/43b67939fde91046ab7fb0c362f500c2b46eb59e/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/informers/impl/DefaultSharedIndexInformer.java#L273"> * method
in fabric8 client informer implementation. * - *

- * The main goal, is to be able to use limited caches or provide any custom implementation. - *

+ *

The main goal, is to be able to use limited caches or provide any custom implementation. * - *

- * See {@link BoundedItemStore} and See {@link BoundedItemStore} and CaffeinBoundedCache - *

* * @return the class of the {@link ItemStore} implementation to use */ @@ -115,5 +113,4 @@ * the informer cache. */ long informerListLimit() default NO_LONG_VALUE_SET; - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java index 0c55353b86..958a2a7a6f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerConfiguration.java @@ -21,7 +21,6 @@ import static io.javaoperatorsdk.operator.api.reconciler.Constants.*; - @SuppressWarnings("unused") public class InformerConfiguration { private final Builder builder = new Builder(); @@ -38,11 +37,18 @@ public class InformerConfiguration { private ItemStore itemStore; private Long informerListLimit; - protected InformerConfiguration(Class resourceClass, String name, Set namespaces, + protected InformerConfiguration( + Class resourceClass, + String name, + Set namespaces, boolean followControllerNamespaceChanges, - String labelSelector, OnAddFilter onAddFilter, - OnUpdateFilter onUpdateFilter, OnDeleteFilter onDeleteFilter, - GenericFilter genericFilter, ItemStore itemStore, Long informerListLimit) { + String labelSelector, + OnAddFilter onAddFilter, + OnUpdateFilter onUpdateFilter, + OnDeleteFilter onDeleteFilter, + GenericFilter genericFilter, + ItemStore itemStore, + Long informerListLimit) { this(resourceClass); this.name = name; this.namespaces = namespaces; @@ -58,11 +64,13 @@ protected InformerConfiguration(Class resourceClass, String name, Set private InformerConfiguration(Class resourceClass) { this.resourceClass = resourceClass; - this.resourceTypeName = resourceClass.isAssignableFrom(GenericKubernetesResource.class) - // in general this is irrelevant now for secondary resources it is used just by controller - // where GenericKubernetesResource now does not apply - ? GenericKubernetesResource.class.getSimpleName() - : ReconcilerUtils.getResourceTypeName(resourceClass); + this.resourceTypeName = + resourceClass.isAssignableFrom(GenericKubernetesResource.class) + // in general this is irrelevant now for secondary resources it is used just by + // controller + // where GenericKubernetesResource now does not apply + ? GenericKubernetesResource.class.getSimpleName() + : ReconcilerUtils.getResourceTypeName(resourceClass); } @SuppressWarnings({"rawtypes", "unchecked"}) @@ -74,10 +82,19 @@ public static InformerConfiguration.Builder builder( @SuppressWarnings({"rawtypes", "unchecked"}) public static InformerConfiguration.Builder builder( InformerConfiguration original) { - return new InformerConfiguration(original.resourceClass, original.name, original.namespaces, - original.followControllerNamespaceChanges, original.labelSelector, original.onAddFilter, - original.onUpdateFilter, original.onDeleteFilter, original.genericFilter, - original.itemStore, original.informerListLimit).builder; + return new InformerConfiguration( + original.resourceClass, + original.name, + original.namespaces, + original.followControllerNamespaceChanges, + original.labelSelector, + original.onAddFilter, + original.onUpdateFilter, + original.onDeleteFilter, + original.genericFilter, + original.itemStore, + original.informerListLimit) + .builder; } public static String ensureValidLabelSelector(String labelSelector) { @@ -97,8 +114,8 @@ public static boolean currentNamespaceWatched(Set namespaces) { public static void failIfNotValid(Set namespaces) { if (namespaces != null && !namespaces.isEmpty()) { - final var present = namespaces.contains(WATCH_CURRENT_NAMESPACE) - || namespaces.contains(WATCH_ALL_NAMESPACES); + final var present = + namespaces.contains(WATCH_CURRENT_NAMESPACE) || namespaces.contains(WATCH_ALL_NAMESPACES); if (!present || namespaces.size() == 1) { return; } @@ -107,7 +124,8 @@ public static void failIfNotValid(Set namespaces) { "Must specify namespaces. To watch all namespaces, use only '" + WATCH_ALL_NAMESPACES + "'. To watch only the namespace in which the operator is deployed, use only '" - + WATCH_CURRENT_NAMESPACE + "'"); + + WATCH_CURRENT_NAMESPACE + + "'"); } public static Set ensureValidNamespaces(Collection namespaces) { @@ -165,11 +183,16 @@ public Set getEffectiveNamespaces(ControllerConfiguration controllerC var targetNamespaces = getNamespaces(); if (watchCurrentNamespace()) { final String namespace = - controllerConfiguration.getConfigurationService().getKubernetesClient().getConfiguration() + controllerConfiguration + .getConfigurationService() + .getKubernetesClient() + .getConfiguration() .getNamespace(); if (namespace == null) { throw new OperatorException( - "Couldn't retrieve the currently connected namespace. Make sure it's correctly set in your ~/.kube/config file, using, e.g. 'kubectl config set-context --namespace='"); + "Couldn't retrieve the currently connected namespace. Make sure it's correctly set in" + + " your ~/.kube/config file, using, e.g. 'kubectl config set-context --namespace='"); } targetNamespaces = Collections.singleton(namespace); } @@ -190,9 +213,9 @@ public boolean getFollowControllerNamespaceChanges() { /** * Retrieves the label selector that is used to filter which resources are actually watched by the - * associated informer. See the official documentation on the - * topic - * for more details on syntax. + * associated informer. See the official documentation on the topic for + * more details on syntax. * * @return the label selector filtering watched resources */ @@ -221,17 +244,13 @@ public GenericFilter getGenericFilter() { * "https://github.com/fabric8io/kubernetes-client/blob/43b67939fde91046ab7fb0c362f500c2b46eb59e/kubernetes-client/src/main/java/io/fabric8/kubernetes/client/informers/impl/DefaultSharedIndexInformer.java#L273">method * in fabric8 client informer implementation. * - *

- * The main goal, is to be able to use limited caches or provide any custom implementation. - *

+ *

The main goal, is to be able to use limited caches or provide any custom implementation. * - *

- * See {@link BoundedItemStore} and See {@link BoundedItemStore} and CaffeineBoundedCache - *

* * @return Optional {@link ItemStore} implementation. If present this item store will be used by - * the informers. + * the informers. */ public ItemStore getItemStore() { return itemStore; @@ -245,7 +264,6 @@ public Long getInformerListLimit() { return informerListLimit; } - @SuppressWarnings("UnusedReturnValue") public class Builder { @@ -253,7 +271,8 @@ public class Builder { public InformerConfiguration buildForController() { // if the informer config uses the default "same as controller" value, reset the namespaces to // the default set for controllers - if (namespaces == null || namespaces.isEmpty() + if (namespaces == null + || namespaces.isEmpty() || inheritsNamespacesFromController(namespaces)) { namespaces = Constants.DEFAULT_NAMESPACES_SET; } @@ -268,15 +287,14 @@ public InformerConfiguration build() { namespaces = Constants.SAME_AS_CONTROLLER_NAMESPACES_SET; } if (followControllerNamespaceChanges == null) { - followControllerNamespaceChanges = - DEFAULT_FOLLOW_CONTROLLER_NAMESPACE_CHANGES; + followControllerNamespaceChanges = DEFAULT_FOLLOW_CONTROLLER_NAMESPACE_CHANGES; } return InformerConfiguration.this; } @SuppressWarnings({"unchecked"}) - public InformerConfiguration.Builder initFromAnnotation(Informer informerConfig, - String context) { + public InformerConfiguration.Builder initFromAnnotation( + Informer informerConfig, String context) { if (informerConfig != null) { // override default name if more specific one is provided @@ -291,24 +309,21 @@ public InformerConfiguration.Builder initFromAnnotation(Informer informerConf var labelSelector = Constants.NO_VALUE_SET.equals(fromAnnotation) ? null : fromAnnotation; withLabelSelector(labelSelector); - withOnAddFilter(Utils.instantiate(informerConfig.onAddFilter(), - OnAddFilter.class, context)); + withOnAddFilter( + Utils.instantiate(informerConfig.onAddFilter(), OnAddFilter.class, context)); - withOnUpdateFilter(Utils.instantiate(informerConfig.onUpdateFilter(), - OnUpdateFilter.class, context)); + withOnUpdateFilter( + Utils.instantiate(informerConfig.onUpdateFilter(), OnUpdateFilter.class, context)); - withOnDeleteFilter(Utils.instantiate(informerConfig.onDeleteFilter(), - OnDeleteFilter.class, context)); + withOnDeleteFilter( + Utils.instantiate(informerConfig.onDeleteFilter(), OnDeleteFilter.class, context)); - withGenericFilter(Utils.instantiate(informerConfig.genericFilter(), - GenericFilter.class, - context)); + withGenericFilter( + Utils.instantiate(informerConfig.genericFilter(), GenericFilter.class, context)); - withFollowControllerNamespacesChanges( - informerConfig.followControllerNamespaceChanges()); + withFollowControllerNamespacesChanges(informerConfig.followControllerNamespaceChanges()); - withItemStore(Utils.instantiate(informerConfig.itemStore(), - ItemStore.class, context)); + withItemStore(Utils.instantiate(informerConfig.itemStore(), ItemStore.class, context)); final var informerListLimitValue = informerConfig.informerListLimit(); final var informerListLimit = @@ -324,8 +339,7 @@ public Builder withName(String name) { } public Builder withNamespaces(Set namespaces) { - InformerConfiguration.this.namespaces = - ensureValidNamespaces(namespaces); + InformerConfiguration.this.namespaces = ensureValidNamespaces(namespaces); return this; } @@ -334,13 +348,13 @@ public Set namespaces() { } /** - * Sets the initial set of namespaces to watch (typically extracted from the parent - * {@link io.javaoperatorsdk.operator.processing.Controller}'s configuration), specifying - * whether changes made to the parent controller configured namespaces should be tracked or not. + * Sets the initial set of namespaces to watch (typically extracted from the parent {@link + * io.javaoperatorsdk.operator.processing.Controller}'s configuration), specifying whether + * changes made to the parent controller configured namespaces should be tracked or not. * * @param namespaces the initial set of namespaces to watch * @param followChanges {@code true} to follow the changes made to the parent controller - * namespaces, {@code false} otherwise + * namespaces, {@code false} otherwise * @return the builder instance so that calls can be chained fluently */ public Builder withNamespaces(Set namespaces, boolean followChanges) { @@ -363,47 +377,40 @@ public Builder withWatchCurrentNamespace() { return this; } - /** - * Whether the associated informer should track changes made to the parent - * {@link io.javaoperatorsdk.operator.processing.Controller}'s namespaces configuration. + * Whether the associated informer should track changes made to the parent {@link + * io.javaoperatorsdk.operator.processing.Controller}'s namespaces configuration. * * @param followChanges {@code true} to reconfigure the associated informer when the parent - * controller's namespaces are reconfigured, {@code false} otherwise + * controller's namespaces are reconfigured, {@code false} otherwise * @return the builder instance so that calls can be chained fluently */ public Builder withFollowControllerNamespacesChanges(boolean followChanges) { - InformerConfiguration.this.followControllerNamespaceChanges = - followChanges; + InformerConfiguration.this.followControllerNamespaceChanges = followChanges; return this; } public Builder withLabelSelector(String labelSelector) { - InformerConfiguration.this.labelSelector = - ensureValidLabelSelector(labelSelector); + InformerConfiguration.this.labelSelector = ensureValidLabelSelector(labelSelector); return this; } - public Builder withOnAddFilter( - OnAddFilter onAddFilter) { + public Builder withOnAddFilter(OnAddFilter onAddFilter) { InformerConfiguration.this.onAddFilter = onAddFilter; return this; } - public Builder withOnUpdateFilter( - OnUpdateFilter onUpdateFilter) { + public Builder withOnUpdateFilter(OnUpdateFilter onUpdateFilter) { InformerConfiguration.this.onUpdateFilter = onUpdateFilter; return this; } - public Builder withOnDeleteFilter( - OnDeleteFilter onDeleteFilter) { + public Builder withOnDeleteFilter(OnDeleteFilter onDeleteFilter) { InformerConfiguration.this.onDeleteFilter = onDeleteFilter; return this; } - public Builder withGenericFilter( - GenericFilter genericFilter) { + public Builder withGenericFilter(GenericFilter genericFilter) { InformerConfiguration.this.genericFilter = genericFilter; return this; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java index c3c2777049..9fb5ad4c82 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/config/informer/InformerEventSourceConfiguration.java @@ -22,8 +22,7 @@ import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_ALL_NAMESPACE_SET; import static io.javaoperatorsdk.operator.api.reconciler.Constants.WATCH_CURRENT_NAMESPACE_SET; -public interface InformerEventSourceConfiguration - extends Informable { +public interface InformerEventSourceConfiguration extends Informable { static Builder from( Class resourceClass, Class primaryResourceClass) { @@ -55,7 +54,7 @@ default boolean followControllerNamespaceChanges() { * * @return the configured {@link SecondaryToPrimaryMapper} * @see SecondaryToPrimaryMapper for more explanations on when using such a mapper is useful / - * needed + * needed */ SecondaryToPrimaryMapper getSecondaryToPrimaryMapper(); @@ -135,19 +134,20 @@ class Builder { private SecondaryToPrimaryMapper secondaryToPrimaryMapper; private KubernetesClient kubernetesClient; - private Builder(Class resourceClass, - Class primaryResourceClass) { + private Builder(Class resourceClass, Class primaryResourceClass) { this(resourceClass, primaryResourceClass, null); } @SuppressWarnings("unchecked") - private Builder(GroupVersionKind groupVersionKind, - Class primaryResourceClass) { + private Builder( + GroupVersionKind groupVersionKind, Class primaryResourceClass) { this((Class) GenericKubernetesResource.class, primaryResourceClass, groupVersionKind); } - private Builder(Class resourceClass, - Class primaryResourceClass, GroupVersionKind groupVersionKind) { + private Builder( + Class resourceClass, + Class primaryResourceClass, + GroupVersionKind groupVersionKind) { this.resourceClass = resourceClass; this.groupVersionKind = groupVersionKind; this.primaryResourceClass = primaryResourceClass; @@ -176,8 +176,7 @@ public Builder withSecondaryToPrimaryMapper( * Use this is case want to create an InformerEventSource that handles resources from different * cluster. */ - public Builder withKubernetesClient( - KubernetesClient kubernetesClient) { + public Builder withKubernetesClient(KubernetesClient kubernetesClient) { this.kubernetesClient = kubernetesClient; return this; } @@ -210,13 +209,12 @@ public Builder withWatchCurrentNamespace() { return this; } - /** - * Whether the associated informer should track changes made to the parent - * {@link io.javaoperatorsdk.operator.processing.Controller}'s namespaces configuration. + * Whether the associated informer should track changes made to the parent {@link + * io.javaoperatorsdk.operator.processing.Controller}'s namespaces configuration. * * @param followChanges {@code true} to reconfigure the associated informer when the parent - * controller's namespaces are reconfigured, {@code false} otherwise + * controller's namespaces are reconfigured, {@code false} otherwise * @return the builder instance so that calls can be chained fluently */ public Builder withFollowControllerNamespacesChanges(boolean followChanges) { @@ -229,26 +227,22 @@ public Builder withLabelSelector(String labelSelector) { return this; } - public Builder withOnAddFilter( - OnAddFilter onAddFilter) { + public Builder withOnAddFilter(OnAddFilter onAddFilter) { config.withOnAddFilter(onAddFilter); return this; } - public Builder withOnUpdateFilter( - OnUpdateFilter onUpdateFilter) { + public Builder withOnUpdateFilter(OnUpdateFilter onUpdateFilter) { config.withOnUpdateFilter(onUpdateFilter); return this; } - public Builder withOnDeleteFilter( - OnDeleteFilter onDeleteFilter) { + public Builder withOnDeleteFilter(OnDeleteFilter onDeleteFilter) { config.withOnDeleteFilter(onDeleteFilter); return this; } - public Builder withGenericFilter( - GenericFilter genericFilter) { + public Builder withGenericFilter(GenericFilter genericFilter) { config.withGenericFilter(genericFilter); return this; } @@ -269,7 +263,8 @@ public void updateFrom(InformerConfiguration informerConfig) { if (informerConfigName != null) { this.name = informerConfigName; } - config.withNamespaces(informerConfig.getNamespaces()) + config + .withNamespaces(informerConfig.getNamespaces()) .withFollowControllerNamespacesChanges( informerConfig.getFollowControllerNamespaceChanges()) .withLabelSelector(informerConfig.getLabelSelector()) @@ -286,16 +281,21 @@ public InformerEventSourceConfiguration build() { if (groupVersionKind != null && !GenericKubernetesResource.class.isAssignableFrom(resourceClass)) { throw new IllegalStateException( - "If GroupVersionKind is set the resource type must be GenericKubernetesDependentResource"); + "If GroupVersionKind is set the resource type must be" + + " GenericKubernetesDependentResource"); } return new DefaultInformerEventSourceConfiguration<>( groupVersionKind, primaryToSecondaryMapper, - Objects.requireNonNullElse(secondaryToPrimaryMapper, - Mappers.fromOwnerReferences(HasMetadata.getApiVersion(primaryResourceClass), - HasMetadata.getKind(primaryResourceClass), false)), - config.build(), kubernetesClient); + Objects.requireNonNullElse( + secondaryToPrimaryMapper, + Mappers.fromOwnerReferences( + HasMetadata.getApiVersion(primaryResourceClass), + HasMetadata.getKind(primaryResourceClass), + false)), + config.build(), + kubernetesClient); } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/monitoring/Metrics.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/monitoring/Metrics.java index bb34e5f760..3e3c834c3e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/monitoring/Metrics.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/monitoring/Metrics.java @@ -15,9 +15,7 @@ */ public interface Metrics { - /** - * The default Metrics provider: a no-operation implementation. - */ + /** The default Metrics provider: a no-operation implementation. */ Metrics NOOP = new Metrics() {}; /** @@ -36,7 +34,6 @@ default void controllerRegistered(Controller controller) */ default void receivedEvent(Event event, Map metadata) {} - /** * Called right before a resource is dispatched to the ExecutorService for reconciliation. * @@ -44,27 +41,24 @@ default void receivedEvent(Event event, Map metadata) {} * @param retryInfo the current retry state information for the reconciliation request * @param metadata metadata associated with the resource being processed */ - default void reconcileCustomResource(HasMetadata resource, RetryInfo retryInfo, - Map metadata) {} + default void reconcileCustomResource( + HasMetadata resource, RetryInfo retryInfo, Map metadata) {} /** - * Called when a precedent reconciliation for the resource associated with the specified - * {@link ResourceID} resulted in the provided exception, resulting in a retry of the - * reconciliation. + * Called when a precedent reconciliation for the resource associated with the specified {@link + * ResourceID} resulted in the provided exception, resulting in a retry of the reconciliation. * * @param resource the {@link ResourceID} associated with the resource being processed * @param exception the exception that caused the failed reconciliation resulting in a retry * @param metadata metadata associated with the resource being processed */ - default void failedReconciliation(HasMetadata resource, Exception exception, - Map metadata) {} - + default void failedReconciliation( + HasMetadata resource, Exception exception, Map metadata) {} default void reconciliationExecutionStarted(HasMetadata resource, Map metadata) {} - default void reconciliationExecutionFinished(HasMetadata resource, - Map metadata) {} - + default void reconciliationExecutionFinished( + HasMetadata resource, Map metadata) {} /** * Called when the resource associated with the specified {@link ResourceID} has been successfully @@ -76,10 +70,10 @@ default void reconciliationExecutionFinished(HasMetadata resource, default void cleanupDoneFor(ResourceID resourceID, Map metadata) {} /** - * Called when the - * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler#reconcile(HasMetadata, Context)} - * method of the Reconciler associated with the resource associated with the specified - * {@link ResourceID} has sucessfully finished. + * Called when the {@link + * io.javaoperatorsdk.operator.api.reconciler.Reconciler#reconcile(HasMetadata, Context)} method + * of the Reconciler associated with the resource associated with the specified {@link ResourceID} + * has sucessfully finished. * * @param resource the {@link ResourceID} associated with the resource being processed * @param metadata metadata associated with the resource being processed @@ -87,16 +81,16 @@ default void cleanupDoneFor(ResourceID resourceID, Map metadata) default void finishedReconciliation(HasMetadata resource, Map metadata) {} /** - * Encapsulates the information about a controller execution i.e. a call to either - * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler#reconcile(HasMetadata, Context)} - * or {@link io.javaoperatorsdk.operator.api.reconciler.Cleaner#cleanup(HasMetadata, Context)}. - * Note that instances are automatically created for you by the SDK and passed to your Metrics - * implementation at the appropriate time to the - * {@link #timeControllerExecution(ControllerExecution)} method. + * Encapsulates the information about a controller execution i.e. a call to either {@link + * io.javaoperatorsdk.operator.api.reconciler.Reconciler#reconcile(HasMetadata, Context)} or + * {@link io.javaoperatorsdk.operator.api.reconciler.Cleaner#cleanup(HasMetadata, Context)}. Note + * that instances are automatically created for you by the SDK and passed to your Metrics + * implementation at the appropriate time to the {@link + * #timeControllerExecution(ControllerExecution)} method. * - * @param the outcome type associated with the controller execution. Currently, one of - * {@link io.javaoperatorsdk.operator.api.reconciler.UpdateControl} or - * {@link io.javaoperatorsdk.operator.api.reconciler.DeleteControl} + * @param the outcome type associated with the controller execution. Currently, one of {@link + * io.javaoperatorsdk.operator.api.reconciler.UpdateControl} or {@link + * io.javaoperatorsdk.operator.api.reconciler.DeleteControl} */ interface ControllerExecution { @@ -117,9 +111,9 @@ interface ControllerExecution { /** * Retrieves the name of the successful result when the reconciliation ended positively. - * Possible values comes from the different outcomes provided by - * {@link io.javaoperatorsdk.operator.api.reconciler.UpdateControl} or - * {@link io.javaoperatorsdk.operator.api.reconciler.DeleteControl}. + * Possible values comes from the different outcomes provided by {@link + * io.javaoperatorsdk.operator.api.reconciler.UpdateControl} or {@link + * io.javaoperatorsdk.operator.api.reconciler.DeleteControl}. * * @param result the reconciliation result * @return a name associated with the specified outcome @@ -152,14 +146,14 @@ interface ControllerExecution { } /** - * Times the execution of the controller operation encapsulated by the provided - * {@link ControllerExecution}. + * Times the execution of the controller operation encapsulated by the provided {@link + * ControllerExecution}. * * @param execution the controller operation to be timed * @return the result of the controller's execution if successful * @param the type of the outcome/result of the controller's execution * @throws Exception if an error occurred during the controller's execution, usually this should - * just be a pass-through of whatever the controller returned + * just be a pass-through of whatever the controller returned */ default T timeControllerExecution(ControllerExecution execution) throws Exception { return execution.execute(); @@ -172,7 +166,7 @@ default T timeControllerExecution(ControllerExecution execution) throws E * @param map the Map which size is to be monitored * @param name the name of the provided Map to be used in metrics data * @return the Map that was passed in so the registration can be done as part of an assignment - * statement. + * statement. * @param the type of the Map being monitored */ @SuppressWarnings("unused") diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java index cd683fbc31..edc7713846 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Cleaner.java @@ -6,27 +6,25 @@ public interface Cleaner

{ /** * This method turns on automatic finalizer usage. - *

- * The implementation should delete the associated component(s). This method is called when an + * + *

The implementation should delete the associated component(s). This method is called when an * object is marked for deletion. After it's executed the custom resource finalizer is - * automatically removed by the framework; unless the return value is - * {@link DeleteControl#noFinalizerRemoval()}, which indicates that the controller has determined - * that the resource should not be deleted yet. This is usually a corner case, when a cleanup is - * tried again eventually. + * automatically removed by the framework; unless the return value is {@link + * DeleteControl#noFinalizerRemoval()}, which indicates that the controller has determined that + * the resource should not be deleted yet. This is usually a corner case, when a cleanup is tried + * again eventually. * - *

- * It's important for implementations of this method to be idempotent, since it can be called + *

It's important for implementations of this method to be idempotent, since it can be called * several times. * * @param resource the resource that is marked for deletion * @param context the context with which the operation is executed * @return {@link DeleteControl#defaultDelete()} - so the finalizer is automatically removed after - * the call. Use {@link DeleteControl#noFinalizerRemoval()} when you don't want to remove - * the finalizer immediately but rather wait asynchronously until all secondary resources - * are deleted, thus allowing you to keep the primary resource around until you are sure - * that it can be safely deleted. + * the call. Use {@link DeleteControl#noFinalizerRemoval()} when you don't want to remove the + * finalizer immediately but rather wait asynchronously until all secondary resources are + * deleted, thus allowing you to keep the primary resource around until you are sure that it + * can be safely deleted. * @see DeleteControl#noFinalizerRemoval() */ DeleteControl cleanup(P resource, Context

context) throws Exception; - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java index 0134ea0a04..f47deb9734 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Context.java @@ -31,9 +31,9 @@ default Stream getSecondaryResourcesAsStream(Class expectedType) { ControllerConfiguration

getControllerConfiguration(); /** - * Retrieve the {@link ManagedWorkflowAndDependentResourceContext} used to interact with - * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource}s and associated - * {@link io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow} + * Retrieve the {@link ManagedWorkflowAndDependentResourceContext} used to interact with {@link + * io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource}s and associated {@link + * io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow} * * @return the {@link ManagedWorkflowAndDependentResourceContext} */ @@ -43,16 +43,21 @@ default Stream getSecondaryResourcesAsStream(Class expectedType) { KubernetesClient getClient(); + /** ExecutorService initialized by framework for workflows. Used for workflow standalone mode. */ + ExecutorService getWorkflowExecutorService(); + /** - * ExecutorService initialized by framework for workflows. Used for workflow standalone mode. + * Retrieves the primary resource. + * + * @return the primary resource associated with the current reconciliation */ - ExecutorService getWorkflowExecutorService(); + P getPrimaryResource(); /** * Retrieves the primary resource cache. * * @return the {@link IndexerResourceCache} associated with the associated {@link Reconciler} for - * this context + * this context */ @SuppressWarnings("unused") IndexedResourceCache

getPrimaryCache(); @@ -65,7 +70,6 @@ default Stream getSecondaryResourcesAsStream(Class expectedType) { * rendering the current one moot. * * @return {@code true} is another reconciliation is already scheduled, {@code false} otherwise - **/ + */ boolean isNextReconciliationImminent(); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java index eb709b0d5a..d407ed0fc6 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/ControllerConfiguration.java @@ -26,8 +26,8 @@ /** * Optional finalizer name, if it is not provided, one will be automatically generated. Note that * finalizers are only added when Reconciler implement {@link Cleaner} interface and/or at least - * one managed dependent resource implements the - * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter} interface. + * one managed dependent resource implements the {@link + * io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter} interface. * * @return the finalizer name */ @@ -44,18 +44,20 @@ /** * Optional configuration of the maximal interval the SDK will wait for a reconciliation request - * to happen before one will be automatically triggered. + * to happen before one will be automatically triggered. The intention behind this feature is to + * have a failsafe, not to artificially force repeated reconciliations. For that use {@link + * UpdateControl#rescheduleAfter(long)}. * * @return the maximal reconciliation interval configuration */ - MaxReconciliationInterval maxReconciliationInterval() default @MaxReconciliationInterval( - interval = MaxReconciliationInterval.DEFAULT_INTERVAL); + MaxReconciliationInterval maxReconciliationInterval() default + @MaxReconciliationInterval(interval = MaxReconciliationInterval.DEFAULT_INTERVAL); /** * Optional {@link Retry} implementation for the associated controller to use. * * @return the class providing the {@link Retry} implementation to use, needs to provide an - * accessible no-arg constructor. + * accessible no-arg constructor. */ Class retry() default GenericRetry.class; @@ -63,14 +65,14 @@ MaxReconciliationInterval maxReconciliationInterval() default @MaxReconciliation * Optional {@link RateLimiter} implementation for the associated controller to use. * * @return the class providing the {@link RateLimiter} implementation to use, needs to provide an - * accessible no-arg constructor. + * accessible no-arg constructor. */ Class rateLimiter() default LinearRateLimiter.class; /** - * Retrieves the name used to assign as field manager for - * Server-Side - * Apply (SSA) operations. If unset, the sanitized controller name will be used. + * Retrieves the name used to assign as field manager for Server-Side Apply + * (SSA) operations. If unset, the sanitized controller name will be used. * * @return the name used as field manager for SSA operations */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java index ce6f67176c..2acf8d13ca 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContext.java @@ -22,7 +22,8 @@ public class DefaultContext

implements Context

{ private final Controller

controller; private final P primaryResource; private final ControllerConfiguration

controllerConfiguration; - private final DefaultManagedWorkflowAndDependentResourceContext

defaultManagedDependentResourceContext; + private final DefaultManagedWorkflowAndDependentResourceContext

+ defaultManagedDependentResourceContext; public DefaultContext(RetryInfo retryInfo, Controller

controller, P primaryResource) { this.retryInfo = retryInfo; @@ -43,17 +44,6 @@ public Set getSecondaryResources(Class expectedType) { return getSecondaryResourcesAsStream(expectedType).collect(Collectors.toSet()); } - @Override - public IndexedResourceCache

getPrimaryCache() { - return controller.getEventSourceManager().getControllerEventSource(); - } - - @Override - public boolean isNextReconciliationImminent() { - return controller.getEventProcessor() - .isNextReconciliationImminent(ResourceID.fromResource(primaryResource)); - } - @Override public Stream getSecondaryResourcesAsStream(Class expectedType) { return controller.getEventSourceManager().getEventSourcesFor(expectedType).stream() @@ -112,12 +102,25 @@ public ExecutorService getWorkflowExecutorService() { return controller.getExecutorServiceManager().workflowExecutorService(); } + @Override + public P getPrimaryResource() { + return primaryResource; + } + + @Override + public IndexedResourceCache

getPrimaryCache() { + return controller.getEventSourceManager().getControllerEventSource(); + } + + @Override + public boolean isNextReconciliationImminent() { + return controller + .getEventProcessor() + .isNextReconciliationImminent(ResourceID.fromResource(primaryResource)); + } + public DefaultContext

setRetryInfo(RetryInfo retryInfo) { this.retryInfo = retryInfo; return this; } - - public P getPrimaryResource() { - return primaryResource; - } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java index 4f0f08b3b8..5f198a3d01 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceContext.java @@ -18,7 +18,8 @@ public class EventSourceContext

{ private final KubernetesClient client; private final Class

primaryResourceClass; - public EventSourceContext(IndexerResourceCache

primaryCache, + public EventSourceContext( + IndexerResourceCache

primaryCache, ControllerConfiguration

controllerConfiguration, KubernetesClient client, Class

primaryResourceClass) { @@ -48,11 +49,11 @@ public ControllerConfiguration

getControllerConfiguration() { } /** - * Provides access to the {@link KubernetesClient} used by the current - * {@link io.javaoperatorsdk.operator.Operator} instance. + * Provides access to the {@link KubernetesClient} used by the current {@link + * io.javaoperatorsdk.operator.Operator} instance. * - * @return the {@link KubernetesClient} used by the current - * {@link io.javaoperatorsdk.operator.Operator} instance. + * @return the {@link KubernetesClient} used by the current {@link + * io.javaoperatorsdk.operator.Operator} instance. */ public KubernetesClient getClient() { return client; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java index 9531599072..cf6cf21486 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/EventSourceUtils.java @@ -13,14 +13,15 @@ public class EventSourceUtils { public static

List> dependentEventSources( EventSourceContext

eventSourceContext, DependentResource... dependentResources) { return Arrays.stream(dependentResources) - .flatMap(dr -> dr.eventSource(eventSourceContext).stream()).toList(); + .flatMap(dr -> dr.eventSource(eventSourceContext).stream()) + .toList(); } @SuppressWarnings("unchecked") public static

List> eventSourcesFromWorkflow( - EventSourceContext

context, - Workflow

workflow) { + EventSourceContext

context, Workflow

workflow) { return workflow.getDependentResourcesWithoutActivationCondition().stream() - .flatMap(dr -> dr.eventSource(context).stream()).toList(); + .flatMap(dr -> dr.eventSource(context).stream()) + .toList(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Ignore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Ignore.java index ad2a755db8..2383e9c399 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Ignore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Ignore.java @@ -12,6 +12,4 @@ */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) -public @interface Ignore { - -} +public @interface Ignore {} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/MaxReconciliationInterval.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/MaxReconciliationInterval.java index 056cdec805..9a1635b16b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/MaxReconciliationInterval.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/MaxReconciliationInterval.java @@ -17,22 +17,21 @@ * reconciliation is scheduled with a target interval after the last reconciliation. Note that * this not applies for retries, in case of an exception reconciliation is not scheduled. This is * not a fixed rate, in other words a new reconciliation is scheduled after each reconciliation. - *

- * If an interval is specified by {@link UpdateControl} or {@link DeleteControl}, those take + * + *

If an interval is specified by {@link UpdateControl} or {@link DeleteControl}, those take * precedence. - *

- * This is a fail-safe feature, in the sense that if informers are in place and the reconciler + * + *

This is a fail-safe feature, in the sense that if informers are in place and the reconciler * implementation is correct, this feature can be turned off. - *

- * Use {@link Constants#NO_MAX_RECONCILIATION_INTERVAL} to turn off this feature. + * + *

Use {@link Constants#NO_MAX_RECONCILIATION_INTERVAL} to turn off this feature. * * @return max delay between reconciliations - **/ + */ long interval(); /** * @return time unit for max delay between reconciliations */ TimeUnit timeUnit() default TimeUnit.HOURS; - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java new file mode 100644 index 0000000000..ac0fe9675c --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtils.java @@ -0,0 +1,193 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.function.UnaryOperator; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.base.PatchContext; +import io.fabric8.kubernetes.client.dsl.base.PatchType; +import io.javaoperatorsdk.operator.OperatorException; +import io.javaoperatorsdk.operator.processing.event.ResourceID; + +/** + * Utility methods to patch the primary resource state and store it to the related cache, to make + * sure that the latest version of the resource is present for the next reconciliation. The main use + * case for such updates is to store state is resource status. + * + *

The way the framework handles this is with retryable updates with optimistic locking, and + * caches the updated resource from the response in an overlay cache on top of the Informer cache. + * If the update fails, it reads the primary resource from the cluster, applies the modifications + * again and retries the update. + */ +public class PrimaryUpdateAndCacheUtils { + + public static final int DEFAULT_MAX_RETRY = 10; + + private PrimaryUpdateAndCacheUtils() {} + + private static final Logger log = LoggerFactory.getLogger(PrimaryUpdateAndCacheUtils.class); + + /** + * Updates the status with optimistic locking and caches the result for next reconciliation. For + * details see {@link #updateAndCacheResource}. + */ + public static

P updateStatusAndCacheResource( + P primary, Context

context, UnaryOperator

modificationFunction) { + return updateAndCacheResource( + primary, + context, + modificationFunction, + r -> context.getClient().resource(r).updateStatus()); + } + + /** + * Patches the status using JSON Merge Patch with optimistic locking and caches the result for + * next reconciliation. For details see {@link #updateAndCacheResource}. + */ + public static

P mergePatchStatusAndCacheResource( + P primary, Context

context, UnaryOperator

modificationFunction) { + return updateAndCacheResource( + primary, context, modificationFunction, r -> context.getClient().resource(r).patchStatus()); + } + + /** + * Patches the status using JSON Patch with optimistic locking and caches the result for next + * reconciliation. For details see {@link #updateAndCacheResource}. + */ + public static

P patchStatusAndCacheResource( + P primary, Context

context, UnaryOperator

modificationFunction) { + return updateAndCacheResource( + primary, + context, + UnaryOperator.identity(), + r -> context.getClient().resource(r).editStatus(modificationFunction)); + } + + /** + * Patches the status using Server Side Apply with optimistic locking and caches the result for + * next reconciliation. For details see {@link #updateAndCacheResource}. + */ + public static

P ssaPatchStatusAndCacheResource( + P primary, P freshResourceWithStatus, Context

context) { + return updateAndCacheResource( + primary, + context, + r -> freshResourceWithStatus, + r -> + context + .getClient() + .resource(r) + .subresource("status") + .patch( + new PatchContext.Builder() + .withForce(true) + .withFieldManager(context.getControllerConfiguration().fieldManager()) + .withPatchType(PatchType.SERVER_SIDE_APPLY) + .build())); + } + + /** + * Same as {@link #updateAndCacheResource(HasMetadata, Context, UnaryOperator, UnaryOperator, + * int)} using the default maximum retry number as defined by {@link #DEFAULT_MAX_RETRY}. + * + * @param resourceToUpdate original resource to update + * @param context of reconciliation + * @param modificationFunction modifications to make on primary + * @param updateMethod the update method implementation + * @param

primary type + * @return the updated resource + */ + public static

P updateAndCacheResource( + P resourceToUpdate, + Context

context, + UnaryOperator

modificationFunction, + UnaryOperator

updateMethod) { + return updateAndCacheResource( + resourceToUpdate, context, modificationFunction, updateMethod, DEFAULT_MAX_RETRY); + } + + /** + * Modifies the primary using the specified modification function, then uses the modified resource + * for the request to update with provided update method. As the {@code resourceVersion} field of + * the modified resource is set to the value found in the specified resource to update, the update + * operation will therefore use optimistic locking on the server. If the request fails on + * optimistic update, we read the resource again from the K8S API server and retry the whole + * process. In short, we make sure we always update the resource with optimistic locking, then we + * cache the resource in an internal cache. Without further going into details, the optimistic + * locking is needed so we can reliably handle the caching. + * + * @param resourceToUpdate original resource to update + * @param context of reconciliation + * @param modificationFunction modifications to make on primary + * @param updateMethod the update method implementation + * @param maxRetry maximum number of retries before giving up + * @param

primary type + * @return the updated resource + */ + @SuppressWarnings("unchecked") + public static

P updateAndCacheResource( + P resourceToUpdate, + Context

context, + UnaryOperator

modificationFunction, + UnaryOperator

updateMethod, + int maxRetry) { + + if (log.isDebugEnabled()) { + log.debug("Conflict retrying update for: {}", ResourceID.fromResource(resourceToUpdate)); + } + P modified = null; + int retryIndex = 0; + while (true) { + try { + modified = modificationFunction.apply(resourceToUpdate); + modified + .getMetadata() + .setResourceVersion(resourceToUpdate.getMetadata().getResourceVersion()); + var updated = updateMethod.apply(modified); + context + .eventSourceRetriever() + .getControllerEventSource() + .handleRecentResourceUpdate( + ResourceID.fromResource(resourceToUpdate), updated, resourceToUpdate); + return updated; + } catch (KubernetesClientException e) { + log.trace("Exception during patch for resource: {}", resourceToUpdate); + retryIndex++; + // only retry on conflict (409) and unprocessable content (422) which + // can happen if JSON Patch is not a valid request since there was + // a concurrent request which already removed another finalizer: + // List element removal from a list is by index in JSON Patch + // so if addressing a second finalizer but first is meanwhile removed + // it is a wrong request. + if (e.getCode() != 409 && e.getCode() != 422) { + throw e; + } + if (retryIndex > maxRetry) { + log.warn("Retry exhausted, last desired resource: {}", modified); + throw new OperatorException( + "Exceeded maximum (" + + maxRetry + + ") retry attempts to patch resource: " + + ResourceID.fromResource(resourceToUpdate), + e); + } + log.debug( + "Retrying patch for resource name: {}, namespace: {}; HTTP code: {}", + resourceToUpdate.getMetadata().getName(), + resourceToUpdate.getMetadata().getNamespace(), + e.getCode()); + resourceToUpdate = + (P) + context + .getClient() + .resources(resourceToUpdate.getClass()) + .inNamespace(resourceToUpdate.getMetadata().getNamespace()) + .withName(resourceToUpdate.getMetadata().getName()) + .get(); + } + } + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java index 1545a87fe9..4075903787 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Reconciler.java @@ -15,7 +15,7 @@ public interface Reconciler

{ * @param resource the resource that has been created or updated * @param context the context with which the operation is executed * @return UpdateControl to manage updates on the custom resource (usually the status) after - * reconciliation. + * reconciliation. */ UpdateControl

reconcile(P resource, Context

context) throws Exception; @@ -24,7 +24,7 @@ public interface Reconciler

{ * be registered by the SDK. * * @param context a {@link EventSourceContext} providing access to information useful to event - * sources + * sources * @return a list of event sources */ default List> prepareEventSources(EventSourceContext

context) { @@ -32,27 +32,25 @@ default List> prepareEventSources(EventSourceContext

contex } /** - *

* Reconciler can override this method in order to update the status sub-resource in the case an * exception in thrown. In that case {@link #updateErrorStatus(HasMetadata, Context, Exception)} * is called automatically. - *

- * The result of the method call is used to make a status update on the custom resource. This is - * always a sub-resource update request, so no update on custom resource itself (like spec of + * + *

The result of the method call is used to make a status update on the custom resource. This + * is always a sub-resource update request, so no update on custom resource itself (like spec of * metadata) happens. Note that this update request will also produce an event, and will result in * a reconciliation if the controller is not generation aware. - *

- * Note that the scope of this feature is only the reconcile method of the reconciler, since there - * should not be updates on custom resource after it is marked for deletion. + * + *

Note that the scope of this feature is only the reconcile method of the reconciler, since + * there should not be updates on custom resource after it is marked for deletion. * * @param resource to update the status on * @param context the current context * @param e exception thrown from the reconciler * @return the updated resource */ - default ErrorStatusUpdateControl

updateErrorStatus(P resource, Context

context, - Exception e) { + default ErrorStatusUpdateControl

updateErrorStatus( + P resource, Context

context, Exception e) { return ErrorStatusUpdateControl.defaultErrorProcessing(); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/RetryInfo.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/RetryInfo.java index f746c20dce..26996d6c06 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/RetryInfo.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/RetryInfo.java @@ -8,7 +8,7 @@ public interface RetryInfo { /** * @return true, if the current attempt is the last one in regard to the retry limit - * configuration. + * configuration. */ boolean isLastAttempt(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java index a8ae1331d7..1b5eefd7ff 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/UpdateControl.java @@ -11,8 +11,7 @@ public class UpdateControl

extends BaseControl - * Note that this does not work, if the {@link CustomResource#initStatus()} is implemented, since - * it breaks the diffing process. Don't implement it if using this method. - *

- * There is also an issue with setting value to {@code null} with older Kubernetes versions (1.19 - * and below). See: Note that this does not work, if the {@link CustomResource#initStatus()} is implemented, + * since it breaks the diffing process. Don't implement it if using this method. There is also an + * issue with setting value to {@code null} with older Kubernetes versions (1.19 and below). See: + * https://github.com/fabric8io/kubernetes-client/issues/4158 * * @param resource type @@ -76,5 +74,4 @@ public boolean isNoUpdate() { public boolean isPatchResourceAndStatus() { return patchResource && patchStatus; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java index 3fb6c2c830..16a9f7ba4b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/Workflow.java @@ -20,23 +20,22 @@ /** * If {@code true}, the managed workflow should be explicitly invoked within the reconciler - * implementation. If {@code false}, the workflow is invoked just before the - * {@link Reconciler#reconcile(HasMetadata, Context)} method. + * implementation. If {@code false}, the workflow is invoked just before the {@link + * Reconciler#reconcile(HasMetadata, Context)} method. */ boolean explicitInvocation() default false; /** * If {@code true} and exceptions are thrown during the workflow's execution, the reconciler won't * throw an {@link io.javaoperatorsdk.operator.AggregatedOperatorException} at the end of the - * execution as would normally be the case. Instead, it will proceed to its - * {@link Reconciler#reconcile(HasMetadata, Context)} method as if no error occurred. It is then - * up to the developer to decide how to proceed by retrieving the errored dependents (and their - * associated exception) via {@link WorkflowReconcileResult#getErroredDependents()} or - * {@link WorkflowCleanupResult#getErroredDependents()}, the workflow result itself being accessed - * from {@link Context#managedWorkflowAndDependentResourceContext()}. If {@code false}, an - * exception will be automatically thrown at the end of the workflow execution, presenting an - * aggregated view of what happened. + * execution as would normally be the case. Instead, it will proceed to its {@link + * Reconciler#reconcile(HasMetadata, Context)} method as if no error occurred. It is then up to + * the developer to decide how to proceed by retrieving the errored dependents (and their + * associated exception) via {@link WorkflowReconcileResult#getErroredDependents()} or {@link + * WorkflowCleanupResult#getErroredDependents()}, the workflow result itself being accessed from + * {@link Context#managedWorkflowAndDependentResourceContext()}. If {@code false}, an exception + * will be automatically thrown at the end of the workflow execution, presenting an aggregated + * view of what happened. */ boolean handleExceptionsInReconciler() default false; - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java index 754e2c85be..dd7fd8404e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/Dependent.java @@ -5,8 +5,8 @@ import static io.javaoperatorsdk.operator.api.reconciler.Constants.NO_VALUE_SET; /** - * The annotation used to create managed {@link DependentResource} associated with a given - * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler} + * The annotation used to create managed {@link DependentResource} associated with a given {@link + * io.javaoperatorsdk.operator.api.reconciler.Reconciler} */ public @interface Dependent { @@ -17,8 +17,8 @@ * The name of this dependent. This is needed to be able to refer to it when creating dependencies * between dependent resources. * - * @return the name if it has been set, - * {@link io.javaoperatorsdk.operator.api.reconciler.Constants#NO_VALUE_SET} otherwise + * @return the name if it has been set, {@link + * io.javaoperatorsdk.operator.api.reconciler.Constants#NO_VALUE_SET} otherwise */ String name() default NO_VALUE_SET; @@ -26,17 +26,17 @@ * The condition (if it exists) that needs to become true before the workflow can further proceed. * * @return a {@link Condition} implementation, defaulting to the interface itself if no value is - * set + * set */ Class readyPostcondition() default Condition.class; /** - * The condition (if it exists) that needs to become true before the associated - * {@link DependentResource} is reconciled. Note that if this condition is set and the condition - * doesn't hold true, the associated secondary will be deleted. + * The condition (if it exists) that needs to become true before the associated {@link + * DependentResource} is reconciled. Note that if this condition is set and the condition doesn't + * hold true, the associated secondary will be deleted. * * @return a {@link Condition} implementation, defaulting to the interface itself if no value is - * set + * set */ Class reconcilePrecondition() default Condition.class; @@ -46,12 +46,11 @@ * to have been deleted. * * @return a {@link Condition} implementation, defaulting to the interface itself if no value is - * set + * set */ Class deletePostcondition() default Condition.class; /** - *

* A condition that needs to become true for the dependent to even be considered as part of the * workflow. This is useful for dependents that represent optional resources on the cluster and * might not be present. In this case, a reconcile pre-condition is not enough because in that @@ -60,13 +59,11 @@ * behaves like a reconcile pre-condition in the sense that dependents, that depend on this one, * will only get created if the condition is met and will get deleted if the condition becomes * false. - *

- *

- * As other conditions, this gets evaluated at the beginning of every reconciliation, which means - * that it allows to react to optional resources becoming available on the cluster as the operator - * runs. More specifically, this means that the associated event source can get dynamically - * registered or de-registered during reconciliation. - *

+ * + *

As other conditions, this gets evaluated at the beginning of every reconciliation, which + * means that it allows to react to optional resources becoming available on the cluster as the + * operator runs. More specifically, this means that the associated event source can get + * dynamically registered or de-registered during reconciliation. */ Class activationCondition() default Condition.class; @@ -74,7 +71,7 @@ * The list of named dependents that need to be reconciled before this one can be. * * @return the list (possibly empty) of named dependents that need to be reconciled before this - * one can be + * one can be */ String[] dependsOn() default {}; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java index 7c4e530ce2..49c9df3c7d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResource.java @@ -19,7 +19,7 @@ public interface DependentResource { * Computes a default name for the specified DependentResource class * * @param dependentResourceClass the DependentResource class for which we want to compute a - * default name + * default name * @return the default name for the specified DependentResource class */ @SuppressWarnings("rawtypes") @@ -46,11 +46,12 @@ static String defaultNameFor(Class dependentResourc /** * Dependent resources are designed to provide event sources by default. There are, however, cases * where they might not: + * *

    - *
  • If an event source is shared between multiple dependent resources. In this case only one or - * none of the dependent resources sharing the event source should provide one, if any.
  • - *
  • Some special implementation of an event source that just executes some action might not - * provide one.
  • + *
  • If an event source is shared between multiple dependent resources. In this case only one + * or none of the dependent resources sharing the event source should provide one, if any. + *
  • Some special implementation of an event source that just executes some action might not + * provide one. *
* * @param eventSourceContext context of event source initialization @@ -67,7 +68,7 @@ default Optional> eventSource( * for this DependentResource. * * @param primary the primary resource for which we want to retrieve the secondary resource - * associated with this DependentResource + * associated with this DependentResource * @param context the current {@link Context} in which the operation is called * @return the secondary resource or {@link Optional#empty()} if it doesn't exist * @throws IllegalStateException if more than one secondary is found to match the primary resource @@ -81,13 +82,12 @@ default Optional getSecondaryResource(P primary, Context

context) { * deleted, usually meaning that the dependent implements {@link Deleter} * * @return {@code true} if explicit handling of resource deletion is needed, {@code false} - * otherwise + * otherwise */ default boolean isDeletable() { return this instanceof Deleter; } - /** * Retrieves the name identifying this DependentResource implementation, useful to refer to this * in {@link io.javaoperatorsdk.operator.processing.dependent.workflow.Workflow} instances @@ -97,5 +97,4 @@ default boolean isDeletable() { default String name() { return defaultNameFor(getClass()); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java index cc9a1dd6c3..8803c15b8c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/DependentResourceFactory.java @@ -6,13 +6,15 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.managed.ConfiguredDependentResource; @SuppressWarnings({"rawtypes", "unchecked"}) -public interface DependentResourceFactory, D extends DependentResourceSpec> { +public interface DependentResourceFactory< + C extends ControllerConfiguration, D extends DependentResourceSpec> { DependentResourceFactory DEFAULT = new DependentResourceFactory() {}; default DependentResource createFrom(D spec, C controllerConfiguration) { final var dependentResourceClass = spec.getDependentResourceClass(); - return Utils.instantiateAndConfigureIfNeeded(dependentResourceClass, + return Utils.instantiateAndConfigureIfNeeded( + dependentResourceClass, DependentResource.class, Utils.contextFor(controllerConfiguration, dependentResourceClass, Dependent.class), (instance) -> configure(instance, spec, controllerConfiguration)); @@ -29,9 +31,9 @@ default void configure(DependentResource instance, D spec, C controllerConfigura default Class associatedResourceType(D spec) { final var dependentResourceClass = spec.getDependentResourceClass(); - final var dr = Utils.instantiateAndConfigureIfNeeded(dependentResourceClass, - DependentResource.class, - null, null); + final var dr = + Utils.instantiateAndConfigureIfNeeded( + dependentResourceClass, DependentResource.class, null, null); return dr != null ? dr.resourceType() : null; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceReferencer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceReferencer.java index b2f8c95692..d1b288df24 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceReferencer.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/EventSourceReferencer.java @@ -15,5 +15,4 @@ default void useEventSourceWithName(String name) {} */ void resolveEventSource(EventSourceRetriever

eventSourceRetriever) throws EventSourceNotFoundException; - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/GarbageCollected.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/GarbageCollected.java index 91afbf5e5c..002f95ec1b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/GarbageCollected.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/GarbageCollected.java @@ -4,26 +4,21 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; /** - *

- * Can be implemented by a dependent resource extending - * {@link io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource} - * to express that the resource deletion is handled by the controller during - * {@link DependentResource#reconcile(HasMetadata, Context)}. This takes effect during a - * reconciliation workflow, but not during a cleanup workflow, when a {@code reconcilePrecondition} - * is not met for the resource. In this case, {@link #delete(HasMetadata, Context)} is called. - * During a cleanup workflow, however, {@link #delete(HasMetadata, Context)} is not called, letting - * the Kubernetes garbage collector do its work instead (using owner references). - *

- *

- * If a dependent resource implement this interface, an owner reference pointing to the associated - * primary resource will be automatically added to this managed resource. - *

- *

- * See this issue - * for more details. - *

+ * Can be implemented by a dependent resource extending {@link + * io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource} to + * express that the resource deletion is handled by the controller during {@link + * DependentResource#reconcile(HasMetadata, Context)}. This takes effect during a reconciliation + * workflow, but not during a cleanup workflow, when a {@code reconcilePrecondition} is not met for + * the resource. In this case, {@link #delete(HasMetadata, Context)} is called. During a cleanup + * workflow, however, {@link #delete(HasMetadata, Context)} is not called, letting the Kubernetes + * garbage collector do its work instead (using owner references). + * + *

If a dependent resource implement this interface, an owner reference pointing to the + * associated primary resource will be automatically added to this managed resource. + * + *

See this + * issue for more details. * * @param

primary resource type */ -public interface GarbageCollected

extends Deleter

{ -} +public interface GarbageCollected

extends Deleter

{} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java index 952bf14490..25c87045f1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/NameSetter.java @@ -3,5 +3,4 @@ public interface NameSetter { void setName(String name); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java index 66d982f01d..af416a748a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/ReconcileResult.java @@ -38,9 +38,11 @@ public static ReconcileResult aggregatedResult(List> r @Override public String toString() { - return resourceOperations.entrySet().stream().collect(Collectors.toMap( - e -> e instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) e) : e, - Map.Entry::getValue)) + return resourceOperations.entrySet().stream() + .collect( + Collectors.toMap( + e -> e instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) e) : e, + Map.Entry::getValue)) .toString(); } @@ -57,7 +59,9 @@ public Optional getSingleResource() { } public Operation getSingleOperation() { - return resourceOperations.entrySet().stream().findFirst().map(Map.Entry::getValue) + return resourceOperations.entrySet().stream() + .findFirst() + .map(Map.Entry::getValue) .orElseThrow(); } @@ -67,6 +71,8 @@ public Map getResourceOperations() { } public enum Operation { - CREATED, UPDATED, NONE + CREATED, + UPDATED, + NONE } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ConfiguredDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ConfiguredDependentResource.java index 326482aab0..2c9946d866 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ConfiguredDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ConfiguredDependentResource.java @@ -2,7 +2,6 @@ import java.util.Optional; - public interface ConfiguredDependentResource { void configureWith(C config); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java index bf9ead4c97..8adfaad44c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedWorkflowAndDependentResourceContext.java @@ -24,9 +24,8 @@ public class DefaultManagedWorkflowAndDependentResourceContext

context; - public DefaultManagedWorkflowAndDependentResourceContext(Controller

controller, - P primaryResource, - Context

context) { + public DefaultManagedWorkflowAndDependentResourceContext( + Controller

controller, P primaryResource, Context

context) { this.controller = controller; this.primaryResource = primaryResource; this.context = context; @@ -50,11 +49,16 @@ public T put(Object key, T value) { } if (previous != null && !previous.getClass().isAssignableFrom(value.getClass())) { - logWarning("Previous value (" + previous + - ") for key (" + key + - ") was not of type " + value.getClass() + - ". This might indicate an issue in your code. If not, use put(" + key + - ", null) first to remove the previous value."); + logWarning( + "Previous value (" + + previous + + ") for key (" + + key + + ") was not of type " + + value.getClass() + + ". This might indicate an issue in your code. If not, use put(" + + key + + ", null) first to remove the previous value."); } return (T) previous; } @@ -67,9 +71,15 @@ void logWarning(String message) { @Override @SuppressWarnings("unused") public T getMandatory(Object key, Class expectedType) { - return get(key, expectedType).orElseThrow(() -> new IllegalStateException( - "Mandatory attribute (key: " + key + ", type: " + expectedType.getName() - + ") is missing or not of the expected type")); + return get(key, expectedType) + .orElseThrow( + () -> + new IllegalStateException( + "Mandatory attribute (key: " + + key + + ", type: " + + expectedType.getName() + + ") is missing or not of the expected type")); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceException.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceException.java index 4fbdd65241..5c09a47e08 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceException.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedDependentResourceException.java @@ -5,8 +5,8 @@ public class ManagedDependentResourceException extends OperatorException { private final String associatedDependentName; - public ManagedDependentResourceException(String associatedDependentName, String message, - Throwable cause) { + public ManagedDependentResourceException( + String associatedDependentName, String message, Throwable cause) { super(message, cause); this.associatedDependentName = associatedDependentName; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java index 0dfd323d9a..0217787049 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/ManagedWorkflowAndDependentResourceContext.java @@ -21,7 +21,7 @@ public interface ManagedWorkflowAndDependentResourceContext { * @param expectedType the class representing the expected type of the contextual object * @param the type of the expected contextual object * @return an Optional containing the contextual object or {@link Optional#empty()} if no such - * object exists or doesn't match the expected type + * object exists or doesn't match the expected type */ Optional get(Object key, Class expectedType); @@ -30,22 +30,20 @@ public interface ManagedWorkflowAndDependentResourceContext { * the semantics of this operation is defined as removing the mapping associated with the * specified key. * - *

- * Note that, while implementations shouldn't throw a {@link ClassCastException} when the new + *

Note that, while implementations shouldn't throw a {@link ClassCastException} when the new * value type differs from the type of the existing value, calling sites might encounter such * exceptions if they bind the return value to a specific type. Users are either expected to * disregard the return value (most common case) or "reset" the value type associated with the * specified key by first calling {@code put(key, null)} if they want to ensure some level of type * safety in their code (where attempting to store values of different types under the same key * might be indicative of an issue). - *

* * @param object type * @param key the key identifying which contextual object to add or remove from the context * @param value the value to add to the context or {@code null} to remove an existing entry - * associated with the specified key + * associated with the specified key * @return the previous value if one was associated with the specified key, {@code null} - * otherwise. + * otherwise. */ T put(Object key, T value); @@ -67,8 +65,8 @@ public interface ManagedWorkflowAndDependentResourceContext { Optional getWorkflowCleanupResult(); /** - * Explicitly reconcile the declared workflow for the associated - * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler} + * Explicitly reconcile the declared workflow for the associated {@link + * io.javaoperatorsdk.operator.api.reconciler.Reconciler} * * @return the result of the workflow reconciliation * @throws IllegalStateException if called when explicit invocation is not requested @@ -76,14 +74,13 @@ public interface ManagedWorkflowAndDependentResourceContext { WorkflowReconcileResult reconcileManagedWorkflow(); /** - * Explicitly clean-up dependent resources in the declared workflow for the associated - * {@link io.javaoperatorsdk.operator.api.reconciler.Reconciler}. Note that calling this method is - * only needed if the associated reconciler implements the - * {@link io.javaoperatorsdk.operator.api.reconciler.Cleaner} interface. + * Explicitly clean-up dependent resources in the declared workflow for the associated {@link + * io.javaoperatorsdk.operator.api.reconciler.Reconciler}. Note that calling this method is only + * needed if the associated reconciler implements the {@link + * io.javaoperatorsdk.operator.api.reconciler.Cleaner} interface. * * @return the result of the workflow reconciliation on cleanup * @throws IllegalStateException if called when explicit invocation is not requested */ WorkflowCleanupResult cleanupManageWorkflow(); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/ControllerHealthInfo.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/ControllerHealthInfo.java index 1d65922f11..de96dd27a9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/ControllerHealthInfo.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/ControllerHealthInfo.java @@ -27,25 +27,27 @@ public Map unhealthyEventSources() { .collect(Collectors.toMap(EventSource::name, e -> e)); } - public Map informerEventSourceHealthIndicators() { + public Map + informerEventSourceHealthIndicators() { return eventSourceManager.allEventSources().stream() .filter(e -> e instanceof InformerWrappingEventSourceHealthIndicator) - .collect(Collectors.toMap(EventSource::name, - e -> (InformerWrappingEventSourceHealthIndicator) e)); - + .collect( + Collectors.toMap( + EventSource::name, e -> (InformerWrappingEventSourceHealthIndicator) e)); } /** - * @return Map with event sources that wraps an informer. Thus, either a - * {@link ControllerEventSource} or an - * {@link io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}. + * @return Map with event sources that wraps an informer. Thus, either a {@link + * ControllerEventSource} or an {@link + * io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}. */ - public Map unhealthyInformerEventSourceHealthIndicators() { + public Map + unhealthyInformerEventSourceHealthIndicators() { return eventSourceManager.allEventSources().stream() .filter(e -> e.getStatus() == Status.UNHEALTHY) .filter(e -> e instanceof InformerWrappingEventSourceHealthIndicator) - .collect(Collectors.toMap(EventSource::name, - e -> (InformerWrappingEventSourceHealthIndicator) e)); + .collect( + Collectors.toMap( + EventSource::name, e -> (InformerWrappingEventSourceHealthIndicator) e)); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java index 2732c16707..ac8ddd4b1f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/EventSourceHealthIndicator.java @@ -3,8 +3,8 @@ public interface EventSourceHealthIndicator { /** - * Retrieves the health status of an - * {@link io.javaoperatorsdk.operator.processing.event.source.EventSource} + * Retrieves the health status of an {@link + * io.javaoperatorsdk.operator.processing.event.source.EventSource} * * @return the health status * @see Status diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/InformerWrappingEventSourceHealthIndicator.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/InformerWrappingEventSourceHealthIndicator.java index da9b2ace2c..2c337f3cd7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/InformerWrappingEventSourceHealthIndicator.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/InformerWrappingEventSourceHealthIndicator.java @@ -11,8 +11,10 @@ public interface InformerWrappingEventSourceHealthIndicator i.getStatus() != Status.HEALTHY).findAny(); + var nonUp = + informerHealthIndicators().values().stream() + .filter(i -> i.getStatus() != Status.HEALTHY) + .findAny(); return nonUp.isPresent() ? Status.UNHEALTHY : Status.HEALTHY; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java index 272c360a87..ec61251132 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/health/Status.java @@ -4,11 +4,8 @@ * The health status of an {@link io.javaoperatorsdk.operator.processing.event.source.EventSource} */ public enum Status { - - HEALTHY, UNHEALTHY, - /** - * For event sources where it cannot be determined if it is healthy ot not. - */ + HEALTHY, + UNHEALTHY, + /** For event sources where it cannot be determined if it is healthy ot not. */ UNKNOWN - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java index 491ac7fb01..a53d52c429 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/Controller.java @@ -51,8 +51,7 @@ @SuppressWarnings({"unchecked", "rawtypes"}) @Ignore public class Controller

- implements Reconciler

, LifecycleAware, Cleaner

, - RegisteredController

{ + implements Reconciler

, LifecycleAware, Cleaner

, RegisteredController

{ private static final Logger log = LoggerFactory.getLogger(Controller.class); private static final String CLEANUP = "cleanup"; @@ -78,7 +77,8 @@ public class Controller

private final ControllerHealthInfo controllerHealthInfo; private final EventSourceContext

eventSourceContext; - public Controller(Reconciler

reconciler, + public Controller( + Reconciler

reconciler, ControllerConfiguration

configuration, KubernetesClient kubernetesClient) { // needs to be initialized early since it's used in other downstream classes @@ -95,16 +95,18 @@ public Controller(Reconciler

reconciler, final var managed = configurationService.getWorkflowFactory().workflowFor(configuration); managedWorkflow = managed.resolve(kubernetesClient, configuration); explicitWorkflowInvocation = - configuration.getWorkflowSpec().map(WorkflowSpec::isExplicitInvocation) - .orElse(false); + configuration.getWorkflowSpec().map(WorkflowSpec::isExplicitInvocation).orElse(false); eventSourceManager = new EventSourceManager<>(this); eventProcessor = new EventProcessor<>(eventSourceManager, configurationService); eventSourceManager.postProcessDefaultEventSourcesAfterProcessorInitializer(); controllerHealthInfo = new ControllerHealthInfo(eventSourceManager); - eventSourceContext = new EventSourceContext<>( - eventSourceManager.getControllerEventSource(), configuration, kubernetesClient, - configuration.getResourceClass()); + eventSourceContext = + new EventSourceContext<>( + eventSourceManager.getControllerEventSource(), + configuration, + kubernetesClient, + configuration.getResourceClass()); initAndRegisterEventSources(eventSourceContext); configurationService.getMetrics().controllerRegistered(this); } @@ -148,11 +150,14 @@ public Map metadata() { @Override public UpdateControl

execute() throws Exception { initContextIfNeeded(resource, context); - configuration.getWorkflowSpec().ifPresent(ws -> { - if (!managedWorkflow.isEmpty() && !explicitWorkflowInvocation) { - managedWorkflow.reconcile(resource, context); - } - }); + configuration + .getWorkflowSpec() + .ifPresent( + ws -> { + if (!managedWorkflow.isEmpty() && !explicitWorkflowInvocation) { + managedWorkflow.reconcile(resource, context); + } + }); return reconciler.reconcile(resource, context); } }); @@ -223,7 +228,8 @@ private DeleteControl workflowCleanupResultToDefaultDelete( if (workflowCleanupResult == null) { return DeleteControl.defaultDelete(); } else { - return workflowCleanupResult.allPostConditionsMet() ? DeleteControl.defaultDelete() + return workflowCleanupResult.allPostConditionsMet() + ? DeleteControl.defaultDelete() : DeleteControl.noFinalizerRemoval(); } } @@ -243,23 +249,27 @@ public void initAndRegisterEventSources(EventSourceContext

context) { managedWorkflow.getDependentResourcesWithoutActivationCondition(); final var size = dependentResourcesByName.size(); if (size > 0) { - dependentResourcesByName.forEach(dependentResource -> { - Optional eventSource = dependentResource.eventSource(context); - eventSource.ifPresent(eventSourceManager::registerEventSource); - }); + dependentResourcesByName.forEach( + dependentResource -> { + Optional eventSource = dependentResource.eventSource(context); + eventSource.ifPresent(eventSourceManager::registerEventSource); + }); // resolve event sources referenced by name for dependents that reuse an existing event source final Map> unresolvable = new HashMap<>(size); dependentResourcesByName.stream() .filter(EventSourceReferencer.class::isInstance) .map(EventSourceReferencer.class::cast) - .forEach(dr -> { - try { - ((EventSourceReferencer

) dr).resolveEventSource(eventSourceManager); - } catch (EventSourceNotFoundException e) { - unresolvable.computeIfAbsent(e.getEventSourceName(), s -> new ArrayList<>()).add(dr); - } - }); + .forEach( + dr -> { + try { + ((EventSourceReferencer

) dr).resolveEventSource(eventSourceManager); + } catch (EventSourceNotFoundException e) { + unresolvable + .computeIfAbsent(e.getEventSourceName(), s -> new ArrayList<>()) + .add(dr); + } + }); if (!unresolvable.isEmpty()) { throw new IllegalStateException( "Couldn't resolve referenced EventSources: " + unresolvable); @@ -317,8 +327,8 @@ public void start() throws OperatorException { /** * Registers the specified controller with this operator, overriding its default configuration by - * the specified one (usually created via - * {@link io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider#override(ControllerConfiguration)}, + * the specified one (usually created via {@link + * io.javaoperatorsdk.operator.api.config.ControllerConfigurationOverrider#override(ControllerConfiguration)}, * passing it the controller's original configuration. * * @param startEventProcessor if event processing should be started automatically @@ -329,8 +339,11 @@ public synchronized void start(boolean startEventProcessor) throws OperatorExcep final String controllerName = configuration.getName(); final var crdName = configuration.getResourceTypeName(); final var specVersion = "v1"; - log.info("Starting '{}' controller for reconciler: {}, resource: {}", controllerName, - configuration.getAssociatedReconcilerClassName(), resClass.getCanonicalName()); + log.info( + "Starting '{}' controller for reconciler: {}, resource: {}", + controllerName, + configuration.getAssociatedReconcilerClassName(), + resClass.getCanonicalName()); // fail early if we're missing the current namespace information failOnMissingCurrentNS(); @@ -348,14 +361,13 @@ public synchronized void start(boolean startEventProcessor) throws OperatorExcep } } - - private void validateCRDWithLocalModelIfRequired(Class

resClass, String controllerName, - String crdName, String specVersion) { + private void validateCRDWithLocalModelIfRequired( + Class

resClass, String controllerName, String crdName, String specVersion) { final CustomResourceDefinition crd; if (getConfiguration().getConfigurationService().checkCRDAndValidateLocalModel() && CustomResource.class.isAssignableFrom(resClass)) { - crd = kubernetesClient.apiextensions().v1().customResourceDefinitions().withName(crdName) - .get(); + crd = + kubernetesClient.apiextensions().v1().customResourceDefinitions().withName(crdName).get(); if (crd == null) { throwMissingCRDException(crdName, specVersion, controllerName); } @@ -414,7 +426,8 @@ private void failOnMissingCurrentNS() { throw new OperatorException( "Controller '" + configuration.getName() - + "' is configured to watch the current namespace but it couldn't be inferred from the current configuration."); + + "' is configured to watch the current namespace but it couldn't be inferred from" + + " the current configuration."); } } @@ -473,5 +486,4 @@ public boolean workflowContainsDependentForType(Class clazz) { return managedWorkflow.getDependentResourcesByName().values().stream() .anyMatch(d -> d.resourceType().equals(clazz)); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java index f9cf38fa56..6c0a5c95ba 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/GroupVersionKind.java @@ -12,7 +12,7 @@ public class GroupVersionKind { private final String version; private final String kind; private final String apiVersion; - protected final static Map, GroupVersionKind> CACHE = + protected static final Map, GroupVersionKind> CACHE = new ConcurrentHashMap<>(); public GroupVersionKind(String apiVersion, String kind) { @@ -33,8 +33,8 @@ public static GroupVersionKind gvkFor(Class resourceClass } private static GroupVersionKind computeGVK(Class rc) { - return new GroupVersionKind(HasMetadata.getGroup(rc), - HasMetadata.getVersion(rc), HasMetadata.getKind(rc)); + return new GroupVersionKind( + HasMetadata.getGroup(rc), HasMetadata.getVersion(rc), HasMetadata.getKind(rc)); } public GroupVersionKind(String group, String version, String kind) { @@ -56,7 +56,7 @@ public GroupVersionKind(String group, String version, String kind) { *

    *     Sample: v1/ConfigMap
    * 
- **/ + */ public static GroupVersionKind fromString(String gvk) { String[] parts = gvk.split(SEPARATOR); if (parts.length == 3) { @@ -100,12 +100,16 @@ public String apiVersion() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - GroupVersionKind that = (GroupVersionKind) o; - return Objects.equals(apiVersion, that.apiVersion) && Objects.equals(kind, that.kind); + if (this == o) return true; + if (!(o instanceof GroupVersionKind that)) return false; + return Objects.equals(apiVersion, that.apiVersion) + && Objects.equals(kind, that.kind) + && specificEquals(that) + && that.specificEquals(this); + } + + protected boolean specificEquals(GroupVersionKind that) { + return true; } @Override @@ -115,10 +119,6 @@ public int hashCode() { @Override public String toString() { - return "GroupVersionKind{" + - "apiVersion='" + apiVersion + '\'' + - ", kind='" + kind + '\'' + - '}'; + return toGVKString(); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/LoggingUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/LoggingUtils.java index 1a180f6698..3b093cd818 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/LoggingUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/LoggingUtils.java @@ -10,5 +10,4 @@ private LoggingUtils() {} public static boolean isNotSensitiveResource(HasMetadata resource) { return !(resource instanceof Secret); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MDCUtils.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MDCUtils.java index d8bb3897c3..12348ed932 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MDCUtils.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/MDCUtils.java @@ -1,10 +1,9 @@ package io.javaoperatorsdk.operator.processing; -import io.fabric8.kubernetes.api.model.ObjectMeta; -import io.javaoperatorsdk.operator.api.config.Utils; import org.slf4j.MDC; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.config.Utils; import io.javaoperatorsdk.operator.processing.event.ResourceID; public class MDCUtils { @@ -17,50 +16,51 @@ public class MDCUtils { private static final String GENERATION = "resource.generation"; private static final String UID = "resource.uid"; private static final String NO_NAMESPACE = "no namespace"; - private static final boolean enabled = Utils.getBooleanFromSystemPropsOrDefault(Utils.USE_MDC_ENV_KEY, true); + private static final boolean enabled = + Utils.getBooleanFromSystemPropsOrDefault(Utils.USE_MDC_ENV_KEY, true); public static void addResourceIDInfo(ResourceID resourceID) { - if (enabled) { - MDC.put(NAME, resourceID.getName()); - MDC.put(NAMESPACE, resourceID.getNamespace().orElse(NO_NAMESPACE)); - } + if (enabled) { + MDC.put(NAME, resourceID.getName()); + MDC.put(NAMESPACE, resourceID.getNamespace().orElse(NO_NAMESPACE)); + } } public static void removeResourceIDInfo() { - if (enabled) { - MDC.remove(NAME); - MDC.remove(NAMESPACE); - } + if (enabled) { + MDC.remove(NAME); + MDC.remove(NAMESPACE); + } } public static void addResourceInfo(HasMetadata resource) { - if (enabled) { - MDC.put(API_VERSION, resource.getApiVersion()); - MDC.put(KIND, resource.getKind()); - final var metadata = resource.getMetadata(); - if (metadata != null) { - MDC.put(NAME, metadata.getName()); - if (metadata.getNamespace() != null) { - MDC.put(NAMESPACE, metadata.getNamespace()); - } - MDC.put(RESOURCE_VERSION, metadata.getResourceVersion()); - if (metadata.getGeneration() != null) { - MDC.put(GENERATION, metadata.getGeneration().toString()); - } - MDC.put(UID, metadata.getUid()); - } + if (enabled) { + MDC.put(API_VERSION, resource.getApiVersion()); + MDC.put(KIND, resource.getKind()); + final var metadata = resource.getMetadata(); + if (metadata != null) { + MDC.put(NAME, metadata.getName()); + if (metadata.getNamespace() != null) { + MDC.put(NAMESPACE, metadata.getNamespace()); + } + MDC.put(RESOURCE_VERSION, metadata.getResourceVersion()); + if (metadata.getGeneration() != null) { + MDC.put(GENERATION, metadata.getGeneration().toString()); + } + MDC.put(UID, metadata.getUid()); } + } } public static void removeResourceInfo() { - if (enabled) { - MDC.remove(API_VERSION); - MDC.remove(KIND); - MDC.remove(NAME); - MDC.remove(NAMESPACE); - MDC.remove(RESOURCE_VERSION); - MDC.remove(GENERATION); - MDC.remove(UID); - } + if (enabled) { + MDC.remove(API_VERSION); + MDC.remove(KIND); + MDC.remove(NAME); + MDC.remove(NAMESPACE); + MDC.remove(RESOURCE_VERSION); + MDC.remove(GENERATION); + MDC.remove(UID); + } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java index db69d8134b..9471d52cc4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResource.java @@ -20,7 +20,7 @@ * An abstract implementation of {@link DependentResource} to be used as base for custom * implementations, providing, in particular, the core {@link #reconcile(HasMetadata, Context)} * logic for dependents - * + * * @param the dependent resource type * @param

the associated primary resource type */ @@ -46,9 +46,10 @@ protected AbstractDependentResource(String name) { creator = creatable ? (Creator) this : null; updater = updatable ? (Updater) this : null; - dependentResourceReconciler = this instanceof BulkDependentResource - ? new BulkDependentResourceReconciler<>((BulkDependentResource) this) - : new SingleDependentResourceReconciler<>(this); + dependentResourceReconciler = + this instanceof BulkDependentResource + ? new BulkDependentResourceReconciler<>((BulkDependentResource) this) + : new SingleDependentResourceReconciler<>(this); this.name = name == null ? DependentResource.defaultNameFor(this.getClass()) : name; } @@ -85,13 +86,15 @@ protected ReconcileResult reconcile(P primary, R actualResource, Context

c var updatedResource = handleUpdate(actualResource, desired, primary, context); return ReconcileResult.resourceUpdated(updatedResource); } else { - log.debug("Update skipped for dependent {} as it matched the existing one", + log.debug( + "Update skipped for dependent {} as it matched the existing one", actualResource instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) actualResource) : getClass().getSimpleName()); } } else { - log.debug("Update skipped for dependent {} implement Updater interface to modify it", + log.debug( + "Update skipped for dependent {} implement Updater interface to modify it", actualResource instanceof HasMetadata ? ResourceID.fromResource((HasMetadata) actualResource) : getClass().getSimpleName()); @@ -116,7 +119,6 @@ public Optional getSecondaryResource(P primary, Context

context) { } else { return selectTargetSecondaryResource(secondaryResources, primary, context); } - } /** @@ -130,10 +132,10 @@ public Optional getSecondaryResource(P primary, Context

context) { * @param context the context in which this method is called * @return the matching secondary resource or {@link Optional#empty()} if none matches * @throws IllegalStateException if more than one candidate is found, in which case some other - * mechanism might be necessary to distinguish between candidate secondary resources + * mechanism might be necessary to distinguish between candidate secondary resources */ - protected Optional selectTargetSecondaryResource(Set secondaryResources, P primary, - Context

context) { + protected Optional selectTargetSecondaryResource( + Set secondaryResources, P primary, Context

context) { R desired = desired(primary, context); var targetResources = secondaryResources.stream().filter(r -> r.equals(desired)).toList(); if (targetResources.size() > 1) { @@ -151,10 +153,13 @@ private void throwIfNull(R desired, P primary, String descriptor) { } private void logForOperation(String operation, P primary, R desired) { - final var desiredDesc = desired instanceof HasMetadata - ? "'" + ((HasMetadata) desired).getMetadata().getName() + "' " - + ((HasMetadata) desired).getKind() - : desired.getClass().getSimpleName(); + final var desiredDesc = + desired instanceof HasMetadata + ? "'" + + ((HasMetadata) desired).getMetadata().getName() + + "' " + + ((HasMetadata) desired).getKind() + : desired.getClass().getSimpleName(); log.debug("{} {} for primary {}", operation, desiredDesc, ResourceID.fromResource(primary)); } @@ -170,7 +175,7 @@ protected R handleCreate(R desired, P primary, Context

context) { * needed. * * @param primary the {@link ResourceID} of the primary resource associated with the newly created - * resource + * resource * @param created the newly created resource * @param context the context in which this operation is called */ @@ -180,7 +185,7 @@ protected R handleCreate(R desired, P primary, Context

context) { * Allows subclasses to perform additional processing on the updated resource if needed. * * @param primary the {@link ResourceID} of the primary resource associated with the newly updated - * resource + * resource * @param updated the updated resource * @param actual the resource as it was before the update * @param context the context in which this operation is called @@ -196,7 +201,8 @@ protected R handleUpdate(R actual, R desired, P primary, Context

context) { protected R desired(P primary, Context

context) { throw new IllegalStateException( - "desired method must be implemented if this DependentResource can be created and/or updated"); + "desired method must be implemented if this DependentResource can be created and/or" + + " updated"); } public void delete(P primary, Context

context) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java index 6745a45a72..7f2674892f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractEventSourceHolderDependentResource.java @@ -3,6 +3,7 @@ import java.util.Optional; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.api.config.Utils; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.api.reconciler.Ignore; @@ -14,7 +15,8 @@ import io.javaoperatorsdk.operator.processing.event.source.EventSource; @Ignore -public abstract class AbstractEventSourceHolderDependentResource> +public abstract class AbstractEventSourceHolderDependentResource< + R, P extends HasMetadata, T extends EventSource> extends AbstractDependentResource implements EventSourceReferencer

{ private T eventSource; @@ -22,13 +24,22 @@ public abstract class AbstractEventSourceHolderDependentResource resourceType) { this(resourceType, null); } protected AbstractEventSourceHolderDependentResource(Class resourceType, String name) { super(name); - this.resourceType = resourceType; + if (resourceType == null) { + this.resourceType = (Class) Utils.getTypeArgumentFromHierarchyByIndex(getClass(), 0); + } else { + this.resourceType = resourceType; + } } /** @@ -99,15 +110,15 @@ public Optional eventSource() { protected void onCreated(P primary, R created, Context

context) { if (isCacheFillerEventSource) { - recentOperationCacheFiller().handleRecentResourceCreate(ResourceID.fromResource(primary), - created); + recentOperationCacheFiller() + .handleRecentResourceCreate(ResourceID.fromResource(primary), created); } } protected void onUpdated(P primary, R updated, R actual, Context

context) { if (isCacheFillerEventSource) { - recentOperationCacheFiller().handleRecentResourceUpdate(ResourceID.fromResource(primary), - updated, actual); + recentOperationCacheFiller() + .handleRecentResourceUpdate(ResourceID.fromResource(primary), updated, actual); } } @@ -115,5 +126,4 @@ protected void onUpdated(P primary, R updated, R actual, Context

context) { private RecentOperationCacheFiller recentOperationCacheFiller() { return (RecentOperationCacheFiller) eventSource; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java index acb6cb99d3..4c828b7eb9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/AbstractExternalDependentResource.java @@ -8,16 +8,21 @@ import io.javaoperatorsdk.operator.processing.event.source.EventSource; import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; -public abstract class AbstractExternalDependentResource> +public abstract class AbstractExternalDependentResource< + R, P extends HasMetadata, T extends EventSource> extends AbstractEventSourceHolderDependentResource { private final boolean isDependentResourceWithExplicitState = this instanceof DependentResourceWithExplicitState; private final boolean isBulkDependentResource = this instanceof BulkDependentResource; + @SuppressWarnings("rawtypes") private DependentResourceWithExplicitState dependentResourceWithExplicitState; + private InformerEventSource externalStateEventSource; + protected AbstractExternalDependentResource() {} + @SuppressWarnings("unchecked") protected AbstractExternalDependentResource(Class resourceType) { super(resourceType); @@ -31,13 +36,13 @@ protected AbstractExternalDependentResource(Class resourceType) { public void resolveEventSource(EventSourceRetriever

eventSourceRetriever) { super.resolveEventSource(eventSourceRetriever); if (isDependentResourceWithExplicitState) { - final var eventSourceName = (String) dependentResourceWithExplicitState - .eventSourceName().orElse(null); - externalStateEventSource = (InformerEventSource) eventSourceRetriever - .getEventSourceFor(dependentResourceWithExplicitState.stateResourceClass(), - eventSourceName); + final var eventSourceName = + (String) dependentResourceWithExplicitState.eventSourceName().orElse(null); + externalStateEventSource = + (InformerEventSource) + eventSourceRetriever.getEventSourceFor( + dependentResourceWithExplicitState.stateResourceClass(), eventSourceName); } - } @Override @@ -83,18 +88,17 @@ public Matcher.Result match(R resource, P primary, Context

context) { } @SuppressWarnings("unchecked") - public void deleteTargetResource(P primary, R resource, String key, - Context

context) { + public void deleteTargetResource(P primary, R resource, String key, Context

context) { if (isDependentResourceWithExplicitState) { - context.getClient() + context + .getClient() .resource(dependentResourceWithExplicitState.stateResource(primary, resource)) .delete(); } handleDeleteTargetResource(primary, resource, key, context); } - public void handleDeleteTargetResource(P primary, R resource, String key, - Context

context) { + public void handleDeleteTargetResource(P primary, R resource, String key, Context

context) { throw new IllegalStateException("Override this method in case you manage an bulk resource"); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java index 957a969ecc..4f55041c04 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResource.java @@ -22,10 +22,10 @@ public interface BulkDependentResource { * identified by an arbitrary key. * * @param primary the primary resource with which we want to identify which secondary resources - * are associated + * are associated * @param context the {@link Context} associated with the current reconciliation * @return a Map associating desired secondary resources with the specified primary via arbitrary - * identifiers + * identifiers */ default Map desiredResources(P primary, Context

context) { throw new IllegalStateException( @@ -37,10 +37,10 @@ default Map desiredResources(P primary, Context

context) { * the specified primary resource. * * @param primary the primary resource for which we want to retrieve the associated secondary - * resources + * resources * @param context the {@link Context} associated with the current reconciliation * @return a Map associating actual secondary resources with the specified primary via arbitrary - * identifiers + * identifiers */ Map getSecondaryResources(P primary, Context

context); @@ -49,7 +49,7 @@ default Map desiredResources(P primary, Context

context) { * secondary resources for the specified primary. * * @param primary the primary resource for which we want to remove now undesired secondary - * resources still present on the cluster + * resources still present on the cluster * @param resource the actual resource existing on the cluster that needs to be removed * @param key key of the resource * @param context actual context @@ -58,17 +58,17 @@ default Map desiredResources(P primary, Context

context) { /** * Determines whether the specified secondary resource matches the desired state with target index - * of a bulk resource as defined from the specified primary resource, given the specified - * {@link Context}. + * of a bulk resource as defined from the specified primary resource, given the specified {@link + * Context}. * * @param actualResource the resource we want to determine whether it's matching the desired state * @param desired the resource's desired state * @param primary the primary resource from which the desired state is inferred * @param context the context in which the resource is being matched * @return a {@link Result} encapsulating whether the resource matched its desired state and this - * associated state if it was computed as part of the matching process. Use the static - * convenience methods ({@link Result#nonComputed(boolean)} and - * {@link Result#computed(boolean, Object)}) + * associated state if it was computed as part of the matching process. Use the static + * convenience methods ({@link Result#nonComputed(boolean)} and {@link + * Result#computed(boolean, Object)}) */ default Result match(R actualResource, R desired, P primary, Context

context) { return Matcher.Result.computed(desired.equals(actualResource), desired); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java index 1ed36edaa2..ebb47fd355 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkDependentResourceReconciler.java @@ -29,9 +29,8 @@ public ReconcileResult reconcile(P primary, Context

context) { if (!(bulkDependentResource instanceof Creator) && !(bulkDependentResource instanceof Deleter) && !(bulkDependentResource instanceof Updater)) { - return ReconcileResult - .aggregatedResult(actualResources.values().stream().map(ReconcileResult::noOperation) - .toList()); + return ReconcileResult.aggregatedResult( + actualResources.values().stream().map(ReconcileResult::noOperation).toList()); } final var desiredResources = bulkDependentResource.desiredResources(primary, context); @@ -42,10 +41,11 @@ public ReconcileResult reconcile(P primary, Context

context) { } final List> results = new ArrayList<>(desiredResources.size()); - desiredResources.forEach((key, value) -> { - final var instance = new BulkDependentResourceInstance<>(bulkDependentResource, value); - results.add(instance.reconcile(primary, actualResources.get(key), context)); - }); + desiredResources.forEach( + (key, value) -> { + final var instance = new BulkDependentResourceInstance<>(bulkDependentResource, value); + results.add(instance.reconcile(primary, actualResources.get(key), context)); + }); return ReconcileResult.aggregatedResult(results); } @@ -56,13 +56,14 @@ public void delete(P primary, Context

context) { deleteExtraResources(Collections.emptySet(), actualResources, primary, context); } - private void deleteExtraResources(Set expectedKeys, - Map actualResources, P primary, Context

context) { - actualResources.forEach((key, value) -> { - if (!expectedKeys.contains(key)) { - bulkDependentResource.deleteTargetResource(primary, value, key, context); - } - }); + private void deleteExtraResources( + Set expectedKeys, Map actualResources, P primary, Context

context) { + actualResources.forEach( + (key, value) -> { + if (!expectedKeys.contains(key)) { + bulkDependentResource.deleteTargetResource(primary, value, key, context); + } + }); } /** @@ -74,13 +75,12 @@ private void deleteExtraResources(Set expectedKeys, */ @Ignore private static class BulkDependentResourceInstance - extends AbstractDependentResource - implements Creator, Deleter

, Updater { + extends AbstractDependentResource implements Creator, Deleter

, Updater { private final BulkDependentResource bulkDependentResource; private final R desired; - private BulkDependentResourceInstance(BulkDependentResource bulkDependentResource, - R desired) { + private BulkDependentResourceInstance( + BulkDependentResource bulkDependentResource, R desired) { this.bulkDependentResource = bulkDependentResource; this.desired = desired; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java index 26e8a158a4..67fe71b197 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/BulkUpdater.java @@ -15,10 +15,14 @@ public interface BulkUpdater extends Updater { default Matcher.Result match(R actualResource, P primary, Context

context) { if (!(this instanceof BulkDependentResource)) { throw new IllegalStateException( - BulkUpdater.class.getSimpleName() + " interface should only be implemented by " - + BulkDependentResource.class.getSimpleName() + " implementations"); + BulkUpdater.class.getSimpleName() + + " interface should only be implemented by " + + BulkDependentResource.class.getSimpleName() + + " implementations"); } - throw new IllegalStateException("This method should not be called from a " - + BulkDependentResource.class.getSimpleName() + " implementation"); + throw new IllegalStateException( + "This method should not be called from a " + + BulkDependentResource.class.getSimpleName() + + " implementation"); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java index aa650bf1b5..6e86f04a5c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/CRUDBulkDependentResource.java @@ -4,8 +4,4 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Deleter; public interface CRUDBulkDependentResource - extends BulkDependentResource, - Creator, - BulkUpdater, - Deleter

{ -} + extends BulkDependentResource, Creator, BulkUpdater, Deleter

{} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java index 4609b4a9c7..7c3fff3c49 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/DependentResourceWithExplicitState.java @@ -44,5 +44,4 @@ default Optional eventSourceName() { * @return that stores state */ S stateResource(P primary, R resource); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java index f8bababb42..286bef86c5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Matcher.java @@ -7,14 +7,14 @@ /** * Implement this interface to provide custom matching logic when determining whether secondary - * resources match their desired state. This is used by some default implementations of the - * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} interface, notably - * {@link io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource}. + * resources match their desired state. This is used by some default implementations of the {@link + * io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} interface, notably {@link + * io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource}. * * @param the type associated with the secondary resources we want to match - * @param

the type associated with the primary resources with which the related - * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} - * implementation is associated + * @param

the type associated with the primary resources with which the related {@link + * io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} implementation is + * associated */ public interface Matcher { @@ -31,7 +31,7 @@ interface Result { * Whether or not the actual resource matched the desired state * * @return {@code true} if the observed resource matched the desired state, {@code false} - * otherwise + * otherwise */ boolean matched(); @@ -40,7 +40,7 @@ interface Result { * empty if not. * * @return an {@link Optional} holding the desired state if it has been computed during the - * matching process or {@link Optional#empty()} if not + * matching process or {@link Optional#empty()} if not */ default Optional computedDesired() { return Optional.empty(); @@ -90,9 +90,9 @@ public Optional computedDesired() { * @param primary the primary resource from which the desired state is inferred * @param context the context in which the resource is being matched * @return a {@link Result} encapsulating whether the resource matched its desired state and this - * associated state if it was computed as part of the matching process. Use the static - * convenience methods ({@link Result#nonComputed(boolean)} and - * {@link Result#computed(boolean, Object)}) to create your return {@link Result}. + * associated state if it was computed as part of the matching process. Use the static + * convenience methods ({@link Result#nonComputed(boolean)} and {@link + * Result#computed(boolean, Object)}) to create your return {@link Result}. */ Result match(R actualResource, P primary, Context

context); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java index 2f229abe34..8780022a73 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/Updater.java @@ -6,5 +6,4 @@ public interface Updater extends Matcher { R update(R actual, R desired, P primary, Context

context); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java index 659b8b4720..3cf93cba53 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/AbstractPollingDependentResource.java @@ -16,6 +16,8 @@ public abstract class AbstractPollingDependentResource public static final Duration DEFAULT_POLLING_PERIOD = Duration.ofMillis(5000); private Duration pollingPeriod; + protected AbstractPollingDependentResource() {} + protected AbstractPollingDependentResource(Class resourceType) { this(resourceType, DEFAULT_POLLING_PERIOD); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java index f315619cde..c0181207d8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PerResourcePollingDependentResource.java @@ -14,6 +14,7 @@ public abstract class PerResourcePollingDependentResource implements PerResourcePollingEventSource.ResourceFetcher { + public PerResourcePollingDependentResource() {} public PerResourcePollingDependentResource(Class resourceType) { super(resourceType); @@ -27,9 +28,10 @@ public PerResourcePollingDependentResource(Class resourceType, Duration polli protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { - return new PerResourcePollingEventSource<>(resourceType(), context, - new PerResourcePollingConfigurationBuilder<>( - this, getPollingPeriod()) + return new PerResourcePollingEventSource<>( + resourceType(), + context, + new PerResourcePollingConfigurationBuilder<>(this, getPollingPeriod()) .withCacheKeyMapper(this) .withName(name()) .build()); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java index 01860cb4c5..674cbdd906 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/external/PollingDependentResource.java @@ -22,8 +22,8 @@ public PollingDependentResource(Class resourceType, CacheKeyMapper cacheKe this.cacheKeyMapper = cacheKeyMapper; } - public PollingDependentResource(Class resourceType, Duration pollingPeriod, - CacheKeyMapper cacheKeyMapper) { + public PollingDependentResource( + Class resourceType, Duration pollingPeriod, CacheKeyMapper cacheKeyMapper) { super(resourceType, pollingPeriod); this.cacheKeyMapper = cacheKeyMapper; } @@ -31,8 +31,8 @@ public PollingDependentResource(Class resourceType, Duration pollingPeriod, @Override protected ExternalResourceCachingEventSource createEventSource( EventSourceContext

context) { - return new PollingEventSource<>(resourceType(), + return new PollingEventSource<>( + resourceType(), new PollingConfiguration<>(name(), this, getPollingPeriod(), cacheKeyMapper)); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/BooleanWithUndefined.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/BooleanWithUndefined.java index a8aa3f5d05..ee59a1c487 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/BooleanWithUndefined.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/BooleanWithUndefined.java @@ -1,10 +1,10 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; -/** - * A replacement for {@link Boolean}, which can't be used in annotations. - */ +/** A replacement for {@link Boolean}, which can't be used in annotations. */ public enum BooleanWithUndefined { - TRUE, FALSE, UNDEFINED; + TRUE, + FALSE, + UNDEFINED; public Boolean asBoolean() { switch (this) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDKubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDKubernetesDependentResource.java index f4ccf647ed..392ac6d894 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDKubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDKubernetesDependentResource.java @@ -15,10 +15,11 @@ */ @Ignore public abstract class CRUDKubernetesDependentResource - extends - KubernetesDependentResource + extends KubernetesDependentResource implements Creator, Updater, GarbageCollected

{ + public CRUDKubernetesDependentResource() {} + public CRUDKubernetesDependentResource(Class resourceType) { super(resourceType); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDNoGCKubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDNoGCKubernetesDependentResource.java index e335ef74e5..3b3c11b006 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDNoGCKubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/CRUDNoGCKubernetesDependentResource.java @@ -7,20 +7,20 @@ import io.javaoperatorsdk.operator.processing.dependent.Updater; /** - * * Adaptor class resources that manage Create, Read and Update operations, however resource is NOT * garbage collected by Kubernetes when the associated primary resource is destroyed, instead * explicitly deleted. This is useful when resource needs to be deleted before another one in a - * workflow, in other words an ordering matters during a cleanup. See also: - * Related issue + * workflow, in other words an ordering matters during a cleanup. See also: Related issue * * @param the type of the managed dependent resource * @param

the type of the associated primary resource */ @Ignore public class CRUDNoGCKubernetesDependentResource - extends KubernetesDependentResource - implements Creator, Updater, Deleter

{ + extends KubernetesDependentResource implements Creator, Updater, Deleter

{ + + public CRUDNoGCKubernetesDependentResource() {} public CRUDNoGCKubernetesDependentResource(Class resourceType) { super(resourceType); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java index c5dd08069e..3ed1fd5c4d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesDependentResource.java @@ -15,15 +15,24 @@ public GenericKubernetesDependentResource(GroupVersionKind groupVersionKind) { this(GroupVersionKindPlural.from(groupVersionKind)); } + public GenericKubernetesDependentResource(GroupVersionKind groupVersionKind, String name) { + this(GroupVersionKindPlural.from(groupVersionKind), name); + } + public GenericKubernetesDependentResource(GroupVersionKindPlural groupVersionKind) { - super(GenericKubernetesResource.class); + super(GenericKubernetesResource.class, null); + this.groupVersionKind = groupVersionKind; + } + + public GenericKubernetesDependentResource(GroupVersionKindPlural groupVersionKind, String name) { + super(GenericKubernetesResource.class, name); this.groupVersionKind = groupVersionKind; } - protected InformerEventSourceConfiguration.Builder informerConfigurationBuilder( - EventSourceContext

context) { - return InformerEventSourceConfiguration.from(groupVersionKind, - context.getPrimaryResourceClass()); + protected InformerEventSourceConfiguration.Builder + informerConfigurationBuilder(EventSourceContext

context) { + return InformerEventSourceConfiguration.from( + groupVersionKind, context.getPrimaryResourceClass()); } @SuppressWarnings("unused") 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 00f68f36a7..f96c28d60a 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 @@ -24,7 +24,6 @@ public class GenericKubernetesResourceMatcher resource * @return results of matching */ - public static Matcher.Result match(R desired, + public static Matcher.Result match( + R desired, R actualResource, boolean labelsAndAnnotationsEquality, - boolean valuesEquality, Context

context) { - return match(desired, actualResource, - labelsAndAnnotationsEquality, valuesEquality, context, EMPTY_ARRAY); + boolean valuesEquality, + Context

context) { + return match( + desired, + actualResource, + labelsAndAnnotationsEquality, + valuesEquality, + context, + EMPTY_ARRAY); } - public static Matcher.Result match(R desired, - R actualResource, Context

context) { - return match(desired, actualResource, - false, false, context, EMPTY_ARRAY); + public static Matcher.Result match( + R desired, R actualResource, Context

context) { + return match(desired, actualResource, false, false, context, EMPTY_ARRAY); } /** @@ -69,21 +74,23 @@ public static Matcher.Result m * @param desired the desired resource * @param actualResource the actual resource * @param labelsAndAnnotationsEquality if true labels and annotation match exactly in the actual - * and desired state if false, additional elements are allowed in actual annotations. - * Considered only if considerLabelsAndAnnotations is true. + * and desired state if false, additional elements are allowed in actual annotations. + * Considered only if considerLabelsAndAnnotations is true. * @param ignorePaths are paths in the resource that are ignored on matching (basically an ignore - * list). All changes with a target prefix path on a calculated JSON Patch between actual - * and desired will be ignored. If there are other changes, non-present on ignore list - * match fails. + * list). All changes with a target prefix path on a calculated JSON Patch between actual and + * desired will be ignored. If there are other changes, non-present on ignore list match + * fails. * @param resource * @return results of matching */ - public static Matcher.Result match(R desired, + public static Matcher.Result match( + R desired, R actualResource, boolean labelsAndAnnotationsEquality, - Context

context, String... ignorePaths) { - return match(desired, actualResource, - labelsAndAnnotationsEquality, false, context, ignorePaths); + Context

context, + String... ignorePaths) { + return match( + desired, actualResource, labelsAndAnnotationsEquality, false, context, ignorePaths); } /** @@ -92,51 +99,57 @@ public static Matcher.Result m * specified primary resource. * * @param dependentResource the {@link KubernetesDependentResource} implementation used to compute - * the desired state associated with the specified primary resource + * the desired state associated with the specified primary resource * @param actualResource the observed dependent resource for which we want to determine whether it - * matches the desired state or not + * matches the desired state or not * @param primary the primary resource from which we want to compute the desired state * @param context the {@link Context} instance within which this method is called * @param labelsAndAnnotationsEquality if true labels and annotation match exactly in the actual - * and desired state if false, additional elements are allowed in actual annotations. - * Considered only if considerLabelsAndAnnotations is true. + * and desired state if false, additional elements are allowed in actual annotations. + * Considered only if considerLabelsAndAnnotations is true. * @param the type of resource we want to determine whether they match or not * @param

the type of primary resources associated with the secondary resources we want to - * match + * match * @param ignorePaths are paths in the resource that are ignored on matching (basically an ignore - * list). All changes with a target prefix path on a calculated JSON Patch between actual - * and desired will be ignored. If there are other changes, non-present on ignore list - * match fails. + * list). All changes with a target prefix path on a calculated JSON Patch between actual and + * desired will be ignored. If there are other changes, non-present on ignore list match + * fails. * @return a {@link io.javaoperatorsdk.operator.processing.dependent.Matcher.Result} object */ public static Matcher.Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, + KubernetesDependentResource dependentResource, + R actualResource, + P primary, Context

context, boolean labelsAndAnnotationsEquality, String... ignorePaths) { final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, - labelsAndAnnotationsEquality, context, - ignorePaths); + return match(desired, actualResource, labelsAndAnnotationsEquality, context, ignorePaths); } public static Matcher.Result match( - KubernetesDependentResource dependentResource, R actualResource, P primary, + KubernetesDependentResource dependentResource, + R actualResource, + P primary, Context

context, boolean specEquality, boolean labelsAndAnnotationsEquality, String... ignorePaths) { final var desired = dependentResource.desired(primary, context); - return match(desired, actualResource, - labelsAndAnnotationsEquality, specEquality, context, ignorePaths); + return match( + desired, actualResource, labelsAndAnnotationsEquality, specEquality, context, ignorePaths); } - public static Matcher.Result match(R desired, - R actualResource, boolean labelsAndAnnotationsEquality, boolean valuesEquality, + public static Matcher.Result match( + R desired, + R actualResource, + boolean labelsAndAnnotationsEquality, + boolean valuesEquality, Context

context, String... ignoredPaths) { final List ignoreList = - ignoredPaths != null && ignoredPaths.length > 0 ? Arrays.asList(ignoredPaths) + ignoredPaths != null && ignoredPaths.length > 0 + ? Arrays.asList(ignoredPaths) : Collections.emptyList(); if (valuesEquality && !ignoreList.isEmpty()) { @@ -167,8 +180,7 @@ public static Matcher.Result m return Matcher.Result.computed(matched, desired); } - private static boolean match(boolean equality, JsonNode diff, - final List ignoreList) { + private static boolean match(boolean equality, JsonNode diff, final List ignoreList) { if (equality) { return false; } @@ -186,5 +198,4 @@ static boolean nodeIsChildOf(JsonNode n, List prefixes) { static String getPath(JsonNode n) { return n.get(PATH).asText(); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdater.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdater.java index 4a8fef01e9..c8123c39e5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdater.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdater.java @@ -32,5 +32,4 @@ public static void updateLabelsAndAnnotation(K actual, K actual.getMetadata().getLabels().putAll(desired.getMetadata().getLabels()); actual.getMetadata().getAnnotations().putAll(desired.getMetadata().getAnnotations()); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java index 3354ccedae..9771aba3dc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GroupVersionKindPlural.java @@ -1,5 +1,6 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; +import java.util.Objects; import java.util.Optional; import io.fabric8.kubernetes.api.Pluralize; @@ -24,12 +25,35 @@ protected GroupVersionKindPlural(String apiVersion, String kind, String plural) } protected GroupVersionKindPlural(GroupVersionKind gvk, String plural) { - this(gvk.getGroup(), gvk.getVersion(), gvk.getKind(), - plural != null ? plural - : (gvk instanceof GroupVersionKindPlural ? ((GroupVersionKindPlural) gvk).plural + this( + gvk.getGroup(), + gvk.getVersion(), + gvk.getKind(), + plural != null + ? plural + : (gvk instanceof GroupVersionKindPlural + ? ((GroupVersionKindPlural) gvk).plural : null)); } + @Override + protected boolean specificEquals(GroupVersionKind that) { + if (plural == null) { + return true; + } + return that instanceof GroupVersionKindPlural gvkp && gvkp.plural.equals(plural); + } + + @Override + public int hashCode() { + return plural != null ? Objects.hash(super.hashCode(), plural) : super.hashCode(); + } + + @Override + public String toString() { + return toGVKString() + (plural != null ? " (plural: " + plural + ")" : ""); + } + /** * Creates a new GroupVersionKindPlural from the specified {@link GroupVersionKind}. * @@ -37,7 +61,8 @@ protected GroupVersionKindPlural(GroupVersionKind gvk, String plural) { * @return a new GroupVersionKindPlural object matching the specified {@link GroupVersionKind} */ public static GroupVersionKindPlural from(GroupVersionKind gvk) { - return gvk instanceof GroupVersionKindPlural ? ((GroupVersionKindPlural) gvk) + return gvk instanceof GroupVersionKindPlural + ? ((GroupVersionKindPlural) gvk) : gvkWithPlural(gvk, null); } @@ -47,13 +72,12 @@ public static GroupVersionKindPlural from(GroupVersionKind gvk) { * * @param gvk the base {@link GroupVersionKind} from which to derive a new GroupVersionKindPlural * @param plural the plural form to use for the new instance or {@code null} if the default plural - * form is desired. Note that the specified plural form will override any existing plural - * form for the specified {@link GroupVersionKind} (in particular, if the specified - * {@link GroupVersionKind} was already an instance of GroupVersionKindPlural, its plural - * form will only be considered in the new instance if the specified plural form is - * {@code null} + * form is desired. Note that the specified plural form will override any existing plural form + * for the specified {@link GroupVersionKind} (in particular, if the specified {@link + * GroupVersionKind} was already an instance of GroupVersionKindPlural, its plural form will + * only be considered in the new instance if the specified plural form is {@code null} * @return a new GroupVersionKindPlural derived from the specified {@link GroupVersionKind} and - * plural form + * plural form */ public static GroupVersionKindPlural gvkWithPlural(GroupVersionKind gvk, String plural) { return new GroupVersionKindPlural(gvk, plural); @@ -64,9 +88,9 @@ public static GroupVersionKindPlural gvkWithPlural(GroupVersionKind gvk, String * {@link HasMetadata} implementation * * @param resourceClass the {@link HasMetadata} from which group, version, kind and plural form - * are extracted + * are extracted * @return a new GroupVersionKindPlural instance based on the specified {@link HasMetadata} - * implementation + * implementation */ public static GroupVersionKindPlural gvkFor(Class resourceClass) { final var gvk = GroupVersionKind.gvkFor(resourceClass); @@ -90,7 +114,7 @@ public static String getDefaultPluralFor(String kind) { * manually by the user, or determined from the associated resource class definition) * * @return {@link Optional#empty()} if the plural form was not provided explicitly, or the plural - * form if it was provided explicitly + * form if it was provided explicitly */ public Optional getPlural() { return Optional.ofNullable(plural); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java index 818768474c..484ffb64c8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependent.java @@ -7,29 +7,44 @@ import io.javaoperatorsdk.operator.api.config.informer.Informer; - - @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE}) public @interface KubernetesDependent { - /** - * Creates the resource only if did not exist before, this applies only if SSA is used. - */ - boolean createResourceOnlyIfNotExistingWithSSA() default KubernetesDependentResourceConfig.DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA; + /** Creates the resource only if did not exist before, this applies only if SSA is used. */ + boolean createResourceOnlyIfNotExistingWithSSA() default + KubernetesDependentResourceConfig.DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA; /** * Determines whether to use SSA (Server-Side Apply) for this dependent. If SSA is used, the - * dependent resource will only be created if it did not exist before. Default value is - * {@link BooleanWithUndefined#UNDEFINED}, which specifies that the behavior with respect to SSA - * is inherited from the global configuration. + * dependent resource will only be created if it did not exist before. Default value is {@link + * BooleanWithUndefined#UNDEFINED}, which specifies that the behavior with respect to SSA is + * inherited from the global configuration. * - * @return {@code true} if SSA is enabled, {@code false} if SSA is disabled, - * {@link BooleanWithUndefined#UNDEFINED} if the SSA behavior should be inherited from the - * global configuration + * @return {@code true} if SSA is enabled, {@code false} if SSA is disabled, {@link + * BooleanWithUndefined#UNDEFINED} if the SSA behavior should be inherited from the global + * configuration */ BooleanWithUndefined useSSA() default BooleanWithUndefined.UNDEFINED; + /** + * The underlying Informer event source configuration + * + * @return the {@link Informer} configuration + */ Informer informer() default @Informer; + /** + * The specific matcher implementation to use when Server-Side Apply (SSA) is used, when case the + * default one isn't working appropriately. Typically, this could be needed to cover border cases + * with some Kubernetes resources that are modified by their controllers to normalize or add + * default values, which could result in infinite loops with the default matcher. Using a specific + * matcher could also be an optimization decision if determination of whether two resources match + * can be done faster than what can be done with the default exhaustive algorithm. + * + * @return the class of the specific matcher to use for the associated dependent resources + * @since 5.1 + */ + Class matcher() default + SSABasedGenericKubernetesResourceMatcher.class; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverter.java index 15cb4f172a..7d68b0e106 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentConverter.java @@ -9,30 +9,45 @@ import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResourceConfig.DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA; -public class KubernetesDependentConverter implements - ConfigurationConverter> { +public class KubernetesDependentConverter + implements ConfigurationConverter> { @Override @SuppressWarnings("unchecked") - public KubernetesDependentResourceConfig configFrom(KubernetesDependent configAnnotation, + public KubernetesDependentResourceConfig configFrom( + KubernetesDependent configAnnotation, DependentResourceSpec> spec, ControllerConfiguration controllerConfig) { var createResourceOnlyIfNotExistingWithSSA = DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA; Boolean useSSA = null; + SSABasedGenericKubernetesResourceMatcher matcher = + SSABasedGenericKubernetesResourceMatcher.getInstance(); if (configAnnotation != null) { createResourceOnlyIfNotExistingWithSSA = configAnnotation.createResourceOnlyIfNotExistingWithSSA(); useSSA = configAnnotation.useSSA().asBoolean(); + + // check if we have a specific matcher + Class> dependentResourceClass = + (Class>) spec.getDependentResourceClass(); + final var context = + Utils.contextFor( + controllerConfig, dependentResourceClass, configAnnotation.annotationType()); + matcher = + Utils.instantiate( + configAnnotation.matcher(), SSABasedGenericKubernetesResourceMatcher.class, context); } - var informerConfiguration = createInformerConfig(configAnnotation, - (DependentResourceSpec>) spec, - controllerConfig); + var informerConfiguration = + createInformerConfig( + configAnnotation, + (DependentResourceSpec>) spec, + controllerConfig); - return new KubernetesDependentResourceConfig<>(useSSA, createResourceOnlyIfNotExistingWithSSA, - informerConfiguration); + return new KubernetesDependentResourceConfig<>( + useSSA, createResourceOnlyIfNotExistingWithSSA, informerConfiguration, matcher); } @SuppressWarnings({"unchecked"}) @@ -43,14 +58,18 @@ private InformerConfiguration createInformerConfig( Class> dependentResourceClass = (Class>) spec.getDependentResourceClass(); - final var resourceType = controllerConfig.getConfigurationService().dependentResourceFactory() - .associatedResourceType(spec); + final var resourceType = + controllerConfig + .getConfigurationService() + .dependentResourceFactory() + .associatedResourceType(spec); InformerConfiguration.Builder config = InformerConfiguration.builder(resourceType); if (configAnnotation != null) { final var informerConfig = configAnnotation.informer(); - final var context = Utils.contextFor(controllerConfig, dependentResourceClass, - configAnnotation.annotationType()); + final var context = + Utils.contextFor( + controllerConfig, dependentResourceClass, configAnnotation.annotationType()); config = config.initFromAnnotation(informerConfig, context); } return config.build(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java index a3373e2d6c..ebd6089aa7 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResource.java @@ -28,16 +28,22 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; @Ignore -@Configured(by = KubernetesDependent.class, with = KubernetesDependentResourceConfig.class, +@Configured( + by = KubernetesDependent.class, + with = KubernetesDependentResourceConfig.class, converter = KubernetesDependentConverter.class) public abstract class KubernetesDependentResource extends AbstractEventSourceHolderDependentResource> implements ConfiguredDependentResource> { private static final Logger log = LoggerFactory.getLogger(KubernetesDependentResource.class); + private final boolean garbageCollected = this instanceof GarbageCollected; private KubernetesDependentResourceConfig kubernetesDependentResourceConfig; private volatile Boolean useSSA; + private volatile Boolean usePreviousAnnotationForEventFiltering; + + public KubernetesDependentResource() {} public KubernetesDependentResource(Class resourceType) { this(resourceType, null); @@ -56,9 +62,11 @@ public void configureWith(KubernetesDependentResourceConfig config) { public R create(R desired, P primary, Context

context) { if (useSSA(context)) { // setting resource version for SSA so only created if it doesn't exist already - var createIfNotExisting = kubernetesDependentResourceConfig == null - ? KubernetesDependentResourceConfig.DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA - : kubernetesDependentResourceConfig.createResourceOnlyIfNotExistingWithSSA(); + var createIfNotExisting = + kubernetesDependentResourceConfig == null + ? KubernetesDependentResourceConfig + .DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA + : kubernetesDependentResourceConfig.createResourceOnlyIfNotExistingWithSSA(); if (createIfNotExisting) { desired.getMetadata().setResourceVersion("1"); } @@ -75,21 +83,25 @@ public R create(R desired, P primary, Context

context) { public R update(R actual, R desired, P primary, Context

context) { if (log.isDebugEnabled()) { - log.debug("Updating actual resource: {} version: {}", ResourceID.fromResource(actual), + log.debug( + "Updating actual resource: {} version: {}", + ResourceID.fromResource(actual), actual.getMetadata().getResourceVersion()); } R updatedResource; addMetadata(false, actual, desired, primary, context); if (useSSA(context)) { - updatedResource = prepare(context, desired, primary, "Updating") - .fieldManager(context.getControllerConfiguration().fieldManager()) - .forceConflicts().serverSideApply(); + updatedResource = + prepare(context, desired, primary, "Updating") + .fieldManager(context.getControllerConfiguration().fieldManager()) + .forceConflicts() + .serverSideApply(); } else { var updatedActual = GenericResourceUpdater.updateResource(actual, desired, context); updatedResource = prepare(context, updatedActual, primary, "Updating").update(); } - log.debug("Resource version after update: {}", - updatedResource.getMetadata().getResourceVersion()); + log.debug( + "Resource version after update: {}", updatedResource.getMetadata().getResourceVersion()); return updatedResource; } @@ -99,25 +111,31 @@ public Result match(R actualResource, P primary, Context

context) { return match(actualResource, desired, primary, context); } - public Result match(R actualResource, R desired, P primary, - Context

context) { + public Result match(R actualResource, R desired, P primary, Context

context) { final boolean matches; addMetadata(true, actualResource, desired, primary, context); if (useSSA(context)) { - matches = SSABasedGenericKubernetesResourceMatcher.getInstance() - .matches(actualResource, desired, context); + matches = + configuration() + .map(KubernetesDependentResourceConfig::matcher) + .orElse(SSABasedGenericKubernetesResourceMatcher.getInstance()) + .matches(actualResource, desired, context); } else { - matches = GenericKubernetesResourceMatcher.match(desired, actualResource, - false, false, context).matched(); + matches = + GenericKubernetesResourceMatcher.match(desired, actualResource, false, false, context) + .matched(); } return Result.computed(matches, desired); } - protected void addMetadata(boolean forMatch, R actualResource, final R target, P primary, - Context

context) { + protected void addMetadata( + boolean forMatch, R actualResource, final R target, P primary, Context

context) { if (forMatch) { // keep the current previous annotation - String actual = actualResource.getMetadata().getAnnotations() - .get(InformerEventSource.PREVIOUS_ANNOTATION_KEY); + String actual = + actualResource + .getMetadata() + .getAnnotations() + .get(InformerEventSource.PREVIOUS_ANNOTATION_KEY); Map annotations = target.getMetadata().getAnnotations(); if (actual != null) { annotations.put(InformerEventSource.PREVIOUS_ANNOTATION_KEY, actual); @@ -125,25 +143,42 @@ protected void addMetadata(boolean forMatch, R actualResource, final R target, P annotations.remove(InformerEventSource.PREVIOUS_ANNOTATION_KEY); } } else if (usePreviousAnnotation(context)) { // set a new one - eventSource().orElseThrow().addPreviousAnnotation( - Optional.ofNullable(actualResource).map(r -> r.getMetadata().getResourceVersion()) - .orElse(null), - target); + eventSource() + .orElseThrow() + .addPreviousAnnotation( + Optional.ofNullable(actualResource) + .map(r -> r.getMetadata().getResourceVersion()) + .orElse(null), + target); } addReferenceHandlingMetadata(target, primary); } protected boolean useSSA(Context

context) { if (useSSA == null) { - useSSA = context.getControllerConfiguration().getConfigurationService() - .shouldUseSSA(getClass(), resourceType(), configuration().orElse(null)); + useSSA = + context + .getControllerConfiguration() + .getConfigurationService() + .shouldUseSSA(getClass(), resourceType(), configuration().orElse(null)); } return useSSA; } private boolean usePreviousAnnotation(Context

context) { - return context.getControllerConfiguration().getConfigurationService() - .previousAnnotationForDependentResourcesEventFiltering(); + if (usePreviousAnnotationForEventFiltering == null) { + usePreviousAnnotationForEventFiltering = + context + .getControllerConfiguration() + .getConfigurationService() + .previousAnnotationForDependentResourcesEventFiltering() + && !context + .getControllerConfiguration() + .getConfigurationService() + .withPreviousAnnotationForDependentResourcesBlocklist() + .contains(this.resourceType()); + } + return usePreviousAnnotationForEventFiltering; } @Override @@ -160,7 +195,8 @@ public void deleteTargetResource(P primary, R resource, String key, Context

c @SuppressWarnings("unused") protected Resource prepare(Context

context, R desired, P primary, String actionName) { - log.debug("{} target resource with type: {}, with id: {}", + log.debug( + "{} target resource with type: {}, with id: {}", actionName, desired.getClass(), ResourceID.fromResource(desired)); @@ -208,12 +244,16 @@ private boolean useNonOwnerRefBasedSecondaryToPrimaryMapping() { } protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary) { - addSecondaryToPrimaryMapperAnnotations(desired, primary, Mappers.DEFAULT_ANNOTATION_FOR_NAME, - Mappers.DEFAULT_ANNOTATION_FOR_NAMESPACE, Mappers.DEFAULT_ANNOTATION_FOR_PRIMARY_TYPE); + addSecondaryToPrimaryMapperAnnotations( + desired, + primary, + Mappers.DEFAULT_ANNOTATION_FOR_NAME, + Mappers.DEFAULT_ANNOTATION_FOR_NAMESPACE, + Mappers.DEFAULT_ANNOTATION_FOR_PRIMARY_TYPE); } - protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary, String nameKey, - String namespaceKey, String typeKey) { + protected void addSecondaryToPrimaryMapperAnnotations( + R desired, P primary, String nameKey, String namespaceKey, String typeKey) { var annotations = desired.getMetadata().getAnnotations(); annotations.put(nameKey, primary.getMetadata().getName()); var primaryNamespaces = primary.getMetadata().getNamespace(); @@ -224,13 +264,16 @@ protected void addSecondaryToPrimaryMapperAnnotations(R desired, P primary, Stri } @Override - protected Optional selectTargetSecondaryResource(Set secondaryResources, P primary, - Context

context) { + protected Optional selectTargetSecondaryResource( + Set secondaryResources, P primary, Context

context) { ResourceID managedResourceID = targetSecondaryResourceID(primary, context); return secondaryResources.stream() - .filter(r -> r.getMetadata().getName().equals(managedResourceID.getName()) && - Objects.equals(r.getMetadata().getNamespace(), - managedResourceID.getNamespace().orElse(null))) + .filter( + r -> + r.getMetadata().getName().equals(managedResourceID.getName()) + && Objects.equals( + r.getMetadata().getNamespace(), + managedResourceID.getNamespace().orElse(null))) .findFirst(); } @@ -273,8 +316,8 @@ protected Optional> getSecondaryToPrimaryMapper( } else { var clustered = !Namespaced.class.isAssignableFrom(context.getPrimaryResourceClass()); if (garbageCollected) { - return Optional - .of(Mappers.fromOwnerReferences(context.getPrimaryResourceClass(), clustered)); + return Optional.of( + Mappers.fromOwnerReferences(context.getPrimaryResourceClass(), clustered)); } else if (isCreatable()) { return Optional.of(Mappers.fromDefaultAnnotations(context.getPrimaryResourceClass())); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java index 2b419c2d0d..6f626d2628 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfig.java @@ -1,10 +1,8 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; - import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; - public class KubernetesDependentResourceConfig { public static final boolean DEFAULT_CREATE_RESOURCE_ONLY_IF_NOT_EXISTING_WITH_SSA = true; @@ -12,14 +10,25 @@ public class KubernetesDependentResourceConfig { private final Boolean useSSA; private final boolean createResourceOnlyIfNotExistingWithSSA; private final InformerConfiguration informerConfig; + private final SSABasedGenericKubernetesResourceMatcher matcher; public KubernetesDependentResourceConfig( Boolean useSSA, boolean createResourceOnlyIfNotExistingWithSSA, InformerConfiguration informerConfig) { + this(useSSA, createResourceOnlyIfNotExistingWithSSA, informerConfig, null); + } + + public KubernetesDependentResourceConfig( + Boolean useSSA, + boolean createResourceOnlyIfNotExistingWithSSA, + InformerConfiguration informerConfig, + SSABasedGenericKubernetesResourceMatcher matcher) { this.useSSA = useSSA; this.createResourceOnlyIfNotExistingWithSSA = createResourceOnlyIfNotExistingWithSSA; this.informerConfig = informerConfig; + this.matcher = + matcher != null ? matcher : SSABasedGenericKubernetesResourceMatcher.getInstance(); } public boolean createResourceOnlyIfNotExistingWithSSA() { @@ -33,4 +42,8 @@ public Boolean useSSA() { public InformerConfiguration informerConfig() { return informerConfig; } + + public SSABasedGenericKubernetesResourceMatcher matcher() { + return matcher; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfigBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfigBuilder.java index 3610cb074b..371fb700c3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfigBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceConfigBuilder.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; - import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.config.informer.InformerConfiguration; @@ -9,6 +8,7 @@ public final class KubernetesDependentResourceConfigBuilder informerConfiguration; + private SSABasedGenericKubernetesResourceMatcher matcher; public KubernetesDependentResourceConfigBuilder() {} @@ -30,9 +30,14 @@ public KubernetesDependentResourceConfigBuilder withKubernetesDependentInform return this; } + public KubernetesDependentResourceConfigBuilder withSSAMatcher( + SSABasedGenericKubernetesResourceMatcher matcher) { + this.matcher = matcher; + return this; + } + public KubernetesDependentResourceConfig build() { return new KubernetesDependentResourceConfig<>( - useSSA, createResourceOnlyIfNotExistingWithSSA, - informerConfiguration); + useSSA, createResourceOnlyIfNotExistingWithSSA, informerConfiguration, matcher); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java new file mode 100644 index 0000000000..962059961e --- /dev/null +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizer.java @@ -0,0 +1,198 @@ +package io.javaoperatorsdk.operator.processing.dependent.kubernetes; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +import io.fabric8.kubernetes.api.model.Container; +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.GenericKubernetesResource; +import io.fabric8.kubernetes.api.model.PodTemplateSpec; +import io.fabric8.kubernetes.api.model.Quantity; +import io.fabric8.kubernetes.api.model.ResourceRequirements; + +/** + * Sanitizes the {@link ResourceRequirements} and the {@link EnvVar} in the containers of a pair of + * {@link PodTemplateSpec} instances. + * + *

When the sanitizer finds a mismatch in the structure of the given templates, before it gets to + * the nested fields, it returns early without fixing the actual map. This is an optimization + * because the given templates will anyway differ at this point. This means we do not have to + * attempt to sanitize the fields for these use cases, since there will anyway be an update of the + * K8s resource. + * + *

The algorithm traverses the whole template structure because we need the actual and desired + * {@link Quantity} and {@link EnvVar} instances. Using the {@link + * GenericKubernetesResource#get(Map, Object...)} shortcut would need to create new instances just + * for the sanitization check. + */ +class PodTemplateSpecSanitizer { + + static void sanitizePodTemplateSpec( + final Map actualMap, + final PodTemplateSpec actualTemplate, + final PodTemplateSpec desiredTemplate) { + if (actualTemplate == null || desiredTemplate == null) { + return; + } + if (actualTemplate.getSpec() == null || desiredTemplate.getSpec() == null) { + return; + } + sanitizePodTemplateSpec( + actualMap, + actualTemplate.getSpec().getInitContainers(), + desiredTemplate.getSpec().getInitContainers(), + "initContainers"); + sanitizePodTemplateSpec( + actualMap, + actualTemplate.getSpec().getContainers(), + desiredTemplate.getSpec().getContainers(), + "containers"); + } + + private static void sanitizePodTemplateSpec( + final Map actualMap, + final List actualContainers, + final List desiredContainers, + final String containerPath) { + int containers = desiredContainers.size(); + if (containers == actualContainers.size()) { + for (int containerIndex = 0; containerIndex < containers; containerIndex++) { + final var desiredContainer = desiredContainers.get(containerIndex); + final var actualContainer = actualContainers.get(containerIndex); + if (!desiredContainer.getName().equals(actualContainer.getName())) { + return; + } + sanitizeEnvVars( + actualMap, + actualContainer.getEnv(), + desiredContainer.getEnv(), + containerPath, + containerIndex); + sanitizeResourceRequirements( + actualMap, + actualContainer.getResources(), + desiredContainer.getResources(), + containerPath, + containerIndex); + } + } + } + + private static void sanitizeResourceRequirements( + final Map actualMap, + final ResourceRequirements actualResource, + final ResourceRequirements desiredResource, + final String containerPath, + final int containerIndex) { + if (desiredResource == null || actualResource == null) { + return; + } + sanitizeQuantities( + actualMap, + actualResource.getRequests(), + desiredResource.getRequests(), + containerPath, + containerIndex, + "requests"); + sanitizeQuantities( + actualMap, + actualResource.getLimits(), + desiredResource.getLimits(), + containerPath, + containerIndex, + "limits"); + } + + @SuppressWarnings("unchecked") + private static void sanitizeQuantities( + final Map actualMap, + final Map actualResource, + final Map desiredResource, + final String containerPath, + final int containerIndex, + final String quantityPath) { + Optional.ofNullable( + GenericKubernetesResource.get( + actualMap, + "spec", + "template", + "spec", + containerPath, + containerIndex, + "resources", + quantityPath)) + .map(Map.class::cast) + .filter(m -> m.size() == desiredResource.size()) + .ifPresent( + m -> + actualResource.forEach( + (key, actualQuantity) -> { + final var desiredQuantity = desiredResource.get(key); + if (desiredQuantity == null) { + return; + } + // check if the string representation of the Quantity instances is equal + if (actualQuantity.getAmount().equals(desiredQuantity.getAmount()) + && actualQuantity.getFormat().equals(desiredQuantity.getFormat())) { + return; + } + // check if the numerical amount of the Quantity instances is equal + if (actualQuantity.equals(desiredQuantity)) { + // replace the actual Quantity with the desired Quantity to prevent a + // resource update + m.replace(key, desiredQuantity.toString()); + } + })); + } + + @SuppressWarnings("unchecked") + private static void sanitizeEnvVars( + final Map actualMap, + final List actualEnvVars, + final List desiredEnvVars, + final String containerPath, + final int containerIndex) { + if (desiredEnvVars.isEmpty() || actualEnvVars.isEmpty()) { + return; + } + Optional.ofNullable( + GenericKubernetesResource.get( + actualMap, "spec", "template", "spec", containerPath, containerIndex, "env")) + .map(List.class::cast) + .ifPresent( + envVars -> + actualEnvVars.forEach( + actualEnvVar -> { + final var actualEnvVarName = actualEnvVar.getName(); + final var actualEnvVarValue = actualEnvVar.getValue(); + // check if the actual EnvVar value string is not null or the desired EnvVar + // already contains the same EnvVar name with a non empty EnvVar value + final var isDesiredEnvVarEmpty = + hasEnvVarNoEmptyValue(actualEnvVarName, desiredEnvVars); + if (actualEnvVarValue != null || isDesiredEnvVarEmpty) { + return; + } + envVars.stream() + .filter( + envVar -> + ((Map) envVar) + .get("name") + .equals(actualEnvVarName)) + // add the actual EnvVar value with an empty string to prevent a + // resource update + .forEach(envVar -> ((Map) envVar).put("value", "")); + })); + } + + private static boolean hasEnvVarNoEmptyValue( + final String envVarName, final List envVars) { + return envVars.stream() + .anyMatch( + envVar -> + Objects.equals(envVarName, envVar.getName()) + && envVar.getValue() != null + && !envVar.getValue().isEmpty()); + } +} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceComparators.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceComparators.java index 037cb597f4..eeb22353d2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceComparators.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceComparators.java @@ -8,13 +8,13 @@ public class ResourceComparators { public static boolean compareConfigMapData(ConfigMap c1, ConfigMap c2) { - return Objects.equals(c1.getData(), c2.getData()) && - Objects.equals(c1.getBinaryData(), c2.getBinaryData()); + return Objects.equals(c1.getData(), c2.getData()) + && Objects.equals(c1.getBinaryData(), c2.getBinaryData()); } public static boolean compareSecretData(Secret s1, Secret s2) { - return Objects.equals(s1.getType(), s2.getType()) && - Objects.equals(s1.getData(), s2.getData()) && - Objects.equals(s1.getStringData(), s2.getStringData()); + return Objects.equals(s1.getType(), s2.getType()) + && Objects.equals(s1.getData(), s2.getData()) + && Objects.equals(s1.getStringData(), s2.getStringData()); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceRequirementsSanitizer.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceRequirementsSanitizer.java deleted file mode 100644 index 3d83002692..0000000000 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceRequirementsSanitizer.java +++ /dev/null @@ -1,100 +0,0 @@ -package io.javaoperatorsdk.operator.processing.dependent.kubernetes; - -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import io.fabric8.kubernetes.api.model.Container; -import io.fabric8.kubernetes.api.model.GenericKubernetesResource; -import io.fabric8.kubernetes.api.model.PodTemplateSpec; -import io.fabric8.kubernetes.api.model.Quantity; -import io.fabric8.kubernetes.api.model.ResourceRequirements; - -/** - * Sanitizes the {@link ResourceRequirements} in the containers of a pair of {@link PodTemplateSpec} - * instances. - *

- * When the sanitizer finds a mismatch in the structure of the given templates, before it gets to - * the nested resource limits and requests, it returns early without fixing the actual map. This is - * an optimization because the given templates will anyway differ at this point. This means we do - * not have to attempt to sanitize the resources for these use cases, since there will anyway be an - * update of the K8s resource. - *

- * The algorithm traverses the whole template structure because we need the actual and desired - * {@link Quantity} instances to compare their numerical amount. Using the - * {@link GenericKubernetesResource#get(Map, Object...)} shortcut would need to create new instances - * just for the sanitization check. - */ -class ResourceRequirementsSanitizer { - - static void sanitizeResourceRequirements(final Map actualMap, - final PodTemplateSpec actualTemplate, final PodTemplateSpec desiredTemplate) { - if (actualTemplate == null || desiredTemplate == null) { - return; - } - if (actualTemplate.getSpec() == null || desiredTemplate.getSpec() == null) { - return; - } - sanitizeResourceRequirements(actualMap, actualTemplate.getSpec().getInitContainers(), - desiredTemplate.getSpec().getInitContainers(), "initContainers"); - sanitizeResourceRequirements(actualMap, actualTemplate.getSpec().getContainers(), - desiredTemplate.getSpec().getContainers(), "containers"); - } - - private static void sanitizeResourceRequirements(final Map actualMap, - final List actualContainers, final List desiredContainers, - final String containerPath) { - int containers = desiredContainers.size(); - if (containers == actualContainers.size()) { - for (int containerIndex = 0; containerIndex < containers; containerIndex++) { - var desiredContainer = desiredContainers.get(containerIndex); - var actualContainer = actualContainers.get(containerIndex); - if (!desiredContainer.getName().equals(actualContainer.getName())) { - return; - } - sanitizeResourceRequirements(actualMap, actualContainer.getResources(), - desiredContainer.getResources(), - containerPath, containerIndex); - } - } - } - - private static void sanitizeResourceRequirements(final Map actualMap, - final ResourceRequirements actualResource, final ResourceRequirements desiredResource, - final String containerPath, final int containerIndex) { - if (desiredResource == null || actualResource == null) { - return; - } - sanitizeQuantities(actualMap, actualResource.getRequests(), desiredResource.getRequests(), - containerPath, containerIndex, "requests"); - sanitizeQuantities(actualMap, actualResource.getLimits(), desiredResource.getLimits(), - containerPath, containerIndex, "limits"); - } - - @SuppressWarnings("unchecked") - private static void sanitizeQuantities(final Map actualMap, - final Map actualResource, final Map desiredResource, - final String containerPath, final int containerIndex, final String quantityPath) { - Optional.ofNullable( - GenericKubernetesResource.get(actualMap, "spec", "template", "spec", containerPath, - containerIndex, "resources", quantityPath)) - .map(Map.class::cast) - .filter(m -> m.size() == desiredResource.size()) - .ifPresent(m -> actualResource.forEach((key, actualQuantity) -> { - var desiredQuantity = desiredResource.get(key); - if (desiredQuantity == null) { - return; - } - // check if the string representation of the Quantity instances is equal - if (actualQuantity.getAmount().equals(desiredQuantity.getAmount()) - && actualQuantity.getFormat().equals(desiredQuantity.getFormat())) { - return; - } - // check if the numerical amount of the Quantity instances is equal - if (actualQuantity.equals(desiredQuantity)) { - // replace the actual Quantity with the desired Quantity to prevent a resource update - m.replace(key, desiredQuantity.toString()); - } - })); - } -} diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdaterMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdaterMatcher.java index 0da1aef9dc..d893ff3e86 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdaterMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceUpdaterMatcher.java @@ -8,5 +8,4 @@ public interface ResourceUpdaterMatcher { R updateResource(R actual, R desired, Context context); boolean matches(R actual, R desired, Context context); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java index 261ab6c825..4954dfd17a 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcher.java @@ -31,18 +31,18 @@ import com.github.difflib.DiffUtils; import com.github.difflib.UnifiedDiffUtils; -import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.ResourceRequirementsSanitizer.sanitizeResourceRequirements; +import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.PodTemplateSpecSanitizer.sanitizePodTemplateSpec; /** * Matches the actual state on the server vs the desired state. Based on the managedFields of SSA. - *

- * The basis of the algorithm is to extract the managed fields by converting resources to a Map/List - * composition. The actual resource (from the server) is pruned, all the fields which are not - * mentioned in managedFields of the target manager are removed. Some irrelevant fields are also + * + *

The basis of the algorithm is to extract the managed fields by converting resources to a + * Map/List composition. The actual resource (from the server) is pruned, all the fields which are + * not mentioned in managedFields of the target manager are removed. Some irrelevant fields are also * removed from the desired resource. Finally, the two resulting maps are compared for equality. - *

- * The implementation is a bit nasty since we have to deal with some specific cases of managedFields - * formats. + * + *

The implementation is a bit nasty since we have to deal with some specific cases of + * managedFields formats. * * @param matched resource type */ @@ -55,17 +55,6 @@ public class SSABasedGenericKubernetesResourceMatcher { public static final String APPLY_OPERATION = "Apply"; public static final String DOT_KEY = "."; - @SuppressWarnings("rawtypes") - private static final SSABasedGenericKubernetesResourceMatcher INSTANCE = - new SSABasedGenericKubernetesResourceMatcher<>(); - private static final List IGNORED_METADATA = - List.of("creationTimestamp", "deletionTimestamp", "generation", "selfLink", "uid"); - - @SuppressWarnings("unchecked") - public static SSABasedGenericKubernetesResourceMatcher getInstance() { - return INSTANCE; - } - private static final String F_PREFIX = "f:"; private static final String K_PREFIX = "k:"; private static final String V_PREFIX = "v:"; @@ -75,13 +64,25 @@ public static SSABasedGenericKubernetesResourceMatcher(); + + private static final List IGNORED_METADATA = + List.of("creationTimestamp", "deletionTimestamp", "generation", "selfLink", "uid"); + private static final Logger log = LoggerFactory.getLogger(SSABasedGenericKubernetesResourceMatcher.class); + @SuppressWarnings("unchecked") + public static SSABasedGenericKubernetesResourceMatcher getInstance() { + return INSTANCE; + } + @SuppressWarnings("unchecked") public boolean matches(R actual, R desired, Context context) { - var optionalManagedFieldsEntry = checkIfFieldManagerExists(actual, - context.getControllerConfiguration().fieldManager()); + var optionalManagedFieldsEntry = + checkIfFieldManagerExists(actual, context.getControllerConfiguration().fieldManager()); // If no field is managed by our controller, that means the controller hasn't touched the // resource yet and the resource probably doesn't match the desired state. Not matching here // means that the resource will need to be updated and since this will be done using SSA, the @@ -101,75 +102,84 @@ public boolean matches(R actual, R desired, Context context) { sanitizeState(actual, desired, actualMap); var prunedActual = new HashMap(actualMap.size()); - keepOnlyManagedFields(prunedActual, actualMap, + keepOnlyManagedFields( + prunedActual, + actualMap, managedFieldsEntry.getFieldsV1().getAdditionalProperties(), objectMapper); removeIrrelevantValues(desiredMap); - var matches = prunedActual.equals(desiredMap); + var matches = matches(prunedActual, desiredMap, actual, desired, context); if (!matches && log.isDebugEnabled() && LoggingUtils.isNotSensitiveResource(desired)) { var diff = getDiff(prunedActual, desiredMap, objectMapper); log.debug( - "Diff between actual and desired state for resource: {} with name: {} in namespace: {} is:\n{}", - actual.getKind(), actual.getMetadata().getName(), actual.getMetadata().getNamespace(), + "Diff between actual and desired state for resource: {} with name: {} in namespace: {}" + + " is:\n" + + "{}", + actual.getKind(), + actual.getMetadata().getName(), + actual.getMetadata().getNamespace(), diff); } return matches; } - private String getDiff(Map prunedActualMap, Map desiredMap, - KubernetesSerialization serialization) { - var actualYaml = serialization.asYaml(sortMap(prunedActualMap)); - var desiredYaml = serialization.asYaml(sortMap(desiredMap)); - if (log.isTraceEnabled()) { - log.trace("Pruned actual resource:\n {} \ndesired resource:\n {} ", actualYaml, desiredYaml); - } - - var patch = DiffUtils.diff(actualYaml.lines().toList(), desiredYaml.lines().toList()); - var unifiedDiff = - UnifiedDiffUtils.generateUnifiedDiff("", "", actualYaml.lines().toList(), patch, 1); - return String.join("\n", unifiedDiff); + /** + * Compares the desired and actual resources for equality. + * + *

This method can be overridden to implement custom matching logic. The {@code actualMap} is a + * cleaned-up version of the actual resource with managed fields and irrelevant values removed. + * + * @param actualMap the actual resource represented as a map + * @param desiredMap the desired resource represented as a map + * @param actual the actual resource object + * @param desired the desired resource object + * @param context the current matching context + * @return {@code true} if the resources are equal, otherwise {@code false} + */ + protected boolean matches( + Map actualMap, + Map desiredMap, + R actual, + R desired, + Context context) { + return actualMap.equals(desiredMap); } - @SuppressWarnings("unchecked") - Map sortMap(Map map) { - var sortedKeys = new ArrayList<>(map.keySet()); - Collections.sort(sortedKeys); - - var sortedMap = new LinkedHashMap(); - for (var key : sortedKeys) { - var value = map.get(key); - if (value instanceof Map) { - sortedMap.put(key, sortMap((Map) value)); - } else if (value instanceof List) { - sortedMap.put(key, sortListItems((List) value)); - } else { - sortedMap.put(key, value); - } + private Optional checkIfFieldManagerExists(R actual, String fieldManager) { + var targetManagedFields = + actual.getMetadata().getManagedFields().stream() + // Only the apply operations are interesting for us since those were created properly be + // SSA patch. An update can be present with same fieldManager when migrating and having + // the same field manager name + .filter( + f -> + f.getManager().equals(fieldManager) && f.getOperation().equals(APPLY_OPERATION)) + .toList(); + if (targetManagedFields.isEmpty()) { + log.debug( + "No field manager exists for resource: {} with name: {} and operation {}", + actual.getKind(), + actual.getMetadata().getName(), + APPLY_OPERATION); + return Optional.empty(); } - return sortedMap; - } - - @SuppressWarnings("unchecked") - List sortListItems(List list) { - var sortedList = new ArrayList<>(); - for (var item : list) { - if (item instanceof Map) { - sortedList.add(sortMap((Map) item)); - } else if (item instanceof List) { - sortedList.add(sortListItems((List) item)); - } else { - sortedList.add(item); - } + // this should not happen in theory + if (targetManagedFields.size() > 1) { + throw new OperatorException( + "More than one field manager exists with name: " + + fieldManager + + " in resource: " + + actual.getKind() + + " with name: " + + actual.getMetadata().getName()); } - return sortedList; + return Optional.of(targetManagedFields.get(0)); } - /** - * Correct for known issue with SSA - */ - private void sanitizeState(R actual, R desired, Map actualMap) { + /** Correct for known issue with SSA */ + protected void sanitizeState(R actual, R desired, Map actualMap) { if (actual instanceof StatefulSet actualStatefulSet && desired instanceof StatefulSet desiredStatefulSet) { var actualSpec = actualStatefulSet.getSpec(); @@ -180,52 +190,47 @@ private void sanitizeState(R actual, R desired, Map actualMap) { var claim = desiredSpec.getVolumeClaimTemplates().get(i); if (claim.getSpec().getVolumeMode() == null) { Optional.ofNullable( - GenericKubernetesResource.get(actualMap, "spec", "volumeClaimTemplates", i, "spec")) - .map(Map.class::cast).ifPresent(m -> m.remove("volumeMode")); + GenericKubernetesResource.get( + actualMap, "spec", "volumeClaimTemplates", i, "spec")) + .map(Map.class::cast) + .ifPresent(m -> m.remove("volumeMode")); } if (claim.getStatus() == null) { Optional.ofNullable( - GenericKubernetesResource.get(actualMap, "spec", "volumeClaimTemplates", i)) - .map(Map.class::cast).ifPresent(m -> m.remove("status")); + GenericKubernetesResource.get(actualMap, "spec", "volumeClaimTemplates", i)) + .map(Map.class::cast) + .ifPresent(m -> m.remove("status")); } } } - sanitizeResourceRequirements(actualMap, actualSpec.getTemplate(), desiredSpec.getTemplate()); + sanitizePodTemplateSpec(actualMap, actualSpec.getTemplate(), desiredSpec.getTemplate()); } else if (actual instanceof Deployment actualDeployment && desired instanceof Deployment desiredDeployment) { - sanitizeResourceRequirements(actualMap, + sanitizePodTemplateSpec( + actualMap, actualDeployment.getSpec().getTemplate(), desiredDeployment.getSpec().getTemplate()); } else if (actual instanceof ReplicaSet actualReplicaSet && desired instanceof ReplicaSet desiredReplicaSet) { - sanitizeResourceRequirements(actualMap, + sanitizePodTemplateSpec( + actualMap, actualReplicaSet.getSpec().getTemplate(), desiredReplicaSet.getSpec().getTemplate()); } else if (actual instanceof DaemonSet actualDaemonSet && desired instanceof DaemonSet desiredDaemonSet) { - sanitizeResourceRequirements(actualMap, + sanitizePodTemplateSpec( + actualMap, actualDaemonSet.getSpec().getTemplate(), desiredDaemonSet.getSpec().getTemplate()); } } @SuppressWarnings("unchecked") - private static void removeIrrelevantValues(Map desiredMap) { - var metadata = (Map) desiredMap.get(METADATA_KEY); - metadata.remove(NAME_KEY); - metadata.remove(NAMESPACE_KEY); - IGNORED_METADATA.forEach(metadata::remove); - if (metadata.isEmpty()) { - desiredMap.remove(METADATA_KEY); - } - desiredMap.remove(KIND_KEY); - desiredMap.remove(API_VERSION_KEY); - } - - @SuppressWarnings("unchecked") - private static void keepOnlyManagedFields(Map result, + static void keepOnlyManagedFields( + Map result, Map actualMap, - Map managedFields, KubernetesSerialization objectMapper) { + Map managedFields, + KubernetesSerialization objectMapper) { if (managedFields.isEmpty()) { result.putAll(actualMap); return; @@ -244,13 +249,18 @@ private static void keepOnlyManagedFields(Map result, handleSetValues(result, actualMap, objectMapper, keyInActual, managedEntrySet); } else { // basically if we should traverse further - fillResultsAndTraverseFurther(result, actualMap, managedFields, objectMapper, key, + fillResultsAndTraverseFurther( + result, + actualMap, + managedFields, + objectMapper, + key, keyInActual, managedFieldValue); } } else { // this should handle the case when the value is complex in the actual map (not just a - // simple value). + // simple value) result.put(keyInActual, actualMap.get(keyInActual)); } } else { @@ -262,24 +272,33 @@ private static void keepOnlyManagedFields(Map result, } } - @SuppressWarnings("unchecked") - private static void fillResultsAndTraverseFurther(Map result, - Map actualMap, - Map managedFields, KubernetesSerialization objectMapper, String key, - String keyInActual, - Object managedFieldValue) { - var emptyMapValue = new HashMap(); - result.put(keyInActual, emptyMapValue); - var actualMapValue = actualMap.getOrDefault(keyInActual, Collections.emptyMap()); - log.debug("key: {} actual map value: managedFieldValue: {}", keyInActual, managedFieldValue); - keepOnlyManagedFields(emptyMapValue, (Map) actualMapValue, - (Map) managedFields.get(key), objectMapper); - } - private static boolean isNestedValue(Map managedFieldValue) { return !managedFieldValue.isEmpty(); } + private static boolean isListKeyEntrySet(Set> managedEntrySet) { + return isKeyPrefixedSkippingDotKey(managedEntrySet, K_PREFIX); + } + + private static boolean isSetValueField(Set> managedEntrySet) { + return isKeyPrefixedSkippingDotKey(managedEntrySet, V_PREFIX); + } + + /** + * Sometimes (not always) the first subfield of a managed field ("f:") is ".:{}", it looks that + * those are added when there are more subfields of a referenced field. See test samples. Does not + * seem to provide additional functionality, so can be just skipped for now. + */ + private static boolean isKeyPrefixedSkippingDotKey( + Set> managedEntrySet, String prefix) { + var iterator = managedEntrySet.iterator(); + var managedFieldEntry = iterator.next(); + if (managedFieldEntry.getKey().equals(DOT_KEY)) { + managedFieldEntry = iterator.next(); + } + return managedFieldEntry.getKey().startsWith(prefix); + } + /** * List entries referenced by key, or when "k:" prefix is used. It works in a way that it selects * the target element based on the field(s) in "k:" for example when there is a list of element of @@ -291,9 +310,11 @@ private static boolean isNestedValue(Map managedFieldValue) { * order in managed field. */ @SuppressWarnings("unchecked") - private static void handleListKeyEntrySet(Map result, + private static void handleListKeyEntrySet( + Map result, Map actualMap, - KubernetesSerialization objectMapper, String keyInActual, + KubernetesSerialization objectMapper, + String keyInActual, Set> managedEntrySet) { var valueList = new ArrayList<>(); result.put(keyInActual, valueList); @@ -307,30 +328,65 @@ private static void handleListKeyEntrySet(Map result, continue; } var actualListEntry = - selectListEntryBasedOnKey(keyWithoutPrefix(listEntry.getKey()), actualValueList, - objectMapper); + selectListEntryBasedOnKey( + keyWithoutPrefix(listEntry.getKey()), actualValueList, objectMapper); targetValuesByIndex.put(actualListEntry.getKey(), actualListEntry.getValue()); managedEntryByIndex.put(actualListEntry.getKey(), (Map) listEntry.getValue()); } - targetValuesByIndex.forEach((key, value) -> { - var emptyResMapValue = new HashMap(); - valueList.add(emptyResMapValue); - keepOnlyManagedFields(emptyResMapValue, value, managedEntryByIndex.get(key), objectMapper); - }); + targetValuesByIndex.forEach( + (key, value) -> { + var emptyResMapValue = new HashMap(); + valueList.add(emptyResMapValue); + keepOnlyManagedFields( + emptyResMapValue, value, managedEntryByIndex.get(key), objectMapper); + }); + } + + @SuppressWarnings("unchecked") + private static Map.Entry> selectListEntryBasedOnKey( + String key, List> values, KubernetesSerialization objectMapper) { + Map ids = objectMapper.unmarshal(key, Map.class); + var possibleTargets = new ArrayList>(1); + int lastIndex = -1; + for (int i = 0; i < values.size(); i++) { + var value = values.get(i); + if (value.entrySet().containsAll(ids.entrySet())) { + possibleTargets.add(value); + lastIndex = i; + } + } + if (possibleTargets.isEmpty()) { + throw new IllegalStateException( + "Cannot find list element for key: " + + key + + " in map: " + + values.stream().map(Map::keySet).toList()); + } + if (possibleTargets.size() > 1) { + throw new IllegalStateException( + "More targets found in list element for key: " + + key + + " in map: " + + values.stream().map(Map::keySet).toList()); + } + return new AbstractMap.SimpleEntry<>(lastIndex, possibleTargets.get(0)); } /** - * Set values, the {@code "v:"} prefix. Form in managed fields: - * {@code "f:some-set":{"v:1":{}},"v:2":{},"v:3":{}}. - *

- * Note that this should be just used in very rare cases, actually was not able to produce a + * Set values, the {@code "v:"} prefix. Form in managed fields: {@code + * "f:some-set":{"v:1":{}},"v:2":{},"v:3":{}}. + * + *

Note that this should be just used in very rare cases, actually was not able to produce a * sample. Kubernetes developers who worked on this feature were not able to provide one either * when prompted. Basically this method just adds the values from {@code "v:"} to the * result. */ - private static void handleSetValues(Map result, Map actualMap, - KubernetesSerialization objectMapper, String keyInActual, + private static void handleSetValues( + Map result, + Map actualMap, + KubernetesSerialization objectMapper, + String keyInActual, Set> managedEntrySet) { var valueList = new ArrayList<>(); result.put(keyInActual, valueList); @@ -346,82 +402,93 @@ private static void handleSetValues(Map result, Map targetClass, - KubernetesSerialization objectMapper) { + public static Object parseKeyValue( + String stringValue, Class targetClass, KubernetesSerialization objectMapper) { var type = Objects.requireNonNullElse(targetClass, Map.class); return objectMapper.unmarshal(stringValue.trim(), type); } - private static boolean isSetValueField(Set> managedEntrySet) { - return isKeyPrefixedSkippingDotKey(managedEntrySet, V_PREFIX); + @SuppressWarnings("unchecked") + private static void fillResultsAndTraverseFurther( + Map result, + Map actualMap, + Map managedFields, + KubernetesSerialization objectMapper, + String key, + String keyInActual, + Object managedFieldValue) { + var emptyMapValue = new HashMap(); + result.put(keyInActual, emptyMapValue); + var actualMapValue = actualMap.getOrDefault(keyInActual, Collections.emptyMap()); + log.debug("key: {} actual map value: managedFieldValue: {}", keyInActual, managedFieldValue); + keepOnlyManagedFields( + emptyMapValue, + (Map) actualMapValue, + (Map) managedFields.get(key), + objectMapper); } - private static boolean isListKeyEntrySet(Set> managedEntrySet) { - return isKeyPrefixedSkippingDotKey(managedEntrySet, K_PREFIX); + @SuppressWarnings("unchecked") + private static void removeIrrelevantValues(Map desiredMap) { + var metadata = (Map) desiredMap.get(METADATA_KEY); + metadata.remove(NAME_KEY); + metadata.remove(NAMESPACE_KEY); + IGNORED_METADATA.forEach(metadata::remove); + if (metadata.isEmpty()) { + desiredMap.remove(METADATA_KEY); + } + desiredMap.remove(KIND_KEY); + desiredMap.remove(API_VERSION_KEY); } - /** - * Sometimes (not always) the first subfield of a managed field ("f:") is ".:{}", it looks that - * those are added when there are more subfields of a referenced field. See test samples. Does not - * seem to provide additional functionality, so can be just skipped for now. - */ - private static boolean isKeyPrefixedSkippingDotKey(Set> managedEntrySet, - String prefix) { - var iterator = managedEntrySet.iterator(); - var managedFieldEntry = iterator.next(); - if (managedFieldEntry.getKey().equals(DOT_KEY)) { - managedFieldEntry = iterator.next(); + private static String getDiff( + Map prunedActualMap, + Map desiredMap, + KubernetesSerialization serialization) { + var actualYaml = serialization.asYaml(sortMap(prunedActualMap)); + var desiredYaml = serialization.asYaml(sortMap(desiredMap)); + if (log.isTraceEnabled()) { + log.trace("Pruned actual resource:\n {} \ndesired resource:\n {} ", actualYaml, desiredYaml); } - return managedFieldEntry.getKey().startsWith(prefix); + + var patch = DiffUtils.diff(actualYaml.lines().toList(), desiredYaml.lines().toList()); + var unifiedDiff = + UnifiedDiffUtils.generateUnifiedDiff("", "", actualYaml.lines().toList(), patch, 1); + return String.join("\n", unifiedDiff); } @SuppressWarnings("unchecked") - private static Map.Entry> selectListEntryBasedOnKey( - String key, - List> values, KubernetesSerialization objectMapper) { - Map ids = objectMapper.unmarshal(key, Map.class); - var possibleTargets = new ArrayList>(1); - int lastIndex = -1; - for (int i = 0; i < values.size(); i++) { - var value = values.get(i); - if (value.entrySet().containsAll(ids.entrySet())) { - possibleTargets.add(value); - lastIndex = i; + static Map sortMap(Map map) { + var sortedKeys = new ArrayList<>(map.keySet()); + Collections.sort(sortedKeys); + + var sortedMap = new LinkedHashMap(); + for (var key : sortedKeys) { + var value = map.get(key); + if (value instanceof Map) { + sortedMap.put(key, sortMap((Map) value)); + } else if (value instanceof List) { + sortedMap.put(key, sortListItems((List) value)); + } else { + sortedMap.put(key, value); } } - if (possibleTargets.isEmpty()) { - throw new IllegalStateException("Cannot find list element for key: " + key + " in map: " - + values.stream().map(Map::keySet).toList()); - } - if (possibleTargets.size() > 1) { - throw new IllegalStateException( - "More targets found in list element for key: " + key + " in map: " - + values.stream().map(Map::keySet).toList()); - } - return new AbstractMap.SimpleEntry<>(lastIndex, possibleTargets.get(0)); + return sortedMap; } - private Optional checkIfFieldManagerExists(R actual, String fieldManager) { - var targetManagedFields = actual.getMetadata().getManagedFields().stream() - // Only the apply operations are interesting for us since those were created properly be SSA - // Patch. An update can be present with same fieldManager when migrating and having the same - // field manager name. - .filter( - f -> f.getManager().equals(fieldManager) && f.getOperation().equals(APPLY_OPERATION)) - .toList(); - if (targetManagedFields.isEmpty()) { - log.debug("No field manager exists for resource: {} with name: {} and operation {}", - actual.getKind(), - actual.getMetadata().getName(), - APPLY_OPERATION); - return Optional.empty(); - } - // this should not happen in theory - if (targetManagedFields.size() > 1) { - throw new OperatorException("More than one field manager exists with name: " + fieldManager - + " in resource: " + actual.getKind() + " with name: " + actual.getMetadata().getName()); + @SuppressWarnings("unchecked") + static List sortListItems(List list) { + var sortedList = new ArrayList<>(); + for (var item : list) { + if (item instanceof Map) { + sortedList.add(sortMap((Map) item)); + } else if (item instanceof List) { + sortedList.add(sortListItems((List) item)); + } else { + sortedList.add(item); + } } - return Optional.of(targetManagedFields.get(0)); + return sortedList; } private static String keyWithoutPrefix(String key) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java index 60d137fa1a..6629ed8f62 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutor.java @@ -25,10 +25,10 @@ abstract class AbstractWorkflowExecutor

{ protected final ResourceID primaryID; protected final Context

context; protected final Map, BaseWorkflowResult.DetailBuilder> results; - /** - * Covers both deleted and reconciled - */ + + /** Covers both deleted and reconciled */ private final Map> actualExecutions = new ConcurrentHashMap<>(); + private final ExecutorService executorService; protected AbstractWorkflowExecutor(DefaultWorkflow

workflow, P primary, Context

context) { @@ -87,8 +87,8 @@ protected boolean isMarkedForDelete(DependentResourceNode drn) { protected synchronized BaseWorkflowResult.DetailBuilder createOrGetResultFor( DependentResourceNode dependentResourceNode) { - return results.computeIfAbsent(dependentResourceNode, - unused -> new BaseWorkflowResult.DetailBuilder()); + return results.computeIfAbsent( + dependentResourceNode, unused -> new BaseWorkflowResult.DetailBuilder()); } protected synchronized Optional> getResultFor( @@ -96,7 +96,8 @@ protected synchronized Optional> getResultFo return Optional.ofNullable(results.get(dependentResourceNode)); } - protected boolean getResultFlagFor(DependentResourceNode dependentResourceNode, + protected boolean getResultFlagFor( + DependentResourceNode dependentResourceNode, Function, Boolean> flag) { return getResultFor(dependentResourceNode).map(flag).orElse(false); } @@ -105,14 +106,13 @@ protected boolean isExecutingNow(DependentResourceNode dependentResourceNo return actualExecutions.containsKey(dependentResourceNode); } - protected void markAsExecuting(DependentResourceNode dependentResourceNode, - Future future) { + protected void markAsExecuting( + DependentResourceNode dependentResourceNode, Future future) { actualExecutions.put(dependentResourceNode, future); } protected synchronized void handleExceptionInExecutor( - DependentResourceNode dependentResourceNode, - RuntimeException e) { + DependentResourceNode dependentResourceNode, RuntimeException e) { createOrGetResultFor(dependentResourceNode).withError(e); } @@ -138,27 +138,34 @@ protected boolean isConditionMet( Optional> condition, DependentResourceNode dependentResource) { final var dr = dependentResource.getDependentResource(); - return condition.map(c -> { - final DetailedCondition.Result r = c.detailedIsMet(dr, primary, context); - synchronized (this) { - results.computeIfAbsent(dependentResource, unused -> new BaseWorkflowResult.DetailBuilder()) - .withResultForCondition(c, r); - } - return r; - }).orElse(DetailedCondition.Result.metWithoutResult).isSuccess(); - } - - protected void submit(DependentResourceNode dependentResourceNode, - NodeExecutor nodeExecutor, String operation) { + return condition + .map( + c -> { + final DetailedCondition.Result r = c.detailedIsMet(dr, primary, context); + synchronized (this) { + results + .computeIfAbsent( + dependentResource, unused -> new BaseWorkflowResult.DetailBuilder()) + .withResultForCondition(c, r); + } + return r; + }) + .orElse(DetailedCondition.Result.metWithoutResult) + .isSuccess(); + } + + protected void submit( + DependentResourceNode dependentResourceNode, + NodeExecutor nodeExecutor, + String operation) { final Future future = executorService.submit(nodeExecutor); markAsExecuting(dependentResourceNode, future); - logger().debug("Submitted to {}: {} primaryID: {}", operation, dependentResourceNode, - primaryID); + logger() + .debug("Submitted to {}: {} primaryID: {}", operation, dependentResourceNode, primaryID); } protected void registerOrDeregisterEventSourceBasedOnActivation( - boolean activationConditionMet, - DependentResourceNode dependentResourceNode) { + boolean activationConditionMet, DependentResourceNode dependentResourceNode) { if (dependentResourceNode.getActivationCondition().isPresent()) { final var dr = dependentResourceNode.getDependentResource(); final var eventSourceRetriever = context.eventSourceRetriever(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResult.java index cf759022dc..f751c88f97 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResult.java @@ -44,8 +44,10 @@ public Optional getDependentResourceByName(String name) { } @Override - public Optional getDependentConditionResult(DependentResource dependentResource, - Condition.Type conditionType, Class expectedResultType) { + public Optional getDependentConditionResult( + DependentResource dependentResource, + Condition.Type conditionType, + Class expectedResultType) { if (dependentResource == null) { return Optional.empty(); } @@ -57,15 +59,19 @@ public Optional getDependentConditionResult(DependentResource dependentRe .map(r -> result[0] = r.getDetail()) .map(expectedResultType::cast); } catch (Exception e) { - throw new IllegalArgumentException("Condition " + - "result " + result[0] + - " for Dependent " + dependentResource.name() + " doesn't match expected type " - + expectedResultType.getSimpleName(), e); + throw new IllegalArgumentException( + "Condition " + + "result " + + result[0] + + " for Dependent " + + dependentResource.name() + + " doesn't match expected type " + + expectedResultType.getSimpleName(), + e); } } - protected List listFilteredBy( - Function filter) { + protected List listFilteredBy(Function filter) { return results.entrySet().stream() .filter(e -> filter.apply(e.getValue())) .map(Map.Entry::getKey) @@ -83,7 +89,8 @@ public boolean erroredDependentsExist() { @Override public void throwAggregateExceptionIfErrorsPresent() { if (erroredDependentsExist()) { - throw new AggregatedOperatorException("Exception(s) during workflow execution.", + throw new AggregatedOperatorException( + "Exception(s) during workflow execution.", getErroredDependentsStream() .collect(Collectors.toMap(e -> e.getKey().name(), e -> e.getValue().error))); } @@ -102,21 +109,27 @@ static class DetailBuilder { private boolean markedForDelete; Detail build() { - return new Detail<>(error, reconcileResult, activationConditionResult, - deletePostconditionResult, readyPostconditionResult, reconcilePostconditionResult, - deleted, visited, markedForDelete); + return new Detail<>( + error, + reconcileResult, + activationConditionResult, + deletePostconditionResult, + readyPostconditionResult, + reconcilePostconditionResult, + deleted, + visited, + markedForDelete); } DetailBuilder withResultForCondition( - ConditionWithType conditionWithType, - DetailedCondition.Result conditionResult) { + ConditionWithType conditionWithType, DetailedCondition.Result conditionResult) { switch (conditionWithType.type()) { case ACTIVATION -> activationConditionResult = conditionResult; case DELETE -> deletePostconditionResult = conditionResult; case READY -> readyPostconditionResult = conditionResult; case RECONCILE -> reconcilePostconditionResult = conditionResult; default -> - throw new IllegalStateException("Unexpected condition type: " + conditionWithType); + throw new IllegalStateException("Unexpected condition type: " + conditionWithType); } return this; } @@ -167,16 +180,20 @@ DetailBuilder markForDelete() { } } - - record Detail(Exception error, ReconcileResult reconcileResult, + record Detail( + Exception error, + ReconcileResult reconcileResult, DetailedCondition.Result activationConditionResult, DetailedCondition.Result deletePostconditionResult, DetailedCondition.Result readyPostconditionResult, DetailedCondition.Result reconcilePostconditionResult, - boolean deleted, boolean visited, boolean markedForDelete) { + boolean deleted, + boolean visited, + boolean markedForDelete) { boolean isConditionWithTypeMet(Condition.Type conditionType) { - return getResultForConditionWithType(conditionType).map(DetailedCondition.Result::isSuccess) + return getResultForConditionWithType(conditionType) + .map(DetailedCondition.Result::isSuccess) .orElse(true); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java index 5a60b63d41..8e7c987c69 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationCondition.java @@ -19,7 +19,7 @@ * * @param the resource type associated with the CRD to check for presence * @param

the primary resource type associated with the reconciler processing dependents - * associated with this condition + * associated with this condition */ public class CRDPresentActivationCondition implements Condition { @@ -42,27 +42,24 @@ public CRDPresentActivationCondition(int checkLimit, Duration crdCheckInterval) } // for testing purposes only - CRDPresentActivationCondition(CRDPresentChecker crdPresentChecker, int checkLimit, - Duration crdCheckInterval) { + CRDPresentActivationCondition( + CRDPresentChecker crdPresentChecker, int checkLimit, Duration crdCheckInterval) { this.crdPresentChecker = crdPresentChecker; this.checkLimit = checkLimit; this.crdCheckInterval = crdCheckInterval; } @Override - public boolean isMet(DependentResource dependentResource, - P primary, Context

context) { + public boolean isMet(DependentResource dependentResource, P primary, Context

context) { var resourceClass = dependentResource.resourceType(); final var crdName = HasMetadata.getFullResourceName(resourceClass); - var crdCheckState = crdPresenceCache.computeIfAbsent(crdName, - g -> new CRDCheckState()); + var crdCheckState = crdPresenceCache.computeIfAbsent(crdName, g -> new CRDCheckState()); synchronized (crdCheckState) { if (shouldCheckStateNow(crdCheckState)) { - boolean isPresent = crdPresentChecker - .checkIfCRDPresent(crdName, context.getClient()); + boolean isPresent = crdPresentChecker.checkIfCRDPresent(crdName, context.getClient()); crdCheckState.checkedNow(isPresent); } } @@ -73,9 +70,7 @@ public boolean isMet(DependentResource dependentResource, return crdCheckState.isCrdPresent(); } - /** - * Override this method to fine tune when the crd state should be refreshed; - */ + /** Override this method to fine tune when the crd state should be refreshed; */ protected boolean shouldCheckStateNow(CRDCheckState crdCheckState) { if (crdCheckState.isCrdPresent() == null) { return true; @@ -119,8 +114,7 @@ public int getCheckCount() { public static class CRDPresentChecker { boolean checkIfCRDPresent(String crdName, KubernetesClient client) { - return client.resources(CustomResourceDefinition.class) - .withName(crdName).get() != null; + return client.resources(CustomResourceDefinition.class).withName(crdName).get() != null; } } @@ -128,5 +122,4 @@ boolean checkIfCRDPresent(String crdName, KubernetesClient client) { public static void clearState() { crdPresenceCache.clear(); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/Condition.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/Condition.java index de96c99ca5..a0af3ddf5c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/Condition.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/Condition.java @@ -7,13 +7,16 @@ public interface Condition { enum Type { - ACTIVATION, DELETE, READY, RECONCILE + ACTIVATION, + DELETE, + READY, + RECONCILE } /** - * Checks whether a condition holds true for a given - * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} based on the - * observed cluster state. + * Checks whether a condition holds true for a given {@link + * io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} based on the observed + * cluster state. * * @param dependentResource for which the condition applies to * @param primary the primary resource being considered diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ConditionWithType.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ConditionWithType.java index de95830a92..97e00a760c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ConditionWithType.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ConditionWithType.java @@ -19,13 +19,12 @@ public Type type() { @SuppressWarnings("unchecked") @Override - public Result detailedIsMet(DependentResource dependentResource, P primary, - Context

context) { + public Result detailedIsMet( + DependentResource dependentResource, P primary, Context

context) { if (condition instanceof DetailedCondition detailedCondition) { return detailedCondition.detailedIsMet(dependentResource, primary, context); } else { - return Result - .withoutResult(condition.isMet(dependentResource, primary, context)); + return Result.withoutResult(condition.isMet(dependentResource, primary, context)); } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java index 017dbb74a5..587c7fbdc8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultManagedWorkflow.java @@ -27,9 +27,8 @@ public class DefaultManagedWorkflow

implements ManagedWor protected DefaultManagedWorkflow(List orderedSpecs, boolean hasCleaner) { this.hasCleaner = hasCleaner; topLevelResources = new HashSet<>(orderedSpecs.size()); - bottomLevelResources = orderedSpecs.stream() - .map(DependentResourceSpec::getName) - .collect(Collectors.toSet()); + bottomLevelResources = + orderedSpecs.stream().map(DependentResourceSpec::getName).collect(Collectors.toSet()); this.orderedSpecs = orderedSpecs; for (DependentResourceSpec spec : orderedSpecs) { // add cycle detection? @@ -73,37 +72,42 @@ public boolean isEmpty() { @Override @SuppressWarnings("unchecked") - public Workflow

resolve(KubernetesClient client, - ControllerConfiguration

configuration) { + public Workflow

resolve(KubernetesClient client, ControllerConfiguration

configuration) { final var alreadyResolved = new HashMap(orderedSpecs.size()); for (DependentResourceSpec spec : orderedSpecs) { final var dependentResource = resolve(spec, client, configuration); - final var node = new DependentResourceNode( - spec.getReconcileCondition(), - spec.getDeletePostCondition(), - spec.getReadyCondition(), - spec.getActivationCondition(), - dependentResource); + final var node = + new DependentResourceNode( + spec.getReconcileCondition(), + spec.getDeletePostCondition(), + spec.getReadyCondition(), + spec.getActivationCondition(), + dependentResource); alreadyResolved.put(dependentResource.name(), node); - spec.getDependsOn() - .forEach(depend -> node.addDependsOnRelation(alreadyResolved.get(depend))); + spec.getDependsOn().forEach(depend -> node.addDependsOnRelation(alreadyResolved.get(depend))); } final var bottom = bottomLevelResources.stream().map(alreadyResolved::get).collect(Collectors.toSet()); final var top = topLevelResources.stream().map(alreadyResolved::get).collect(Collectors.toSet()); - return new DefaultWorkflow<>(alreadyResolved, bottom, top, + return new DefaultWorkflow<>( + alreadyResolved, + bottom, + top, configuration.getWorkflowSpec().map(w -> !w.handleExceptionsInReconciler()).orElseThrow(), hasCleaner); } @SuppressWarnings({"rawtypes", "unchecked"}) - private DependentResource resolve(DependentResourceSpec spec, + private DependentResource resolve( + DependentResourceSpec spec, KubernetesClient client, ControllerConfiguration

configuration) { final DependentResource dependentResource = - configuration.getConfigurationService().dependentResourceFactory() + configuration + .getConfigurationService() + .dependentResourceFactory() .createFrom(spec, configuration); final var name = spec.getName(); @@ -112,16 +116,20 @@ private DependentResource resolve(DependentResourceSpec spec, } spec.getUseEventSourceWithName() - .ifPresent(esName -> { - if (dependentResource instanceof EventSourceReferencer) { - ((EventSourceReferencer) dependentResource).useEventSourceWithName(esName); - } else { - throw new IllegalStateException( - "DependentResource " + spec + " wants to use EventSource named " + esName - + " but doesn't implement support for this feature by implementing " - + EventSourceReferencer.class.getSimpleName()); - } - }); + .ifPresent( + esName -> { + if (dependentResource instanceof EventSourceReferencer) { + ((EventSourceReferencer) dependentResource).useEventSourceWithName(esName); + } else { + throw new IllegalStateException( + "DependentResource " + + spec + + " wants to use EventSource named " + + esName + + " but doesn't implement support for this feature by implementing " + + EventSourceReferencer.class.getSimpleName()); + } + }); return dependentResource; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java index 3b928ad78a..3ee1499543 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflow.java @@ -36,7 +36,8 @@ class DefaultWorkflow

implements Workflow

{ this(dependentResourceNodes, THROW_EXCEPTION_AUTOMATICALLY_DEFAULT, false); } - DefaultWorkflow(Set dependentResourceNodes, + DefaultWorkflow( + Set dependentResourceNodes, boolean throwExceptionAutomatically, boolean hasCleaner) { this.throwExceptionAutomatically = throwExceptionAutomatically; @@ -53,8 +54,10 @@ class DefaultWorkflow

implements Workflow

{ } } - protected DefaultWorkflow(Map dependentResourceNodes, - Set bottomLevelResource, Set topLevelResources, + protected DefaultWorkflow( + Map dependentResourceNodes, + Set bottomLevelResource, + Set topLevelResources, boolean throwExceptionAutomatically, boolean hasCleaner) { this.throwExceptionAutomatically = throwExceptionAutomatically; @@ -84,7 +87,8 @@ private Map toMap(Set node } if (topLevelResources.isEmpty()) { throw new IllegalStateException( - "No top-level dependent resources found. This might indicate a cyclic Set of DependentResourceNode has been provided."); + "No top-level dependent resources found. This might indicate a cyclic Set of" + + " DependentResourceNode has been provided."); } return map; } @@ -151,8 +155,8 @@ public int size() { @Override public Map getDependentResourcesByName() { final var resources = new HashMap(dependentResourceNodes.size()); - dependentResourceNodes - .forEach((name, node) -> resources.put(name, node.getDependentResource())); + dependentResourceNodes.forEach( + (name, node) -> resources.put(name, node.getDependentResource())); return resources; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflowReconcileResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflowReconcileResult.java index 3221308312..c7ed8290d0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflowReconcileResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DefaultWorkflowReconcileResult.java @@ -12,7 +12,6 @@ class DefaultWorkflowReconcileResult extends BaseWorkflowResult implements Workf super(results); } - public List getReconciledDependents() { return listFilteredBy(detail -> detail.reconcileResult() != null); } @@ -21,8 +20,8 @@ public List getNotReadyDependents() { return listFilteredBy(detail -> !detail.isConditionWithTypeMet(Condition.Type.READY)); } - public Optional getNotReadyDependentResult(DependentResource dependentResource, - Class expectedResultType) { + public Optional getNotReadyDependentResult( + DependentResource dependentResource, Class expectedResultType) { return getDependentConditionResult(dependentResource, Condition.Type.READY, expectedResultType); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java index aa8bb31c66..87646a56d9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DependentResourceNode.java @@ -23,9 +23,12 @@ class DependentResourceNode { this(null, null, null, null, dependentResource); } - public DependentResourceNode(Condition reconcilePrecondition, - Condition deletePostcondition, Condition readyPostcondition, - Condition activationCondition, DependentResource dependentResource) { + public DependentResourceNode( + Condition reconcilePrecondition, + Condition deletePostcondition, + Condition readyPostcondition, + Condition activationCondition, + DependentResource dependentResource) { setReconcilePrecondition(reconcilePrecondition); setDeletePostcondition(deletePostcondition); setReadyPostcondition(readyPostcondition); @@ -67,23 +70,31 @@ public List getParents() { } void setReconcilePrecondition(Condition reconcilePrecondition) { - this.reconcilePrecondition = reconcilePrecondition == null ? null - : new ConditionWithType<>(reconcilePrecondition, Condition.Type.RECONCILE); + this.reconcilePrecondition = + reconcilePrecondition == null + ? null + : new ConditionWithType<>(reconcilePrecondition, Condition.Type.RECONCILE); } void setDeletePostcondition(Condition deletePostcondition) { - this.deletePostcondition = deletePostcondition == null ? null - : new ConditionWithType<>(deletePostcondition, Condition.Type.DELETE); + this.deletePostcondition = + deletePostcondition == null + ? null + : new ConditionWithType<>(deletePostcondition, Condition.Type.DELETE); } void setActivationCondition(Condition activationCondition) { - this.activationCondition = activationCondition == null ? null - : new ConditionWithType<>(activationCondition, Condition.Type.ACTIVATION); + this.activationCondition = + activationCondition == null + ? null + : new ConditionWithType<>(activationCondition, Condition.Type.ACTIVATION); } void setReadyPostcondition(Condition readyPostcondition) { - this.readyPostcondition = readyPostcondition == null ? null - : new ConditionWithType<>(readyPostcondition, Condition.Type.READY); + this.readyPostcondition = + readyPostcondition == null + ? null + : new ConditionWithType<>(readyPostcondition, Condition.Type.READY); } public DependentResource getDependentResource() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DetailedCondition.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DetailedCondition.java index 200743cc0e..578d921e28 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DetailedCondition.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/DetailedCondition.java @@ -6,10 +6,10 @@ /** * A condition that can return extra information in addition of whether it is met or not. - * + * * @param the resource type this condition applies to * @param

the primary resource type associated with the dependent workflow this condition is - * part of + * part of * @param the type of the extra information returned by the condition */ public interface DetailedCondition extends Condition { @@ -17,12 +17,12 @@ public interface DetailedCondition extends Conditio /** * Checks whether a condition holds true for the specified {@link DependentResource}, returning * additional information as needed. - * + * * @param dependentResource the {@link DependentResource} for which we want to check the condition * @param primary the primary resource being considered * @param context the current reconciliation {@link Context} * @return a {@link Result} instance containing the result of the evaluation of the condition as - * well as additional detail + * well as additional detail * @see Condition#isMet(DependentResource, HasMetadata, Context) */ Result detailedIsMet(DependentResource dependentResource, P primary, Context

context); @@ -34,24 +34,20 @@ default boolean isMet(DependentResource dependentResource, P primary, Cont /** * Holds a more detailed {@link Condition} result. - * + * * @param the type of the extra information provided in condition evaluation */ @SuppressWarnings({"rawtypes", "unchecked"}) interface Result { - /** - * A result expressing a condition has been met without extra information - */ + /** A result expressing a condition has been met without extra information */ Result metWithoutResult = new DefaultResult(true, null); - /** - * A result expressing a condition has not been met without extra information - */ + /** A result expressing a condition has not been met without extra information */ Result unmetWithoutResult = new DefaultResult(false, null); /** * Creates a {@link Result} without extra information - * + * * @param success whether or not the condition has been met * @return a {@link Result} without extra information */ @@ -61,7 +57,7 @@ static Result withoutResult(boolean success) { /** * Creates a {@link Result} with the specified condition evaluation result and extra information - * + * * @param success whether or not the condition has been met * @param detail the extra information that the condition provided during its evaluation * @return a {@link Result} with the specified condition evaluation result and extra information @@ -78,15 +74,15 @@ default String asString() { /** * The extra information provided by the associated {@link DetailedCondition} during its * evaluation - * + * * @return extra information provided by the associated {@link DetailedCondition} during its - * evaluation or {@code null} if none was provided + * evaluation or {@code null} if none was provided */ T getDetail(); /** * Whether the associated condition held true - * + * * @return {@code true} if the associated condition was met, {@code false} otherwise */ boolean isSuccess(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/KubernetesResourceDeletedCondition.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/KubernetesResourceDeletedCondition.java index 28342f2d17..3cf464d46b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/KubernetesResourceDeletedCondition.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/KubernetesResourceDeletedCondition.java @@ -4,24 +4,18 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; -/** - * A condition implementation meant to be used as a delete post-condition on Kubernetes dependent +/* A condition implementation meant to be used as a delete post-condition on Kubernetes dependent * resources to prevent the workflow from proceeding until the associated resource is actually - * deleted from the server (or, at least, doesn't have any finalizers anymore). This is needed in - * cases where a cleaning process depends on resources being actually removed from the server - * because, by default, workflows simply request the deletion but do NOT wait for the resources to - * be actually deleted. + * deleted from the server. */ public class KubernetesResourceDeletedCondition implements Condition { @Override - public boolean isMet(DependentResource dependentResource, - HasMetadata primary, Context context) { + public boolean isMet( + DependentResource dependentResource, + HasMetadata primary, + Context context) { var optionalResource = dependentResource.getSecondaryResource(primary, context); - if (optionalResource.isEmpty()) { - return true; - } else { - return optionalResource.orElseThrow().getMetadata().getFinalizers().isEmpty(); - } + return optionalResource.isEmpty(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java index ef839896f4..365dcef138 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowFactory.java @@ -8,14 +8,15 @@ public interface ManagedWorkflowFactory> { @SuppressWarnings({"rawtypes", "unchecked"}) - ManagedWorkflowFactory DEFAULT = (configuration) -> { - final Optional workflowSpec = configuration.getWorkflowSpec(); - if (workflowSpec.isEmpty()) { - return (ManagedWorkflow) (client, configuration1) -> new DefaultWorkflow(null); - } - ManagedWorkflowSupport support = new ManagedWorkflowSupport(); - return support.createWorkflow(workflowSpec.orElseThrow()); - }; + ManagedWorkflowFactory DEFAULT = + (configuration) -> { + final Optional workflowSpec = configuration.getWorkflowSpec(); + if (workflowSpec.isEmpty()) { + return (ManagedWorkflow) (client, configuration1) -> new DefaultWorkflow(null); + } + ManagedWorkflowSupport support = new ManagedWorkflowSupport(); + return support.createWorkflow(workflowSpec.orElseThrow()); + }; @SuppressWarnings("rawtypes") ManagedWorkflow workflowFor(C configuration); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java index d6404aa392..012dcdff56 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupport.java @@ -28,12 +28,13 @@ public void checkForNameDuplication(List dependentResourc final var uniqueNames = new HashSet<>(size); final var duplicatedNames = new HashSet<>(size); - dependentResourceSpecs.forEach(spec -> { - final var name = spec.getName(); - if (!uniqueNames.add(name)) { - duplicatedNames.add(name); - } - }); + dependentResourceSpecs.forEach( + spec -> { + final var name = spec.getName(); + if (!uniqueNames.add(name)) { + duplicatedNames.add(name); + } + }); if (!duplicatedNames.isEmpty()) { throw new OperatorException("Duplicated dependent resource name(s): " + duplicatedNames); } @@ -65,23 +66,25 @@ private List orderAndDetectCycles( while (!toVisit.isEmpty()) { final var toVisitNext = new HashSet(); - toVisit.forEach(dr -> { - if (cleanerHolder != null) { - cleanerHolder[0] = - cleanerHolder[0] || DefaultWorkflow.isDeletable(dr.getDependentResourceClass()); - } - final var name = dr.getName(); - var drInfo = drInfosByName.get(name); - if (drInfo != null) { - drInfo.waitingForCompletion.forEach(spec -> { - if (isReadyForVisit(spec, alreadyVisited, name)) { - toVisitNext.add(spec); + toVisit.forEach( + dr -> { + if (cleanerHolder != null) { + cleanerHolder[0] = + cleanerHolder[0] || DefaultWorkflow.isDeletable(dr.getDependentResourceClass()); + } + final var name = dr.getName(); + var drInfo = drInfosByName.get(name); + if (drInfo != null) { + drInfo.waitingForCompletion.forEach( + spec -> { + if (isReadyForVisit(spec, alreadyVisited, name)) { + toVisitNext.add(spec); + } + }); + orderedSpecs.add(dr); } + alreadyVisited.add(name); }); - orderedSpecs.add(dr); - } - alreadyVisited.add(name); - }); toVisit = toVisitNext; } @@ -122,8 +125,8 @@ String name() { } } - private boolean isReadyForVisit(DependentResourceSpec dr, Set alreadyVisited, - String alreadyPresentName) { + private boolean isReadyForVisit( + DependentResourceSpec dr, Set alreadyVisited, String alreadyPresentName) { for (var name : dr.getDependsOn()) { if (name.equals(alreadyPresentName)) { continue; @@ -137,23 +140,28 @@ private boolean isReadyForVisit(DependentResourceSpec dr, Set alreadyVis private Set getTopDependentResources( List dependentResourceSpecs) { - return dependentResourceSpecs.stream().filter(r -> r.getDependsOn().isEmpty()) + return dependentResourceSpecs.stream() + .filter(r -> r.getDependsOn().isEmpty()) .collect(Collectors.toSet()); } private Map createDRInfos(List dependentResourceSpecs) { // first create mappings - final var infos = dependentResourceSpecs.stream() - .map(DRInfo::new) - .collect(Collectors.toMap(DRInfo::name, Function.identity())); + final var infos = + dependentResourceSpecs.stream() + .map(DRInfo::new) + .collect(Collectors.toMap(DRInfo::name, Function.identity())); // then populate the reverse depends on information - dependentResourceSpecs.forEach(spec -> spec.getDependsOn().forEach(name -> { - final var drInfo = infos.get(name); - drInfo.add(spec); - })); + dependentResourceSpecs.forEach( + spec -> + spec.getDependsOn() + .forEach( + name -> { + final var drInfo = infos.get(name); + drInfo.add(spec); + })); return infos; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/NodeExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/NodeExecutor.java index 2006486dc1..740d10710d 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/NodeExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/NodeExecutor.java @@ -7,7 +7,8 @@ abstract class NodeExecutor implements Runnable { private final DependentResourceNode dependentResourceNode; private final AbstractWorkflowExecutor

workflowExecutor; - protected NodeExecutor(DependentResourceNode dependentResourceNode, + protected NodeExecutor( + DependentResourceNode dependentResourceNode, AbstractWorkflowExecutor

workflowExecutor) { this.dependentResourceNode = dependentResourceNode; this.workflowExecutor = workflowExecutor; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java index 7493058723..e85434f10f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilder.java @@ -60,8 +60,8 @@ public Workflow

build() { } DefaultWorkflow

buildAsDefaultWorkflow() { - return new DefaultWorkflow(new HashSet<>(dependentResourceNodes.values()), - throwExceptionAutomatically, isCleaner); + return new DefaultWorkflow( + new HashSet<>(dependentResourceNodes.values()), throwExceptionAutomatically, isCleaner); } public class WorkflowNodeConfigurationBuilder { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java index 4681502a3b..29274bb7b9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutor.java @@ -21,8 +21,8 @@ class WorkflowCleanupExecutor

extends AbstractWorkflowExe } public synchronized WorkflowCleanupResult cleanup() { - for (DependentResourceNode dependentResourceNode : workflow - .getBottomLevelDependentResources()) { + for (DependentResourceNode dependentResourceNode : + workflow.getBottomLevelDependentResources()) { handleCleanup(dependentResourceNode); } waitForScheduledExecutionsToRun(); @@ -57,8 +57,11 @@ private synchronized void handleCleanup(DependentResourceNode dependentResourceN if (hasErroredDependent) { causes.add("errored dependent"); } - log.debug("Skipping: {} primaryID: {} causes: {}", dependentResourceNode, - primaryID, String.join(", ", causes)); + log.debug( + "Skipping: {} primaryID: {} causes: {}", + dependentResourceNode, + primaryID, + String.join(", ", causes)); } return; } @@ -66,7 +69,6 @@ private synchronized void handleCleanup(DependentResourceNode dependentResourceN submit(dependentResourceNode, new CleanupExecutor<>(dependentResourceNode), CLEANUP); } - private class CleanupExecutor extends NodeExecutor { private CleanupExecutor(DependentResourceNode drn) { @@ -104,11 +106,15 @@ private synchronized void handleDependentCleaned( DependentResourceNode dependentResourceNode) { var dependOns = dependentResourceNode.getDependsOn(); if (dependOns != null) { - dependOns.forEach(d -> { - log.debug("Handle cleanup for dependent: {} of parent: {} primaryID: {}", d, - dependentResourceNode, primaryID); - handleCleanup(d); - }); + dependOns.forEach( + d -> { + log.debug( + "Handle cleanup for dependent: {} of parent: {} primaryID: {}", + d, + dependentResourceNode, + primaryID); + handleCleanup(d); + }); } } @@ -116,8 +122,7 @@ private synchronized void handleDependentCleaned( private boolean allDependentsCleaned(DependentResourceNode dependentResourceNode) { List parents = dependentResourceNode.getParents(); return parents.isEmpty() - || parents.stream() - .allMatch(d -> alreadyVisited(d) && !postDeleteConditionNotMet(d)); + || parents.stream().allMatch(d -> alreadyVisited(d) && !postDeleteConditionNotMet(d)); } @SuppressWarnings("unchecked") diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java index 432a168ad7..065e790ba4 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutor.java @@ -44,7 +44,10 @@ private synchronized void handleReconcile(DependentResourceNode depend final var isWaitingOnParents = !allParentsReconciledAndReady(dependentResourceNode); final var isMarkedForDelete = isMarkedForDelete(dependentResourceNode); final var hasErroredParent = hasErroredParent(dependentResourceNode); - if (isWaitingOnParents || alreadyVisited || executingNow || isMarkedForDelete + if (isWaitingOnParents + || alreadyVisited + || executingNow + || isMarkedForDelete || hasErroredParent) { if (log.isDebugEnabled()) { final var causes = new ArrayList(); @@ -63,20 +66,23 @@ private synchronized void handleReconcile(DependentResourceNode depend if (hasErroredParent) { causes.add("errored parent"); } - log.debug("Skipping: {} primaryID: {} causes: {}", dependentResourceNode, - primaryID, String.join(", ", causes)); + log.debug( + "Skipping: {} primaryID: {} causes: {}", + dependentResourceNode, + primaryID, + String.join(", ", causes)); } return; } - boolean activationConditionMet = isConditionMet(dependentResourceNode.getActivationCondition(), - dependentResourceNode); + boolean activationConditionMet = + isConditionMet(dependentResourceNode.getActivationCondition(), dependentResourceNode); registerOrDeregisterEventSourceBasedOnActivation(activationConditionMet, dependentResourceNode); boolean reconcileConditionMet = true; if (activationConditionMet) { - reconcileConditionMet = isConditionMet(dependentResourceNode.getReconcilePrecondition(), - dependentResourceNode); + reconcileConditionMet = + isConditionMet(dependentResourceNode.getReconcilePrecondition(), dependentResourceNode); } if (!reconcileConditionMet || !activationConditionMet) { handleReconcileOrActivationConditionNotMet(dependentResourceNode, activationConditionMet); @@ -107,21 +113,23 @@ private synchronized void handleDelete(DependentResourceNode dependentResourceNo if (isWaitingOnDependents) { causes.add("waiting on dependents"); } - log.debug("Skipping submit for delete of: {} primaryID: {} causes: {}", + log.debug( + "Skipping submit for delete of: {} primaryID: {} causes: {}", dependentResourceNode, - primaryID, String.join(", ", causes)); + primaryID, + String.join(", ", causes)); } return; } - submit(dependentResourceNode, - new NodeDeleteExecutor<>(dependentResourceNode), DELETE); + submit(dependentResourceNode, new NodeDeleteExecutor<>(dependentResourceNode), DELETE); } private boolean allDependentsDeletedAlready(DependentResourceNode dependentResourceNode) { var dependents = dependentResourceNode.getParents(); - return dependents.stream().allMatch(d -> alreadyVisited(d) && isReady(d) - && !isInError(d) && !postDeleteConditionNotMet(d)); + return dependents.stream() + .allMatch( + d -> alreadyVisited(d) && isReady(d) && !isInError(d) && !postDeleteConditionNotMet(d)); } private class NodeReconcileExecutor extends NodeExecutor { @@ -133,15 +141,14 @@ private NodeReconcileExecutor(DependentResourceNode dependentResourceNode) @Override protected void doRun(DependentResourceNode dependentResourceNode) { final var dependentResource = dependentResourceNode.getDependentResource(); - log.debug( - "Reconciling for primary: {} node: {} ", primaryID, dependentResourceNode); + log.debug("Reconciling for primary: {} node: {} ", primaryID, dependentResourceNode); ReconcileResult reconcileResult = dependentResource.reconcile(primary, context); final var detailBuilder = createOrGetResultFor(dependentResourceNode); detailBuilder.withReconcileResult(reconcileResult).markAsVisited(); if (isConditionMet(dependentResourceNode.getReadyPostcondition(), dependentResourceNode)) { - log.debug("Setting already reconciled for: {} primaryID: {}", - dependentResourceNode, primaryID); + log.debug( + "Setting already reconciled for: {} primaryID: {}", dependentResourceNode, primaryID); handleDependentsReconcile(dependentResourceNode); } else { log.debug("Setting already reconciled but not ready for: {}", dependentResourceNode); @@ -180,33 +187,44 @@ protected void doRun(DependentResourceNode dependentResourceNode) { private synchronized void handleDependentDeleted( DependentResourceNode dependentResourceNode) { - dependentResourceNode.getDependsOn().forEach(dr -> { - log.debug("Handle deleted for: {} with dependent: {} primaryID: {}", dr, - dependentResourceNode, primaryID); - handleDelete(dr); - }); + dependentResourceNode + .getDependsOn() + .forEach( + dr -> { + log.debug( + "Handle deleted for: {} with dependent: {} primaryID: {}", + dr, + dependentResourceNode, + primaryID); + handleDelete(dr); + }); } private synchronized void handleDependentsReconcile( DependentResourceNode dependentResourceNode) { var dependents = dependentResourceNode.getParents(); - dependents.forEach(d -> { - log.debug("Handle reconcile for dependent: {} of parent:{} primaryID: {}", d, - dependentResourceNode, primaryID); - handleReconcile(d); - }); + dependents.forEach( + d -> { + log.debug( + "Handle reconcile for dependent: {} of parent:{} primaryID: {}", + d, + dependentResourceNode, + primaryID); + handleReconcile(d); + }); } private void handleReconcileOrActivationConditionNotMet( - DependentResourceNode dependentResourceNode, - boolean activationConditionMet) { + DependentResourceNode dependentResourceNode, boolean activationConditionMet) { Set bottomNodes = new HashSet<>(); markDependentsForDelete(dependentResourceNode, bottomNodes, activationConditionMet); bottomNodes.forEach(this::handleDelete); } - private void markDependentsForDelete(DependentResourceNode dependentResourceNode, - Set bottomNodes, boolean activationConditionMet) { + private void markDependentsForDelete( + DependentResourceNode dependentResourceNode, + Set bottomNodes, + boolean activationConditionMet) { // this is a check so the activation condition is not evaluated twice, // so if the activation condition was false, this node is not meant to be deleted. var dependents = dependentResourceNode.getParents(); @@ -236,8 +254,7 @@ private boolean allParentsReconciledAndReady(DependentResourceNode depende private boolean hasErroredParent(DependentResourceNode dependentResourceNode) { return !dependentResourceNode.getDependsOn().isEmpty() - && dependentResourceNode.getDependsOn().stream() - .anyMatch(this::isInError); + && dependentResourceNode.getDependsOn().stream().anyMatch(this::isInError); } private WorkflowReconcileResult createReconcileResult() { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileResult.java index dd05e3f8e3..939f112697 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileResult.java @@ -17,8 +17,8 @@ default List getNotReadyDependents() { return List.of(); } - default Optional getNotReadyDependentResult(DependentResource dependentResource, - Class expectedResultType) { + default Optional getNotReadyDependentResult( + DependentResource dependentResource, Class expectedResultType) { return Optional.empty(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java index 0d7e74fa78..b9bbd45233 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowResult.java @@ -12,12 +12,12 @@ default Map getErroredDependents() { } /** - * Retrieves the {@link DependentResource} associated with the specified name if it exists, - * {@link Optional#empty()} otherwise. + * Retrieves the {@link DependentResource} associated with the specified name if it exists, {@link + * Optional#empty()} otherwise. * * @param name the name of the {@link DependentResource} to retrieve - * @return the {@link DependentResource} associated with the specified name if it exists, - * {@link Optional#empty()} otherwise + * @return the {@link DependentResource} associated with the specified name if it exists, {@link + * Optional#empty()} otherwise */ default Optional getDependentResourceByName(String name) { return Optional.empty(); @@ -29,16 +29,17 @@ default Optional getDependentResourceByName(String name) { * * @param the expected result type of the condition * @param dependentResourceName the dependent resource for which we want to retrieve a condition - * result + * result * @param conditionType the condition type which result we're interested in * @param expectedResultType the expected result type of the condition * @return the dependent condition result if it exists or {@link Optional#empty()} otherwise * @throws IllegalArgumentException if a result exists but is not of the expected type */ - default Optional getDependentConditionResult(String dependentResourceName, - Condition.Type conditionType, Class expectedResultType) { + default Optional getDependentConditionResult( + String dependentResourceName, Condition.Type conditionType, Class expectedResultType) { return getDependentConditionResult( - getDependentResourceByName(dependentResourceName).orElse(null), conditionType, + getDependentResourceByName(dependentResourceName).orElse(null), + conditionType, expectedResultType); } @@ -48,14 +49,16 @@ default Optional getDependentConditionResult(String dependentResourceName * * @param the expected result type of the condition * @param dependentResource the dependent resource for which we want to retrieve a condition - * result + * result * @param conditionType the condition type which result we're interested in * @param expectedResultType the expected result type of the condition * @return the dependent condition result if it exists or {@link Optional#empty()} otherwise * @throws IllegalArgumentException if a result exists but is not of the expected type */ - default Optional getDependentConditionResult(DependentResource dependentResource, - Condition.Type conditionType, Class expectedResultType) { + default Optional getDependentConditionResult( + DependentResource dependentResource, + Condition.Type conditionType, + Class expectedResultType) { return Optional.empty(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java index 227a297795..9ed00625bb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/Event.java @@ -16,17 +16,13 @@ public ResourceID getRelatedCustomResourceID() { @Override public String toString() { - return "Event{" + - "relatedCustomResource=" + relatedCustomResource + - '}'; + return "Event{" + "relatedCustomResource=" + relatedCustomResource + '}'; } @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; Event event = (Event) o; return Objects.equals(relatedCustomResource, event.relatedCustomResource); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java index 73f7867da4..064b566220 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventHandler.java @@ -3,5 +3,4 @@ public interface EventHandler { void handleEvent(Event event); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java index e05ea4830f..bdaf575814 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventProcessor.java @@ -47,12 +47,14 @@ public class EventProcessor

implements EventHandler, Life private final Map metricsMetadata; private ExecutorService executor; - public EventProcessor(EventSourceManager

eventSourceManager, - ConfigurationService configurationService) { + public EventProcessor( + EventSourceManager

eventSourceManager, ConfigurationService configurationService) { this( eventSourceManager.getController().getConfiguration(), - new ReconciliationDispatcher<>(eventSourceManager.getController()), eventSourceManager, - configurationService.getMetrics(), eventSourceManager.getControllerEventSource()); + new ReconciliationDispatcher<>(eventSourceManager.getController()), + eventSourceManager, + configurationService.getMetrics(), + eventSourceManager.getControllerEventSource()); } @SuppressWarnings("rawtypes") @@ -63,7 +65,9 @@ public EventProcessor(EventSourceManager

eventSourceManager, Metrics metrics) { this( controllerConfiguration, - reconciliationDispatcher, eventSourceManager, metrics, + reconciliationDispatcher, + eventSourceManager, + metrics, eventSourceManager.getControllerEventSource()); } @@ -71,7 +75,9 @@ public EventProcessor(EventSourceManager

eventSourceManager, private EventProcessor( ControllerConfiguration controllerConfiguration, ReconciliationDispatcher

reconciliationDispatcher, - EventSourceManager

eventSourceManager, Metrics metrics, Cache

cache) { + EventSourceManager

eventSourceManager, + Metrics metrics, + Cache

cache) { this.controllerConfiguration = controllerConfiguration; this.running = false; this.reconciliationDispatcher = reconciliationDispatcher; @@ -81,11 +87,14 @@ private EventProcessor( this.eventSourceManager = eventSourceManager; this.rateLimiter = controllerConfiguration.getRateLimiter(); - metricsMetadata = Optional.ofNullable(eventSourceManager.getController()) - .map(c -> Map.of( - Constants.RESOURCE_GVK_KEY, c.getAssociatedGroupVersionKind(), - Constants.CONTROLLER_NAME, controllerConfiguration.getName())) - .orElseGet(HashMap::new); + metricsMetadata = + Optional.ofNullable(eventSourceManager.getController()) + .map( + c -> + Map.of( + Constants.RESOURCE_GVK_KEY, c.getAssociatedGroupVersionKind(), + Constants.CONTROLLER_NAME, controllerConfiguration.getName())) + .orElseGet(HashMap::new); } @Override @@ -146,7 +155,8 @@ private void submitReconciliationExecution(ResourceState state) { executor.execute(new ReconcilerExecutor(resourceID, executionScope)); } else { log.debug( - "Skipping executing controller for resource id: {}. Controller in execution: {}. Latest Resource present: {}", + "Skipping executing controller for resource id: {}. Controller in execution: {}. Latest" + + " Resource present: {}", resourceID, controllerUnderExecution, maybeLatest.isPresent()); @@ -173,7 +183,8 @@ private void handleEventMarking(Event event, ResourceState state) { } else { if (state.processedMarkForDeletionPresent() && isResourceMarkedForDeletion(resourceEvent)) { log.debug( - "Skipping mark of event received, since already processed mark for deletion and resource marked for deletion: {}", + "Skipping mark of event received, since already processed mark for deletion and" + + " resource marked for deletion: {}", relatedCustomResourceID); return; } @@ -185,11 +196,12 @@ private void handleEventMarking(Event event, ResourceState state) { // event as below. markEventReceived(state); } - } else if (!state.deleteEventPresent() || !state.processedMarkForDeletionPresent()) { + } else if (!state.deleteEventPresent() && !state.processedMarkForDeletionPresent()) { markEventReceived(state); } else if (log.isDebugEnabled()) { log.debug( - "Skipped marking event as received. Delete event present: {}, processed mark for deletion: {}", + "Skipped marking event as received. Delete event present: {}, processed mark for" + + " deletion: {}", state.deleteEventPresent(), state.processedMarkForDeletionPresent()); } @@ -206,10 +218,11 @@ private boolean isResourceMarkedForDeletion(ResourceEvent resourceEvent) { private void handleRateLimitedSubmission(ResourceID resourceID, Duration minimalDuration) { var minimalDurationMillis = minimalDuration.toMillis(); - log.debug("Rate limited resource: {}, rescheduled in {} millis", resourceID, - minimalDurationMillis); - retryEventSource().scheduleOnce(resourceID, - Math.max(minimalDurationMillis, MINIMAL_RATE_LIMIT_RESCHEDULE_DURATION)); + log.debug( + "Rate limited resource: {}, rescheduled in {} millis", resourceID, minimalDurationMillis); + retryEventSource() + .scheduleOnce( + resourceID, Math.max(minimalDurationMillis, MINIMAL_RATE_LIMIT_RESCHEDULE_DURATION)); } synchronized void eventProcessingFinished( @@ -255,10 +268,12 @@ synchronized void eventProcessingFinished( /** * In case retry is configured more complex error logging takes place, see handleRetryOnException */ - private void logErrorIfNoRetryConfigured(ExecutionScope

executionScope, - PostExecutionControl

postExecutionControl) { + private void logErrorIfNoRetryConfigured( + ExecutionScope

executionScope, PostExecutionControl

postExecutionControl) { if (!isRetryConfigured() && postExecutionControl.exceptionDuringExecution()) { - log.error("Error during event processing {}", executionScope, + log.error( + "Error during event processing {}", + executionScope, postExecutionControl.getRuntimeException().orElseThrow()); } } @@ -268,24 +283,29 @@ private void reScheduleExecutionIfInstructed( postExecutionControl .getReScheduleDelay() - .ifPresentOrElse(delay -> { - var resourceID = ResourceID.fromResource(customResource); - log.debug("Rescheduling event for resource: {} with delay: {}", resourceID, delay); - retryEventSource().scheduleOnce(resourceID, delay); - }, () -> scheduleExecutionForMaxReconciliationInterval(customResource)); + .ifPresentOrElse( + delay -> { + var resourceID = ResourceID.fromResource(customResource); + log.debug("Rescheduling event for resource: {} with delay: {}", resourceID, delay); + retryEventSource().scheduleOnce(resourceID, delay); + }, + () -> scheduleExecutionForMaxReconciliationInterval(customResource)); } private void scheduleExecutionForMaxReconciliationInterval(P customResource) { this.controllerConfiguration .maxReconciliationInterval() - .ifPresent(m -> { - var resourceID = ResourceID.fromResource(customResource); - var delay = m.toMillis(); - log.debug("Rescheduling event for max reconciliation interval for resource: {} : " + - "with delay: {}", - resourceID, delay); - retryEventSource().scheduleOnce(resourceID, delay); - }); + .ifPresent( + m -> { + var resourceID = ResourceID.fromResource(customResource); + var delay = m.toMillis(); + log.debug( + "Rescheduling event for max reconciliation interval for resource: {} : " + + "with delay: {}", + resourceID, + delay); + retryEventSource().scheduleOnce(resourceID, delay); + }); } TimerEventSource

retryEventSource() { @@ -297,8 +317,7 @@ TimerEventSource

retryEventSource() { * events (received meanwhile retry is in place or already in buffer) instantly or always wait * according to the retry timing if there was an exception. */ - private void handleRetryOnException( - ExecutionScope

executionScope, Exception exception) { + private void handleRetryOnException(ExecutionScope

executionScope, Exception exception) { final var state = getOrInitRetryExecution(executionScope); var resourceID = state.getId(); boolean eventPresent = state.eventPresent(); @@ -315,9 +334,7 @@ private void handleRetryOnException( nextDelay.ifPresentOrElse( delay -> { log.debug( - "Scheduling timer event for retry with delay:{} for resource: {}", - delay, - resourceID); + "Scheduling timer event for retry with delay:{} for resource: {}", delay, resourceID); metrics.failedReconciliation(executionScope.getResource(), exception, metricsMetadata); retryEventSource().scheduleOnce(resourceID, delay); }, @@ -327,22 +344,34 @@ private void handleRetryOnException( }); } - private void retryAwareErrorLogging(RetryExecution retry, boolean eventPresent, + private void retryAwareErrorLogging( + RetryExecution retry, + boolean eventPresent, Exception exception, ExecutionScope

executionScope) { - if (!eventPresent && !retry.isLastAttempt() - && exception instanceof KubernetesClientException ex) { - if (ex.getCode() == HttpURLConnection.HTTP_CONFLICT) { - log.debug("Full client conflict error during event processing {}", executionScope, - exception); - log.warn( - "Resource Kubernetes Resource Creator/Update Conflict during reconciliation. Message: {} Resource name: {}", - ex.getMessage(), ex.getFullResourceName()); - return; - } + if (!retry.isLastAttempt() + && exception instanceof KubernetesClientException ex + && ex.getCode() == HttpURLConnection.HTTP_CONFLICT) { + log.debug("Full client conflict error during event processing {}", executionScope, exception); + log.info( + "Resource Kubernetes Resource Creator/Update Conflict during reconciliation. Message:" + + " {} Resource name: {}", + ex.getMessage(), + ex.getFullResourceName()); + } else if (eventPresent || !retry.isLastAttempt()) { + log.warn( + "Uncaught error during event processing {} - but another reconciliation will be attempted" + + " because a superseding event has been received or another retry attempt is" + + " pending.", + executionScope, + exception); + } else { + log.error( + "Uncaught error during event processing {} - no superseding event is present and this is" + + " the retry last attempt", + executionScope, + exception); } - log.error("Error during event processing {}", executionScope, - exception); } private void cleanupOnSuccessfulExecution(ExecutionScope

executionScope) { @@ -391,8 +420,11 @@ public synchronized void stop() { public synchronized void start() throws OperatorException { log.debug("Starting event processor: {}", this); // on restart new executor service is created and needs to be set here - executor = controllerConfiguration.getConfigurationService().getExecutorServiceManager() - .reconcileExecutorService(); + executor = + controllerConfiguration + .getConfigurationService() + .getExecutorServiceManager() + .reconcileExecutorService(); this.running = true; handleAlreadyMarkedEvents(); } @@ -432,8 +464,7 @@ public void run() { try { var actualResource = cache.get(resourceID); if (actualResource.isEmpty()) { - log.debug("Skipping execution; primary resource missing from cache: {}", - resourceID); + log.debug("Skipping execution; primary resource missing from cache: {}", resourceID); return; } actualResource.ifPresent(executionScope::setResource); @@ -453,7 +484,8 @@ public void run() { @Override public String toString() { - return controllerName() + " -> " + return controllerName() + + " -> " + (executionScope.getResource() != null ? executionScope : resourceID); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java index 174ffa0978..8b07bf110b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceManager.java @@ -59,10 +59,10 @@ public void postProcessDefaultEventSourcesAfterProcessorInitializer() { * Starts the event sources first and then the processor. Note that it's not desired to start * processing events while the event sources are not "synced". This not fully started and the * caches propagated - although for non k8s related event sources this behavior might be different - * (see - * {@link io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource}). - *

- * Now the event sources are also started sequentially, mainly because others might depend on + * (see {@link + * io.javaoperatorsdk.operator.processing.event.source.polling.PerResourcePollingEventSource}). + * + *

Now the event sources are also started sequentially, mainly because others might depend on * {@link ControllerEventSource} , which is started first. */ @Override @@ -70,13 +70,15 @@ public synchronized void start() { startEventSource(eventSources.controllerEventSource()); executorServiceManager.boundedExecuteAndWaitForAllToComplete( - eventSources.additionalEventSources() + eventSources + .additionalEventSources() .filter(es -> es.priority().equals(EventSourceStartPriority.RESOURCE_STATE_LOADER)), this::startEventSource, getThreadNamer("start")); executorServiceManager.boundedExecuteAndWaitForAllToComplete( - eventSources.additionalEventSources() + eventSources + .additionalEventSources() .filter(es -> es.priority().equals(EventSourceStartPriority.DEFAULT)), this::startEventSource, getThreadNamer("start")); @@ -95,16 +97,13 @@ private static Function getEventSourceThreadNamer(S public synchronized void stop() { stopEventSource(eventSources.controllerEventSource()); executorServiceManager.boundedExecuteAndWaitForAllToComplete( - eventSources.additionalEventSources(), - this::stopEventSource, - getThreadNamer("stop")); + eventSources.additionalEventSources(), this::stopEventSource, getThreadNamer("stop")); } @SuppressWarnings("rawtypes") private void logEventSourceEvent(EventSource eventSource, String event) { if (log.isDebugEnabled()) { - log.debug("{} event source {} for {}", event, eventSource.name(), - eventSource.resourceType()); + log.debug("{} event source {} for {}", event, eventSource.name(), eventSource.resourceType()); } } @@ -138,57 +137,65 @@ public final synchronized void registerEventSource(EventSource eventSo Objects.requireNonNull(eventSource, "EventSource must not be null"); try { if (eventSource instanceof ManagedInformerEventSource managedInformerEventSource) { - managedInformerEventSource.setControllerConfiguration( - controller.getConfiguration()); + managedInformerEventSource.setControllerConfiguration(controller.getConfiguration()); } eventSources.add(eventSource); eventSource.setEventHandler(controller.getEventProcessor()); } catch (IllegalStateException | MissingCRDException e) { throw e; // leave untouched } catch (Exception e) { - throw new OperatorException("Couldn't register event source: " + eventSource.name() + " for " - + controller.getConfiguration().getName() + " controller", e); + throw new OperatorException( + "Couldn't register event source: " + + eventSource.name() + + " for " + + controller.getConfiguration().getName() + + " controller", + e); } } @SuppressWarnings("unchecked") public void broadcastOnResourceEvent(ResourceAction action, P resource, P oldResource) { - eventSources.additionalEventSources() - .forEach(source -> { - if (source instanceof ResourceEventAware) { - var lifecycleAwareES = ((ResourceEventAware

) source); - switch (action) { - case ADDED: - lifecycleAwareES.onResourceCreated(resource); - break; - case UPDATED: - lifecycleAwareES.onResourceUpdated(resource, oldResource); - break; - case DELETED: - lifecycleAwareES.onResourceDeleted(resource); - break; - } - } - }); + eventSources + .additionalEventSources() + .forEach( + source -> { + if (source instanceof ResourceEventAware) { + var lifecycleAwareES = ((ResourceEventAware

) source); + switch (action) { + case ADDED: + lifecycleAwareES.onResourceCreated(resource); + break; + case UPDATED: + lifecycleAwareES.onResourceUpdated(resource, oldResource); + break; + case DELETED: + lifecycleAwareES.onResourceDeleted(resource); + break; + } + } + }); } public void changeNamespaces(Set namespaces) { eventSources.controllerEventSource().changeNamespaces(namespaces); - final var namespaceChangeables = eventSources - .additionalEventSources() - .filter(NamespaceChangeable.class::isInstance) - .map(NamespaceChangeable.class::cast) - .filter(NamespaceChangeable::allowsNamespaceChanges); - executorServiceManager.boundedExecuteAndWaitForAllToComplete(namespaceChangeables, e -> { - e.changeNamespaces(namespaces); - return null; - }, + final var namespaceChangeables = + eventSources + .additionalEventSources() + .filter(NamespaceChangeable.class::isInstance) + .map(NamespaceChangeable.class::cast) + .filter(NamespaceChangeable::allowsNamespaceChanges); + executorServiceManager.boundedExecuteAndWaitForAllToComplete( + namespaceChangeables, + e -> { + e.changeNamespaces(namespaces); + return null; + }, getEventSourceThreadNamer("changeNamespace")); } public Set> getRegisteredEventSources() { - return eventSources.flatMappedSources() - .collect(Collectors.toCollection(LinkedHashSet::new)); + return eventSources.flatMappedSources().collect(Collectors.toCollection(LinkedHashSet::new)); } @SuppressWarnings("rawtypes") @@ -196,12 +203,12 @@ public List allEventSources() { return eventSources.allEventSources().toList(); } - @SuppressWarnings("unused") public Stream> getEventSourcesStream() { return eventSources.flatMappedSources(); } + @Override public ControllerEventSource

getControllerEventSource() { return eventSources.controllerEventSource(); } @@ -243,8 +250,7 @@ public EventSourceContext

eventSourceContextForDynamicRegistration() { } @Override - public EventSource getEventSourceFor( - Class dependentType, String name) { + public EventSource getEventSourceFor(Class dependentType, String name) { Objects.requireNonNull(dependentType, "dependentType is Mandatory"); return eventSources.get(dependentType, name); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java index 16b03303a4..c5a219a026 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSourceRetriever.java @@ -6,6 +6,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.source.EventSource; +import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerEventSource; public interface EventSourceRetriever

{ @@ -17,25 +18,23 @@ default EventSource getEventSourceFor(Class dependentType) { List> getEventSourcesFor(Class dependentType); + ControllerEventSource

getControllerEventSource(); + /** - *

* Registers (and starts) the specified {@link EventSource} dynamically during the reconciliation. * If an EventSource is already registered with the specified name, the registration will be * ignored. It is the user's responsibility to handle the naming correctly. - *

- *

- * This is only needed when your operator needs to adapt dynamically based on optional resources - * that may or may not be present on the target cluster. Even in this situation, it should be - * possible to make these decisions at when the "regular" EventSources are registered so this - * method should not typically be called directly but rather by the framework to support + * + *

This is only needed when your operator needs to adapt dynamically based on optional + * resources that may or may not be present on the target cluster. Even in this situation, it + * should be possible to make these decisions at when the "regular" EventSources are registered so + * this method should not typically be called directly but rather by the framework to support * activation conditions of dependents, for example. - *

- *

- * This method will block until the event source is synced (if needed, as it is the case for + * + *

This method will block until the event source is synced (if needed, as it is the case for * {@link io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource}). - *

- *

- * IMPORTANT: Should multiple reconciliations happen concurrently, only one + * + *

IMPORTANT: Should multiple reconciliations happen concurrently, only one * EventSource with the specified name will ever be registered. It is therefore important to * explicitly name the event sources that you want to reuse because the name will be used to * identify which event sources need to be created or not. If you let JOSDK implicitly name event @@ -44,7 +43,6 @@ default EventSource getEventSourceFor(Class dependentType) { * to be registered under different, automatically generated names. If you clearly identify your * event sources with names, then, if the concurrent process determines that an event source with * the specified name, it won't register it again. - *

* * @param eventSource to register * @return the actual event source registered. Might not be the same as the parameter. @@ -54,15 +52,13 @@ default EventSource getEventSourceFor(Class dependentType) { /** * De-registers (and stops) the {@link EventSource} associated with the specified name. If no such * source exists, this method will do nothing. - *

- * This method will block until the event source is de-registered and stopped. If multiple + * + *

This method will block until the event source is de-registered and stopped. If multiple * reconciliations happen concurrently, all will be blocked until the event source is * de-registered. - *

- *

- * This method is meant only to be used for dynamically registered event sources and should not be - * typically called directly. - *

+ * + *

This method is meant only to be used for dynamically registered event sources and should not + * be typically called directly. * * @param name of the event source * @return the actual event source deregistered if there is one. @@ -70,5 +66,4 @@ default EventSource getEventSourceFor(Class dependentType) { Optional> dynamicallyDeRegisterEventSource(String name); EventSourceContext

eventSourceContextForDynamicRegistration(); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java index c21509f41c..6a8b290c4f 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/EventSources.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing.event; - import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -31,12 +30,13 @@ public void add(EventSource eventSource) { final var name = eventSource.name(); var existing = sourceByName.get(name); if (existing != null) { - throw new IllegalArgumentException("Event source " + existing - + " is already registered with name: " + name); + throw new IllegalArgumentException( + "Event source " + existing + " is already registered with name: " + name); } sourceByName.put(name, eventSource); - sources.computeIfAbsent(keyFor(eventSource), k -> new ConcurrentHashMap<>()).put(name, - eventSource); + sources + .computeIfAbsent(keyFor(eventSource), k -> new ConcurrentHashMap<>()) + .put(name, eventSource); } public EventSource remove(String name) { @@ -76,16 +76,13 @@ public Stream allEventSources() { @SuppressWarnings("rawtypes") Stream additionalEventSources() { return Stream.concat( - Stream.of(retryEventSource()).filter(Objects::nonNull), - flatMappedSources()); + Stream.of(retryEventSource()).filter(Objects::nonNull), flatMappedSources()); } Stream> flatMappedSources() { return sources.values().stream().flatMap(c -> c.values().stream()); } - - private String keyFor(EventSource source) { return keyFor(source.resourceType()); } @@ -111,25 +108,35 @@ public EventSource get(Class dependentType, String name) { source = (EventSource) sourcesForType.values().stream().findFirst().orElseThrow(); } else { if (name == null || name.isBlank()) { - throw new IllegalArgumentException("There are multiple EventSources registered for type " - + dependentType.getCanonicalName() - + ", you need to provide a name to specify which EventSource you want to query. Known names: " - + String.join(",", sourcesForType.keySet())); + throw new IllegalArgumentException( + "There are multiple EventSources registered for type " + + dependentType.getCanonicalName() + + ", you need to provide a name to specify which EventSource you want to query." + + " Known names: " + + String.join(",", sourcesForType.keySet())); } source = (EventSource) sourcesForType.get(name); if (source == null) { - throw new IllegalArgumentException("There is no event source found for class:" + - " " + dependentType.getName() + ", name:" + name); + throw new IllegalArgumentException( + "There is no event source found for class:" + + " " + + dependentType.getName() + + ", name:" + + name); } } final var resourceClass = source.resourceType(); if (!resourceClass.isAssignableFrom(dependentType)) { - throw new IllegalArgumentException(source + " associated with " - + keyAsString(dependentType, name) - + " is handling " + resourceClass.getName() + " resources but asked for " - + dependentType.getName()); + throw new IllegalArgumentException( + source + + " associated with " + + keyAsString(dependentType, name) + + " is handling " + + resourceClass.getName() + + " resources but asked for " + + dependentType.getName()); } return source; } @@ -147,7 +154,6 @@ public List> getEventSources(Class dependentType) { if (sourcesForType == null) { return Collections.emptyList(); } - return sourcesForType.values().stream() - .map(es -> (EventSource) es).toList(); + return sourcesForType.values().stream().map(es -> (EventSource) es).toList(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/PostExecutionControl.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/PostExecutionControl.java index 3343cff80a..42311c1cb5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/PostExecutionControl.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/PostExecutionControl.java @@ -16,7 +16,8 @@ final class PostExecutionControl { private PostExecutionControl( boolean finalizerRemoved, R updatedCustomResource, - boolean updateIsStatusPatch, Exception runtimeException) { + boolean updateIsStatusPatch, + Exception runtimeException) { this.finalizerRemoved = finalizerRemoved; this.updatedCustomResource = updatedCustomResource; this.updateIsStatusPatch = updateIsStatusPatch; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java index baa7c36121..c4b161ef27 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcher.java @@ -28,9 +28,7 @@ import static io.javaoperatorsdk.operator.processing.KubernetesResourceUtils.*; -/** - * Handles calls and results of a Reconciler and finalizer related logic - */ +/** Handles calls and results of a Reconciler and finalizer related logic */ class ReconciliationDispatcher

{ public static final int MAX_UPDATE_RETRY = 10; @@ -57,9 +55,12 @@ class ReconciliationDispatcher

{ } public ReconciliationDispatcher(Controller

controller) { - this(controller, - new CustomResourceFacade<>(controller.getCRClient(), controller.getConfiguration(), - controller.getConfiguration().getConfigurationService().getResourceCloner())); + this( + controller, + new CustomResourceFacade<>( + controller.getCRClient(), + controller.getConfiguration(), + controller.getConfiguration().getConfigurationService().getResourceCloner())); } public PostExecutionControl

handleExecution(ExecutionScope

executionScope) { @@ -74,7 +75,9 @@ private PostExecutionControl

handleDispatch(ExecutionScope

executionScope) throws Exception { P originalResource = executionScope.getResource(); var resourceForExecution = cloneResource(originalResource); - log.debug("Handling dispatch for resource name: {} namespace: {}", getName(originalResource), + log.debug( + "Handling dispatch for resource name: {} namespace: {}", + getName(originalResource), originalResource.getMetadata().getNamespace()); final var markedForDeletion = originalResource.isMarkedForDeletion(); @@ -96,14 +99,17 @@ private PostExecutionControl

handleDispatch(ExecutionScope

executionScope) } private boolean shouldNotDispatchToCleanupWhenMarkedForDeletion(P resource) { - var alreadyRemovedFinalizer = controller.useFinalizer() - && !resource.hasFinalizer(configuration().getFinalizerName()); + var alreadyRemovedFinalizer = + controller.useFinalizer() && !resource.hasFinalizer(configuration().getFinalizerName()); return !controller.useFinalizer() || alreadyRemovedFinalizer; } private PostExecutionControl

handleReconcile( - ExecutionScope

executionScope, P resourceForExecution, P originalResource, - Context

context) throws Exception { + ExecutionScope

executionScope, + P resourceForExecution, + P originalResource, + Context

context) + throws Exception { if (controller.useFinalizer() && !originalResource.hasFinalizer(configuration().getFinalizerName())) { /* @@ -116,8 +122,7 @@ private PostExecutionControl

handleReconcile( if (useSSA) { updatedResource = addFinalizerWithSSA(originalResource); } else { - updatedResource = - updateCustomResourceWithFinalizer(resourceForExecution, originalResource); + updatedResource = updateCustomResourceWithFinalizer(resourceForExecution, originalResource); } return PostExecutionControl.onlyFinalizerAdded(updatedResource); } else { @@ -133,8 +138,12 @@ private P cloneResource(P resource) { return cloner.clone(resource); } - private PostExecutionControl

reconcileExecution(ExecutionScope

executionScope, - P resourceForExecution, P originalResource, Context

context) throws Exception { + private PostExecutionControl

reconcileExecution( + ExecutionScope

executionScope, + P resourceForExecution, + P originalResource, + Context

context) + throws Exception { log.debug( "Reconciling resource {} with version: {} with execution scope: {}", getName(resourceForExecution), @@ -159,7 +168,8 @@ private PostExecutionControl

reconcileExecution(ExecutionScope

executionSc if (updateControl.isPatchResource()) { updatedCustomResource = patchResource(toUpdate, originalResource); if (!useSSA) { - toUpdate.getMetadata() + toUpdate + .getMetadata() .setResourceVersion(updatedCustomResource.getMetadata().getResourceVersion()); } } @@ -170,27 +180,29 @@ private PostExecutionControl

reconcileExecution(ExecutionScope

executionSc return createPostExecutionControl(updatedCustomResource, updateControl); } - @SuppressWarnings("unchecked") - private PostExecutionControl

handleErrorStatusHandler(P resource, P originalResource, - Context

context, - Exception e) throws Exception { - - RetryInfo retryInfo = context.getRetryInfo().orElseGet(() -> new RetryInfo() { - @Override - public int getAttemptCount() { - return 0; - } - - @Override - public boolean isLastAttempt() { - // check also if the retry is limited to 0 - return retryConfigurationHasZeroAttempts || - controller.getConfiguration().getRetry() == null; - } - }); + private PostExecutionControl

handleErrorStatusHandler( + P resource, P originalResource, Context

context, Exception e) throws Exception { + RetryInfo retryInfo = + context + .getRetryInfo() + .orElseGet( + () -> + new RetryInfo() { + @Override + public int getAttemptCount() { + return 0; + } + + @Override + public boolean isLastAttempt() { + // check also if the retry is limited to 0 + return retryConfigurationHasZeroAttempts + || controller.getConfiguration().getRetry() == null; + } + }); ((DefaultContext

) context).setRetryInfo(retryInfo); - var errorStatusUpdateControl = controller.getReconciler() - .updateErrorStatus(resource, context, e); + var errorStatusUpdateControl = + controller.getReconciler().updateErrorStatus(resource, context, e); if (errorStatusUpdateControl.isDefaultErrorProcessing()) { throw e; @@ -198,26 +210,34 @@ public boolean isLastAttempt() { P updatedResource = null; if (errorStatusUpdateControl.getResource().isPresent()) { - updatedResource = customResourceFacade - .patchStatus(errorStatusUpdateControl.getResource().orElseThrow(), originalResource); + try { + updatedResource = + customResourceFacade.patchStatus( + errorStatusUpdateControl.getResource().orElseThrow(), originalResource); + } catch (Exception ex) { + log.error( + "updateErrorStatus failed for resource: {} with version: {} for error {}", + getUID(resource), + getVersion(resource), + e.getMessage(), + ex); + } } if (errorStatusUpdateControl.isNoRetry()) { PostExecutionControl

postExecutionControl; if (updatedResource != null) { - postExecutionControl = - PostExecutionControl.customResourceStatusPatched(updatedResource); + postExecutionControl = PostExecutionControl.customResourceStatusPatched(updatedResource); } else { postExecutionControl = PostExecutionControl.defaultDispatch(); } - errorStatusUpdateControl.getScheduleDelay() - .ifPresent(postExecutionControl::withReSchedule); + errorStatusUpdateControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule); return postExecutionControl; } throw e; } - private PostExecutionControl

createPostExecutionControl(P updatedCustomResource, - UpdateControl

updateControl) { + private PostExecutionControl

createPostExecutionControl( + P updatedCustomResource, UpdateControl

updateControl) { PostExecutionControl

postExecutionControl; if (updatedCustomResource != null) { postExecutionControl = @@ -230,13 +250,12 @@ private PostExecutionControl

createPostExecutionControl(P updatedCustomResour } private void updatePostExecutionControlWithReschedule( - PostExecutionControl

postExecutionControl, - BaseControl baseControl) { + PostExecutionControl

postExecutionControl, BaseControl baseControl) { baseControl.getScheduleDelay().ifPresent(postExecutionControl::withReSchedule); } - private PostExecutionControl

handleCleanup(P resourceForExecution, - P originalResource, Context

context) { + private PostExecutionControl

handleCleanup( + P resourceForExecution, P originalResource, Context

context) { if (log.isDebugEnabled()) { log.debug( "Executing delete for resource: {} with version: {}", @@ -250,23 +269,30 @@ private PostExecutionControl

handleCleanup(P resourceForExecution, // cleanup is finished, nothing left to be done final var finalizerName = configuration().getFinalizerName(); if (deleteControl.isRemoveFinalizer() && resourceForExecution.hasFinalizer(finalizerName)) { - P customResource = conflictRetryingPatch(resourceForExecution, originalResource, r -> { - // the operator might not be allowed to retrieve the resource on a retry, e.g. when its - // permissions are removed by deleting the namespace concurrently - if (r == null) { - log.warn( - "Could not remove finalizer on null resource: {} with version: {}", - getUID(resourceForExecution), - getVersion(resourceForExecution)); - return false; - } - return r.removeFinalizer(finalizerName); - }, true); + P customResource = + conflictRetryingPatch( + resourceForExecution, + originalResource, + r -> { + // the operator might not be allowed to retrieve the resource on a retry, e.g. + // when its + // permissions are removed by deleting the namespace concurrently + if (r == null) { + log.warn( + "Could not remove finalizer on null resource: {} with version: {}", + getUID(resourceForExecution), + getVersion(resourceForExecution)); + return false; + } + return r.removeFinalizer(finalizerName); + }, + true); return PostExecutionControl.customResourceFinalizerRemoved(customResource); } } log.debug( - "Skipping finalizer remove for resource: {} with version: {}. delete control: {}, uses finalizer: {}", + "Skipping finalizer remove for resource: {} with version: {}. delete control: {}, uses" + + " finalizer: {}", getUID(resourceForExecution), getVersion(resourceForExecution), deleteControl, @@ -280,7 +306,8 @@ private PostExecutionControl

handleCleanup(P resourceForExecution, private P addFinalizerWithSSA(P originalResource) { log.debug( "Adding finalizer (using SSA) for resource: {} version: {}", - getUID(originalResource), getVersion(originalResource)); + getUID(originalResource), + getVersion(originalResource)); try { P resource = (P) originalResource.getClass().getConstructor().newInstance(); ObjectMeta objectMeta = new ObjectMeta(); @@ -289,26 +316,32 @@ private P addFinalizerWithSSA(P originalResource) { resource.setMetadata(objectMeta); resource.addFinalizer(configuration().getFinalizerName()); return customResourceFacade.patchResourceWithSSA(resource); - } catch (InstantiationException | IllegalAccessException | InvocationTargetException + } catch (InstantiationException + | IllegalAccessException + | InvocationTargetException | NoSuchMethodException e) { - throw new RuntimeException("Issue with creating custom resource instance with reflection." + - " Custom Resources must provide a no-arg constructor. Class: " - + originalResource.getClass().getName(), + throw new RuntimeException( + "Issue with creating custom resource instance with reflection." + + " Custom Resources must provide a no-arg constructor. Class: " + + originalResource.getClass().getName(), e); } } private P updateCustomResourceWithFinalizer(P resourceForExecution, P originalResource) { log.debug( - "Adding finalizer for resource: {} version: {}", getUID(originalResource), + "Adding finalizer for resource: {} version: {}", + getUID(originalResource), getVersion(originalResource)); - return conflictRetryingPatch(resourceForExecution, originalResource, - r -> r.addFinalizer(configuration().getFinalizerName()), false); + return conflictRetryingPatch( + resourceForExecution, + originalResource, + r -> r.addFinalizer(configuration().getFinalizerName()), + false); } private P patchResource(P resource, P originalResource) { - log.debug("Updating resource: {} with version: {}", getUID(resource), - getVersion(resource)); + log.debug("Updating resource: {} with version: {}", getUID(resource), getVersion(resource)); log.trace("Resource before update: {}", resource); final var finalizerName = configuration().getFinalizerName(); @@ -323,8 +356,11 @@ ControllerConfiguration

configuration() { return controller.getConfiguration(); } - public P conflictRetryingPatch(P resource, P originalResource, - Function modificationFunction, boolean forceNotUseSSA) { + public P conflictRetryingPatch( + P resource, + P originalResource, + Function modificationFunction, + boolean forceNotUseSSA) { if (log.isDebugEnabled()) { log.debug("Conflict retrying update for: {}", ResourceID.fromResource(resource)); } @@ -343,18 +379,30 @@ public P conflictRetryingPatch(P resource, P originalResource, } catch (KubernetesClientException e) { log.trace("Exception during patch for resource: {}", resource); retryIndex++; - // only retry on conflict (HTTP 409), otherwise fail - if (e.getCode() != 409) { + // only retry on conflict (409) and unprocessable content (422) which + // can happen if JSON Patch is not a valid request since there was + // a concurrent request which already removed another finalizer: + // List element removal from a list is by index in JSON Patch + // so if addressing a second finalizer but first is meanwhile removed + // it is a wrong request. + if (e.getCode() != 409 && e.getCode() != 422) { throw e; } if (retryIndex >= MAX_UPDATE_RETRY) { throw new OperatorException( - "Exceeded maximum (" + MAX_UPDATE_RETRY + "Exceeded maximum (" + + MAX_UPDATE_RETRY + ") retry attempts to patch resource: " + ResourceID.fromResource(resource)); } - resource = customResourceFacade.getResource(resource.getMetadata().getNamespace(), - resource.getMetadata().getName()); + log.debug( + "Retrying patch for resource name: {}, namespace: {}; HTTP code: {}", + resource.getMetadata().getName(), + resource.getMetadata().getNamespace(), + e.getCode()); + resource = + customResourceFacade.getResource( + resource.getMetadata().getNamespace(), resource.getMetadata().getName()); } } } @@ -368,11 +416,11 @@ static class CustomResourceFacade { private final Cloner cloner; public CustomResourceFacade( - MixedOperation, Resource> resourceOperation, - ControllerConfiguration configuration, Cloner cloner) { + MixedOperation, Resource> resourceOperation, + ControllerConfiguration configuration, + Cloner cloner) { this.resourceOperation = resourceOperation; - this.useSSA = - configuration.getConfigurationService().useSSAToPatchPrimaryResource(); + this.useSSA = configuration.getConfigurationService().useSSAToPatchPrimaryResource(); this.fieldManager = configuration.fieldManager(); this.cloner = cloner; } @@ -410,11 +458,13 @@ public R patchStatus(R resource, R originalResource) { try { resource.getMetadata().setManagedFields(null); var res = resource(resource); - return res.subresource("status").patch(new PatchContext.Builder() - .withFieldManager(fieldManager) - .withForce(true) - .withPatchType(PatchType.SERVER_SIDE_APPLY) - .build()); + return res.subresource("status") + .patch( + new PatchContext.Builder() + .withFieldManager(fieldManager) + .withForce(true) + .withPatchType(PatchType.SERVER_SIDE_APPLY) + .build()); } finally { resource.getMetadata().setManagedFields(managedFields); } @@ -441,17 +491,19 @@ private R editStatus(R resource, R originalResource) { } public R patchResourceWithSSA(R resource) { - return resource(resource).patch(new PatchContext.Builder() - .withFieldManager(fieldManager) - .withForce(true) - .withPatchType(PatchType.SERVER_SIDE_APPLY) - .build()); + return resource(resource) + .patch( + new PatchContext.Builder() + .withFieldManager(fieldManager) + .withForce(true) + .withPatchType(PatchType.SERVER_SIDE_APPLY) + .build()); } private Resource resource(R resource) { - return resource instanceof Namespaced ? resourceOperation - .inNamespace(resource.getMetadata().getNamespace()) - .resource(resource) : resourceOperation.resource(resource); + return resource instanceof Namespaced + ? resourceOperation.inNamespace(resource.getMetadata().getNamespace()).resource(resource) + : resourceOperation.resource(resource); } } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java index 071dd49f29..5354cad09e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceID.java @@ -10,14 +10,13 @@ public class ResourceID implements Serializable { public static ResourceID fromResource(HasMetadata resource) { - return new ResourceID(resource.getMetadata().getName(), - resource.getMetadata().getNamespace()); + return new ResourceID(resource.getMetadata().getName(), resource.getMetadata().getNamespace()); } - public static ResourceID fromOwnerReference(HasMetadata resource, OwnerReference ownerReference, - boolean clusterScoped) { - return new ResourceID(ownerReference.getName(), - clusterScoped ? null : resource.getMetadata().getNamespace()); + public static ResourceID fromOwnerReference( + HasMetadata resource, OwnerReference ownerReference, boolean clusterScoped) { + return new ResourceID( + ownerReference.getName(), clusterScoped ? null : resource.getMetadata().getNamespace()); } private final String name; @@ -42,19 +41,16 @@ public Optional getNamespace() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; ResourceID that = (ResourceID) o; - return Objects.equals(name, that.name) && Objects.equals(namespace, - that.namespace); + return Objects.equals(name, that.name) && Objects.equals(namespace, that.namespace); } public boolean isSameResource(HasMetadata hasMetadata) { final var metadata = hasMetadata.getMetadata(); - return getName().equals(metadata.getName()) && - getNamespace().map(ns -> ns.equals(metadata.getNamespace())).orElse(true); + return getName().equals(metadata.getName()) + && getNamespace().map(ns -> ns.equals(metadata.getNamespace())).orElse(true); } @Override @@ -72,10 +68,6 @@ public static String toString(HasMetadata resource) { } private static String toString(String name, String namespace) { - return "ResourceID{" + - "name='" + name + '\'' + - ", namespace='" + namespace + '\'' + - '}'; + return "ResourceID{" + "name='" + name + '\'' + ", namespace='" + namespace + '\'' + '}'; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceState.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceState.java index d52c1b19ec..5d4e74d681 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceState.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceState.java @@ -15,14 +15,11 @@ class ResourceState { * for cleanup. */ private enum EventingState { - EVENT_PRESENT, NO_EVENT_PRESENT, - /** - * Resource has been marked for deletion, and cleanup already executed successfully - */ + EVENT_PRESENT, + NO_EVENT_PRESENT, + /** Resource has been marked for deletion, and cleanup already executed successfully */ PROCESSED_MARK_FOR_DELETION, - /** - * Delete event present, from this point other events are not relevant - */ + /** Delete event present, from this point other events are not relevant */ DELETE_EVENT_PRESENT, } @@ -114,12 +111,17 @@ public void unMarkEventReceived() { @Override public String toString() { - return "ResourceState{" + - "id=" + id + - ", underProcessing=" + underProcessing + - ", retry=" + retry + - ", eventing=" + eventing + - ", rateLimit=" + rateLimit + - '}'; + return "ResourceState{" + + "id=" + + id + + ", underProcessing=" + + underProcessing + + ", retry=" + + retry + + ", eventing=" + + eventing + + ", rateLimit=" + + rateLimit + + '}'; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java index 4e03d1a34e..6932e1ca5e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManager.java @@ -11,7 +11,6 @@ class ResourceStateManager { // take time and memory? private final Map states = new ConcurrentHashMap<>(100); - public ResourceState getOrCreate(ResourceID resourceID) { return states.computeIfAbsent(resourceID, ResourceState::new); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiter.java index 2692b60bd0..9ebea4f081 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiter.java @@ -6,14 +6,13 @@ import io.javaoperatorsdk.operator.api.config.AnnotationConfigurable; -/** - * A simple rate limiter that limits the number of permission for a time interval. - */ +/** A simple rate limiter that limits the number of permission for a time interval. */ public class LinearRateLimiter implements RateLimiter, AnnotationConfigurable { /** To turn off rate limiting set limit for period to a non-positive number */ public static final int NO_LIMIT_PERIOD = -1; + public static final int DEFAULT_REFRESH_PERIOD_SECONDS = 10; public static final Duration DEFAULT_REFRESH_PERIOD = Duration.ofSeconds(DEFAULT_REFRESH_PERIOD_SECONDS); @@ -43,7 +42,8 @@ public Optional isLimited(RateLimitState rateLimitState) { if (actualState.getCount() < limitForPeriod) { actualState.increaseCount(); return Optional.empty(); - } else if (actualState.getLastRefreshTime() + } else if (actualState + .getLastRefreshTime() .isBefore(LocalDateTime.now().minus(refreshPeriod))) { actualState.reset(); actualState.increaseCount(); @@ -60,8 +60,7 @@ public RateState initState() { @Override public void initFrom(RateLimited configuration) { - this.refreshPeriod = Duration.of(configuration.within(), - configuration.unit().toChronoUnit()); + this.refreshPeriod = Duration.of(configuration.within(), configuration.unit().toChronoUnit()); this.limitForPeriod = configuration.maxReconciliations(); } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/RateLimiter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/RateLimiter.java index 7708e6dfe3..c856ca2197 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/RateLimiter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/rate/RateLimiter.java @@ -6,13 +6,12 @@ import io.javaoperatorsdk.operator.processing.event.rate.RateLimiter.RateLimitState; public interface RateLimiter { - interface RateLimitState { - } + interface RateLimitState {} /** * @param rateLimitState state implementation * @return empty if permission acquired or minimal duration until a permission could be acquired - * again + * again */ Optional isLimited(RateLimitState rateLimitState); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java index a2306378d4..fc27e79124 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSource.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing.event.source; - import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.processing.event.EventHandler; @@ -80,18 +79,15 @@ public void setOnAddFilter(OnAddFilter onAddFilter) { this.onAddFilter = onAddFilter; } - public void setOnUpdateFilter( - OnUpdateFilter onUpdateFilter) { + public void setOnUpdateFilter(OnUpdateFilter onUpdateFilter) { this.onUpdateFilter = onUpdateFilter; } - public void setOnDeleteFilter( - OnDeleteFilter onDeleteFilter) { + public void setOnDeleteFilter(OnDeleteFilter onDeleteFilter) { this.onDeleteFilter = onDeleteFilter; } public void setGenericFilter(GenericFilter genericFilter) { this.genericFilter = genericFilter; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java index d290e15496..a77e6448cb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/CacheKeyMapper.java @@ -14,5 +14,4 @@ public interface CacheKeyMapper { static CacheKeyMapper singleResourceCacheKeyMapper() { return r -> "id"; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java index 850a9deb35..cefe35f6dd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSource.java @@ -20,7 +20,7 @@ * * @param the resource type that this EventSource is associated with * @param

the primary resource type which reconciler needs to be triggered when events occur on - * resources of type R + * resources of type R */ public interface EventSource extends LifecycleAware, EventSourceHealthIndicator { @@ -67,7 +67,7 @@ default EventSourceStartPriority priority() { * Retrieves the optional unique secondary resource associated with the specified primary * resource. Note that this operation will fail if multiple resources are associated with the * specified primary resource. - * + * * @param primary the primary resource for which the secondary resource is requested * @return the secondary resource associated with the specified primary resource * @throws IllegalStateException if multiple resources are associated with the primary one @@ -86,7 +86,7 @@ default Optional getSecondaryResource(P primary) { /** * Retrieves a potential empty set of resources tracked by this EventSource associated with the * specified primary resource - * + * * @param primary the primary resource for which the secondary resource is requested * @return the set of secondary resources associated with the specified primary */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java index 8284d611f2..4971b51829 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/EventSourceStartPriority.java @@ -12,16 +12,15 @@ public enum EventSourceStartPriority { * In this situation, it is needed to initialize this event source before the one associated with * resources which state is being tracked since that state information might be required to * properly retrieve the other resources. - * - *

- * For example a {@code ConfigMap} could store the identifier of a fictional external resource + * + *

For example a {@code ConfigMap} could store the identifier of a fictional external resource * {@code A}. In this case, the event source tracking {@code A} resources might need the * identifier from the {@code ConfigMap} to identify and check the state of {@code A} resources. * This is usually needed before any reconciliation occurs and the only way to ensure the proper * behavior in this case is to make sure that the event source tracking the {@code ConfigMaps} (in * this example) is started/cache-synced before the event source for {@code A} resources gets * started. - *

*/ - RESOURCE_STATE_LOADER, DEFAULT + RESOURCE_STATE_LOADER, + DEFAULT } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java index 130e3db179..efaf3232b1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSource.java @@ -23,12 +23,12 @@ /** * Handles caching and related operation of external event sources. It can handle multiple secondary * resources for a single primary resources. - *

- * There are two related concepts to understand: + * + *

There are two related concepts to understand: + * *

    - *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the - * cache
  • - *
  • Object equals usage - compares if the two resources are the same or same version.
  • + *
  • CacheKeyMapper - maps/extracts a key used to reference the associated resource in the cache + *
  • Object equals usage - compares if the two resources are the same or same version. *
* * When a resource is added for a primary resource its key is used to put in a map. Equals is used @@ -49,13 +49,13 @@ public abstract class ExternalResourceCachingEventSource> cache = new ConcurrentHashMap<>(); - protected ExternalResourceCachingEventSource(Class resourceClass, - CacheKeyMapper cacheKeyMapper) { + protected ExternalResourceCachingEventSource( + Class resourceClass, CacheKeyMapper cacheKeyMapper) { this(null, resourceClass, cacheKeyMapper); } - protected ExternalResourceCachingEventSource(String name, Class resourceClass, - CacheKeyMapper cacheKeyMapper) { + protected ExternalResourceCachingEventSource( + String name, Class resourceClass, CacheKeyMapper cacheKeyMapper) { super(resourceClass, name); this.cacheKeyMapper = cacheKeyMapper; } @@ -68,8 +68,8 @@ protected synchronized void handleDelete(ResourceID primaryID) { } protected synchronized void handleDeletes(ResourceID primaryID, Set resource) { - handleDelete(primaryID, - resource.stream().map(cacheKeyMapper::keyFor).collect(Collectors.toSet())); + handleDelete( + primaryID, resource.stream().map(cacheKeyMapper::keyFor).collect(Collectors.toSet())); } protected synchronized void handleDelete(ResourceID primaryID, R resource) { @@ -81,9 +81,12 @@ protected synchronized void handleDelete(ResourceID primaryID, Set resou return; } var cachedValues = cache.get(primaryID); - List removedResources = cachedValues == null ? Collections.emptyList() - : resourceIDs.stream() - .flatMap(id -> Stream.ofNullable(cachedValues.remove(id))).collect(Collectors.toList()); + List removedResources = + cachedValues == null + ? Collections.emptyList() + : resourceIDs.stream() + .flatMap(id -> Stream.ofNullable(cachedValues.remove(id))) + .collect(Collectors.toList()); if (cachedValues != null && cachedValues.isEmpty()) { cache.remove(primaryID); @@ -107,10 +110,10 @@ protected synchronized void handleResources(Map> allNewResour allNewResources.forEach(this::handleResources); } - protected synchronized void handleResources(ResourceID primaryID, Set newResources, - boolean propagateEvent) { - log.debug("Handling resources update for: {} numberOfResources: {} ", primaryID, - newResources.size()); + protected synchronized void handleResources( + ResourceID primaryID, Set newResources, boolean propagateEvent) { + log.debug( + "Handling resources update for: {} numberOfResources: {} ", primaryID, newResources.size()); if (!isRunning()) { return; } @@ -121,21 +124,22 @@ protected synchronized void handleResources(ResourceID primaryID, Set newReso var newResourcesMap = newResources.stream().collect(Collectors.toMap(cacheKeyMapper::keyFor, r -> r)); cache.put(primaryID, newResourcesMap); - if (propagateEvent && !newResourcesMap.equals(cachedResources) + if (propagateEvent + && !newResourcesMap.equals(cachedResources) && acceptedByFiler(cachedResources, newResourcesMap)) { getEventHandler().handleEvent(new Event(primaryID)); } } - private boolean acceptedByFiler(Map cachedResourceMap, - Map newResourcesMap) { + private boolean acceptedByFiler( + Map cachedResourceMap, Map newResourcesMap) { var addedResources = new HashMap<>(newResourcesMap); addedResources.keySet().removeAll(cachedResourceMap.keySet()); if (onAddFilter != null || genericFilter != null) { var anyAddAccepted = - addedResources.values().stream().anyMatch(r -> acceptedByGenericFiler(r) && - onAddFilter.accept(r)); + addedResources.values().stream() + .anyMatch(r -> acceptedByGenericFiler(r) && onAddFilter.accept(r)); if (anyAddAccepted) { return true; } @@ -158,21 +162,20 @@ private boolean acceptedByFiler(Map cachedResourceMap, Map possibleUpdatedResources = new HashMap<>(cachedResourceMap); possibleUpdatedResources.keySet().retainAll(newResourcesMap.keySet()); - possibleUpdatedResources = possibleUpdatedResources.entrySet().stream() - .filter(entry -> !newResourcesMap - .get(entry.getKey()).equals(entry.getValue())) - .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + possibleUpdatedResources = + possibleUpdatedResources.entrySet().stream() + .filter(entry -> !newResourcesMap.get(entry.getKey()).equals(entry.getValue())) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); if (onUpdateFilter != null || genericFilter != null) { return possibleUpdatedResources.entrySet().stream() .anyMatch( entry -> { var newResource = newResourcesMap.get(entry.getKey()); - return acceptedByGenericFiler(newResource) && - onUpdateFilter.accept(newResource, entry.getValue()); + return acceptedByGenericFiler(newResource) + && onUpdateFilter.accept(newResource, entry.getValue()); }); - } else - return !possibleUpdatedResources.isEmpty(); + } else return !possibleUpdatedResources.isEmpty(); } private boolean acceptedByGenericFiler(R resource) { diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/PrimaryToSecondaryMapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/PrimaryToSecondaryMapper.java index 2ff61f81e8..0e13293886 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/PrimaryToSecondaryMapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/PrimaryToSecondaryMapper.java @@ -7,31 +7,27 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; /** - *

* Identifies the set of secondary resources associated with a given primary resource. This is * typically needed when multiple secondary resources can be associated with one or several multiple * primary resources *without* a standard way (e.g. owner reference or annotations) to materialize * that relations. When owner references are present, a {@code PrimaryToSecondaryMapper} instance - * should not be needed. In other words, associating such a mapper with your - * {@link InformerEventSourceConfiguration} is usually needed when your secondary resources are - * referenced in some way by your primary resource but that this link does not exist in the - * secondary resource information. The mapper implementation instructs the SDK on how to find all - * the secondary resources associated with a given primary resource so that this primary resource - * can properly be reconciled when changes impact the associated secondary resources, even though - * these don't contain any information allowing to make such an inference. - *

- *

- * This helps particularly in cases where several secondary resources, listed in some way in the + * should not be needed. In other words, associating such a mapper with your {@link + * InformerEventSourceConfiguration} is usually needed when your secondary resources are referenced + * in some way by your primary resource but that this link does not exist in the secondary resource + * information. The mapper implementation instructs the SDK on how to find all the secondary + * resources associated with a given primary resource so that this primary resource can properly be + * reconciled when changes impact the associated secondary resources, even though these don't + * contain any information allowing to make such an inference. + * + *

This helps particularly in cases where several secondary resources, listed in some way in the * primary resource, need to or can be created before the primary resource exists. In that - * situation, attempting to retrieve the associated secondary resources by calling - * {@link io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResource(Class)} would fail + * situation, attempting to retrieve the associated secondary resources by calling {@link + * io.javaoperatorsdk.operator.api.reconciler.Context#getSecondaryResource(Class)} would fail * without providing a mapper to tell JOSDK how to retrieve the secondary resources. - *

- *

- * You can see an example of this in action in the You can see an example of this in action in the Reconciler * for the PrimaryToSecondaryIT integration tests that handles many-to-many relationship. - *

* * @param

primary resource type */ diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventAware.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventAware.java index dcb15a4229..9ff14d83e0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventAware.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/ResourceEventAware.java @@ -9,5 +9,4 @@ default void onResourceCreated(T resource) {} default void onResourceUpdated(T newResource, T oldResource) {} default void onResourceDeleted(T resource) {} - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java index 1651d44dc7..4c6deb82b1 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedCache.java @@ -7,5 +7,4 @@ public interface BoundedCache { R remove(K key); void put(K key, R object); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java index 4f0fcad280..f3f0c8e1a0 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStore.java @@ -17,8 +17,7 @@ import io.fabric8.kubernetes.client.informers.cache.ItemStore; import io.javaoperatorsdk.operator.api.config.Utils; -public class BoundedItemStore - implements ItemStore { +public class BoundedItemStore implements ItemStore { private static final Logger log = LoggerFactory.getLogger(BoundedItemStore.class); @@ -28,13 +27,17 @@ public class BoundedItemStore private final Map existingMinimalResources = new ConcurrentHashMap<>(); private final Constructor resourceConstructor; - public BoundedItemStore(BoundedCache cache, Class resourceClass, - KubernetesClient client) { - this(cache, resourceClass, namespaceKeyFunc(), + public BoundedItemStore( + BoundedCache cache, Class resourceClass, KubernetesClient client) { + this( + cache, + resourceClass, + namespaceKeyFunc(), new KubernetesResourceFetcher<>(resourceClass, client)); } - public BoundedItemStore(BoundedCache cache, + public BoundedItemStore( + BoundedCache cache, Class resourceClass, Function keyFunction, ResourceFetcher resourceFetcher) { @@ -61,11 +64,12 @@ private R createMinimalResource(R obj) { try { R minimal = resourceConstructor.newInstance(); final var metadata = obj.getMetadata(); - minimal.setMetadata(new ObjectMetaBuilder() - .withName(metadata.getName()) - .withNamespace(metadata.getNamespace()) - .withResourceVersion(metadata.getResourceVersion()) - .build()); + minimal.setMetadata( + new ObjectMetaBuilder() + .withName(metadata.getName()) + .withNamespace(metadata.getNamespace()) + .withResourceVersion(metadata.getResourceVersion()) + .build()); return minimal; } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { throw new IllegalStateException(e); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java index ad996afc36..cd82f50a22 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcher.java @@ -17,9 +17,8 @@ public KubernetesResourceFetcher(Class rClass, KubernetesClient client) { this(rClass, client, inverseNamespaceKeyFunction()); } - public KubernetesResourceFetcher(Class rClass, - KubernetesClient client, - Function resourceIDFunction) { + public KubernetesResourceFetcher( + Class rClass, KubernetesClient client, Function resourceIDFunction) { this.rClass = rClass; this.client = client; this.resourceIDFunction = resourceIDFunction; @@ -28,8 +27,9 @@ public KubernetesResourceFetcher(Class rClass, @Override public R fetchResource(String key) { var resourceId = resourceIDFunction.apply(key); - return resourceId.getNamespace().map(ns -> client.resources(rClass).inNamespace(ns) - .withName(resourceId.getName()).get()) + return resourceId + .getNamespace() + .map(ns -> client.resources(rClass).inNamespace(ns).withName(resourceId.getName()).get()) .orElse(client.resources(rClass).withName(resourceId.getName()).get()); } @@ -43,5 +43,4 @@ public static Function inverseNamespaceKeyFunction() { } }; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java index 9cc4fe7a35..702b9efdb9 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/cache/ResourceFetcher.java @@ -3,5 +3,4 @@ public interface ResourceFetcher { R fetchResource(K key); - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSource.java index 07e5bd3fa2..eb9f65eafc 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSource.java @@ -37,8 +37,7 @@ public ControllerEventSource(Controller controller) { final var config = controller.getConfiguration(); OnUpdateFilter internalOnUpdateFilter = - onUpdateFinalizerNeededAndApplied(controller.useFinalizer(), - config.getFinalizerName()) + onUpdateFinalizerNeededAndApplied(controller.useFinalizer(), config.getFinalizerName()) .or(onUpdateGenerationAware(config.isGenerationAware())) .or(onUpdateMarkedForDeletion()); @@ -46,7 +45,8 @@ public ControllerEventSource(Controller controller) { final var informerConfig = config.getInformerConfig(); Optional.ofNullable(informerConfig.getOnAddFilter()).ifPresent(this::setOnAddFilter); Optional.ofNullable(informerConfig.getOnUpdateFilter()) - .ifPresentOrElse(filter -> setOnUpdateFilter(filter.and(internalOnUpdateFilter)), + .ifPresentOrElse( + filter -> setOnUpdateFilter(filter.and(internalOnUpdateFilter)), () -> setOnUpdateFilter(internalOnUpdateFilter)); Optional.ofNullable(informerConfig.getGenericFilter()).ifPresent(this::setGenericFilter); setControllerConfiguration(config); @@ -65,19 +65,21 @@ public synchronized void start() { public void eventReceived(ResourceAction action, T resource, T oldResource) { try { if (log.isDebugEnabled()) { - log.debug("Event received for resource: {} version: {} uuid: {} action: {}", + log.debug( + "Event received for resource: {} version: {} uuid: {} action: {}", ResourceID.fromResource(resource), - getVersion(resource), resource.getMetadata().getUid(), action); + getVersion(resource), + resource.getMetadata().getUid(), + action); log.trace("Event Old resource: {},\n new resource: {}", oldResource, resource); } MDCUtils.addResourceInfo(resource); controller.getEventSourceManager().broadcastOnResourceEvent(action, resource, oldResource); if (isAcceptedByFilters(action, resource, oldResource)) { - getEventHandler().handleEvent( - new ResourceEvent(action, ResourceID.fromResource(resource), resource)); + getEventHandler() + .handleEvent(new ResourceEvent(action, ResourceID.fromResource(resource), resource)); } else { - log.debug("Skipping event handling resource {}", - ResourceID.fromResource(resource)); + log.debug("Skipping event handling resource {}", ResourceID.fromResource(resource)); } } finally { MDCUtils.removeResourceInfo(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/InternalEventFilters.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/InternalEventFilters.java index b7c4249411..3b87778b2b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/InternalEventFilters.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/InternalEventFilters.java @@ -11,8 +11,8 @@ static OnUpdateFilter onUpdateMarkedForDeletion() { // the old resource is checked since in corner cases users might still want to update the status // for a resource that is marked for deletion - return (newResource, oldResource) -> !oldResource.isMarkedForDeletion() - && newResource.isMarkedForDeletion(); + return (newResource, oldResource) -> + !oldResource.isMarkedForDeletion() && newResource.isMarkedForDeletion(); } static OnUpdateFilter onUpdateGenerationAware( @@ -27,14 +27,12 @@ static OnUpdateFilter onUpdateGenerationAware( return true; } - return oldResource.getMetadata().getGeneration() < newResource - .getMetadata().getGeneration(); + return oldResource.getMetadata().getGeneration() < newResource.getMetadata().getGeneration(); }; } static OnUpdateFilter onUpdateFinalizerNeededAndApplied( - boolean useFinalizer, - String finalizerName) { + boolean useFinalizer, String finalizerName) { return (newResource, oldResource) -> { if (useFinalizer) { boolean oldFinalizer = oldResource.hasFinalizer(finalizerName); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceAction.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceAction.java index 7a04dc9164..d1dbcb9e1b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceAction.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceAction.java @@ -1,5 +1,7 @@ package io.javaoperatorsdk.operator.processing.event.source.controller; public enum ResourceAction { - ADDED, UPDATED, DELETED + ADDED, + UPDATED, + DELETED } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEvent.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEvent.java index ba9ea72cd9..f97cedf7f5 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEvent.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/controller/ResourceEvent.java @@ -12,8 +12,7 @@ public class ResourceEvent extends Event { private final ResourceAction action; private final HasMetadata resource; - public ResourceEvent(ResourceAction action, - ResourceID resourceID, HasMetadata resource) { + public ResourceEvent(ResourceAction action, ResourceID resourceID, HasMetadata resource) { super(resourceID); this.action = action; this.resource = resource; @@ -21,10 +20,12 @@ public ResourceEvent(ResourceAction action, @Override public String toString() { - return "ResourceEvent{" + - "action=" + action + - ", associated resource id=" + getRelatedCustomResourceID() + - '}'; + return "ResourceEvent{" + + "action=" + + action + + ", associated resource id=" + + getRelatedCustomResourceID() + + '}'; } public ResourceAction getAction() { @@ -37,12 +38,9 @@ public Optional getResource() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - if (!super.equals(o)) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; ResourceEvent that = (ResourceEvent) o; return action == that.action; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnDeleteFilter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnDeleteFilter.java index 0a0f5955a7..1e87648425 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnDeleteFilter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnDeleteFilter.java @@ -6,17 +6,18 @@ public interface OnDeleteFilter { boolean accept(R hasMetadata, Boolean deletedFinalStateUnknown); default OnDeleteFilter and(OnDeleteFilter OnDeleteFilter) { - return (resource, deletedFinalStateUnknown) -> this.accept(resource, deletedFinalStateUnknown) - && OnDeleteFilter.accept(resource, deletedFinalStateUnknown); + return (resource, deletedFinalStateUnknown) -> + this.accept(resource, deletedFinalStateUnknown) + && OnDeleteFilter.accept(resource, deletedFinalStateUnknown); } default OnDeleteFilter or(OnDeleteFilter OnDeleteFilter) { - return (resource, deletedFinalStateUnknown) -> this.accept(resource, deletedFinalStateUnknown) - || OnDeleteFilter.accept(resource, deletedFinalStateUnknown); + return (resource, deletedFinalStateUnknown) -> + this.accept(resource, deletedFinalStateUnknown) + || OnDeleteFilter.accept(resource, deletedFinalStateUnknown); } default OnDeleteFilter not() { return (resource, deletedFinalStateUnknown) -> !this.accept(resource, deletedFinalStateUnknown); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnUpdateFilter.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnUpdateFilter.java index 0e7fdab276..dec1d9be6e 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnUpdateFilter.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/filter/OnUpdateFilter.java @@ -6,17 +6,16 @@ public interface OnUpdateFilter { boolean accept(R newResource, R oldResource); default OnUpdateFilter and(OnUpdateFilter onUpdateFilter) { - return (newResource, oldResource) -> this.accept(newResource, oldResource) - && onUpdateFilter.accept(newResource, oldResource); + return (newResource, oldResource) -> + this.accept(newResource, oldResource) && onUpdateFilter.accept(newResource, oldResource); } default OnUpdateFilter or(OnUpdateFilter onUpdateFilter) { - return (newResource, oldResource) -> this.accept(newResource, oldResource) - || onUpdateFilter.accept(newResource, oldResource); + return (newResource, oldResource) -> + this.accept(newResource, oldResource) || onUpdateFilter.accept(newResource, oldResource); } default OnUpdateFilter not() { return (newResource, oldResource) -> !this.accept(newResource, oldResource); } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java index 450e308904..6c3ebf6916 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/inbound/CachingInboundEventSource.java @@ -12,14 +12,14 @@ import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; public class CachingInboundEventSource - extends ExternalResourceCachingEventSource - implements ResourceEventAware

{ + extends ExternalResourceCachingEventSource implements ResourceEventAware

{ private final ResourceFetcher resourceFetcher; private final Set fetchedForPrimaries = ConcurrentHashMap.newKeySet(); public CachingInboundEventSource( - ResourceFetcher resourceFetcher, Class resourceClass, + ResourceFetcher resourceFetcher, + Class resourceClass, CacheKeyMapper cacheKeyMapper) { super(resourceClass, cacheKeyMapper); this.resourceFetcher = resourceFetcher; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java index 48534a27b3..b52dc278f2 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSource.java @@ -13,6 +13,7 @@ import io.fabric8.kubernetes.client.dsl.MixedOperation; import io.fabric8.kubernetes.client.informers.ResourceEventHandler; import io.javaoperatorsdk.operator.api.config.informer.InformerEventSourceConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; import io.javaoperatorsdk.operator.processing.event.Event; import io.javaoperatorsdk.operator.processing.event.EventHandler; @@ -20,57 +21,45 @@ import io.javaoperatorsdk.operator.processing.event.source.PrimaryToSecondaryMapper; /** - *

- * Wraps informer(s) so it is connected to the eventing system of the framework. Note that since - * it's it is built on top of Informers, it also support caching resources using caching from - * fabric8 client Informer caches and additional caches described below. - *

- *

- * InformerEventSource also supports two features to better handle events and caching of resources - * on top of Informers from fabric8 Kubernetes client. These two features implementation wise are - * related to each other: - *

- *
- *

- * 1. API that allows to make sure the cache contains the fresh resource after an update. This is - * important for {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} and - * mainly for - * {@link io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource} - * so after reconcile if getResource() called always return the fresh resource. To achieve this - * handleRecentResourceUpdate() and handleRecentResourceCreate() needs to be called explicitly after - * resource created/updated using the kubernetes client. (These calls are done automatically by - * KubernetesDependentResource implementation.). In the background this will store the new resource - * in a temporary cache {@link TemporaryResourceCache} which do additional checks. After a new event - * is received the cachec object is removed from this cache, since in general then it is already in - * the cache of informer. - *

- *
- *

- * 2. Additional API is provided that is meant to be used with the combination of the previous one, - * and the goal is to filter out events that are the results of updates and creates made by the - * controller itself. For example if in reconciler a ConfigMaps is created, there should be an - * Informer in place to handle change events of that ConfigMap, but since it has bean created (or - * updated) by the reconciler this should not trigger an additional reconciliation by default. In - * order to achieve this prepareForCreateOrUpdateEventFiltering(..) method needs to be called before - * the operation of the k8s client. And the operation from point 1. after the k8s client call. See - * it's usage in CreateUpdateEventFilterTestReconciler integration test for the usage. (Again this - * is managed for the developer if using dependent resources.)
- * Roughly it works in a way that before the K8S API call is made, we set mark the resource ID, and - * from that point informer won't propagate events further just will start record them. After the - * client operation is done, it's checked and analysed what events were received and based on that - * it will propagate event or not and/or put the new resource into the temporal cache - so if the - * event not arrived yet about the update will be able to filter it in the future. - *

+ * Wraps informer(s) so they are connected to the eventing system of the framework. Note that since + * this is built on top of Fabric8 client Informers, it also supports caching resources using + * caching from informer caches as well as additional caches described below. * - * @param resource type watching - * @param

type of the primary resource + *

InformerEventSource also supports two features to better handle events and caching of + * resources on top of Informers from the Fabric8 Kubernetes client. These two features are related + * to each other as follows: + * + *

    + *
  1. Ensuring the cache contains the fresh resource after an update. This is important for + * {@link io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource} and mainly + * for {@link + * io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource} so + * that {@link + * io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource#getSecondaryResource(HasMetadata, + * Context)} always returns the latest version of the resource after a reconciliation. To + * achieve this {@link #handleRecentResourceUpdate(ResourceID, HasMetadata, HasMetadata)} and + * {@link #handleRecentResourceCreate(ResourceID, HasMetadata)} need to be called explicitly + * after a resource is created or updated using the kubernetes client. These calls are done + * automatically by the KubernetesDependentResource implementation. In the background this + * will store the new resource in a temporary cache {@link TemporaryResourceCache} which does + * additional checks. After a new event is received the cached object is removed from this + * cache, since it is then usually already in the informer cache. + *
  2. Avoiding unneeded reconciliations after resources are created or updated. This filters out + * events that are the results of updates and creates made by the controller itself because we + * typically don't want the associated informer to trigger an event causing a useless + * reconciliation (as the change originates from the reconciler itself). For the details see + * {@link #canSkipEvent(HasMetadata, HasMetadata, ResourceID)} and related usage. + *
+ * + * @param resource type being watched + * @param

type of the associated primary resource */ public class InformerEventSource extends ManagedInformerEventSource> implements ResourceEventHandler { - private static final Logger log = LoggerFactory.getLogger(InformerEventSource.class); public static final String PREVIOUS_ANNOTATION_KEY = "javaoperatorsdk.io/previous"; + private static final Logger log = LoggerFactory.getLogger(InformerEventSource.class); // we need direct control for the indexer to propagate the just update resource also to the index private final PrimaryToSecondaryIndex primaryToSecondaryIndex; private final PrimaryToSecondaryMapper

primaryToSecondaryMapper; @@ -78,9 +67,12 @@ public class InformerEventSource public InformerEventSource( InformerEventSourceConfiguration configuration, EventSourceContext

context) { - this(configuration, + this( + configuration, configuration.getKubernetesClient().orElse(context.getClient()), - context.getControllerConfiguration().getConfigurationService() + context + .getControllerConfiguration() + .getConfigurationService() .parseResourceVersionsForEventFilteringAndCaching()); } @@ -89,14 +81,18 @@ public InformerEventSource( } @SuppressWarnings({"unchecked", "rawtypes"}) - private InformerEventSource(InformerEventSourceConfiguration configuration, + private InformerEventSource( + InformerEventSourceConfiguration configuration, KubernetesClient client, boolean parseResourceVersions) { - super(configuration.name(), - configuration.getGroupVersionKind() + super( + configuration.name(), + configuration + .getGroupVersionKind() .map(gvk -> client.genericKubernetesResources(gvk.apiVersion(), gvk.getKind())) .orElseGet(() -> (MixedOperation) client.resources(configuration.getResourceClass())), - configuration, parseResourceVersions); + configuration, + parseResourceVersions); // If there is a primary to secondary mapper there is no need for primary to secondary index. primaryToSecondaryMapper = configuration.getPrimaryToSecondaryMapper(); if (primaryToSecondaryMapper == null) { @@ -117,13 +113,15 @@ private InformerEventSource(InformerEventSourceConfiguration configuration, @Override public void onAdd(R newResource) { if (log.isDebugEnabled()) { - log.debug("On add event received for resource id: {} type: {} version: {}", + log.debug( + "On add event received for resource id: {} type: {} version: {}", ResourceID.fromResource(newResource), - resourceType().getSimpleName(), newResource.getMetadata().getResourceVersion()); + resourceType().getSimpleName(), + newResource.getMetadata().getResourceVersion()); } primaryToSecondaryIndex.onAddOrUpdate(newResource); - onAddOrUpdate(Operation.ADD, newResource, null, - () -> InformerEventSource.super.onAdd(newResource)); + onAddOrUpdate( + Operation.ADD, newResource, null, () -> InformerEventSource.super.onAdd(newResource)); } @Override @@ -137,14 +135,18 @@ public void onUpdate(R oldObject, R newObject) { oldObject.getMetadata().getResourceVersion()); } primaryToSecondaryIndex.onAddOrUpdate(newObject); - onAddOrUpdate(Operation.UPDATE, newObject, oldObject, + onAddOrUpdate( + Operation.UPDATE, + newObject, + oldObject, () -> InformerEventSource.super.onUpdate(oldObject, newObject)); } @Override public void onDelete(R resource, boolean b) { if (log.isDebugEnabled()) { - log.debug("On delete event received for resource id: {} type: {}", + log.debug( + "On delete event received for resource id: {} type: {}", ResourceID.fromResource(resource), resourceType().getSimpleName()); } @@ -155,13 +157,14 @@ public void onDelete(R resource, boolean b) { } } - private synchronized void onAddOrUpdate(Operation operation, R newObject, R oldObject, - Runnable superOnOp) { + private synchronized void onAddOrUpdate( + Operation operation, R newObject, R oldObject, Runnable superOnOp) { var resourceID = ResourceID.fromResource(newObject); if (canSkipEvent(newObject, oldObject, resourceID)) { log.debug( - "Skipping event propagation for {}, since was a result of a reconcile action. Resource ID: {}", + "Skipping event propagation for {}, since was a result of a reconcile action. Resource" + + " ID: {}", operation, ResourceID.fromResource(newObject)); superOnOp.run(); @@ -169,7 +172,8 @@ private synchronized void onAddOrUpdate(Operation operation, R newObject, R oldO superOnOp.run(); if (eventAcceptedByFilter(operation, newObject, oldObject)) { log.debug( - "Propagating event for {}, resource with same version not result of a reconciliation. Resource ID: {}", + "Propagating event for {}, resource with same version not result of a reconciliation." + + " Resource ID: {}", operation, resourceID); propagateEvent(newObject); @@ -187,10 +191,15 @@ private boolean canSkipEvent(R newObject, R oldObject, ResourceID resourceID) { if (res.isEmpty()) { return isEventKnownFromAnnotation(newObject, oldObject); } - boolean resVersionsEqual = newObject.getMetadata().getResourceVersion() - .equals(res.get().getMetadata().getResourceVersion()); - log.debug("Resource found in temporal cache for id: {} resource versions equal: {}", - resourceID, resVersionsEqual); + boolean resVersionsEqual = + newObject + .getMetadata() + .getResourceVersion() + .equals(res.get().getMetadata().getResourceVersion()); + log.debug( + "Resource found in temporal cache for id: {} resource versions equal: {}", + resourceID, + resVersionsEqual); return resVersionsEqual; } @@ -202,7 +211,8 @@ private boolean isEventKnownFromAnnotation(R newObject, R oldObject) { if (id.equals(parts[0])) { if (oldObject == null && parts.length == 1) { known = true; - } else if (oldObject != null && parts.length == 2 + } else if (oldObject != null + && parts.length == 2 && oldObject.getMetadata().getResourceVersion().equals(parts[1])) { known = true; } @@ -239,21 +249,27 @@ public Set getSecondaryResources(P primary) { var primaryResourceID = ResourceID.fromResource(primary); secondaryIDs = primaryToSecondaryIndex.getSecondaryResources(primaryResourceID); log.debug( - "Using PrimaryToSecondaryIndex to find secondary resources for primary: {}. Found secondary ids: {} ", - primaryResourceID, secondaryIDs); + "Using PrimaryToSecondaryIndex to find secondary resources for primary: {}. Found" + + " secondary ids: {} ", + primaryResourceID, + secondaryIDs); } else { secondaryIDs = primaryToSecondaryMapper.toSecondaryResourceIDs(primary); log.debug( - "Using PrimaryToSecondaryMapper to find secondary resources for primary: {}. Found secondary ids: {} ", - primary, secondaryIDs); + "Using PrimaryToSecondaryMapper to find secondary resources for primary: {}. Found" + + " secondary ids: {} ", + primary, + secondaryIDs); } - return secondaryIDs.stream().map(this::get).flatMap(Optional::stream) + return secondaryIDs.stream() + .map(this::get) + .flatMap(Optional::stream) .collect(Collectors.toSet()); } @Override - public synchronized void handleRecentResourceUpdate(ResourceID resourceID, R resource, - R previousVersionOfResource) { + public synchronized void handleRecentResourceUpdate( + ResourceID resourceID, R resource, R previousVersionOfResource) { handleRecentCreateOrUpdate(Operation.UPDATE, resource, previousVersionOfResource); } @@ -264,8 +280,11 @@ public synchronized void handleRecentResourceCreate(ResourceID resourceID, R res private void handleRecentCreateOrUpdate(Operation operation, R newResource, R oldResource) { primaryToSecondaryIndex.onAddOrUpdate(newResource); - temporaryResourceCache.putResource(newResource, Optional.ofNullable(oldResource) - .map(r -> r.getMetadata().getResourceVersion()).orElse(null)); + temporaryResourceCache.putResource( + newResource, + Optional.ofNullable(oldResource) + .map(r -> r.getMetadata().getResourceVersion()) + .orElse(null)); } private boolean useSecondaryToPrimaryIndex() { @@ -289,8 +308,8 @@ private boolean eventAcceptedByFilter(Operation operation, R newObject, R oldObj } private boolean acceptedByDeleteFilters(R resource, boolean b) { - return (onDeleteFilter == null || onDeleteFilter.accept(resource, b)) && - (genericFilter == null || genericFilter.accept(resource)); + return (onDeleteFilter == null || onDeleteFilter.accept(resource, b)) + && (genericFilter == null || genericFilter.accept(resource)); } /** @@ -300,12 +319,17 @@ private boolean acceptedByDeleteFilters(R resource, boolean b) { * @param target mutable resource that will be returned */ public R addPreviousAnnotation(String resourceVersion, R target) { - target.getMetadata().getAnnotations().put(PREVIOUS_ANNOTATION_KEY, - id + Optional.ofNullable(resourceVersion).map(rv -> "," + rv).orElse("")); + target + .getMetadata() + .getAnnotations() + .put( + PREVIOUS_ANNOTATION_KEY, + id + Optional.ofNullable(resourceVersion).map(rv -> "," + rv).orElse("")); return target; } private enum Operation { - ADD, UPDATE + ADD, + UPDATE } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java index 57bbf2a8ce..1e1607dd8b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerManager.java @@ -41,7 +41,8 @@ class InformerManager> private final Map>> indexers = new HashMap<>(); private ControllerConfiguration controllerConfiguration; - InformerManager(MixedOperation, Resource> client, + InformerManager( + MixedOperation, Resource> client, C configuration, ResourceEventHandler eventHandler) { this.client = client; @@ -57,15 +58,20 @@ void setControllerConfiguration(ControllerConfiguration controllerConfigurati public void start() throws OperatorException { initSources(); // make sure informers are all started before proceeding further - controllerConfiguration.getConfigurationService().getExecutorServiceManager() + controllerConfiguration + .getConfigurationService() + .getExecutorServiceManager() .boundedExecuteAndWaitForAllToComplete( sources.values().stream(), iw -> { iw.start(); return null; }, - iw -> "InformerStarter-" + iw.getTargetNamespace() + "-" - + configuration.getResourceClass().getSimpleName()); + iw -> + "InformerStarter-" + + iw.getTargetNamespace() + + "-" + + configuration.getResourceClass().getSimpleName()); } private void initSources() { @@ -81,8 +87,7 @@ private void initSources() { targetNamespaces.forEach( ns -> { final var source = createEventSourceForNamespace(ns); - log.debug("Registered {} -> {} for namespace: {}", this, source, - ns); + log.debug("Registered {} -> {} for namespace: {}", this, source, ns); }); } } @@ -92,33 +97,33 @@ C configuration() { } public void changeNamespaces(Set namespaces) { - var sourcesToRemove = sources.keySet().stream() - .filter(k -> !namespaces.contains(k)).collect(Collectors.toSet()); + var sourcesToRemove = + sources.keySet().stream().filter(k -> !namespaces.contains(k)).collect(Collectors.toSet()); log.debug("Stopped informer {} for namespaces: {}", this, sourcesToRemove); sourcesToRemove.forEach(k -> sources.remove(k).stop()); - namespaces.forEach(ns -> { - if (!sources.containsKey(ns)) { - final InformerWrapper source = createEventSourceForNamespace(ns); - source.start(); - log.debug("Registered new {} -> {} for namespace: {}", this, source, - ns); - } - }); + namespaces.forEach( + ns -> { + if (!sources.containsKey(ns)) { + final InformerWrapper source = createEventSourceForNamespace(ns); + source.start(); + log.debug("Registered new {} -> {} for namespace: {}", this, source, ns); + } + }); } - private InformerWrapper createEventSourceForNamespace(String namespace) { final InformerWrapper source; final var labelSelector = configuration.getInformerConfig().getLabelSelector(); if (namespace.equals(WATCH_ALL_NAMESPACES)) { - final var filteredBySelectorClient = - client.inAnyNamespace().withLabelSelector(labelSelector); + final var filteredBySelectorClient = client.inAnyNamespace().withLabelSelector(labelSelector); source = createEventSource(filteredBySelectorClient, eventHandler, WATCH_ALL_NAMESPACES); } else { - source = createEventSource( - client.inNamespace(namespace).withLabelSelector(labelSelector), - eventHandler, namespace); + source = + createEventSource( + client.inNamespace(namespace).withLabelSelector(labelSelector), + eventHandler, + namespace); } source.addIndexers(indexers); return source; @@ -126,14 +131,18 @@ private InformerWrapper createEventSourceForNamespace(String namespace) { private InformerWrapper createEventSource( FilterWatchListDeletable, Resource> filteredBySelectorClient, - ResourceEventHandler eventHandler, String namespaceIdentifier) { + ResourceEventHandler eventHandler, + String namespaceIdentifier) { final var informerConfig = configuration.getInformerConfig(); - var informer = Optional.ofNullable(informerConfig.getInformerListLimit()) - .map(filteredBySelectorClient::withLimit) - .orElse(filteredBySelectorClient).runnableInformer(0); + var informer = + Optional.ofNullable(informerConfig.getInformerListLimit()) + .map(filteredBySelectorClient::withLimit) + .orElse(filteredBySelectorClient) + .runnableInformer(0); Optional.ofNullable(informerConfig.getItemStore()).ifPresent(informer::itemStore); - var source = new InformerWrapper<>(informer, controllerConfiguration.getConfigurationService(), - namespaceIdentifier); + var source = + new InformerWrapper<>( + informer, controllerConfiguration.getConfigurationService(), namespaceIdentifier); source.addEventHandler(eventHandler); sources.put(namespaceIdentifier, source); return source; @@ -141,14 +150,15 @@ private InformerWrapper createEventSource( @Override public void stop() { - sources.forEach((ns, source) -> { - try { - log.debug("Stopping informer for namespace: {} -> {}", ns, source); - source.stop(); - } catch (Exception e) { - log.warn("Error stopping informer for namespace: {} -> {}", ns, source, e); - } - }); + sources.forEach( + (ns, source) -> { + try { + log.debug("Stopping informer for namespace: {} -> {}", ns, source); + source.stop(); + } catch (Exception e) { + log.warn("Error stopping informer for namespace: {} -> {}", ns, source, e); + } + }); sources.clear(); } @@ -167,9 +177,7 @@ public Stream list(String namespace, Predicate predicate) { .map(source -> source.list(namespace, predicate)) .orElseGet(Stream::empty); } else { - return getSource(namespace) - .map(source -> source.list(predicate)) - .orElseGet(Stream::empty); + return getSource(namespace).map(source -> source.list(predicate)).orElseGet(Stream::empty); } } @@ -177,10 +185,13 @@ public Stream list(String namespace, Predicate predicate) { public Optional get(ResourceID resourceID) { return getSource(resourceID.getNamespace().orElse(WATCH_ALL_NAMESPACES)) .flatMap(source -> source.get(resourceID)) - .map(r -> controllerConfiguration.getConfigurationService() - .cloneSecondaryResourcesWhenGettingFromCache() - ? controllerConfiguration.getConfigurationService().getResourceCloner().clone(r) - : r); + .map( + r -> + controllerConfiguration + .getConfigurationService() + .cloneSecondaryResourcesWhenGettingFromCache() + ? controllerConfiguration.getConfigurationService().getResourceCloner().clone(r) + : r); } @Override @@ -204,8 +215,10 @@ public void addIndexers(Map>> indexers) { @Override public List byIndex(String indexName, String indexKey) { - return sources.values().stream().map(s -> s.byIndex(indexName, indexKey)) - .flatMap(List::stream).collect(Collectors.toList()); + return sources.values().stream() + .map(s -> s.byIndex(indexName, indexKey)) + .flatMap(List::stream) + .collect(Collectors.toList()); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerWrapper.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerWrapper.java index 28cf33a041..c07ffdbf46 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerWrapper.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerWrapper.java @@ -38,7 +38,9 @@ class InformerWrapper private final String namespaceIdentifier; private final ConfigurationService configurationService; - public InformerWrapper(SharedIndexInformer informer, ConfigurationService configurationService, + public InformerWrapper( + SharedIndexInformer informer, + ConfigurationService configurationService, String namespaceIdentifier) { this.informer = informer; this.namespaceIdentifier = namespaceIdentifier; @@ -51,23 +53,29 @@ public void start() throws OperatorException { try { // register stopped handler if we have one defined - configurationService.getInformerStoppedHandler().ifPresent(ish -> { - final var stopped = informer.stopped(); - if (stopped != null) { - stopped.handle((res, ex) -> { - ish.onStop(informer, ex); - return null; - }); - } else { - final var apiTypeClass = informer.getApiTypeClass(); - final var fullResourceName = - HasMetadata.getFullResourceName(apiTypeClass); - final var version = HasMetadata.getVersion(apiTypeClass); - throw new IllegalStateException( - "Cannot retrieve 'stopped' callback to listen to informer stopping for informer for " - + fullResourceName + "/" + version); - } - }); + configurationService + .getInformerStoppedHandler() + .ifPresent( + ish -> { + final var stopped = informer.stopped(); + if (stopped != null) { + stopped.handle( + (res, ex) -> { + ish.onStop(informer, ex); + return null; + }); + } else { + final var apiTypeClass = informer.getApiTypeClass(); + final var fullResourceName = HasMetadata.getFullResourceName(apiTypeClass); + final var version = HasMetadata.getVersion(apiTypeClass); + throw new IllegalStateException( + "Cannot retrieve 'stopped' callback to listen to informer stopping for" + + " informer for " + + fullResourceName + + "/" + + version); + } + }); if (!configurationService.stopOnInformerErrorDuringStartup()) { informer.exceptionHandler((b, t) -> !ExceptionHandler.isDeserializationException(t)); } @@ -77,18 +85,21 @@ public void start() throws OperatorException { try { thread.setName(informerInfo() + " " + thread.getId()); final var resourceName = informer.getApiTypeClass().getSimpleName(); - log.debug("Starting informer for namespace: {} resource: {}", namespaceIdentifier, - resourceName); + log.debug( + "Starting informer for namespace: {} resource: {}", namespaceIdentifier, resourceName); var start = informer.start(); // note that in case we don't put here timeout and stopOnInformerErrorDuringStartup is // false, and there is a rbac issue the get never returns; therefore operator never really // starts - log.trace("Waiting informer to start namespace: {} resource: {}", namespaceIdentifier, - resourceName); - start.toCompletableFuture().get(configurationService.cacheSyncTimeout().toMillis(), - TimeUnit.MILLISECONDS); - log.debug("Started informer for namespace: {} resource: {}", namespaceIdentifier, + log.trace( + "Waiting informer to start namespace: {} resource: {}", + namespaceIdentifier, resourceName); + start + .toCompletableFuture() + .get(configurationService.cacheSyncTimeout().toMillis(), TimeUnit.MILLISECONDS); + log.debug( + "Started informer for namespace: {} resource: {}", namespaceIdentifier, resourceName); } catch (TimeoutException | ExecutionException e) { if (configurationService.stopOnInformerErrorDuringStartup()) { log.error("Informer startup error. Operator will be stopped. Informer: {}", informer, e); @@ -105,8 +116,8 @@ public void start() throws OperatorException { } } catch (Exception e) { - ReconcilerUtils.handleKubernetesClientException(e, - HasMetadata.getFullResourceName(informer.getApiTypeClass())); + ReconcilerUtils.handleKubernetesClientException( + e, HasMetadata.getFullResourceName(informer.getApiTypeClass())); throw new OperatorException( "Couldn't start informer for " + versionedFullResourceName() + " resources", e); } @@ -141,8 +152,8 @@ public Stream list(Predicate predicate) { @Override public Stream list(String namespace, Predicate predicate) { - final var stream = cache.list().stream() - .filter(r -> namespace.equals(r.getMetadata().getNamespace())); + final var stream = + cache.list().stream().filter(r -> namespace.equals(r.getMetadata().getNamespace())); return predicate != null ? stream.filter(predicate) : stream; } @@ -193,9 +204,14 @@ public boolean isRunning() { public Status getStatus() { var status = isRunning() && hasSynced() && isWatching() ? Status.HEALTHY : Status.UNHEALTHY; log.debug( - "Informer status: {} for for type: {}, namespace: {}, details[ is running: {}, has synced: {}, is watching: {} ]", - status, informer.getApiTypeClass().getSimpleName(), namespaceIdentifier, isRunning(), - hasSynced(), isWatching()); + "Informer status: {} for for type: {}, namespace: {}, details[ is running: {}, has synced:" + + " {}, is watching: {} ]", + status, + informer.getApiTypeClass().getSimpleName(), + namespaceIdentifier, + isRunning(), + hasSynced(), + isWatching()); return status; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java index f5e899826d..549d2236cd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/ManagedInformerEventSource.java @@ -27,11 +27,16 @@ import io.javaoperatorsdk.operator.processing.event.source.*; @SuppressWarnings("rawtypes") -public abstract class ManagedInformerEventSource> +public abstract class ManagedInformerEventSource< + R extends HasMetadata, P extends HasMetadata, C extends Informable> extends AbstractEventSource - implements ResourceEventHandler, Cache, IndexerResourceCache, - RecentOperationCacheFiller, NamespaceChangeable, - InformerWrappingEventSourceHealthIndicator, Configurable { + implements ResourceEventHandler, + Cache, + IndexerResourceCache, + RecentOperationCacheFiller, + NamespaceChangeable, + InformerWrappingEventSourceHealthIndicator, + Configurable { private static final Logger log = LoggerFactory.getLogger(ManagedInformerEventSource.class); private InformerManager cache; @@ -42,9 +47,8 @@ public abstract class ManagedInformerEventSource temporaryResourceCache; protected MixedOperation client; - protected ManagedInformerEventSource(String name, - MixedOperation client, C configuration, - boolean parseResourceVersions) { + protected ManagedInformerEventSource( + String name, MixedOperation client, C configuration, boolean parseResourceVersions) { super(configuration.getResourceClass(), name); this.parseResourceVersions = parseResourceVersions; this.client = client; @@ -101,10 +105,10 @@ public synchronized void stop() { } @Override - public void handleRecentResourceUpdate(ResourceID resourceID, R resource, - R previousVersionOfResource) { - temporaryResourceCache.putResource(resource, - previousVersionOfResource.getMetadata().getResourceVersion()); + public void handleRecentResourceUpdate( + ResourceID resourceID, R resource, R previousVersionOfResource) { + temporaryResourceCache.putResource( + resource, previousVersionOfResource.getMetadata().getResourceVersion()); } @Override @@ -119,8 +123,10 @@ public Optional get(ResourceID resourceID) { log.debug("Resource found in temporary cache for Resource ID: {}", resourceID); return resource; } else { - log.debug("Resource not found in temporary cache reading it from informer cache," + - " for Resource ID: {}", resourceID); + log.debug( + "Resource not found in temporary cache reading it from informer cache," + + " for Resource ID: {}", + resourceID); var res = cache.get(resourceID); log.debug("Resource found in cache: {} for id: {}", res.isPresent(), resourceID); return res; @@ -181,13 +187,14 @@ public C configuration() { @Override public String toString() { - return getClass().getSimpleName() + "{" + - "resourceClass: " + configuration().getResourceClass().getSimpleName() + - "}"; + return getClass().getSimpleName() + + "{" + + "resourceClass: " + + configuration().getResourceClass().getSimpleName() + + "}"; } public void setControllerConfiguration(ControllerConfiguration controllerConfiguration) { this.controllerConfiguration = controllerConfiguration; } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java index db3877376c..7ed46a97f3 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/Mappers.java @@ -26,27 +26,33 @@ public static SecondaryToPrimaryMapper fromAnnotation @SuppressWarnings("unused") public static SecondaryToPrimaryMapper fromAnnotation( - String nameKey, String namespaceKey, String typeKey, + String nameKey, + String namespaceKey, + String typeKey, Class primaryResourceType) { return fromMetadata(nameKey, namespaceKey, typeKey, primaryResourceType, false); } @SuppressWarnings("unused") - public static SecondaryToPrimaryMapper fromLabel(String nameKey, - String typeKey, - Class primaryResourceType) { + public static SecondaryToPrimaryMapper fromLabel( + String nameKey, String typeKey, Class primaryResourceType) { return fromLabel(nameKey, null, typeKey, primaryResourceType); } public static SecondaryToPrimaryMapper fromDefaultAnnotations( Class primaryResourceType) { - return fromAnnotation(DEFAULT_ANNOTATION_FOR_NAME, DEFAULT_ANNOTATION_FOR_NAMESPACE, - DEFAULT_ANNOTATION_FOR_PRIMARY_TYPE, primaryResourceType); + return fromAnnotation( + DEFAULT_ANNOTATION_FOR_NAME, + DEFAULT_ANNOTATION_FOR_NAMESPACE, + DEFAULT_ANNOTATION_FOR_PRIMARY_TYPE, + primaryResourceType); } @SuppressWarnings("unused") public static SecondaryToPrimaryMapper fromLabel( - String nameKey, String namespaceKey, String typeKey, + String nameKey, + String namespaceKey, + String typeKey, Class primaryResourceType) { return fromMetadata(nameKey, namespaceKey, typeKey, primaryResourceType, true); } @@ -58,7 +64,8 @@ public static SecondaryToPrimaryMapper fromOwnerRefer public static SecondaryToPrimaryMapper fromOwnerReferences( Class primaryResourceType, boolean clusterScoped) { - return fromOwnerReferences(HasMetadata.getApiVersion(primaryResourceType), + return fromOwnerReferences( + HasMetadata.getApiVersion(primaryResourceType), HasMetadata.getKind(primaryResourceType), clusterScoped); } @@ -69,26 +76,27 @@ public static SecondaryToPrimaryMapper fromOwnerRefer } public static SecondaryToPrimaryMapper fromOwnerReferences( - HasMetadata primaryResource, - boolean clusterScoped) { - return fromOwnerReferences(primaryResource.getApiVersion(), primaryResource.getKind(), - clusterScoped); + HasMetadata primaryResource, boolean clusterScoped) { + return fromOwnerReferences( + primaryResource.getApiVersion(), primaryResource.getKind(), clusterScoped); } public static SecondaryToPrimaryMapper fromOwnerReferences( - String apiVersion, String kind, - boolean clusterScope) { - return resource -> resource.getMetadata().getOwnerReferences() - .stream() - .filter(r -> r.getKind().equals(kind) - && r.getApiVersion().equals(apiVersion)) - .map(or -> ResourceID.fromOwnerReference(resource, or, clusterScope)) - .collect(Collectors.toSet()); + String apiVersion, String kind, boolean clusterScope) { + String correctApiVersion = apiVersion.startsWith("/") ? apiVersion.substring(1) : apiVersion; + return resource -> + resource.getMetadata().getOwnerReferences().stream() + .filter(r -> r.getKind().equals(kind) && r.getApiVersion().equals(correctApiVersion)) + .map(or -> ResourceID.fromOwnerReference(resource, or, clusterScope)) + .collect(Collectors.toSet()); } private static SecondaryToPrimaryMapper fromMetadata( - String nameKey, String namespaceKey, String typeKey, - Class primaryResourceType, boolean isLabel) { + String nameKey, + String namespaceKey, + String typeKey, + Class primaryResourceType, + boolean isLabel) { return resource -> { final var metadata = resource.getMetadata(); if (metadata == null) { @@ -107,8 +115,8 @@ private static SecondaryToPrimaryMapper fromMetadata( String gvkSimple = map.get(typeKey); - if (gvkSimple != null && - !GroupVersionKind.fromString(gvkSimple) + if (gvkSimple != null + && !GroupVersionKind.fromString(gvkSimple) .equals(GroupVersionKind.gvkFor(primaryResourceType))) { return Set.of(); } @@ -134,8 +142,8 @@ public static ResourceID fromString(String cacheKey) { /** * Produces a mapper that will associate a secondary resource with all owners of the primary type. */ - public static SecondaryToPrimaryMapper fromOwnerType( - Class clazz) { + public static + SecondaryToPrimaryMapper fromOwnerType(Class clazz) { String kind = HasMetadata.getKind(clazz); return resource -> { var meta = resource.getMetadata(); @@ -168,5 +176,4 @@ public Set toPrimaryResourceIDs(HasMetadata resource) { return Mappers.fromDefaultAnnotations(primaryResourceType).toPrimaryResourceIDs(resource); } } - } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/NOOPPrimaryToSecondaryIndex.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/NOOPPrimaryToSecondaryIndex.java index 0830d8bb1b..abefbba638 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/NOOPPrimaryToSecondaryIndex.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/NOOPPrimaryToSecondaryIndex.java @@ -5,8 +5,7 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.ResourceID; -class NOOPPrimaryToSecondaryIndex - implements PrimaryToSecondaryIndex { +class NOOPPrimaryToSecondaryIndex implements PrimaryToSecondaryIndex { @SuppressWarnings("rawtypes") private static final NOOPPrimaryToSecondaryIndex instance = new NOOPPrimaryToSecondaryIndex(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCache.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCache.java index 07c9e0a6cc..9ec5b3694c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCache.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCache.java @@ -9,26 +9,23 @@ import org.slf4j.LoggerFactory; import io.fabric8.kubernetes.api.model.HasMetadata; -import io.javaoperatorsdk.operator.api.config.informer.InformerEventSourceConfiguration; +import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.processing.event.ResourceID; /** - *

* Temporal cache is used to solve the problem for {@link KubernetesDependentResource} that is, when * a create or update is executed the subsequent getResource operation might not return the * up-to-date resource from informer cache, since it is not received yet. - *

- *

- * The idea of the solution is, that since an update (for create is simpler) was done successfully, - * and optimistic locking is in place, there were no other operations between reading the resource - * from the cache and the actual update. So when the new resource is stored in the temporal cache - * only if the informer still has the previous resource version, from before the update. If not, - * that means there were already updates on the cache (either by the actual update from - * DependentResource or other) so the resource does not needs to be cached. Subsequently if event - * received from the informer, it means that the cache of the informer was updated, so it already - * contains a more fresh version of the resource. - *

+ * + *

The idea of the solution is, that since an update (for create is simpler) was done + * successfully, and optimistic locking is in place, there were no other operations between reading + * the resource from the cache and the actual update. So when the new resource is stored in the + * temporal cache only if the informer still has the previous resource version, from before the + * update. If not, that means there were already updates on the cache (either by the actual update + * from DependentResource or other) so the resource does not needs to be cached. Subsequently if + * event received from the informer, it means that the cache of the informer was updated, so it + * already contains a more fresh version of the resource. * * @param resource to cache. */ @@ -40,12 +37,13 @@ static class ExpirationCache { public ExpirationCache(int maxEntries, int ttlMs) { this.ttlMs = ttlMs; - this.cache = new LinkedHashMap<>() { - @Override - protected boolean removeEldestEntry(Map.Entry eldest) { - return size() > maxEntries; - } - }; + this.cache = + new LinkedHashMap<>() { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > maxEntries; + } + }; } public void add(K key) { @@ -84,7 +82,8 @@ void clean() { private final boolean parseResourceVersions; private final ExpirationCache knownResourceVersions; - public TemporaryResourceCache(ManagedInformerEventSource managedInformerEventSource, + public TemporaryResourceCache( + ManagedInformerEventSource managedInformerEventSource, boolean parseResourceVersions) { this.managedInformerEventSource = managedInformerEventSource; this.parseResourceVersions = parseResourceVersions; @@ -106,9 +105,10 @@ public synchronized void onAddOrUpdateEvent(T resource) { } synchronized void onEvent(T resource, boolean unknownState) { - cache.computeIfPresent(ResourceID.fromResource(resource), - (id, cached) -> (unknownState || !isLaterResourceVersion(id, cached, resource)) ? null - : cached); + cache.computeIfPresent( + ResourceID.fromResource(resource), + (id, cached) -> + (unknownState || !isLaterResourceVersion(id, cached, resource)) ? null : cached); } public synchronized void putAddedResource(T newResource) { @@ -126,15 +126,17 @@ public synchronized void putResource(T newResource, String previousResourceVersi knownResourceVersions.add(newResource.getMetadata().getResourceVersion()); } var resourceId = ResourceID.fromResource(newResource); - var cachedResource = getResourceFromCache(resourceId) - .orElse(managedInformerEventSource.get(resourceId).orElse(null)); + var cachedResource = + getResourceFromCache(resourceId) + .orElse(managedInformerEventSource.get(resourceId).orElse(null)); boolean moveAhead = false; if (previousResourceVersion == null && cachedResource == null) { if (tombstones.contains(newResource.getMetadata().getUid())) { log.debug( "Won't resurrect uid {} for resource id: {}", - newResource.getMetadata().getUid(), resourceId); + newResource.getMetadata().getUid(), + resourceId); return; } // we can skip further checks as this is a simple add and there's no previous entry to @@ -144,11 +146,15 @@ public synchronized void putResource(T newResource, String previousResourceVersi if (moveAhead || (cachedResource != null - && (cachedResource.getMetadata().getResourceVersion().equals(previousResourceVersion)) + && (cachedResource + .getMetadata() + .getResourceVersion() + .equals(previousResourceVersion)) || isLaterResourceVersion(resourceId, newResource, cachedResource))) { log.debug( "Temporarily moving ahead to target version {} for resource id: {}", - newResource.getMetadata().getResourceVersion(), resourceId); + newResource.getMetadata().getResourceVersion(), + resourceId); cache.put(resourceId, newResource); } else if (cache.remove(resourceId) != null) { log.debug("Removed an obsolete resource from cache for id: {}", resourceId); @@ -161,22 +167,23 @@ public synchronized boolean isKnownResourceVersion(T resource) { } /** - * @return true if {@link InformerEventSourceConfiguration#parseResourceVersions()} is enabled and - * the resourceVersion of newResource is numerically greater than cachedResource, - * otherwise false + * @return true if {@link ConfigurationService#parseResourceVersionsForEventFilteringAndCaching()} + * is enabled and the resourceVersion of newResource is numerically greater than + * cachedResource, otherwise false */ private boolean isLaterResourceVersion(ResourceID resourceId, T newResource, T cachedResource) { try { if (parseResourceVersions - && Long.parseLong(newResource.getMetadata().getResourceVersion()) > Long - .parseLong(cachedResource.getMetadata().getResourceVersion())) { + && Long.parseLong(newResource.getMetadata().getResourceVersion()) + > Long.parseLong(cachedResource.getMetadata().getResourceVersion())) { return true; } } catch (NumberFormatException e) { log.debug( "Could not compare resourceVersions {} and {} for {}", newResource.getMetadata().getResourceVersion(), - cachedResource.getMetadata().getResourceVersion(), resourceId); + cachedResource.getMetadata().getResourceVersion(), + resourceId); } return false; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStore.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStore.java index e5f682b210..0088f99084 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStore.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStore.java @@ -19,8 +19,8 @@ public TransformingItemStore(UnaryOperator transformationFunction) { this(Cache::metaNamespaceKeyFunc, transformationFunction); } - public TransformingItemStore(Function keyFunction, - UnaryOperator transformationFunction) { + public TransformingItemStore( + Function keyFunction, UnaryOperator transformationFunction) { this.keyFunction = keyFunction; this.transformationFunction = transformationFunction; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java index 52e1fbcd68..9a4493ea5b 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfiguration.java @@ -9,25 +9,32 @@ import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; -public record PerResourcePollingConfiguration(String name,ScheduledExecutorService executorService, CacheKeyMapper cacheKeyMapper, - PerResourcePollingEventSource.ResourceFetcher resourceFetcher, - Predicate

registerPredicate, Duration defaultPollingPeriod) { +public record PerResourcePollingConfiguration( + String name, + ScheduledExecutorService executorService, + CacheKeyMapper cacheKeyMapper, + PerResourcePollingEventSource.ResourceFetcher resourceFetcher, + Predicate

registerPredicate, + Duration defaultPollingPeriod) { - public static final int DEFAULT_EXECUTOR_THREAD_NUMBER = 1; + public static final int DEFAULT_EXECUTOR_THREAD_NUMBER = 1; - public PerResourcePollingConfiguration( - String name, - ScheduledExecutorService executorService, - CacheKeyMapper cacheKeyMapper, - PerResourcePollingEventSource.ResourceFetcher resourceFetcher, - Predicate

registerPredicate, - Duration defaultPollingPeriod) { - this.name = name; - this.executorService = executorService == null ? new ScheduledThreadPoolExecutor(DEFAULT_EXECUTOR_THREAD_NUMBER) - : executorService; - this.cacheKeyMapper = cacheKeyMapper == null ? CacheKeyMapper.singleResourceCacheKeyMapper() : cacheKeyMapper; - this.resourceFetcher = Objects.requireNonNull(resourceFetcher); - this.registerPredicate = registerPredicate; - this.defaultPollingPeriod = defaultPollingPeriod; - } + public PerResourcePollingConfiguration( + String name, + ScheduledExecutorService executorService, + CacheKeyMapper cacheKeyMapper, + PerResourcePollingEventSource.ResourceFetcher resourceFetcher, + Predicate

registerPredicate, + Duration defaultPollingPeriod) { + this.name = name; + this.executorService = + executorService == null + ? new ScheduledThreadPoolExecutor(DEFAULT_EXECUTOR_THREAD_NUMBER) + : executorService; + this.cacheKeyMapper = + cacheKeyMapper == null ? CacheKeyMapper.singleResourceCacheKeyMapper() : cacheKeyMapper; + this.resourceFetcher = Objects.requireNonNull(resourceFetcher); + this.registerPredicate = registerPredicate; + this.defaultPollingPeriod = defaultPollingPeriod; + } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java index 85b1fcf2b0..4fab88ffd8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingConfigurationBuilder.java @@ -49,7 +49,12 @@ public PerResourcePollingConfigurationBuilder withName(String name) { } public PerResourcePollingConfiguration build() { - return new PerResourcePollingConfiguration<>(name, executorService, cacheKeyMapper, - resourceFetcher, registerPredicate, defaultPollingPeriod); + return new PerResourcePollingConfiguration<>( + name, + executorService, + cacheKeyMapper, + resourceFetcher, + registerPredicate, + defaultPollingPeriod); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java index 983679a27a..b6f6cd79cd 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSource.java @@ -23,21 +23,18 @@ import io.javaoperatorsdk.operator.processing.event.source.ExternalResourceCachingEventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; - /** - * * Polls the supplier for each controlled resource registered. Resource is registered when created * if there is no registerPredicate provided. If register predicate provided it is evaluated on * resource create and/or update to register polling for the event source. - *

- * For other behavior see {@link ExternalResourceCachingEventSource} + * + *

For other behavior see {@link ExternalResourceCachingEventSource} * * @param the resource polled by the event source * @param

related custom resource */ public class PerResourcePollingEventSource - extends ExternalResourceCachingEventSource - implements ResourceEventAware

{ + extends ExternalResourceCachingEventSource implements ResourceEventAware

{ private static final Logger log = LoggerFactory.getLogger(PerResourcePollingEventSource.class); @@ -50,9 +47,8 @@ public class PerResourcePollingEventSource private final Predicate

registerPredicate; private final Duration period; - - - public PerResourcePollingEventSource(Class resourceClass, + public PerResourcePollingEventSource( + Class resourceClass, EventSourceContext

context, PerResourcePollingConfiguration config) { super(config.name(), resourceClass, config.cacheKeyMapper()); @@ -76,8 +72,10 @@ private void scheduleNextExecution(P primary, Set actualResources) { var fetchDelay = resourceFetcher.fetchDelay(actualResources, primary); var fetchDuration = fetchDelay.orElse(period); - ScheduledFuture scheduledFuture = (ScheduledFuture) executorService - .schedule(new FetchingExecutor(primaryID), fetchDuration.toMillis(), TimeUnit.MILLISECONDS); + ScheduledFuture scheduledFuture = + (ScheduledFuture) + executorService.schedule( + new FetchingExecutor(primaryID), fetchDuration.toMillis(), TimeUnit.MILLISECONDS); scheduledFutures.put(primaryID, scheduledFuture); } @@ -108,8 +106,8 @@ public void onResourceDeleted(P resource) { // important because otherwise there will be a race condition related to the timerTasks. private void checkAndRegisterTask(P resource) { var primaryID = ResourceID.fromResource(resource); - if (scheduledFutures.get(primaryID) == null && (registerPredicate == null - || registerPredicate.test(resource))) { + if (scheduledFutures.get(primaryID) == null + && (registerPredicate == null || registerPredicate.test(resource))) { var cachedResources = cache.get(primaryID); var actualResources = cachedResources == null ? null : new HashSet<>(cachedResources.values()); @@ -177,10 +175,10 @@ public interface ResourceFetcher { * with a lower frequency, compared to the phase when it is being initialized. * * @param lastFetchedResource might be null, in case no fetch happened before. Empty set if - * fetch happened but no resources were found. + * fetch happened but no resources were found. * @param primary related primary resource * @return an Optional containing the Duration to wait until the next fetch. If an empty - * Optional is returned, the default polling period will be used. + * Optional is returned, the default polling period will be used. */ default Optional fetchDelay(Set lastFetchedResource, P primary) { return Optional.empty(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java index c66ef38c8f..f73547a4db 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfiguration.java @@ -5,15 +5,21 @@ import io.javaoperatorsdk.operator.processing.event.source.CacheKeyMapper; -public record PollingConfiguration(String name,PollingEventSource.GenericResourceFetcher genericResourceFetcher, - Duration period, CacheKeyMapper cacheKeyMapper) { +public record PollingConfiguration( + String name, + PollingEventSource.GenericResourceFetcher genericResourceFetcher, + Duration period, + CacheKeyMapper cacheKeyMapper) { - public PollingConfiguration(String name,PollingEventSource.GenericResourceFetcher genericResourceFetcher, Duration period, - CacheKeyMapper cacheKeyMapper) { + public PollingConfiguration( + String name, + PollingEventSource.GenericResourceFetcher genericResourceFetcher, + Duration period, + CacheKeyMapper cacheKeyMapper) { this.name = name; this.genericResourceFetcher = Objects.requireNonNull(genericResourceFetcher); this.period = period; this.cacheKeyMapper = - cacheKeyMapper == null ? CacheKeyMapper.singleResourceCacheKeyMapper() : cacheKeyMapper; + cacheKeyMapper == null ? CacheKeyMapper.singleResourceCacheKeyMapper() : cacheKeyMapper; } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java index c6bccefa82..b86b3be3bb 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingConfigurationBuilder.java @@ -10,8 +10,8 @@ public final class PollingConfigurationBuilder { private CacheKeyMapper cacheKeyMapper; private String name; - public PollingConfigurationBuilder(PollingEventSource.GenericResourceFetcher fetcher, - Duration period) { + public PollingConfigurationBuilder( + PollingEventSource.GenericResourceFetcher fetcher, Duration period) { this.genericResourceFetcher = fetcher; this.period = period; } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java index 6549030c4b..b7e9740552 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSource.java @@ -24,19 +24,19 @@ * does not contain the target resource it means it is not created yet or was deleted while an * operator was not running. * - *

- * Another caveat with this is if the cached object is checked in the reconciler and created since - * not in the cache it should be manually added to the cache, since it can happen that the + *

Another caveat with this is if the cached object is checked in the reconciler and created + * since not in the cache it should be manually added to the cache, since it can happen that the * reconciler is triggered before the cache is propagated with the new resource from a scheduled * execution. See {@link #handleRecentResourceCreate(ResourceID, Object)} and update method. So the * generic workflow in reconciler should be: * *

    - *
  • Check if the cache contains the resource. - *
  • If cache contains the resource reconcile it - compare with target state, update if necessary - *
  • if cache not contains the resource create it. - *
  • If the resource was created or updated, put the new version of the resource manually to the - * cache. + *
  • Check if the cache contains the resource. + *
  • If cache contains the resource reconcile it - compare with target state, update if + * necessary + *
  • if cache not contains the resource create it. + *
  • If the resource was created or updated, put the new version of the resource manually to the + * cache. *
* * @param type of the polled resource @@ -52,8 +52,6 @@ public class PollingEventSource private final Duration period; private final AtomicBoolean healthy = new AtomicBoolean(true); - - public PollingEventSource(Class resourceClass, PollingConfiguration config) { super(config.name(), resourceClass, config.cacheKeyMapper()); this.genericResourceFetcher = config.genericResourceFetcher(); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSource.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSource.java index b909083a00..53c0d328a8 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSource.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSource.java @@ -15,8 +15,7 @@ import io.javaoperatorsdk.operator.processing.event.source.AbstractEventSource; import io.javaoperatorsdk.operator.processing.event.source.ResourceEventAware; -public class TimerEventSource - extends AbstractEventSource +public class TimerEventSource extends AbstractEventSource implements ResourceEventAware { private static final Logger log = LoggerFactory.getLogger(TimerEventSource.class); diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java index d1809de566..a8e1c5b466 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetry.java @@ -82,8 +82,9 @@ public void initFrom(GradualRetry configuration) { this.initialInterval = configuration.initialInterval(); this.maxAttempts = configuration.maxAttempts(); this.intervalMultiplier = configuration.intervalMultiplier(); - this.maxInterval = configuration.maxInterval() == GradualRetry.UNSET_VALUE - ? GradualRetry.DEFAULT_MAX_INTERVAL - : configuration.maxInterval(); + this.maxInterval = + configuration.maxInterval() == GradualRetry.UNSET_VALUE + ? GradualRetry.DEFAULT_MAX_INTERVAL + : configuration.maxInterval(); } } diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java index 32bf154f97..a2c7a9a609 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GenericRetryExecution.java @@ -15,8 +15,7 @@ public GenericRetryExecution(GenericRetry genericRetry) { } public Optional nextDelay() { - if (genericRetry.getMaxAttempts() > -1 - && lastAttemptIndex >= genericRetry.getMaxAttempts()) { + if (genericRetry.getMaxAttempts() > -1 && lastAttemptIndex >= genericRetry.getMaxAttempts()) { return Optional.empty(); } if (lastAttemptIndex > 1) { @@ -31,8 +30,7 @@ public Optional nextDelay() { @Override public boolean isLastAttempt() { - return genericRetry.getMaxAttempts() > -1 - && lastAttemptIndex >= genericRetry.getMaxAttempts(); + return genericRetry.getMaxAttempts() > -1 && lastAttemptIndex >= genericRetry.getMaxAttempts(); } @Override diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GradualRetry.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GradualRetry.java index f5033fc7aa..9eb4063e66 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GradualRetry.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/GradualRetry.java @@ -15,8 +15,10 @@ long DEFAULT_INITIAL_INTERVAL = 2000L; double DEFAULT_MULTIPLIER = 1.5D; - long DEFAULT_MAX_INTERVAL = (long) (GradualRetry.DEFAULT_INITIAL_INTERVAL * Math.pow( - GradualRetry.DEFAULT_MULTIPLIER, GradualRetry.DEFAULT_MAX_ATTEMPTS)); + long DEFAULT_MAX_INTERVAL = + (long) + (GradualRetry.DEFAULT_INITIAL_INTERVAL + * Math.pow(GradualRetry.DEFAULT_MULTIPLIER, GradualRetry.DEFAULT_MAX_ATTEMPTS)); long UNSET_VALUE = Long.MAX_VALUE; diff --git a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java index 78ec6f189c..fb36aaa92c 100644 --- a/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java +++ b/operator-framework-core/src/main/java/io/javaoperatorsdk/operator/processing/retry/Retry.java @@ -4,5 +4,4 @@ public interface Retry { RetryExecution initExecution(); - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ControllerManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ControllerManagerTest.java index c8c4cb5008..f6b2837554 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ControllerManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ControllerManagerTest.java @@ -20,18 +20,23 @@ class ControllerManagerTest { @Test void addingReconcilerWithSameNameShouldNotWork() { final var controllerConfiguration = - new TestControllerConfiguration<>(new TestCustomReconciler(null), - TestCustomResource.class); - var controller = new Controller<>(controllerConfiguration.reconciler, controllerConfiguration, - MockKubernetesClient.client(controllerConfiguration.getResourceClass())); + new TestControllerConfiguration<>(new TestCustomReconciler(null), TestCustomResource.class); + var controller = + new Controller<>( + controllerConfiguration.reconciler, + controllerConfiguration, + MockKubernetesClient.client(controllerConfiguration.getResourceClass())); ConfigurationService configurationService = new BaseConfigurationService(); final var controllerManager = new ControllerManager(configurationService.getExecutorServiceManager()); controllerManager.add(controller); - var ex = assertThrows(OperatorException.class, () -> { - controllerManager.add(controller); - }); + var ex = + assertThrows( + OperatorException.class, + () -> { + controllerManager.add(controller); + }); assertTrue( ex.getMessage().contains(CANNOT_REGISTER_MULTIPLE_CONTROLLERS_WITH_SAME_NAME_MESSAGE)); } @@ -41,13 +46,15 @@ private static class TestControllerConfiguration private final Reconciler reconciler; public TestControllerConfiguration(Reconciler reconciler, Class crClass) { - super(crClass, getControllerName(reconciler), reconciler.getClass(), + super( + crClass, + getControllerName(reconciler), + reconciler.getClass(), new BaseConfigurationService()); this.reconciler = reconciler; } - static String getControllerName( - Reconciler controller) { + static String getControllerName(Reconciler controller) { return controller.getClass().getSimpleName() + "Controller"; } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java index ee56d6a99d..2154c57452 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/LeaderElectionManagerTest.java @@ -34,8 +34,9 @@ private LeaderElectionManager leaderElectionManager(Object selfSubjectReview) { when(kubernetesClient.getConfiguration()).thenReturn(Config.autoConfigure(null)); var configurationService = ConfigurationService.newOverriddenConfigurationService( - o -> o.withLeaderElectionConfiguration(new LeaderElectionConfiguration("test")) - .withKubernetesClient(kubernetesClient)); + o -> + o.withLeaderElectionConfiguration(new LeaderElectionConfiguration("test")) + .withKubernetesClient(kubernetesClient)); return new LeaderElectionManager(controllerManager, configurationService); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java index 31fec8b924..e4cafbdb72 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/MockKubernetesClient.java @@ -43,27 +43,26 @@ public static KubernetesClient client(Class clazz) { return client(clazz, null, null); } - public static KubernetesClient client(Class clazz, - Object selfSubjectReview) { + public static KubernetesClient client( + Class clazz, Object selfSubjectReview) { return client(clazz, null, selfSubjectReview); } - public static KubernetesClient client(Class clazz, - Consumer informerRunBehavior) { + public static KubernetesClient client( + Class clazz, Consumer informerRunBehavior) { return client(clazz, informerRunBehavior, null); } @SuppressWarnings({"unchecked", "rawtypes"}) - public static KubernetesClient client(Class clazz, - Consumer informerRunBehavior, - Object selfSubjectReview) { + public static KubernetesClient client( + Class clazz, Consumer informerRunBehavior, Object selfSubjectReview) { final var client = mock(KubernetesClient.class); MixedOperation, Resource> resources = mock(MixedOperation.class); NonNamespaceOperation, Resource> nonNamespaceOperation = mock(NonNamespaceOperation.class); - AnyNamespaceOperation, Resource> inAnyNamespace = mock( - AnyNamespaceOperation.class); + AnyNamespaceOperation, Resource> inAnyNamespace = + mock(AnyNamespaceOperation.class); FilterWatchListDeletable, Resource> filterable = mock(FilterWatchListDeletable.class); when(resources.inNamespace(anyString())).thenReturn(nonNamespaceOperation); @@ -78,14 +77,17 @@ public static KubernetesClient client(Class clazz, when(informer.stopped()).thenReturn(stopped); when(informer.getApiTypeClass()).thenReturn(clazz); if (informerRunBehavior != null) { - doAnswer(invocation -> { - try { - informerRunBehavior.accept(null); - } catch (Exception e) { - stopped.completeExceptionally(e); - } - return stopped; - }).when(informer).start(); + doAnswer( + invocation -> { + try { + informerRunBehavior.accept(null); + } catch (Exception e) { + stopped.completeExceptionally(e); + } + return stopped; + }) + .when(informer) + .start(); } doAnswer(invocation -> null).when(informer).stop(); Indexer mockIndexer = mock(Indexer.class); @@ -105,8 +107,9 @@ public static KubernetesClient client(Class clazz, when(client.resource(any(SelfSubjectRulesReview.class))) .thenReturn(selfSubjectResourceResourceMock); when(selfSubjectResourceResourceMock.create()) - .thenReturn(Optional.ofNullable(selfSubjectReview) - .orElseGet(MockKubernetesClient::allowSelfSubjectReview)); + .thenReturn( + Optional.ofNullable(selfSubjectReview) + .orElseGet(MockKubernetesClient::allowSelfSubjectReview)); final var apiGroupDSL = mock(ApiextensionsAPIGroupDSL.class); when(client.apiextensions()).thenReturn(apiGroupDSL); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java index d5646c476a..9bdd54ca8d 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/OperatorTest.java @@ -59,5 +59,4 @@ public UpdateControl reconcile(ConfigMap resource, Context context) { return UpdateControl.noUpdate(); } } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java index 827bf7916a..abc83b94ff 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/ReconcilerUtilsTest.java @@ -140,61 +140,62 @@ private Deployment createTestDeployment() { @Test void handleKubernetesExceptionShouldThrowMissingCRDExceptionWhenAppropriate() { var request = mock(HttpRequest.class); - when(request.uri()).thenReturn(URI - .create(RESOURCE_URI)); - assertThrows(MissingCRDException.class, () -> handleKubernetesClientException( - new KubernetesClientException( - "Failure executing: GET at: " + RESOURCE_URI + ". Message: Not Found.", - null, 404, null, request), - HasMetadata.getFullResourceName(Tomcat.class))); + when(request.uri()).thenReturn(URI.create(RESOURCE_URI)); + assertThrows( + MissingCRDException.class, + () -> + handleKubernetesClientException( + new KubernetesClientException( + "Failure executing: GET at: " + RESOURCE_URI + ". Message: Not Found.", + null, + 404, + null, + request), + HasMetadata.getFullResourceName(Tomcat.class))); } - @Test void checksIfOwnerReferenceCanBeAdded() { - assertThrows(OperatorException.class, - () -> ReconcilerUtils.checkIfCanAddOwnerReference(namespacedResource(), - namespacedResourceFromOtherNamespace())); - - assertThrows(OperatorException.class, - () -> ReconcilerUtils.checkIfCanAddOwnerReference(namespacedResource(), - clusterScopedResource())); - - assertDoesNotThrow(() -> { - ReconcilerUtils.checkIfCanAddOwnerReference(clusterScopedResource(), clusterScopedResource()); - ReconcilerUtils.checkIfCanAddOwnerReference(namespacedResource(), namespacedResource()); - }); + assertThrows( + OperatorException.class, + () -> + ReconcilerUtils.checkIfCanAddOwnerReference( + namespacedResource(), namespacedResourceFromOtherNamespace())); + + assertThrows( + OperatorException.class, + () -> + ReconcilerUtils.checkIfCanAddOwnerReference( + namespacedResource(), clusterScopedResource())); + + assertDoesNotThrow( + () -> { + ReconcilerUtils.checkIfCanAddOwnerReference( + clusterScopedResource(), clusterScopedResource()); + ReconcilerUtils.checkIfCanAddOwnerReference(namespacedResource(), namespacedResource()); + }); } private ClusterRole clusterScopedResource() { - return new ClusterRoleBuilder() - .withMetadata(new ObjectMetaBuilder() - .build()) - .build(); + return new ClusterRoleBuilder().withMetadata(new ObjectMetaBuilder().build()).build(); } private ConfigMap namespacedResource() { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withNamespace("testns1") - .build()) + .withMetadata(new ObjectMetaBuilder().withNamespace("testns1").build()) .build(); } private ConfigMap namespacedResourceFromOtherNamespace() { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withNamespace("testns2") - .build()) + .withMetadata(new ObjectMetaBuilder().withNamespace("testns2").build()) .build(); } @Group("tomcatoperator.io") @Version("v1") @ShortNames("tc") - private static class Tomcat extends CustomResource implements Namespaced { - - } + private static class Tomcat extends CustomResource implements Namespaced {} private static class TomcatSpec { private Integer replicas; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/TestUtils.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/TestUtils.java index 3b2c5354f5..afef9e6703 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/TestUtils.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/TestUtils.java @@ -53,5 +53,4 @@ public static T markForDeletion(T customResource) { customResource.getMetadata().setDeletionTimestamp("2019-8-10"); return customResource; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/DeleteControlTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/DeleteControlTest.java index 9907c0405e..66137ed790 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/DeleteControlTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/DeleteControlTest.java @@ -9,8 +9,7 @@ class DeleteControlTest { @Test void cannotReScheduleForDefaultDelete() { - Assertions.assertThrows(IllegalStateException.class, - () -> DeleteControl.defaultDelete().rescheduleAfter(1000L)); + Assertions.assertThrows( + IllegalStateException.class, () -> DeleteControl.defaultDelete().rescheduleAfter(1000L)); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java index 0e2b8e9cc2..4f30458d68 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ConfigurationServiceOverriderTest.java @@ -3,12 +3,14 @@ import java.time.Duration; import java.util.Optional; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadPoolExecutor; import org.junit.jupiter.api.Test; import io.fabric8.kubernetes.api.model.HasMetadata; import io.javaoperatorsdk.operator.api.monitoring.Metrics; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertNotEquals; class ConfigurationServiceOverriderTest { @@ -18,69 +20,87 @@ class ConfigurationServiceOverriderTest { private static final LeaderElectionConfiguration LEADER_ELECTION_CONFIGURATION = new LeaderElectionConfiguration("foo", "fooNS"); - private static final Cloner CLONER = new Cloner() { - @Override - public R clone(R object) { - return null; - } - }; + private static final Cloner CLONER = + new Cloner() { + @Override + public R clone(R object) { + return null; + } + }; + + final BaseConfigurationService config = + new BaseConfigurationService(null) { + @Override + public boolean checkCRDAndValidateLocalModel() { + return false; + } + + @Override + public Metrics getMetrics() { + return METRICS; + } + + @Override + public Cloner getResourceCloner() { + return CLONER; + } + + @Override + public Optional getLeaderElectionConfiguration() { + return Optional.of(LEADER_ELECTION_CONFIGURATION); + } + }; @Test void overrideShouldWork() { - final var config = new BaseConfigurationService(null) { - @Override - public boolean checkCRDAndValidateLocalModel() { - return false; - } - - @Override - public Metrics getMetrics() { - return METRICS; - } - - @Override - public Cloner getResourceCloner() { - return CLONER; - } - - @Override - public Optional getLeaderElectionConfiguration() { - return Optional.of(LEADER_ELECTION_CONFIGURATION); - } - }; - final var overridden = new ConfigurationServiceOverrider(config) - .checkingCRDAndValidateLocalModel(true) - .withExecutorService(Executors.newSingleThreadExecutor()) - .withWorkflowExecutorService(Executors.newFixedThreadPool(4)) - .withCloseClientOnStop(false) - .withResourceCloner(new Cloner() { - @Override - public R clone(R object) { - return null; - } - }) - .withConcurrentReconciliationThreads(25) - .withMetrics(new Metrics() {}) - .withLeaderElectionConfiguration(new LeaderElectionConfiguration("newLease", "newLeaseNS")) - .withInformerStoppedHandler((informer, ex) -> { - }) - .withReconciliationTerminationTimeout(Duration.ofSeconds(30)) - .build(); + + final var overridden = + new ConfigurationServiceOverrider(config) + .checkingCRDAndValidateLocalModel(true) + .withExecutorService(Executors.newSingleThreadExecutor()) + .withWorkflowExecutorService(Executors.newFixedThreadPool(4)) + .withCloseClientOnStop(false) + .withResourceCloner( + new Cloner() { + @Override + public R clone(R object) { + return null; + } + }) + .withConcurrentReconciliationThreads(25) + .withMetrics(new Metrics() {}) + .withLeaderElectionConfiguration( + new LeaderElectionConfiguration("newLease", "newLeaseNS")) + .withInformerStoppedHandler((informer, ex) -> {}) + .withReconciliationTerminationTimeout(Duration.ofSeconds(30)) + .build(); assertNotEquals(config.closeClientOnStop(), overridden.closeClientOnStop()); - assertNotEquals(config.checkCRDAndValidateLocalModel(), - overridden.checkCRDAndValidateLocalModel()); - assertNotEquals(config.concurrentReconciliationThreads(), - overridden.concurrentReconciliationThreads()); + assertNotEquals( + config.checkCRDAndValidateLocalModel(), overridden.checkCRDAndValidateLocalModel()); + assertNotEquals( + config.concurrentReconciliationThreads(), overridden.concurrentReconciliationThreads()); assertNotEquals(config.getExecutorService(), overridden.getExecutorService()); assertNotEquals(config.getWorkflowExecutorService(), overridden.getWorkflowExecutorService()); assertNotEquals(config.getMetrics(), overridden.getMetrics()); - assertNotEquals(config.getLeaderElectionConfiguration(), - overridden.getLeaderElectionConfiguration()); - assertNotEquals(config.getInformerStoppedHandler(), - overridden.getLeaderElectionConfiguration()); - assertNotEquals(config.reconciliationTerminationTimeout(), - overridden.reconciliationTerminationTimeout()); + assertNotEquals( + config.getLeaderElectionConfiguration(), overridden.getLeaderElectionConfiguration()); + assertNotEquals( + config.getInformerStoppedHandler(), overridden.getLeaderElectionConfiguration()); + assertNotEquals( + config.reconciliationTerminationTimeout(), overridden.reconciliationTerminationTimeout()); } + @Test + void threadCountConfiguredProperly() { + final var overridden = + new ConfigurationServiceOverrider(config) + .withConcurrentReconciliationThreads(13) + .withConcurrentWorkflowExecutorThreads(14) + .build(); + assertThat(((ThreadPoolExecutor) overridden.getExecutorService()).getMaximumPoolSize()) + .isEqualTo(13); + assertThat(((ThreadPoolExecutor) overridden.getWorkflowExecutorService()).getMaximumPoolSize()) + .isEqualTo(14); + } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java index 21382adbd5..49d0b76017 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/ControllerConfigurationOverriderTest.java @@ -63,25 +63,25 @@ void overridingNSShouldPreserveUntouchedDependents() { final var externalDRName = DependentResource.defaultNameFor(NamedDependentReconciler.ExternalDependentResource.class); final var stringConfig = "some String configuration"; - configuration = ControllerConfigurationOverrider.override(configuration) - .settingNamespace(namespace) - .replacingNamedDependentResourceConfig(externalDRName, stringConfig) - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration) + .settingNamespace(namespace) + .replacingNamedDependentResourceConfig(externalDRName, stringConfig) + .build(); assertEquals(Set.of(namespace), configuration.getInformerConfig().getNamespaces()); // check that we still have the proper number of dependent configs dependentResources = configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertEquals(2, dependentResources.size()); - final var resourceConfig = extractDependentKubernetesResourceConfig( - configuration, 1); + final var resourceConfig = extractDependentKubernetesResourceConfig(configuration, 1); assertEquals(stringConfig, resourceConfig); } @SuppressWarnings({"rawtypes"}) private KubernetesDependentResourceConfig extractFirstDependentKubernetesResourceConfig( io.javaoperatorsdk.operator.api.config.ControllerConfiguration configuration) { - return (KubernetesDependentResourceConfig) extractDependentKubernetesResourceConfig( - configuration, 0); + return (KubernetesDependentResourceConfig) + extractDependentKubernetesResourceConfig(configuration, 0); } private io.javaoperatorsdk.operator.api.config.ControllerConfiguration createConfiguration( @@ -97,47 +97,45 @@ void overridingNamespacesShouldWork() { assertFalse(informerConfig.watchAllNamespaces()); assertFalse(informerConfig.watchCurrentNamespace()); - configuration = ControllerConfigurationOverrider.override(configuration) - .addingNamespaces("foo", "bar") - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration) + .addingNamespaces("foo", "bar") + .build(); informerConfig = configuration.getInformerConfig(); assertEquals(Set.of("foo", "bar"), informerConfig.getNamespaces()); assertFalse(informerConfig.watchAllNamespaces()); assertFalse(informerConfig.watchCurrentNamespace()); - configuration = ControllerConfigurationOverrider.override(configuration) - .removingNamespaces("bar") - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration).removingNamespaces("bar").build(); informerConfig = configuration.getInformerConfig(); assertEquals(Set.of("foo"), informerConfig.getNamespaces()); assertFalse(informerConfig.watchAllNamespaces()); assertFalse(informerConfig.watchCurrentNamespace()); - configuration = ControllerConfigurationOverrider.override(configuration) - .removingNamespaces("foo") - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration).removingNamespaces("foo").build(); informerConfig = configuration.getInformerConfig(); assertTrue(informerConfig.watchAllNamespaces()); assertFalse(informerConfig.watchCurrentNamespace()); - configuration = ControllerConfigurationOverrider.override(configuration) - .settingNamespace("foo") - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration).settingNamespace("foo").build(); informerConfig = configuration.getInformerConfig(); assertFalse(informerConfig.watchAllNamespaces()); assertFalse(informerConfig.watchCurrentNamespace()); assertEquals(Set.of("foo"), informerConfig.getNamespaces()); - configuration = ControllerConfigurationOverrider.override(configuration) - .watchingOnlyCurrentNamespace() - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration) + .watchingOnlyCurrentNamespace() + .build(); informerConfig = configuration.getInformerConfig(); assertFalse(informerConfig.watchAllNamespaces()); assertTrue(informerConfig.watchCurrentNamespace()); - configuration = ControllerConfigurationOverrider.override(configuration) - .watchingAllNamespaces() - .build(); + configuration = + ControllerConfigurationOverrider.override(configuration).watchingAllNamespaces().build(); informerConfig = configuration.getInformerConfig(); assertTrue(informerConfig.watchAllNamespaces()); assertFalse(informerConfig.watchCurrentNamespace()); @@ -147,8 +145,7 @@ void overridingNamespacesShouldWork() { void itemStorePreserved() { var configuration = createConfiguration(new WatchCurrentReconciler()); - configuration = ControllerConfigurationOverrider.override(configuration) - .build(); + configuration = ControllerConfigurationOverrider.override(configuration).build(); assertNotNull(configuration.getInformerConfig().getItemStore()); } @@ -160,14 +157,16 @@ void configuredDependentShouldNotChangeOnParentOverrideEvenWhenInitialConfigIsSa var kubeDependentConfig = extractFirstDependentKubernetesResourceConfig(configuration); // override the parent NS to match the dependent's - configuration = ControllerConfigurationOverrider.override(configuration) - .settingNamespace(OverriddenNSDependent.DEP_NS).build(); - assertEquals(Set.of(OverriddenNSDependent.DEP_NS), - configuration.getInformerConfig().getNamespaces()); + configuration = + ControllerConfigurationOverrider.override(configuration) + .settingNamespace(OverriddenNSDependent.DEP_NS) + .build(); + assertEquals( + Set.of(OverriddenNSDependent.DEP_NS), configuration.getInformerConfig().getNamespaces()); // check that the DependentResource inherits has its own configured NS - assertEquals(Set.of(OverriddenNSDependent.DEP_NS), - kubeDependentConfig.informerConfig().getNamespaces()); + assertEquals( + Set.of(OverriddenNSDependent.DEP_NS), kubeDependentConfig.informerConfig().getNamespaces()); // override the parent's NS final var newNS = "bar"; @@ -176,8 +175,8 @@ void configuredDependentShouldNotChangeOnParentOverrideEvenWhenInitialConfigIsSa // check that dependent config is still using its own NS kubeDependentConfig = extractFirstDependentKubernetesResourceConfig(configuration); - assertEquals(Set.of(OverriddenNSDependent.DEP_NS), - kubeDependentConfig.informerConfig().getNamespaces()); + assertEquals( + Set.of(OverriddenNSDependent.DEP_NS), kubeDependentConfig.informerConfig().getNamespaces()); } @SuppressWarnings("unchecked") @@ -190,7 +189,6 @@ void dependentShouldWatchAllNamespacesIfParentDoesAsWell() { // check that the DependentResource inherits the controller's configuration if applicable var informerConfig = config.informerConfig(); assertTrue(inheritsNamespacesFromController(informerConfig.getNamespaces())); - } @SuppressWarnings("unchecked") @@ -201,9 +199,7 @@ void shouldBePossibleToForceDependentToWatchAllNamespaces() { var config = extractFirstDependentKubernetesResourceConfig(configuration); // check that the DependentResource inherits the controller's configuration if applicable - assertTrue( - InformerConfiguration - .allNamespacesWatched(config.informerConfig().getNamespaces())); + assertTrue(InformerConfiguration.allNamespacesWatched(config.informerConfig().getNamespaces())); // override the NS final var newNS = "bar"; @@ -212,9 +208,7 @@ void shouldBePossibleToForceDependentToWatchAllNamespaces() { // check that dependent config is still configured to watch all NS config = extractFirstDependentKubernetesResourceConfig(configuration); - assertTrue( - InformerConfiguration - .allNamespacesWatched(config.informerConfig().getNamespaces())); + assertTrue(InformerConfiguration.allNamespacesWatched(config.informerConfig().getNamespaces())); } @Test @@ -234,8 +228,7 @@ void alreadyOverriddenDependentNamespacesShouldNotBePropagated() { var config = extractFirstDependentKubernetesResourceConfig(configuration); // DependentResource has its own NS - assertEquals(Set.of(OverriddenNSDependent.DEP_NS), - config.informerConfig().getNamespaces()); + assertEquals(Set.of(OverriddenNSDependent.DEP_NS), config.informerConfig().getNamespaces()); // override the NS final var newNS = "bar"; @@ -244,8 +237,7 @@ void alreadyOverriddenDependentNamespacesShouldNotBePropagated() { // check that dependent config is still using its own NS config = extractFirstDependentKubernetesResourceConfig(configuration); - assertEquals(Set.of(OverriddenNSDependent.DEP_NS), - config.informerConfig().getNamespaces()); + assertEquals(Set.of(OverriddenNSDependent.DEP_NS), config.informerConfig().getNamespaces()); } @Test @@ -259,9 +251,11 @@ void replaceNamedDependentResourceConfigShouldWork() { final var dependentResourceName = DependentResource.defaultNameFor(ReadOnlyDependent.class); assertTrue(dependents.stream().anyMatch(dr -> dr.getName().equals(dependentResourceName))); - var dependentSpec = dependents.stream() - .filter(dr -> dr.getName().equals(dependentResourceName)) - .findFirst().orElseThrow(); + var dependentSpec = + dependents.stream() + .filter(dr -> dr.getName().equals(dependentResourceName)) + .findFirst() + .orElseThrow(); assertEquals(ReadOnlyDependent.class, dependentSpec.getDependentResourceClass()); var maybeConfig = extractFirstDependentKubernetesResourceConfig(configuration); assertNotNull(maybeConfig); @@ -276,22 +270,25 @@ void replaceNamedDependentResourceConfigShouldWork() { // override the namespaces for the dependent resource final var overriddenNS = "newNS"; final var labelSelector = "foo=bar"; - final var overridingInformerConfig = InformerConfiguration.builder(ConfigMap.class) - .withNamespaces(Set.of(overriddenNS)) - .withLabelSelector(labelSelector) - .build(); - final var overridden = ControllerConfigurationOverrider.override(configuration) - .replacingNamedDependentResourceConfig( - dependentResourceName, - new KubernetesDependentResourceConfigBuilder() - .withKubernetesDependentInformerConfig(overridingInformerConfig) - .build()) - .build(); + final var overridingInformerConfig = + InformerConfiguration.builder(ConfigMap.class) + .withNamespaces(Set.of(overriddenNS)) + .withLabelSelector(labelSelector) + .build(); + final var overridden = + ControllerConfigurationOverrider.override(configuration) + .replacingNamedDependentResourceConfig( + dependentResourceName, + new KubernetesDependentResourceConfigBuilder() + .withKubernetesDependentInformerConfig(overridingInformerConfig) + .build()) + .build(); dependents = overridden.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); - dependentSpec = dependents.stream() - .filter(dr -> dr.getName().equals(dependentResourceName)) - .findFirst() - .orElseThrow(); + dependentSpec = + dependents.stream() + .filter(dr -> dr.getName().equals(dependentResourceName)) + .findFirst() + .orElseThrow(); config = (KubernetesDependentResourceConfig) overridden.getConfigurationFor(dependentSpec); informerConfig = config.informerConfig(); assertEquals(labelSelector, informerConfig.getLabelSelector()); @@ -305,11 +302,9 @@ private static class MyItemStore extends BasicItemStore { @Override @@ -341,17 +336,18 @@ public UpdateControl reconcile(ConfigMap resource, Context private static class TestCondition implements Condition { @Override - public boolean isMet(DependentResource dependentResource, + public boolean isMet( + DependentResource dependentResource, ConfigMap primary, Context context) { return true; } } - @Workflow(dependents = @Dependent(type = ReadOnlyDependent.class, - readyPostcondition = TestCondition.class)) - @ControllerConfiguration( - informer = @Informer(namespaces = OneDepReconciler.CONFIGURED_NS)) + @Workflow( + dependents = + @Dependent(type = ReadOnlyDependent.class, readyPostcondition = TestCondition.class)) + @ControllerConfiguration(informer = @Informer(namespaces = OneDepReconciler.CONFIGURED_NS)) private static class OneDepReconciler implements Reconciler { private static final String CONFIGURED_NS = "foo"; @@ -363,23 +359,11 @@ public UpdateControl reconcile(ConfigMap resource, Context } public static class ReadOnlyDependent extends KubernetesDependentResource - implements GarbageCollected { + implements GarbageCollected {} - public ReadOnlyDependent() { - super(ConfigMap.class); - } - } - - @KubernetesDependent( - informer = @Informer(namespaces = Constants.WATCH_ALL_NAMESPACES)) - public static class WatchAllNSDependent - extends KubernetesDependentResource - implements GarbageCollected { - - public WatchAllNSDependent() { - super(ConfigMap.class); - } - } + @KubernetesDependent(informer = @Informer(namespaces = Constants.WATCH_ALL_NAMESPACES)) + public static class WatchAllNSDependent extends KubernetesDependentResource + implements GarbageCollected {} @Workflow(dependents = @Dependent(type = OverriddenNSDependent.class)) @ControllerConfiguration( @@ -400,16 +384,13 @@ public static class OverriddenNSDependent implements GarbageCollected { private static final String DEP_NS = "dependentNS"; - - public OverriddenNSDependent() { - super(ConfigMap.class); - } } - @Workflow(dependents = { - @Dependent(type = NamedDependentReconciler.NamedDependentResource.class), - @Dependent(type = NamedDependentReconciler.ExternalDependentResource.class) - }) + @Workflow( + dependents = { + @Dependent(type = NamedDependentReconciler.NamedDependentResource.class), + @Dependent(type = NamedDependentReconciler.ExternalDependentResource.class) + }) @ControllerConfiguration public static class NamedDependentReconciler implements Reconciler { @@ -420,15 +401,12 @@ public UpdateControl reconcile(ConfigMap resource, Context private static class NamedDependentResource extends KubernetesDependentResource - implements GarbageCollected { - - public NamedDependentResource() { - super(ConfigMap.class); - } - } + implements GarbageCollected {} - private static class ExternalDependentResource implements DependentResource, - ConfiguredDependentResource, GarbageCollected { + private static class ExternalDependentResource + implements DependentResource, + ConfiguredDependentResource, + GarbageCollected { private String config = "UNSET"; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/InformerConfigurationTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/InformerConfigurationTest.java index 2857ab9335..a64b0e027e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/InformerConfigurationTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/InformerConfigurationTest.java @@ -15,12 +15,16 @@ class InformerConfigurationTest { @Test void allNamespacesWatched() { - assertThrows(IllegalArgumentException.class, - () -> InformerConfiguration.allNamespacesWatched(null)); - assertThrows(IllegalArgumentException.class, () -> InformerConfiguration.allNamespacesWatched( - Set.of(Constants.WATCH_CURRENT_NAMESPACE, Constants.WATCH_ALL_NAMESPACES, "foo"))); - assertThrows(IllegalArgumentException.class, () -> InformerConfiguration.allNamespacesWatched( - Collections.emptySet())); + assertThrows( + IllegalArgumentException.class, () -> InformerConfiguration.allNamespacesWatched(null)); + assertThrows( + IllegalArgumentException.class, + () -> + InformerConfiguration.allNamespacesWatched( + Set.of(Constants.WATCH_CURRENT_NAMESPACE, Constants.WATCH_ALL_NAMESPACES, "foo"))); + assertThrows( + IllegalArgumentException.class, + () -> InformerConfiguration.allNamespacesWatched(Collections.emptySet())); assertFalse(InformerConfiguration.allNamespacesWatched(Set.of("foo", "bar"))); assertTrue(InformerConfiguration.allNamespacesWatched(Set.of(Constants.WATCH_ALL_NAMESPACES))); assertFalse(InformerConfiguration.allNamespacesWatched(Set.of("foo"))); @@ -30,12 +34,15 @@ void allNamespacesWatched() { @Test void currentNamespaceWatched() { - assertThrows(IllegalArgumentException.class, - () -> InformerConfiguration.currentNamespaceWatched(null)); - assertThrows(IllegalArgumentException.class, - () -> InformerConfiguration.currentNamespaceWatched( - Set.of(Constants.WATCH_CURRENT_NAMESPACE, Constants.WATCH_ALL_NAMESPACES, "foo"))); - assertThrows(IllegalArgumentException.class, + assertThrows( + IllegalArgumentException.class, () -> InformerConfiguration.currentNamespaceWatched(null)); + assertThrows( + IllegalArgumentException.class, + () -> + InformerConfiguration.currentNamespaceWatched( + Set.of(Constants.WATCH_CURRENT_NAMESPACE, Constants.WATCH_ALL_NAMESPACES, "foo"))); + assertThrows( + IllegalArgumentException.class, () -> InformerConfiguration.currentNamespaceWatched(Collections.emptySet())); assertFalse(InformerConfiguration.currentNamespaceWatched(Set.of("foo", "bar"))); assertFalse( @@ -66,14 +73,21 @@ void shouldFollowControllerNamespacesByDefaultForInformerEventSource() { @Test void failIfNotValid() { assertThrows(IllegalArgumentException.class, () -> InformerConfiguration.failIfNotValid(null)); - assertThrows(IllegalArgumentException.class, + assertThrows( + IllegalArgumentException.class, () -> InformerConfiguration.failIfNotValid(Collections.emptySet())); - assertThrows(IllegalArgumentException.class, () -> InformerConfiguration.failIfNotValid( - Set.of(Constants.WATCH_CURRENT_NAMESPACE, Constants.WATCH_ALL_NAMESPACES, "foo"))); - assertThrows(IllegalArgumentException.class, () -> InformerConfiguration.failIfNotValid( - Set.of(Constants.WATCH_CURRENT_NAMESPACE, "foo"))); - assertThrows(IllegalArgumentException.class, () -> InformerConfiguration.failIfNotValid( - Set.of(Constants.WATCH_ALL_NAMESPACES, "foo"))); + assertThrows( + IllegalArgumentException.class, + () -> + InformerConfiguration.failIfNotValid( + Set.of(Constants.WATCH_CURRENT_NAMESPACE, Constants.WATCH_ALL_NAMESPACES, "foo"))); + assertThrows( + IllegalArgumentException.class, + () -> + InformerConfiguration.failIfNotValid(Set.of(Constants.WATCH_CURRENT_NAMESPACE, "foo"))); + assertThrows( + IllegalArgumentException.class, + () -> InformerConfiguration.failIfNotValid(Set.of(Constants.WATCH_ALL_NAMESPACES, "foo"))); // should work InformerConfiguration.failIfNotValid(Set.of("foo", "bar")); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java index ec1223377c..a2246f018a 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/UtilsTest.java @@ -87,23 +87,27 @@ void getsFirstTypeArgumentFromExtendedClass() { @Test void getsFirstTypeArgumentFromInterface() { - assertThat(Utils.getFirstTypeArgumentFromInterface(EmptyTestDependentResource.class, - DependentResource.class)) + assertThat( + Utils.getFirstTypeArgumentFromInterface( + EmptyTestDependentResource.class, DependentResource.class)) .isEqualTo(Deployment.class); - assertThatIllegalArgumentException().isThrownBy( - () -> Utils.getFirstTypeArgumentFromInterface(TestKubernetesDependentResource.class, - DependentResource.class)); + assertThatIllegalArgumentException() + .isThrownBy( + () -> + Utils.getFirstTypeArgumentFromInterface( + TestKubernetesDependentResource.class, DependentResource.class)); } @Test void getsFirstTypeArgumentFromInterfaceFromParent() { - assertThat(Utils.getFirstTypeArgumentFromSuperClassOrInterface(ConcreteReconciler.class, - Reconciler.class)).isEqualTo(ConfigMap.class); + assertThat( + Utils.getFirstTypeArgumentFromSuperClassOrInterface( + ConcreteReconciler.class, Reconciler.class)) + .isEqualTo(ConfigMap.class); } - public abstract static class AbstractReconciler

implements Reconciler

{ - } + public abstract static class AbstractReconciler

implements Reconciler

{} public static class ConcreteReconciler extends AbstractReconciler { @Override @@ -114,10 +118,5 @@ public UpdateControl reconcile(ConfigMap resource, Context } public static class TestKubernetesDependentResource - extends KubernetesDependentResource { - - public TestKubernetesDependentResource() { - super(Deployment.class); - } - } + extends KubernetesDependentResource {} } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/VersionTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/VersionTest.java index d902a75860..2c3134b6ad 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/VersionTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/VersionTest.java @@ -13,5 +13,4 @@ void versionShouldReturnTheSameResultFromMavenAndProperties() { assertEquals(versionFromProperties, versionFromMaven); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java index 5cc5472798..27bd2b9dae 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/config/dependent/DependentResourceConfigurationResolverTest.java @@ -31,19 +31,21 @@ class DependentResourceConfigurationResolverTest { // subclass to expose configFor method to this test class - private final static class TestConfigurationService extends BaseConfigurationService { + private static final class TestConfigurationService extends BaseConfigurationService { @Override - protected

io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( - Reconciler

reconciler) { + protected

+ io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( + Reconciler

reconciler) { return super.configFor(reconciler); } } private final TestConfigurationService configurationService = new TestConfigurationService(); - private

io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( - Reconciler

reconciler) { + private

+ io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( + Reconciler

reconciler) { // ensure that a new configuration is created each time return configurationService.configFor(reconciler); } @@ -55,7 +57,8 @@ private static Object extractDependentKubernetesResourceConfig( final var spec = configuration.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().stream() .filter(s -> target.isAssignableFrom(s.getDependentResourceClass())) - .findFirst().orElseThrow(); + .findFirst() + .orElseThrow(); return configuration.getConfigurationFor(spec); } @@ -68,13 +71,15 @@ void controllerConfigurationProvidedShouldBeReturnedIfAvailable() { assertInstanceOf(CustomConfig.class, customConfig); assertEquals(CustomAnnotatedDep.PROVIDED_VALUE, ((CustomConfig) customConfig).getValue()); final var newConfig = new CustomConfig(72); - final var overridden = ControllerConfigurationOverrider.override(cfg) - .replacingNamedDependentResourceConfig(DR_NAME, newConfig) - .build(); - final var spec = cfg.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().stream() - .filter(s -> DR_NAME.equals(s.getName())) - .findFirst() - .orElseThrow(); + final var overridden = + ControllerConfigurationOverrider.override(cfg) + .replacingNamedDependentResourceConfig(DR_NAME, newConfig) + .build(); + final var spec = + cfg.getWorkflowSpec().orElseThrow().getDependentResourceSpecs().stream() + .filter(s -> DR_NAME.equals(s.getName())) + .findFirst() + .orElseThrow(); assertEquals(newConfig, overridden.getConfigurationFor(spec)); } @@ -89,23 +94,25 @@ void getConverterShouldWork() { converter = DependentResourceConfigurationResolver.getConverter(ChildCustomAnnotatedDep.class); assertNotNull(converter); assertEquals(CustomConfigConverter.class, converter.getClass()); - assertEquals(DependentResourceConfigurationResolver.getConverter(CustomAnnotatedDep.class), - converter); + assertEquals( + DependentResourceConfigurationResolver.getConverter(CustomAnnotatedDep.class), converter); } @SuppressWarnings("rawtypes") @Test void registerConverterShouldWork() { - final var overriddenConverter = new ConfigurationConverter() { - - @Override - public Object configFrom(Annotation configAnnotation, DependentResourceSpec spec, - io.javaoperatorsdk.operator.api.config.ControllerConfiguration parentConfiguration) { - return null; - } - }; - DependentResourceConfigurationResolver.registerConverter(ServiceDep.class, - overriddenConverter); + final var overriddenConverter = + new ConfigurationConverter() { + + @Override + public Object configFrom( + Annotation configAnnotation, + DependentResourceSpec spec, + io.javaoperatorsdk.operator.api.config.ControllerConfiguration parentConfiguration) { + return null; + } + }; + DependentResourceConfigurationResolver.registerConverter(ServiceDep.class, overriddenConverter); configFor(new CustomAnnotationReconciler()); // non overridden dependents should use the default converter @@ -117,12 +124,13 @@ public Object configFrom(Annotation configAnnotation, DependentResourceSpec spec assertEquals(overriddenConverter, converter); } - @Workflow(dependents = { - @Dependent(type = CustomAnnotatedDep.class, name = DR_NAME), - @Dependent(type = ChildCustomAnnotatedDep.class), - @Dependent(type = ConfigMapDep.class), - @Dependent(type = ServiceDep.class) - }) + @Workflow( + dependents = { + @Dependent(type = CustomAnnotatedDep.class, name = DR_NAME), + @Dependent(type = ChildCustomAnnotatedDep.class), + @Dependent(type = ConfigMapDep.class), + @Dependent(type = ServiceDep.class) + }) @ControllerConfiguration static class CustomAnnotationReconciler implements Reconciler { @@ -136,26 +144,20 @@ public UpdateControl reconcile(ConfigMap resource, Context } public static class ConfigMapDep extends KubernetesDependentResource - implements GarbageCollected { - - public ConfigMapDep() { - super(ConfigMap.class); - } - } + implements GarbageCollected {} public static class ServiceDep extends KubernetesDependentResource - implements GarbageCollected { - - public ServiceDep() { - super(Service.class); - } - } + implements GarbageCollected {} @CustomAnnotation(value = CustomAnnotatedDep.PROVIDED_VALUE) - @Configured(by = CustomAnnotation.class, with = CustomConfig.class, + @Configured( + by = CustomAnnotation.class, + with = CustomConfig.class, converter = CustomConfigConverter.class) - private static class CustomAnnotatedDep implements DependentResource, - ConfiguredDependentResource, GarbageCollected { + private static class CustomAnnotatedDep + implements DependentResource, + ConfiguredDependentResource, + GarbageCollected { public static final int PROVIDED_VALUE = 42; private CustomConfig config; @@ -181,14 +183,10 @@ public Optional configuration() { } @Override - public void delete(ConfigMap primary, Context context) { - - } + public void delete(ConfigMap primary, Context context) {} } - private static class ChildCustomAnnotatedDep extends CustomAnnotatedDep { - - } + private static class ChildCustomAnnotatedDep extends CustomAnnotatedDep {} @Retention(RetentionPolicy.RUNTIME) private @interface CustomAnnotation { @@ -215,7 +213,8 @@ private static class CustomConfigConverter static final int CONVERTER_PROVIDED_DEFAULT = 7; @Override - public CustomConfig configFrom(CustomAnnotation configAnnotation, + public CustomConfig configFrom( + CustomAnnotation configAnnotation, DependentResourceSpec spec, io.javaoperatorsdk.operator.api.config.ControllerConfiguration parentConfiguration) { if (configAnnotation == null) { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContextTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContextTest.java index 3d8fc9563e..b289d68b22 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContextTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/DefaultContextTest.java @@ -9,19 +9,19 @@ import io.javaoperatorsdk.operator.processing.event.NoEventSourceForClassException; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class DefaultContextTest { - Secret primary = new Secret(); - Controller mockController = mock(Controller.class); + private final Secret primary = new Secret(); + private final Controller mockController = mock(); - DefaultContext context = new DefaultContext<>(null, mockController, primary); + private final DefaultContext context = new DefaultContext<>(null, mockController, primary); @Test + @SuppressWarnings("unchecked") void getSecondaryResourceReturnsEmptyOptionalOnNonActivatedDRType() { var mockManager = mock(EventSourceManager.class); when(mockController.getEventSourceManager()).thenReturn(mockManager); @@ -30,8 +30,14 @@ void getSecondaryResourceReturnsEmptyOptionalOnNonActivatedDRType() { .thenThrow(new NoEventSourceForClassException(ConfigMap.class)); var res = context.getSecondaryResource(ConfigMap.class); - assertThat(res).isEmpty(); } + @Test + void setRetryInfo() { + RetryInfo retryInfo = mock(); + var newContext = context.setRetryInfo(retryInfo); + assertThat(newContext).isSameAs(context); + assertThat(newContext.getRetryInfo()).hasValue(retryInfo); + } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java new file mode 100644 index 0000000000..438941db9c --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/PrimaryUpdateAndCacheUtilsTest.java @@ -0,0 +1,111 @@ +package io.javaoperatorsdk.operator.api.reconciler; + +import java.util.function.UnaryOperator; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientException; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.Resource; +import io.javaoperatorsdk.operator.OperatorException; +import io.javaoperatorsdk.operator.TestUtils; +import io.javaoperatorsdk.operator.processing.event.EventSourceRetriever; +import io.javaoperatorsdk.operator.processing.event.source.controller.ControllerEventSource; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; + +import static io.javaoperatorsdk.operator.api.reconciler.PrimaryUpdateAndCacheUtils.DEFAULT_MAX_RETRY; +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class PrimaryUpdateAndCacheUtilsTest { + + Context context = mock(Context.class); + KubernetesClient client = mock(KubernetesClient.class); + Resource resource = mock(Resource.class); + + @BeforeEach + void setup() { + when(context.getClient()).thenReturn(client); + var esr = mock(EventSourceRetriever.class); + when(context.eventSourceRetriever()).thenReturn(esr); + when(esr.getControllerEventSource()).thenReturn(mock(ControllerEventSource.class)); + var mixedOp = mock(MixedOperation.class); + when(client.resources(any())).thenReturn(mixedOp); + when(mixedOp.inNamespace(any())).thenReturn(mixedOp); + when(mixedOp.withName(any())).thenReturn(resource); + when(resource.get()).thenReturn(TestUtils.testCustomResource1()); + } + + @Test + void handlesUpdate() { + var updated = + PrimaryUpdateAndCacheUtils.updateAndCacheResource( + TestUtils.testCustomResource1(), + context, + r -> { + var res = TestUtils.testCustomResource1(); + // setting this to null to test if value set in the implementation + res.getMetadata().setResourceVersion(null); + res.getSpec().setValue("updatedValue"); + return res; + }, + r -> { + // checks if the resource version is set from the original resource + assertThat(r.getMetadata().getResourceVersion()).isEqualTo("1"); + var res = TestUtils.testCustomResource1(); + res.setSpec(r.getSpec()); + res.getMetadata().setResourceVersion("2"); + return res; + }); + + assertThat(updated.getMetadata().getResourceVersion()).isEqualTo("2"); + assertThat(updated.getSpec().getValue()).isEqualTo("updatedValue"); + } + + @Test + void retriesConflicts() { + var updateOperation = mock(UnaryOperator.class); + + when(updateOperation.apply(any())) + .thenThrow(new KubernetesClientException("", 409, null)) + .thenReturn(TestUtils.testCustomResource1()); + + var updated = + PrimaryUpdateAndCacheUtils.updateAndCacheResource( + TestUtils.testCustomResource1(), + context, + r -> { + var res = TestUtils.testCustomResource1(); + res.getSpec().setValue("updatedValue"); + return res; + }, + updateOperation); + + assertThat(updated).isNotNull(); + verify(resource, times(1)).get(); + } + + @Test + void throwsIfRetryExhausted() { + var updateOperation = mock(UnaryOperator.class); + + when(updateOperation.apply(any())).thenThrow(new KubernetesClientException("", 409, null)); + + assertThrows( + OperatorException.class, + () -> + PrimaryUpdateAndCacheUtils.updateAndCacheResource( + TestUtils.testCustomResource1(), + context, + UnaryOperator.identity(), + updateOperation)); + verify(resource, times(DEFAULT_MAX_RETRY)).get(); + } +} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContextTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContextTest.java index 3ea53668fa..3a1e44de96 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContextTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/api/reconciler/dependent/managed/DefaultManagedDependentResourceContextTest.java @@ -45,12 +45,13 @@ void putNewValueReturnsPriorValue() { void putNewValueLogsWarningIfTypesDiffer() { // to check that we properly log things without setting up a complex fixture final String[] messages = new String[1]; - var context = new DefaultManagedWorkflowAndDependentResourceContext<>(null, null, null) { - @Override - void logWarning(String message) { - messages[0] = message; - } - }; + var context = + new DefaultManagedWorkflowAndDependentResourceContext<>(null, null, null) { + @Override + void logWarning(String message) { + messages[0] = message; + } + }; final var prior = "value"; final var key = "key"; context.put(key, prior); @@ -82,10 +83,13 @@ void getMandatory() { @Test void getMandatoryWhenEmpty() { - assertThatThrownBy(() -> { - context.getMandatory("key", String.class); - }).isInstanceOf(IllegalStateException.class) + assertThatThrownBy( + () -> { + context.getMandatory("key", String.class); + }) + .isInstanceOf(IllegalStateException.class) .hasMessage( - "Mandatory attribute (key: key, type: java.lang.String) is missing or not of the expected type"); + "Mandatory attribute (key: key, type: java.lang.String) is missing or not of the" + + " expected type"); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/ControllerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/ControllerTest.java index a81f524326..82ecdb111a 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/ControllerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/ControllerTest.java @@ -53,15 +53,14 @@ void crdShouldNotBeCheckedForNativeResources() { void crdShouldNotBeCheckedForCustomResourcesIfDisabled() { final var client = MockKubernetesClient.client(TestCustomResource.class); ConfigurationService configurationService = - ConfigurationService.newOverriddenConfigurationService(new BaseConfigurationService(), - o -> o.checkingCRDAndValidateLocalModel(false)); + ConfigurationService.newOverriddenConfigurationService( + new BaseConfigurationService(), o -> o.checkingCRDAndValidateLocalModel(false)); final var configuration = MockControllerConfiguration.forResource(TestCustomResource.class, configurationService); final var controller = new Controller(reconciler, configuration, client); controller.start(); verify(client, never()).apiextensions(); - } @Test @@ -70,24 +69,27 @@ void usesFinalizerIfThereIfReconcilerImplementsCleaner() { final var configuration = MockControllerConfiguration.forResource(Secret.class); when(configuration.getConfigurationService()).thenReturn(new BaseConfigurationService()); - final var controller = new Controller(reconciler, configuration, - MockKubernetesClient.client(Secret.class)); + final var controller = + new Controller( + reconciler, configuration, MockKubernetesClient.client(Secret.class)); assertThat(controller.useFinalizer()).isTrue(); } @ParameterizedTest @CsvSource({ - "true, true, true, false", - "true, true, false, true", - "false, true, true, true", - "false, true, false, true", - "true, false, true, false", + "true, true, true, false", + "true, true, false, true", + "false, true, true, true", + "false, true, false, true", + "true, false, true, false", }) - void callsCleanupOnWorkflowWhenHasCleanerAndReconcilerIsNotCleaner(boolean reconcilerIsCleaner, + void callsCleanupOnWorkflowWhenHasCleanerAndReconcilerIsNotCleaner( + boolean reconcilerIsCleaner, boolean workflowIsCleaner, boolean isExplicitWorkflowInvocation, - boolean workflowCleanerExecuted) throws Exception { + boolean workflowCleanerExecuted) + throws Exception { Reconciler reconciler; if (reconcilerIsCleaner) { @@ -116,8 +118,9 @@ void callsCleanupOnWorkflowWhenHasCleanerAndReconcilerIsNotCleaner(boolean recon var managedWorkflowMock = workflow(workflowIsCleaner); when(mockManagedWorkflow.resolve(any(), any())).thenReturn(managedWorkflowMock); - final var controller = new Controller(reconciler, configuration, - MockKubernetesClient.client(Secret.class)); + final var controller = + new Controller( + reconciler, configuration, MockKubernetesClient.client(Secret.class)); controller.cleanup(new Secret(), new DefaultContext<>(null, controller, new Secret())); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java index b7aeaae670..d871668c4d 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/GroupVersionKindTest.java @@ -29,15 +29,14 @@ void parseGVK() { assertThat(gvk.getVersion()).isEqualTo("v1"); assertThat(gvk.getKind()).isEqualTo("Deployment"); - gvk = GroupVersionKind.fromString("v1/ConfigMap"); assertThat(gvk.getGroup()).isNull(); assertThat(gvk.getVersion()).isEqualTo("v1"); assertThat(gvk.getKind()).isEqualTo("ConfigMap"); assertThrows(IllegalArgumentException.class, () -> GroupVersionKind.fromString("v1#ConfigMap")); - assertThrows(IllegalArgumentException.class, - () -> GroupVersionKind.fromString("api/beta/v1/ConfigMap")); + assertThrows( + IllegalArgumentException.class, () -> GroupVersionKind.fromString("api/beta/v1/ConfigMap")); } @Test @@ -62,18 +61,31 @@ void pluralShouldOnlyBeProvidedIfExplicitlySet() { @Test void pluralShouldBeEmptyIfNotProvided() { final var kind = "MyKind"; - var gvk = - GroupVersionKindPlural.gvkWithPlural(new GroupVersionKind("josdk.io", "v1", kind), null); + final var original = new GroupVersionKind("josdk.io", "v1", kind); + var gvk = GroupVersionKindPlural.gvkWithPlural(original, null); assertThat(gvk.getPlural()).isEmpty(); assertThat(gvk.getPluralOrDefault()) .isEqualTo(GroupVersionKindPlural.getDefaultPluralFor(kind)); + assertThat(gvk).isEqualTo(original); + assertThat(original).isEqualTo(gvk); + assertThat(gvk.hashCode()).isEqualTo(original.hashCode()); } @Test void pluralShouldOverrideDefaultComputedVersionIfProvided() { - var gvk = GroupVersionKindPlural.gvkWithPlural(new GroupVersionKind("josdk.io", "v1", "MyKind"), - "MyPlural"); + final var original = new GroupVersionKind("josdk.io", "v1", "MyKind"); + final var gvk = GroupVersionKindPlural.gvkWithPlural(original, "MyPlural"); assertThat(gvk.getPlural()).hasValue("MyPlural"); + assertThat(gvk).isNotEqualTo(original); + assertThat(original).isNotEqualTo(gvk); + assertThat(gvk.hashCode()).isNotEqualTo(original.hashCode()); + } + + @Test + void equals() { + final var original = new GroupVersionKind("josdk.io", "v1", "MyKind"); + assertEquals(original, original); + assertFalse(original.equals(null)); } @Test diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java index 330f5b85b7..939d046e5d 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/AbstractDependentResourceTest.java @@ -21,9 +21,9 @@ void throwsExceptionIfDesiredIsNullOnCreate() { testDependentResource.setSecondary(null); testDependentResource.setDesired(null); - assertThrows(DependentResourceException.class, + assertThrows( + DependentResourceException.class, () -> testDependentResource.reconcile(new TestCustomResource(), null)); - } @Test @@ -32,7 +32,8 @@ void throwsExceptionIfDesiredIsNullOnUpdate() { testDependentResource.setSecondary(configMap()); testDependentResource.setDesired(null); - assertThrows(DependentResourceException.class, + assertThrows( + DependentResourceException.class, () -> testDependentResource.reconcile(new TestCustomResource(), null)); } @@ -42,7 +43,8 @@ void throwsExceptionIfCreateReturnsNull() { testDependentResource.setSecondary(null); testDependentResource.setDesired(configMap()); - assertThrows(DependentResourceException.class, + assertThrows( + DependentResourceException.class, () -> testDependentResource.reconcile(new TestCustomResource(), null)); } @@ -52,16 +54,15 @@ void throwsExceptionIfUpdateReturnsNull() { testDependentResource.setSecondary(configMap()); testDependentResource.setDesired(configMap()); - assertThrows(DependentResourceException.class, + assertThrows( + DependentResourceException.class, () -> testDependentResource.reconcile(new TestCustomResource(), null)); } private ConfigMap configMap() { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName("test") - .withNamespace("default") - .build()); + configMap.setMetadata( + new ObjectMetaBuilder().withName("test").withNamespace("default").build()); return configMap; } @@ -78,17 +79,20 @@ public Class resourceType() { } @Override - public Optional getSecondaryResource(TestCustomResource primary, - Context context) { + public Optional getSecondaryResource( + TestCustomResource primary, Context context) { return Optional.ofNullable(secondary); } @Override - protected void onCreated(TestCustomResource primary, ConfigMap created, - Context context) {} + protected void onCreated( + TestCustomResource primary, ConfigMap created, Context context) {} @Override - protected void onUpdated(TestCustomResource primary, ConfigMap updated, ConfigMap actual, + protected void onUpdated( + TestCustomResource primary, + ConfigMap updated, + ConfigMap actual, Context context) {} @Override @@ -115,21 +119,24 @@ public TestDependentResource setDesired(ConfigMap desired) { } @Override - public ConfigMap create(ConfigMap desired, TestCustomResource primary, - Context context) { + public ConfigMap create( + ConfigMap desired, TestCustomResource primary, Context context) { return null; } @Override - public ConfigMap update(ConfigMap actual, ConfigMap desired, TestCustomResource primary, + public ConfigMap update( + ConfigMap actual, + ConfigMap desired, + TestCustomResource primary, Context context) { return null; } @Override @SuppressWarnings("unchecked") - public Matcher.Result match(ConfigMap actualResource, TestCustomResource primary, - Context context) { + public Matcher.Result match( + ConfigMap actualResource, TestCustomResource primary, Context context) { var result = mock(Matcher.Result.class); when(result.matched()).thenReturn(false); return result; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java index 25a849e3ab..9d88f90a0f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/EmptyTestDependentResource.java @@ -12,8 +12,8 @@ public class EmptyTestDependentResource private String name; @Override - public ReconcileResult reconcile(TestCustomResource primary, - Context context) { + public ReconcileResult reconcile( + TestCustomResource primary, Context context) { return null; } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java index 2663657157..3062e360e2 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericKubernetesResourceMatcherTest.java @@ -28,7 +28,6 @@ class GenericKubernetesResourceMatcherTest { Deployment desired = createDeployment(); TestDependentResource dependentResource = new TestDependentResource(desired); - @BeforeAll static void setUp() { final var client = MockKubernetesClient.client(HasMetadata.class); @@ -55,8 +54,7 @@ void matchesAdditiveOnlyChanges() { @Test void matchesWithStrongSpecEquality() { actual.getSpec().getTemplate().getMetadata().getLabels().put("new-key", "val"); - assertThat(match(desired, actual, true, true, context) - .matched()) + assertThat(match(desired, actual, true, true, context).matched()) .withFailMessage("Adding values should fail matching when strong equality is required") .isFalse(); } @@ -64,9 +62,10 @@ void matchesWithStrongSpecEquality() { @Test void doesNotMatchRemovedValues() { actual = createDeployment(); - assertThat(GenericKubernetesResourceMatcher - .match(dependentResource.desired(createPrimary("removed"), null), actual, context) - .matched()) + assertThat( + GenericKubernetesResourceMatcher.match( + dependentResource.desired(createPrimary("removed"), null), actual, context) + .matched()) .withFailMessage("Removing values in metadata should lead to a mismatch") .isFalse(); } @@ -119,11 +118,12 @@ void ignoresWholeSubPath() { @Test void matchesMetadata() { - actual = new DeploymentBuilder(createDeployment()) - .editOrNewMetadata() - .addToAnnotations("test", "value") - .endMetadata() - .build(); + actual = + new DeploymentBuilder(createDeployment()) + .editOrNewMetadata() + .addToAnnotations("test", "value") + .endMetadata() + .build(); assertThat(match(dependentResource, actual, null, context, false).matched()) .withFailMessage("Annotations shouldn't matter when metadata is not considered") .isTrue(); @@ -134,9 +134,9 @@ void matchesMetadata() { assertThat(match(desired, actual, false, false, context).matched()) .withFailMessage( - "Should match when strong equality is not considered and only additive changes are made") + "Should match when strong equality is not considered and only additive changes are" + + " made") .isTrue(); - } @Test @@ -144,12 +144,13 @@ void checkServiceAccount() { final var serviceAccountDR = new ServiceAccountDR(); final var desired = serviceAccountDR.desired(null, context); - var actual = new ServiceAccountBuilder(desired) - .addNewImagePullSecret("imagePullSecret3") - .build(); + var actual = + new ServiceAccountBuilder(desired).addNewImagePullSecret("imagePullSecret3").build(); - assertThat(GenericKubernetesResourceMatcher.match(desired, actual, false, false, context) - .matched()).isTrue(); + assertThat( + GenericKubernetesResourceMatcher.match(desired, actual, false, false, context) + .matched()) + .isTrue(); } @Test @@ -158,17 +159,13 @@ void matchConfigMap() { var actual = createConfigMap(); actual.getData().put("key2", "val2"); - var match = GenericKubernetesResourceMatcher.match(desired, actual, - true, false, context); + var match = GenericKubernetesResourceMatcher.match(desired, actual, true, false, context); assertThat(match.matched()).isTrue(); } ConfigMap createConfigMap() { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("tes1") - .withNamespace("default") - .build()) + .withMetadata(new ObjectMetaBuilder().withName("tes1").withNamespace("default").build()) .withData(Map.of("key1", "val1")) .build(); } @@ -189,14 +186,12 @@ HasMetadata createPrimary(String caseName) { private static class ServiceAccountDR extends KubernetesDependentResource { - public ServiceAccountDR() { - super(ServiceAccount.class); - } - @Override protected ServiceAccount desired(HasMetadata primary, Context context) { return new ServiceAccountBuilder() - .withNewMetadata().withName("foo").endMetadata() + .withNewMetadata() + .withName("foo") + .endMetadata() .withAutomountServiceAccountToken() .addNewImagePullSecret("imagePullSecret1") .addNewImagePullSecret("imagePullSecret2") @@ -215,9 +210,10 @@ public TestDependentResource(Deployment desired) { @Override protected Deployment desired(HasMetadata primary, Context context) { - final var currentCase = Optional.ofNullable(primary) - .map(p -> p.getMetadata().getLabels().get("case")) - .orElse(null); + final var currentCase = + Optional.ofNullable(primary) + .map(p -> p.getMetadata().getLabels().get("case")) + .orElse(null); var d = desired; if ("removed".equals(currentCase)) { d = createDeployment(); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdaterTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdaterTest.java index 313520b89e..0acc4ed09e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdaterTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/GenericResourceUpdaterTest.java @@ -85,10 +85,11 @@ void checkSecret() { var desired = new SecretBuilder() .withMetadata(new ObjectMeta()) - .withImmutable().withType("Opaque").addToData("foo", "bar").build(); - var actual = new SecretBuilder() - .withMetadata(new ObjectMeta()) - .build(); + .withImmutable() + .withType("Opaque") + .addToData("foo", "bar") + .build(); + var actual = new SecretBuilder().withMetadata(new ObjectMeta()).build(); final var secret = GenericResourceUpdater.updateResource(actual, desired, context); assertThat(secret.getImmutable()).isTrue(); @@ -98,13 +99,15 @@ void checkSecret() { @Test void checkServiceAccount() { - var desired = new ServiceAccountBuilder() - .withMetadata(new ObjectMetaBuilder().addToLabels("new", "label").build()) - .build(); - var actual = new ServiceAccountBuilder() - .withMetadata(new ObjectMetaBuilder().addToLabels("a", "label").build()) - .withImagePullSecrets(new LocalObjectReferenceBuilder().withName("secret").build()) - .build(); + var desired = + new ServiceAccountBuilder() + .withMetadata(new ObjectMetaBuilder().addToLabels("new", "label").build()) + .build(); + var actual = + new ServiceAccountBuilder() + .withMetadata(new ObjectMetaBuilder().addToLabels("a", "label").build()) + .withImagePullSecrets(new LocalObjectReferenceBuilder().withName("secret").build()) + .build(); final var serviceAccount = GenericResourceUpdater.updateResource(actual, desired, context); assertThat(serviceAccount.getMetadata().getLabels()) @@ -116,5 +119,4 @@ Deployment createDeployment() { return ReconcilerUtils.loadYaml( Deployment.class, GenericResourceUpdaterTest.class, "nginx-deployment.yaml"); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceTest.java new file mode 100644 index 0000000000..6c48311f4b --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/KubernetesDependentResourceTest.java @@ -0,0 +1,76 @@ +package io.javaoperatorsdk.operator.processing.dependent.kubernetes; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.operator.sample.simple.TestCustomResource; + +import static io.javaoperatorsdk.operator.api.config.Utils.GENERIC_PARAMETER_TYPE_ERROR_PREFIX; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +class KubernetesDependentResourceTest { + + @ParameterizedTest + @ValueSource( + classes = { + TestDeploymentDependentResource.class, + ChildTestDeploymentDependentResource.class, + GrandChildTestDeploymentDependentResource.class, + ChildTypeWithValidKubernetesDependentResource.class, + ConstructorOverridedCorrectDeployementDependentResource.class + }) + void checkResourceTypeDerivationWithInheritance(Class clazz) throws Exception { + KubernetesDependentResource dependentResource = + (KubernetesDependentResource) clazz.getDeclaredConstructor().newInstance(); + assertThat(dependentResource).isInstanceOf(KubernetesDependentResource.class); + assertThat(dependentResource.resourceType()).isEqualTo(Deployment.class); + } + + private static class TestDeploymentDependentResource + extends KubernetesDependentResource {} + + private static class ChildTestDeploymentDependentResource + extends TestDeploymentDependentResource {} + + private static class GrandChildTestDeploymentDependentResource + extends ChildTestDeploymentDependentResource {} + + private static class ChildTypeWithValidKubernetesDependentResource + extends KubernetesDependentResource {} + + private static class ConstructorOverridedCorrectDeployementDependentResource + extends KubernetesDependentResource { + public ConstructorOverridedCorrectDeployementDependentResource() { + super(Deployment.class); + } + } + + @Test + void validateInvalidTypeDerivationTypesThrowException() { + assertThatThrownBy(() -> new InvalidChildTestDeploymentDependentResource()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage( + GENERIC_PARAMETER_TYPE_ERROR_PREFIX + + InvalidChildTestDeploymentDependentResource.class.getName() + + " because it doesn't extend a class that is parametrized with the type we want to" + + " retrieve or because it's Object.class. Please provide the resource type in the " + + "constructor (e.g., super(Deployment.class)."); + assertThatThrownBy(() -> new InvalidGrandChildTestDeploymentDependentResource()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage( + GENERIC_PARAMETER_TYPE_ERROR_PREFIX + + InvalidGrandChildTestDeploymentDependentResource.class.getName() + + " because it doesn't extend a class that is parametrized with the type we want to" + + " retrieve or because it's Object.class. Please provide the resource type in the " + + "constructor (e.g., super(Deployment.class)."); + } + + private static class InvalidChildTestDeploymentDependentResource + extends ChildTypeWithValidKubernetesDependentResource {} + + private static class InvalidGrandChildTestDeploymentDependentResource + extends InvalidChildTestDeploymentDependentResource {} +} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java new file mode 100644 index 0000000000..091a1a666c --- /dev/null +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/PodTemplateSpecSanitizerTest.java @@ -0,0 +1,408 @@ +package io.javaoperatorsdk.operator.processing.dependent.kubernetes; + +import java.util.List; +import java.util.Map; + +import org.assertj.core.api.ListAssert; +import org.assertj.core.api.MapAssert; +import org.junit.jupiter.api.Test; + +import io.fabric8.kubernetes.api.model.EnvVar; +import io.fabric8.kubernetes.api.model.EnvVarBuilder; +import io.fabric8.kubernetes.api.model.GenericKubernetesResource; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; +import io.fabric8.kubernetes.api.model.Quantity; +import io.fabric8.kubernetes.api.model.apps.StatefulSet; +import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.utils.KubernetesSerialization; +import io.javaoperatorsdk.operator.MockKubernetesClient; + +import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.PodTemplateSpecSanitizer.sanitizePodTemplateSpec; +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoInteractions; + +/** + * Tests the {@link PodTemplateSpecSanitizer} with combinations of matching and mismatching K8s + * resources, using a mix of containers and init containers, as well as resource requests and limits + * along with environment variables. + */ +class PodTemplateSpecSanitizerTest { + + private final Map actualMap = mock(); + + private final KubernetesClient client = MockKubernetesClient.client(HasMetadata.class); + private final KubernetesSerialization serialization = client.getKubernetesSerialization(); + + @Test + void testSanitizePodTemplateSpec_whenTemplateIsNull_doNothing() { + final var template = new PodTemplateSpecBuilder().build(); + + sanitizePodTemplateSpec(actualMap, null, template); + sanitizePodTemplateSpec(actualMap, template, null); + verifyNoInteractions(actualMap); + } + + @Test + void testSanitizePodTemplateSpec_whenTemplateSpecIsNull_doNothing() { + final var template = new PodTemplateSpecBuilder().withSpec(null).build(); + final var templateWithSpec = new PodTemplateSpecBuilder().withNewSpec().endSpec().build(); + + sanitizePodTemplateSpec(actualMap, template, templateWithSpec); + sanitizePodTemplateSpec(actualMap, templateWithSpec, template); + verifyNoInteractions(actualMap); + } + + @Test + void testSanitizePodTemplateSpec_whenContainerSizeMismatch_doNothing() { + final var template = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .endContainer() + .endSpec() + .build(); + final var templateWithTwoContainers = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .endContainer() + .addNewContainer() + .withName("test-new") + .endContainer() + .endSpec() + .build(); + + sanitizePodTemplateSpec(actualMap, template, templateWithTwoContainers); + sanitizePodTemplateSpec(actualMap, templateWithTwoContainers, template); + verifyNoInteractions(actualMap); + } + + @Test + void testSanitizePodTemplateSpec_whenContainerNameMismatch_doNothing() { + final var template = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .endContainer() + .endSpec() + .build(); + final var templateWithNewContainerName = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test-new") + .endContainer() + .endSpec() + .build(); + + sanitizePodTemplateSpec(actualMap, template, templateWithNewContainerName); + sanitizePodTemplateSpec(actualMap, templateWithNewContainerName, template); + verifyNoInteractions(actualMap); + } + + @Test + void testSanitizePodTemplateSpec_whenResourceIsNull_doNothing() { + final var template = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .endContainer() + .endSpec() + .build(); + final var templateWithResource = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .withNewResources() + .endResources() + .endContainer() + .endSpec() + .build(); + + sanitizePodTemplateSpec(actualMap, template, templateWithResource); + sanitizePodTemplateSpec(actualMap, templateWithResource, template); + verifyNoInteractions(actualMap); + } + + @Test + void testSanitizeResourceRequirements_whenResourceSizeMismatch_doNothing() { + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.CONTAINER, + Map.of("cpu", new Quantity("2")), + Map.of(), + Map.of("cpu", new Quantity("4")), + Map.of("cpu", new Quantity("4"), "memory", new Quantity("4Gi"))); + assertContainerResources(actualMap, "requests").hasSize(1).containsEntry("cpu", "2"); + assertContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "4"); + } + + @Test + void testSanitizeResourceRequirements_whenResourceKeyMismatch_doNothing() { + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.INIT_CONTAINER, + Map.of("cpu", new Quantity("2")), + Map.of("memory", new Quantity("4Gi")), + Map.of(), + Map.of()); + assertInitContainerResources(actualMap, "requests").hasSize(1).containsEntry("cpu", "2"); + assertInitContainerResources(actualMap, "limits").isNull(); + } + + @Test + void testSanitizePodTemplateSpec_whenResourcesHaveSameAmountAndFormat_doNothing() { + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.CONTAINER, + Map.of("memory", new Quantity("4Gi")), + Map.of("memory", new Quantity("4Gi")), + Map.of("cpu", new Quantity("2")), + Map.of("cpu", new Quantity("2"))); + assertContainerResources(actualMap, "requests").hasSize(1).containsEntry("memory", "4Gi"); + assertContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "2"); + } + + @Test + void testSanitizePodTemplateSpec_whenResourcesHaveNumericalAmountMismatch_doNothing() { + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.INIT_CONTAINER, + Map.of("cpu", new Quantity("2"), "memory", new Quantity("4Gi")), + Map.of("cpu", new Quantity("4"), "memory", new Quantity("4Ti")), + Map.of("cpu", new Quantity("2")), + Map.of("cpu", new Quantity("4000m"))); + assertInitContainerResources(actualMap, "requests") + .hasSize(2) + .containsEntry("cpu", "2") + .containsEntry("memory", "4Gi"); + assertInitContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "2"); + } + + @Test + void + testSanitizeResourceRequirements_whenResourcesHaveAmountAndFormatMismatchWithSameNumericalAmount_thenSanitizeActualMap() { + final var actualMap = + sanitizeRequestsAndLimits( + ContainerType.CONTAINER, + Map.of("cpu", new Quantity("2"), "memory", new Quantity("4Gi")), + Map.of("cpu", new Quantity("2000m"), "memory", new Quantity("4096Mi")), + Map.of("cpu", new Quantity("4")), + Map.of("cpu", new Quantity("4000m"))); + assertContainerResources(actualMap, "requests") + .hasSize(2) + .containsEntry("cpu", "2000m") + .containsEntry("memory", "4096Mi"); + assertContainerResources(actualMap, "limits").hasSize(1).containsEntry("cpu", "4000m"); + } + + @Test + void testSanitizePodTemplateSpec_whenEnvVarsIsEmpty_doNothing() { + final var template = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .endContainer() + .endSpec() + .build(); + final var templateWithEnvVars = + new PodTemplateSpecBuilder() + .withNewSpec() + .addNewContainer() + .withName("test") + .withEnv(List.of(new EnvVarBuilder().withName("FOO").withValue("foobar").build())) + .endContainer() + .endSpec() + .build(); + + sanitizePodTemplateSpec(actualMap, template, templateWithEnvVars); + sanitizePodTemplateSpec(actualMap, templateWithEnvVars, template); + verifyNoInteractions(actualMap); + } + + @Test + void testSanitizePodTemplateSpec_whenActualEnvVarValueIsNotEmpty_doNothing() { + final var actualMap = + sanitizeEnvVars( + ContainerType.CONTAINER, + List.of( + new EnvVarBuilder().withName("FOO").withValue("foo").build(), + new EnvVarBuilder().withName("BAR").withValue("bar").build()), + List.of( + new EnvVarBuilder().withName("FOO").withValue("bar").build(), + new EnvVarBuilder().withName("BAR").withValue("foo").build())); + assertContainerEnvVars(actualMap) + .hasSize(2) + .containsExactly( + Map.of("name", "FOO", "value", "foo"), Map.of("name", "BAR", "value", "bar")); + } + + @Test + void testSanitizePodTemplateSpec_whenActualAndDesiredEnvVarsAreDifferent_doNothing() { + final var actualMap = + sanitizeEnvVars( + ContainerType.INIT_CONTAINER, + List.of(new EnvVarBuilder().withName("FOO").withValue("foo").build()), + List.of(new EnvVarBuilder().withName("BAR").withValue("bar").build())); + assertInitContainerEnvVars(actualMap) + .hasSize(1) + .containsExactly(Map.of("name", "FOO", "value", "foo")); + } + + @Test + void testSanitizePodTemplateSpec_whenActualEnvVarIsEmpty_doNothing() { + final var actualMap = + sanitizeEnvVars( + ContainerType.INIT_CONTAINER, + List.of( + new EnvVarBuilder().withName("FOO").withValue("").build(), + new EnvVarBuilder().withName("BAR").withValue("").build()), + List.of( + new EnvVarBuilder().withName("FOO").withValue("foo").build(), + new EnvVarBuilder().withName("BAR").withValue("").build())); + assertInitContainerEnvVars(actualMap) + .hasSize(2) + .containsExactly(Map.of("name", "FOO", "value", ""), Map.of("name", "BAR", "value", "")); + } + + @Test + void testSanitizePodTemplateSpec_whenActualEnvVarIsNull_doNothing() { + final var actualMap = + sanitizeEnvVars( + ContainerType.CONTAINER, + List.of( + new EnvVarBuilder().withName("FOO").withValue(null).build(), + new EnvVarBuilder().withName("BAR").withValue(null).build()), + List.of( + new EnvVarBuilder().withName("FOO").withValue("foo").build(), + new EnvVarBuilder().withName("BAR").withValue(" ").build())); + assertContainerEnvVars(actualMap) + .hasSize(2) + .containsExactly(Map.of("name", "FOO"), Map.of("name", "BAR")); + } + + @Test + void + testSanitizePodTemplateSpec_whenActualEnvVarIsNull_withDesiredEnvVarEmpty_thenSanitizeActualMap() { + final var actualMap = + sanitizeEnvVars( + ContainerType.CONTAINER, + List.of( + new EnvVarBuilder().withName("FOO").withValue(null).build(), + new EnvVarBuilder().withName("BAR").withValue(null).build()), + List.of( + new EnvVarBuilder().withName("FOO").withValue("").build(), + new EnvVarBuilder().withName("BAR").withValue("").build())); + assertContainerEnvVars(actualMap) + .hasSize(2) + .containsExactly(Map.of("name", "FOO", "value", ""), Map.of("name", "BAR", "value", "")); + } + + private Map sanitizeRequestsAndLimits( + final ContainerType type, + final Map actualRequests, + final Map desiredRequests, + final Map actualLimits, + final Map desiredLimits) { + return sanitize( + type, actualRequests, desiredRequests, actualLimits, desiredLimits, List.of(), List.of()); + } + + private Map sanitizeEnvVars( + final ContainerType type, + final List actualEnvVars, + final List desiredEnvVars) { + return sanitize(type, Map.of(), Map.of(), Map.of(), Map.of(), actualEnvVars, desiredEnvVars); + } + + @SuppressWarnings("unchecked") + private Map sanitize( + final ContainerType type, + final Map actualRequests, + final Map desiredRequests, + final Map actualLimits, + final Map desiredLimits, + final List actualEnvVars, + final List desiredEnvVars) { + final var actual = createStatefulSet(type, actualRequests, actualLimits, actualEnvVars); + final var desired = createStatefulSet(type, desiredRequests, desiredLimits, desiredEnvVars); + final var actualMap = serialization.convertValue(actual, Map.class); + sanitizePodTemplateSpec( + actualMap, actual.getSpec().getTemplate(), desired.getSpec().getTemplate()); + return actualMap; + } + + private enum ContainerType { + CONTAINER, + INIT_CONTAINER, + } + + private static StatefulSet createStatefulSet( + final ContainerType type, + final Map requests, + final Map limits, + final List envVars) { + var builder = new StatefulSetBuilder().withNewSpec().withNewTemplate().withNewSpec(); + if (type == ContainerType.CONTAINER) { + builder = + builder + .addNewContainer() + .withName("test") + .withNewResources() + .withRequests(requests) + .withLimits(limits) + .endResources() + .withEnv(envVars) + .endContainer(); + } else { + builder = + builder + .addNewInitContainer() + .withName("test") + .withNewResources() + .withRequests(requests) + .withLimits(limits) + .endResources() + .withEnv(envVars) + .endInitContainer(); + } + return builder.endSpec().endTemplate().endSpec().build(); + } + + private static MapAssert assertContainerResources( + final Map actualMap, final String resourceName) { + return assertThat( + GenericKubernetesResource.>get( + actualMap, "spec", "template", "spec", "containers", 0, "resources", resourceName)); + } + + private static MapAssert assertInitContainerResources( + final Map actualMap, final String resourceName) { + return assertThat( + GenericKubernetesResource.>get( + actualMap, "spec", "template", "spec", "initContainers", 0, "resources", resourceName)); + } + + private static ListAssert> assertContainerEnvVars( + final Map actualMap) { + return assertThat( + GenericKubernetesResource.>>get( + actualMap, "spec", "template", "spec", "containers", 0, "env")); + } + + private static ListAssert> assertInitContainerEnvVars( + final Map actualMap) { + return assertThat( + GenericKubernetesResource.>>get( + actualMap, "spec", "template", "spec", "initContainers", 0, "env")); + } +} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceRequirementsSanitizerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceRequirementsSanitizerTest.java deleted file mode 100644 index b1ed6f0080..0000000000 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/ResourceRequirementsSanitizerTest.java +++ /dev/null @@ -1,223 +0,0 @@ -package io.javaoperatorsdk.operator.processing.dependent.kubernetes; - -import java.util.Map; - -import org.assertj.core.api.MapAssert; -import org.junit.jupiter.api.Test; - -import io.fabric8.kubernetes.api.model.GenericKubernetesResource; -import io.fabric8.kubernetes.api.model.HasMetadata; -import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; -import io.fabric8.kubernetes.api.model.Quantity; -import io.fabric8.kubernetes.api.model.apps.StatefulSet; -import io.fabric8.kubernetes.api.model.apps.StatefulSetBuilder; -import io.fabric8.kubernetes.client.KubernetesClient; -import io.fabric8.kubernetes.client.utils.KubernetesSerialization; -import io.javaoperatorsdk.operator.MockKubernetesClient; - -import static io.javaoperatorsdk.operator.processing.dependent.kubernetes.ResourceRequirementsSanitizer.sanitizeResourceRequirements; -import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verifyNoInteractions; - -/** - * Tests the {@link ResourceRequirementsSanitizer} with combinations of matching and mismatching K8s - * resources, using a mix of containers and init containers, as well as resource requests and - * limits. - */ -class ResourceRequirementsSanitizerTest { - - private final Map actualMap = mock(); - - private final KubernetesClient client = MockKubernetesClient.client(HasMetadata.class); - private final KubernetesSerialization serialization = client.getKubernetesSerialization(); - - @Test - void testSanitizeResourceRequirements_whenTemplateIsNull_doNothing() { - final var template = new PodTemplateSpecBuilder().build(); - - sanitizeResourceRequirements(actualMap, null, template); - sanitizeResourceRequirements(actualMap, template, null); - verifyNoInteractions(actualMap); - } - - @Test - void testSanitizeResourceRequirements_whenTemplateSpecIsNull_doNothing() { - final var template = new PodTemplateSpecBuilder().withSpec(null).build(); - final var templateWithSpec = new PodTemplateSpecBuilder().withNewSpec().endSpec().build(); - - sanitizeResourceRequirements(actualMap, template, templateWithSpec); - sanitizeResourceRequirements(actualMap, templateWithSpec, template); - verifyNoInteractions(actualMap); - } - - @Test - void testSanitizeResourceRequirements_whenContainerSizeMismatch_doNothing() { - final var template = new PodTemplateSpecBuilder().withNewSpec() - .addNewContainer().withName("test").endContainer() - .endSpec().build(); - final var templateWithTwoContainers = new PodTemplateSpecBuilder().withNewSpec() - .addNewContainer().withName("test").endContainer() - .addNewContainer().withName("test-new").endContainer() - .endSpec().build(); - - sanitizeResourceRequirements(actualMap, template, templateWithTwoContainers); - sanitizeResourceRequirements(actualMap, templateWithTwoContainers, template); - verifyNoInteractions(actualMap); - } - - @Test - void testSanitizeResourceRequirements_whenContainerNameMismatch_doNothing() { - final var template = new PodTemplateSpecBuilder().withNewSpec() - .addNewContainer().withName("test").endContainer() - .endSpec().build(); - final var templateWithNewContainerName = new PodTemplateSpecBuilder().withNewSpec() - .addNewContainer().withName("test-new").endContainer() - .endSpec().build(); - - sanitizeResourceRequirements(actualMap, template, templateWithNewContainerName); - sanitizeResourceRequirements(actualMap, templateWithNewContainerName, template); - verifyNoInteractions(actualMap); - } - - @Test - void testSanitizeResourceRequirements_whenResourceIsNull_doNothing() { - final var template = new PodTemplateSpecBuilder().withNewSpec() - .addNewContainer().withName("test").endContainer() - .endSpec().build(); - final var templateWithResource = new PodTemplateSpecBuilder().withNewSpec() - .addNewContainer().withName("test").withNewResources().endResources().endContainer() - .endSpec().build(); - - sanitizeResourceRequirements(actualMap, template, templateWithResource); - sanitizeResourceRequirements(actualMap, templateWithResource, template); - verifyNoInteractions(actualMap); - } - - @Test - void testSanitizeResourceRequirements_whenResourceSizeMismatch_doNothing() { - final var actualMap = sanitizeRequestsAndLimits(ContainerType.CONTAINER, - Map.of("cpu", new Quantity("2")), - Map.of(), - Map.of("cpu", new Quantity("4")), - Map.of("cpu", new Quantity("4"), "memory", new Quantity("4Gi"))); - assertContainerResources(actualMap, "requests") - .hasSize(1) - .containsEntry("cpu", "2"); - assertContainerResources(actualMap, "limits") - .hasSize(1) - .containsEntry("cpu", "4"); - } - - @Test - void testSanitizeResourceRequirements_whenResourceKeyMismatch_doNothing() { - final var actualMap = sanitizeRequestsAndLimits(ContainerType.INIT_CONTAINER, - Map.of("cpu", new Quantity("2")), - Map.of("memory", new Quantity("4Gi")), - Map.of(), - Map.of()); - assertInitContainerResources(actualMap, "requests") - .hasSize(1) - .containsEntry("cpu", "2"); - assertInitContainerResources(actualMap, "limits").isNull(); - } - - @Test - void testSanitizeResourceRequirements_whenResourcesHaveSameAmountAndFormat_doNothing() { - final var actualMap = sanitizeRequestsAndLimits(ContainerType.CONTAINER, - Map.of("memory", new Quantity("4Gi")), - Map.of("memory", new Quantity("4Gi")), - Map.of("cpu", new Quantity("2")), - Map.of("cpu", new Quantity("2"))); - assertContainerResources(actualMap, "requests") - .hasSize(1) - .containsEntry("memory", "4Gi"); - assertContainerResources(actualMap, "limits") - .hasSize(1) - .containsEntry("cpu", "2"); - } - - @Test - void testSanitizeResourceRequirements_whenResourcesHaveNumericalAmountMismatch_doNothing() { - final var actualMap = sanitizeRequestsAndLimits(ContainerType.INIT_CONTAINER, - Map.of("cpu", new Quantity("2"), "memory", new Quantity("4Gi")), - Map.of("cpu", new Quantity("4"), "memory", new Quantity("4Ti")), - Map.of("cpu", new Quantity("2")), - Map.of("cpu", new Quantity("4000m"))); - assertInitContainerResources(actualMap, "requests") - .hasSize(2) - .containsEntry("cpu", "2") - .containsEntry("memory", "4Gi"); - assertInitContainerResources(actualMap, "limits") - .hasSize(1) - .containsEntry("cpu", "2"); - } - - @Test - void testSanitizeResourceRequirements_whenResourcesHaveAmountAndFormatMismatchWithSameNumericalAmount_thenSanitizeActualMap() { - final var actualMap = sanitizeRequestsAndLimits(ContainerType.CONTAINER, - Map.of("cpu", new Quantity("2"), "memory", new Quantity("4Gi")), - Map.of("cpu", new Quantity("2000m"), "memory", new Quantity("4096Mi")), - Map.of("cpu", new Quantity("4")), - Map.of("cpu", new Quantity("4000m"))); - assertContainerResources(actualMap, "requests") - .hasSize(2) - .containsEntry("cpu", "2000m") - .containsEntry("memory", "4096Mi"); - assertContainerResources(actualMap, "limits") - .hasSize(1) - .containsEntry("cpu", "4000m"); - } - - @SuppressWarnings("unchecked") - private Map sanitizeRequestsAndLimits(final ContainerType type, - final Map actualRequests, final Map desiredRequests, - final Map actualLimits, final Map desiredLimits) { - final var actual = createStatefulSet(type, actualRequests, actualLimits); - final var desired = createStatefulSet(type, desiredRequests, desiredLimits); - final var actualMap = serialization.convertValue(actual, Map.class); - sanitizeResourceRequirements(actualMap, - actual.getSpec().getTemplate(), - desired.getSpec().getTemplate()); - return actualMap; - } - - private enum ContainerType { - CONTAINER, INIT_CONTAINER, - } - - private static StatefulSet createStatefulSet(final ContainerType type, - final Map requests, final Map limits) { - var builder = new StatefulSetBuilder().withNewSpec().withNewTemplate().withNewSpec(); - if (type == ContainerType.CONTAINER) { - builder = builder.addNewContainer() - .withName("test") - .withNewResources() - .withRequests(requests) - .withLimits(limits) - .endResources() - .endContainer(); - } else { - builder = builder.addNewInitContainer() - .withName("test") - .withNewResources() - .withRequests(requests) - .withLimits(limits) - .endResources() - .endInitContainer(); - } - return builder.endSpec().endTemplate().endSpec().build(); - } - - private static MapAssert assertContainerResources( - final Map actualMap, final String resourceName) { - return assertThat(GenericKubernetesResource.>get(actualMap, - "spec", "template", "spec", "containers", 0, "resources", resourceName)); - } - - private static MapAssert assertInitContainerResources( - final Map actualMap, final String resourceName) { - return assertThat(GenericKubernetesResource.>get(actualMap, - "spec", "template", "spec", "initContainers", 0, "resources", resourceName)); - } -} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java index f30b6949fa..c339e5ebf6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/kubernetes/SSABasedGenericKubernetesResourceMatcherTest.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.processing.dependent.kubernetes; -import java.util.HashMap; import java.util.List; import java.util.Map; @@ -11,17 +10,21 @@ import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.Secret; import io.fabric8.kubernetes.api.model.apps.DaemonSet; import io.fabric8.kubernetes.api.model.apps.Deployment; import io.fabric8.kubernetes.api.model.apps.ReplicaSet; import io.fabric8.kubernetes.api.model.apps.StatefulSet; import io.javaoperatorsdk.operator.MockKubernetesClient; +import io.javaoperatorsdk.operator.OperatorException; import io.javaoperatorsdk.operator.ReconcilerUtils; import io.javaoperatorsdk.operator.api.config.ConfigurationService; import io.javaoperatorsdk.operator.api.config.ControllerConfiguration; import io.javaoperatorsdk.operator.api.reconciler.Context; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -39,12 +42,61 @@ void setup() { when(mockedContext.getClient()).thenReturn(client); final var configurationService = mock(ConfigurationService.class); + when(configurationService.shouldUseSSA(any(), any(), any())).thenReturn(true); final var controllerConfiguration = mock(ControllerConfiguration.class); when(controllerConfiguration.getConfigurationService()).thenReturn(configurationService); when(controllerConfiguration.fieldManager()).thenReturn("controller"); when(mockedContext.getControllerConfiguration()).thenReturn(controllerConfiguration); } + @Test + void noMatchWhenNoMatchingController() { + var desired = loadResource("nginx-deployment.yaml", Deployment.class); + var actual = + loadResource("deployment-with-managed-fields-additional-controller.yaml", Deployment.class); + actual + .getMetadata() + .getManagedFields() + .removeIf(managedFieldsEntry -> managedFieldsEntry.getManager().equals("controller")); + + assertThat(matcher.matches(actual, desired, mockedContext)).isFalse(); + } + + @Test + void exceptionWhenDuplicateController() { + var desired = loadResource("nginx-deployment.yaml", Deployment.class); + var actual = + loadResource("deployment-with-managed-fields-additional-controller.yaml", Deployment.class); + actual.getMetadata().getManagedFields().stream() + .filter(managedFieldsEntry -> managedFieldsEntry.getManager().equals("controller")) + .findFirst() + .ifPresent( + managedFieldsEntry -> actual.getMetadata().getManagedFields().add(managedFieldsEntry)); + + assertThatThrownBy(() -> matcher.matches(actual, desired, mockedContext)) + .isInstanceOf(OperatorException.class) + .hasMessage( + "More than one field manager exists with name: controller in resource: Deployment with" + + " name: test"); + } + + @Test + void matchWithSensitiveResource() { + var desired = loadResource("secret-desired.yaml", Secret.class); + var actual = loadResource("secret.yaml", Secret.class); + + assertThat(matcher.matches(actual, desired, mockedContext)).isTrue(); + } + + @Test + void noMatchWithSensitiveResource() { + var desired = loadResource("secret-desired.yaml", Secret.class); + var actual = loadResource("secret.yaml", Secret.class); + actual.getData().put("key1", "dmFsMg=="); + + assertThat(matcher.matches(actual, desired, mockedContext)).isFalse(); + } + @Test void checksIfAddsNotAddedByController() { var desired = loadResource("nginx-deployment.yaml", Deployment.class); @@ -54,14 +106,46 @@ void checksIfAddsNotAddedByController() { assertThat(matcher.matches(actual, desired, mockedContext)).isTrue(); } - // In the example the owner reference in a list is referenced by "k:", while all the fields are + @Test + void throwExceptionWhenManagedListEntryNotFound() { + var desired = loadResource("nginx-deployment.yaml", Deployment.class); + var actual = + loadResource("deployment-with-managed-fields-additional-controller.yaml", Deployment.class); + final var container = actual.getSpec().getTemplate().getSpec().getContainers().get(0); + container.setName("foobar"); + + assertThatThrownBy(() -> matcher.matches(actual, desired, mockedContext)) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "Cannot find list element for key: {\"name\":\"nginx\"} in map: [[image," + + " imagePullPolicy, name, ports, resources, terminationMessagePath," + + " terminationMessagePolicy]]"); + } + + @Test + void throwExceptionWhenDuplicateManagedListEntryFound() { + var desired = loadResource("nginx-deployment.yaml", Deployment.class); + var actual = + loadResource("deployment-with-managed-fields-additional-controller.yaml", Deployment.class); + final var container = actual.getSpec().getTemplate().getSpec().getContainers().get(0); + actual.getSpec().getTemplate().getSpec().getContainers().add(container); + + assertThatThrownBy(() -> matcher.matches(actual, desired, mockedContext)) + .isInstanceOf(IllegalStateException.class) + .hasMessage( + "More targets found in list element for key: {\"name\":\"nginx\"} in map: [[image," + + " imagePullPolicy, name, ports, resources, terminationMessagePath," + + " terminationMessagePolicy], [image, imagePullPolicy, name, ports, resources," + + " terminationMessagePath, terminationMessagePolicy]]"); + } + + // in the example the owner reference in a list is referenced by "k:", while all the fields are // managed but not listed @Test void emptyListElementMatchesAllFields() { - var desiredConfigMap = loadResource("configmap.empty-owner-reference-desired.yaml", - ConfigMap.class); - var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", - ConfigMap.class); + var desiredConfigMap = + loadResource("configmap.empty-owner-reference-desired.yaml", ConfigMap.class); + var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", ConfigMap.class); assertThat(matcher.matches(actualConfigMap, desiredConfigMap, mockedContext)).isTrue(); } @@ -69,42 +153,37 @@ void emptyListElementMatchesAllFields() { // the whole "rules:" part is just implicitly managed @Test void wholeComplexFieldManaged() { - var desiredConfigMap = loadResource("sample-whole-complex-part-managed-desired.yaml", - ConfigMap.class); - var actualConfigMap = loadResource("sample-whole-complex-part-managed.yaml", - ConfigMap.class); + var desiredConfigMap = + loadResource("sample-whole-complex-part-managed-desired.yaml", ConfigMap.class); + var actualConfigMap = loadResource("sample-whole-complex-part-managed.yaml", ConfigMap.class); assertThat(matcher.matches(actualConfigMap, desiredConfigMap, mockedContext)).isTrue(); } @Test void multiItemList() { - var desiredConfigMap = loadResource("multi-container-pod-desired.yaml", - ConfigMap.class); - var actualConfigMap = loadResource("multi-container-pod.yaml", - ConfigMap.class); + var desiredConfigMap = loadResource("multi-container-pod-desired.yaml", ConfigMap.class); + var actualConfigMap = loadResource("multi-container-pod.yaml", ConfigMap.class); assertThat(matcher.matches(actualConfigMap, desiredConfigMap, mockedContext)).isTrue(); } @Test void changeValueInDesiredMakesMatchFail() { - var desiredConfigMap = loadResource("configmap.empty-owner-reference-desired.yaml", - ConfigMap.class); + var desiredConfigMap = + loadResource("configmap.empty-owner-reference-desired.yaml", ConfigMap.class); desiredConfigMap.getData().put("key1", "different value"); - var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", - ConfigMap.class); + var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", ConfigMap.class); assertThat(matcher.matches(actualConfigMap, desiredConfigMap, mockedContext)).isFalse(); } @Test void changeValueActualMakesMatchFail() { - var desiredConfigMap = loadResource("configmap.empty-owner-reference-desired.yaml", - ConfigMap.class); + var desiredConfigMap = + loadResource("configmap.empty-owner-reference-desired.yaml", ConfigMap.class); - var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", - ConfigMap.class); + var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", ConfigMap.class); actualConfigMap.getData().put("key1", "different value"); assertThat(matcher.matches(actualConfigMap, desiredConfigMap, mockedContext)).isFalse(); @@ -112,79 +191,48 @@ void changeValueActualMakesMatchFail() { @Test void addedLabelInDesiredMakesMatchFail() { - var desiredConfigMap = loadResource("configmap.empty-owner-reference-desired.yaml", - ConfigMap.class); + var desiredConfigMap = + loadResource("configmap.empty-owner-reference-desired.yaml", ConfigMap.class); desiredConfigMap.getMetadata().setLabels(Map.of("newlabel", "val")); - var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", - ConfigMap.class); + var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", ConfigMap.class); assertThat(matcher.matches(actualConfigMap, desiredConfigMap, mockedContext)).isFalse(); } @Test - @SuppressWarnings("unchecked") - void sortListItemsTest() { - var nestedMap1 = new HashMap(); - nestedMap1.put("z", 26); - nestedMap1.put("y", 25); - - var nestedMap2 = new HashMap(); - nestedMap2.put("b", 26); - nestedMap2.put("c", 25); - nestedMap2.put("a", 24); - - var unsortedListItems = List.of(1, nestedMap1, nestedMap2); - var sortedListItems = matcher.sortListItems(unsortedListItems); - assertThat(sortedListItems).element(0).isEqualTo(1); - - var sortedNestedMap1 = (Map) sortedListItems.get(1); - assertThat(sortedNestedMap1.keySet()).containsExactly("y", "z"); - - var sortedNestedMap2 = (Map) sortedListItems.get(2); - assertThat(sortedNestedMap2.keySet()).containsExactly("a", "b", "c"); - } - - @Test - @SuppressWarnings("unchecked") - void testSortMapWithNestedMap() { - var nestedMap = new HashMap(); - nestedMap.put("z", 26); - nestedMap.put("y", 25); + void withFinalizer() { + var desired = loadResource("secret-with-finalizer-desired.yaml", Secret.class); + var actual = loadResource("secret-with-finalizer.yaml", Secret.class); - var unsortedMap = new HashMap(); - unsortedMap.put("b", nestedMap); - unsortedMap.put("a", 1); - unsortedMap.put("c", 2); - - var sortedMap = matcher.sortMap(unsortedMap); - assertThat(sortedMap.keySet()).containsExactly("a", "b", "c"); - - var sortedNestedMap = (Map) sortedMap.get("b"); - assertThat(sortedNestedMap.keySet()).containsExactly("y", "z"); + assertThat(matcher.matches(actual, desired, mockedContext)).isTrue(); } @ParameterizedTest - @ValueSource(strings = {"sample-sts-volumeclaimtemplates-desired.yaml", - "sample-sts-volumeclaimtemplates-desired-with-status.yaml", - "sample-sts-volumeclaimtemplates-desired-with-volumemode.yaml"}) + @ValueSource( + strings = { + "sample-sts-volumeclaimtemplates-desired.yaml", + "sample-sts-volumeclaimtemplates-desired-with-status.yaml", + "sample-sts-volumeclaimtemplates-desired-with-volumemode.yaml" + }) void testSanitizeState_statefulSetWithVolumeClaims(String desiredResourceFileName) { var desiredStatefulSet = loadResource(desiredResourceFileName, StatefulSet.class); - var actualStatefulSet = loadResource("sample-sts-volumeclaimtemplates.yaml", - StatefulSet.class); + var actualStatefulSet = loadResource("sample-sts-volumeclaimtemplates.yaml", StatefulSet.class); assertThat(matcher.matches(actualStatefulSet, desiredStatefulSet, mockedContext)).isTrue(); } @ParameterizedTest - @ValueSource(strings = {"sample-sts-volumeclaimtemplates-desired-add.yaml", - "sample-sts-volumeclaimtemplates-desired-update.yaml", - "sample-sts-volumeclaimtemplates-desired-with-status-mismatch.yaml", - "sample-sts-volumeclaimtemplates-desired-with-volumemode-mismatch.yaml"}) + @ValueSource( + strings = { + "sample-sts-volumeclaimtemplates-desired-add.yaml", + "sample-sts-volumeclaimtemplates-desired-update.yaml", + "sample-sts-volumeclaimtemplates-desired-with-status-mismatch.yaml", + "sample-sts-volumeclaimtemplates-desired-with-volumemode-mismatch.yaml" + }) void testSanitizeState_statefulSetWithVolumeClaims_withMismatch(String desiredResourceFileName) { var desiredStatefulSet = loadResource(desiredResourceFileName, StatefulSet.class); - var actualStatefulSet = loadResource("sample-sts-volumeclaimtemplates.yaml", - StatefulSet.class); + var actualStatefulSet = loadResource("sample-sts-volumeclaimtemplates.yaml", StatefulSet.class); assertThat(matcher.matches(actualStatefulSet, desiredStatefulSet, mockedContext)).isFalse(); } @@ -192,8 +240,7 @@ void testSanitizeState_statefulSetWithVolumeClaims_withMismatch(String desiredRe @Test void testSanitizeState_statefulSetWithResources() { var desiredStatefulSet = loadResource("sample-sts-resources-desired.yaml", StatefulSet.class); - var actualStatefulSet = loadResource("sample-sts-resources.yaml", - StatefulSet.class); + var actualStatefulSet = loadResource("sample-sts-resources.yaml", StatefulSet.class); assertThat(matcher.matches(actualStatefulSet, desiredStatefulSet, mockedContext)).isTrue(); } @@ -202,17 +249,32 @@ void testSanitizeState_statefulSetWithResources() { void testSanitizeState_statefulSetWithResources_withMismatch() { var desiredStatefulSet = loadResource("sample-sts-resources-desired-update.yaml", StatefulSet.class); - var actualStatefulSet = loadResource("sample-sts-resources.yaml", - StatefulSet.class); + var actualStatefulSet = loadResource("sample-sts-resources.yaml", StatefulSet.class); assertThat(matcher.matches(actualStatefulSet, desiredStatefulSet, mockedContext)).isFalse(); } + @Test + void testSanitizeState_statefulSet_withResourceTypeMismatch() { + var desiredReplicaSet = loadResource("sample-rs-resources-desired.yaml", ReplicaSet.class); + var actualStatefulSet = loadResource("sample-sts-resources.yaml", StatefulSet.class); + + assertThat(matcher.matches(actualStatefulSet, desiredReplicaSet, mockedContext)).isFalse(); + } + + @Test + void testSanitizeState_deployment_withResourceTypeMismatch() { + var desiredReplicaSet = loadResource("sample-rs-resources-desired.yaml", ReplicaSet.class); + var actualDeployment = + loadResource("deployment-with-managed-fields-additional-controller.yaml", Deployment.class); + + assertThat(matcher.matches(actualDeployment, desiredReplicaSet, mockedContext)).isFalse(); + } + @Test void testSanitizeState_replicaSetWithResources() { var desiredReplicaSet = loadResource("sample-rs-resources-desired.yaml", ReplicaSet.class); - var actualReplicaSet = loadResource("sample-rs-resources.yaml", - ReplicaSet.class); + var actualReplicaSet = loadResource("sample-rs-resources.yaml", ReplicaSet.class); assertThat(matcher.matches(actualReplicaSet, desiredReplicaSet, mockedContext)).isTrue(); } @@ -221,33 +283,134 @@ void testSanitizeState_replicaSetWithResources() { void testSanitizeState_replicaSetWithResources_withMismatch() { var desiredReplicaSet = loadResource("sample-rs-resources-desired-update.yaml", ReplicaSet.class); - var actualReplicaSet = loadResource("sample-rs-resources.yaml", - ReplicaSet.class); + var actualReplicaSet = loadResource("sample-rs-resources.yaml", ReplicaSet.class); assertThat(matcher.matches(actualReplicaSet, desiredReplicaSet, mockedContext)).isFalse(); } + @Test + void testSanitizeState_replicaSet_withResourceTypeMismatch() { + var desiredDaemonSet = loadResource("sample-ds-resources-desired.yaml", DaemonSet.class); + var actualReplicaSet = loadResource("sample-rs-resources.yaml", ReplicaSet.class); + + assertThat(matcher.matches(actualReplicaSet, desiredDaemonSet, mockedContext)).isFalse(); + } + @Test void testSanitizeState_daemonSetWithResources() { var desiredDaemonSet = loadResource("sample-ds-resources-desired.yaml", DaemonSet.class); - var actualDaemonSet = loadResource("sample-ds-resources.yaml", - DaemonSet.class); + var actualDaemonSet = loadResource("sample-ds-resources.yaml", DaemonSet.class); assertThat(matcher.matches(actualDaemonSet, desiredDaemonSet, mockedContext)).isTrue(); } @Test void testSanitizeState_daemonSetWithResources_withMismatch() { - var desiredDaemonSet = - loadResource("sample-ds-resources-desired-update.yaml", DaemonSet.class); - var actualDaemonSet = loadResource("sample-ds-resources.yaml", - DaemonSet.class); + var desiredDaemonSet = loadResource("sample-ds-resources-desired-update.yaml", DaemonSet.class); + var actualDaemonSet = loadResource("sample-ds-resources.yaml", DaemonSet.class); assertThat(matcher.matches(actualDaemonSet, desiredDaemonSet, mockedContext)).isFalse(); } + @Test + void testSanitizeState_daemonSet_withResourceTypeMismatch() { + var desiredReplicaSet = loadResource("sample-rs-resources-desired.yaml", ReplicaSet.class); + var actualDaemonSet = loadResource("sample-ds-resources.yaml", DaemonSet.class); + + assertThat(matcher.matches(actualDaemonSet, desiredReplicaSet, mockedContext)).isFalse(); + } + + @ParameterizedTest + @ValueSource(booleans = {true, false}) + void testCustomMatcher_returnsExpectedMatchBasedOnReadOnlyLabel(boolean readOnly) { + var dr = new ConfigMapDR(); + dr.configureWith( + new KubernetesDependentResourceConfigBuilder() + .withSSAMatcher(new ReadOnlyAwareMatcher()) + .build()); + var desiredConfigMap = + loadResource("configmap.empty-owner-reference-desired.yaml", ConfigMap.class); + desiredConfigMap.getData().put("key1", "another value"); + var actualConfigMap = loadResource("configmap.empty-owner-reference.yaml", ConfigMap.class); + actualConfigMap.getMetadata().getLabels().put("readonly", Boolean.toString(readOnly)); + + ConfigMap ignoredPrimary = null; + assertThat( + dr.match( + actualConfigMap, + desiredConfigMap, + ignoredPrimary, + (Context) mockedContext) + .matched()) + .isEqualTo(readOnly); + } + + @Test + void keepOnlyManagedFields_withInvalidManagedFieldsKey() { + assertThatThrownBy( + () -> + SSABasedGenericKubernetesResourceMatcher.keepOnlyManagedFields( + Map.of(), + Map.of(), + Map.of("invalid", 1), + mockedContext.getClient().getKubernetesSerialization())) // + .isInstanceOf(IllegalStateException.class) // + .hasMessage("Key: invalid has no prefix: f:"); + } + + @Test + @SuppressWarnings("unchecked") + void testSortMap() { + final var unsortedMap = Map.of("b", Map.of("z", 26, "y", 25), "a", List.of("w", "v"), "c", 2); + + var sortedMap = SSABasedGenericKubernetesResourceMatcher.sortMap(unsortedMap); + assertThat(sortedMap.keySet()).containsExactly("a", "b", "c"); + + var sortedNestedMap = (Map) sortedMap.get("b"); + assertThat(sortedNestedMap.keySet()).containsExactly("y", "z"); + } + + @Test + @SuppressWarnings("unchecked") + void testSortListItems() { + final var unsortedList = + List.of(1, Map.of("z", 26, "y", 25), Map.of("b", 26, "c", 25, "a", 24), List.of("w", "v")); + + var sortedListItems = SSABasedGenericKubernetesResourceMatcher.sortListItems(unsortedList); + assertThat(sortedListItems).element(0).isEqualTo(1); + + var sortedNestedMap1 = (Map) sortedListItems.get(1); + assertThat(sortedNestedMap1.keySet()).containsExactly("y", "z"); + + var sortedNestedMap2 = (Map) sortedListItems.get(2); + assertThat(sortedNestedMap2.keySet()).containsExactly("a", "b", "c"); + } + private static R loadResource(String fileName, Class clazz) { - return ReconcilerUtils.loadYaml(clazz, SSABasedGenericKubernetesResourceMatcherTest.class, - fileName); + return ReconcilerUtils.loadYaml( + clazz, SSABasedGenericKubernetesResourceMatcherTest.class, fileName); + } + + private static class ConfigMapDR extends KubernetesDependentResource { + public ConfigMapDR() { + super(ConfigMap.class); + } + } + + private static class ReadOnlyAwareMatcher + extends SSABasedGenericKubernetesResourceMatcher { + @Override + protected boolean matches( + Map actualMap, + Map desiredMap, + T actual, + T desired, + Context context) { + var readonly = actual.getMetadata().getLabels().get("readonly"); + if (readonly != null && readonly.equals("true")) { + return true; + } + return actualMap.equals(desiredMap); + } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java index 970e40eff6..bdb67d9bf6 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/AbstractWorkflowExecutorTest.java @@ -33,6 +33,7 @@ public class AbstractWorkflowExecutorTest { @SuppressWarnings("rawtypes") protected final Condition notMetCondition = (primary, secondary, context) -> false; + @SuppressWarnings("rawtypes") protected final Condition metCondition = (primary, secondary, context) -> true; @@ -46,11 +47,11 @@ public TestDependent(String name) { } @Override - public ReconcileResult reconcile(TestCustomResource primary, - Context context) { + public ReconcileResult reconcile( + TestCustomResource primary, Context context) { executionHistory.add(new ReconcileRecord(this)); - return ReconcileResult - .resourceCreated(new ConfigMapBuilder().addToBinaryData("key", VALUE).build()); + return ReconcileResult.resourceCreated( + new ConfigMapBuilder().addToBinaryData("key", VALUE).build()); } @Override @@ -110,8 +111,8 @@ public TestErrorDependent(String name) { } @Override - public ReconcileResult reconcile(TestCustomResource primary, - Context context) { + public ReconcileResult reconcile( + TestCustomResource primary, Context context) { executionHistory.add(new ReconcileRecord(this)); throw new IllegalStateException("Test exception"); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResultTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResultTest.java index 8503e402f1..fb8d56d975 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResultTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/BaseWorkflowResultTest.java @@ -13,26 +13,26 @@ import static org.assertj.core.api.Assertions.assertThat; class BaseWorkflowResultTest { - private final static BaseWorkflowResult.Detail detail = - new BaseWorkflowResult.Detail<>(new RuntimeException(), null, null, null, null, null, false, - false, false); + private static final BaseWorkflowResult.Detail detail = + new BaseWorkflowResult.Detail<>( + new RuntimeException(), null, null, null, null, null, false, false, false); @Test void throwsExceptionWithoutNumberingIfAllDifferentClass() { - var res = new BaseWorkflowResult(Map.of(new DependentA(), detail, - new DependentB(), detail)); + var res = new BaseWorkflowResult(Map.of(new DependentA(), detail, new DependentB(), detail)); try { res.throwAggregateExceptionIfErrorsPresent(); } catch (AggregatedOperatorException e) { - assertThat(e.getAggregatedExceptions()).containsOnlyKeys(DependentA.class.getName(), - DependentB.class.getName()); + assertThat(e.getAggregatedExceptions()) + .containsOnlyKeys(DependentA.class.getName(), DependentB.class.getName()); } } @Test void numbersDependentClassNamesIfMoreOfSameType() { - var res = new BaseWorkflowResult(Map.of(new DependentA("name1"), detail, - new DependentA("name2"), detail)); + var res = + new BaseWorkflowResult( + Map.of(new DependentA("name1"), detail, new DependentA("name2"), detail)); try { res.throwAggregateExceptionIfErrorsPresent(); } catch (AggregatedOperatorException e) { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationConditionTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationConditionTest.java index 58990214a3..95afcc0464 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationConditionTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/CRDPresentActivationConditionTest.java @@ -21,13 +21,11 @@ class CRDPresentActivationConditionTest { private final CRDPresentActivationCondition.CRDPresentChecker checkerMock = mock(CRDPresentActivationCondition.CRDPresentChecker.class); private final CRDPresentActivationCondition condition = - new CRDPresentActivationCondition(checkerMock, 2, - Duration.ofMillis(TEST_CHECK_INTERVAL)); + new CRDPresentActivationCondition(checkerMock, 2, Duration.ofMillis(TEST_CHECK_INTERVAL)); private final DependentResource dr = mock(DependentResource.class); private final Context context = mock(Context.class); - @BeforeEach void setup() { CRDPresentActivationCondition.clearState(); @@ -35,14 +33,13 @@ void setup() { when(dr.resourceType()).thenReturn(TestCustomResource.class); } - @Test - void checkCRDIfNotCheckedBefore() { - when(checkerMock.checkIfCRDPresent(any(),any())).thenReturn(true); + void checkCRDIfNotCheckedBefore() { + when(checkerMock.checkIfCRDPresent(any(), any())).thenReturn(true); - assertThat(condition.isMet(dr,null,context)).isTrue(); - verify(checkerMock, times(1)).checkIfCRDPresent(any(),any()); - } + assertThat(condition.isMet(dr, null, context)).isTrue(); + verify(checkerMock, times(1)).checkIfCRDPresent(any(), any()); + } @Test void instantMetCallSkipsApiCall() { @@ -65,17 +62,17 @@ void intervalExpiredAPICheckedAgain() throws InterruptedException { } @Test - void crdIsNotCheckedAnymoreIfIfOnceFound() throws InterruptedException { - when(checkerMock.checkIfCRDPresent(any(),any())).thenReturn(true); + void crdIsNotCheckedAnymoreIfIfOnceFound() throws InterruptedException { + when(checkerMock.checkIfCRDPresent(any(), any())).thenReturn(true); - condition.isMet(dr,null,context); - verify(checkerMock, times(1)).checkIfCRDPresent(any(),any()); + condition.isMet(dr, null, context); + verify(checkerMock, times(1)).checkIfCRDPresent(any(), any()); - Thread.sleep(TEST_CHECK_INTERVAL_WITH_SLACK); + Thread.sleep(TEST_CHECK_INTERVAL_WITH_SLACK); - condition.isMet(dr,null,context); - verify(checkerMock, times(1)).checkIfCRDPresent(any(),any()); - } + condition.isMet(dr, null, context); + verify(checkerMock, times(1)).checkIfCRDPresent(any(), any()); + } @Test void crdNotCheckedAnymoreIfCountExpires() throws InterruptedException { @@ -87,5 +84,4 @@ void crdNotCheckedAnymoreIfCountExpires() throws InterruptedException { verify(checkerMock, times(2)).checkIfCRDPresent(any(), any()); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ExecutionAssert.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ExecutionAssert.java index d857c0e9dd..8429d0cf8e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ExecutionAssert.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ExecutionAssert.java @@ -8,8 +8,7 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.DependentResource; -public class ExecutionAssert - extends AbstractAssert> { +public class ExecutionAssert extends AbstractAssert> { public ExecutionAssert(List reconcileRecords) { super(reconcileRecords, ExecutionAssert.class); @@ -64,9 +63,8 @@ public ExecutionAssert reconciledInOrder(DependentResource... dependentRes for (int i = 0; i < dependentResources.length - 1; i++) { checkIfReconciled(i, dependentResources); checkIfReconciled(i + 1, dependentResources); - if (getActualDependentResources() - .indexOf(dependentResources[i]) > getActualDependentResources() - .indexOf(dependentResources[i + 1])) { + if (getActualDependentResources().indexOf(dependentResources[i]) + > getActualDependentResources().indexOf(dependentResources[i + 1])) { failWithMessage( "Dependent resource on index %d reconciled after the one on index %d", i, i + 1); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupportTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupportTest.java index ca73c8cae1..4b4a651637 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupportTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowSupportTest.java @@ -36,27 +36,32 @@ void checkFindsDuplicates() { final var drs2 = createDRS(NAME_2); final var drs1 = createDRS(NAME_1); - Assertions.assertThrows(OperatorException.class, () -> managedWorkflowSupport - .checkForNameDuplication(List.of(drs2, drs2))); - - Assertions.assertThrows(OperatorException.class, - () -> managedWorkflowSupport.checkForNameDuplication( - List.of(drs1, drs2, drs2))); - - final var exception = Assertions.assertThrows(OperatorException.class, - () -> managedWorkflowSupport.checkForNameDuplication( - List.of(drs1, drs2, drs2, drs1))); + Assertions.assertThrows( + OperatorException.class, + () -> managedWorkflowSupport.checkForNameDuplication(List.of(drs2, drs2))); + + Assertions.assertThrows( + OperatorException.class, + () -> managedWorkflowSupport.checkForNameDuplication(List.of(drs1, drs2, drs2))); + + final var exception = + Assertions.assertThrows( + OperatorException.class, + () -> managedWorkflowSupport.checkForNameDuplication(List.of(drs1, drs2, drs2, drs1))); assertThat(exception.getMessage()).contains(NAME_1, NAME_2); } @Test void orderingTrivialCases() { assertThat(managedWorkflowSupport.orderAndDetectCycles(List.of(createDRS(NAME_1)))) - .map(DependentResourceSpec::getName).containsExactly(NAME_1); - - assertThat(managedWorkflowSupport - .orderAndDetectCycles(List.of(createDRS(NAME_2, NAME_1), createDRS(NAME_1)))) - .map(DependentResourceSpec::getName).containsExactly(NAME_1, NAME_2); + .map(DependentResourceSpec::getName) + .containsExactly(NAME_1); + + assertThat( + managedWorkflowSupport.orderAndDetectCycles( + List.of(createDRS(NAME_2, NAME_1), createDRS(NAME_1)))) + .map(DependentResourceSpec::getName) + .containsExactly(NAME_1, NAME_2); } @Test @@ -64,10 +69,17 @@ void orderingDiamondShape() { String NAME_3 = "name3"; String NAME_4 = "name4"; - var res = managedWorkflowSupport - .orderAndDetectCycles(List.of(createDRS(NAME_2, NAME_1), createDRS(NAME_1), - createDRS(NAME_3, NAME_1), createDRS(NAME_4, NAME_2, NAME_3))) - .stream().map(DependentResourceSpec::getName).collect(Collectors.toList()); + var res = + managedWorkflowSupport + .orderAndDetectCycles( + List.of( + createDRS(NAME_2, NAME_1), + createDRS(NAME_1), + createDRS(NAME_3, NAME_1), + createDRS(NAME_4, NAME_2, NAME_3))) + .stream() + .map(DependentResourceSpec::getName) + .collect(Collectors.toList()); assertThat(res) .containsExactlyInAnyOrder(NAME_1, NAME_2, NAME_3, NAME_4) @@ -75,7 +87,6 @@ void orderingDiamondShape() { .contains(NAME_4, Index.atIndex(3)); } - @Test void orderingMultipleRoots() { final var NAME_3 = "name3"; @@ -83,15 +94,19 @@ void orderingMultipleRoots() { final var NAME_5 = "name5"; final var NAME_6 = "name6"; - var res = managedWorkflowSupport - .orderAndDetectCycles(List.of( - createDRS(NAME_2, NAME_1, NAME_5), - createDRS(NAME_1), - createDRS(NAME_3, NAME_1), - createDRS(NAME_4, NAME_2, NAME_3), - createDRS(NAME_5, NAME_1, NAME_6), - createDRS(NAME_6))) - .stream().map(DependentResourceSpec::getName).collect(Collectors.toList()); + var res = + managedWorkflowSupport + .orderAndDetectCycles( + List.of( + createDRS(NAME_2, NAME_1, NAME_5), + createDRS(NAME_1), + createDRS(NAME_3, NAME_1), + createDRS(NAME_4, NAME_2, NAME_3), + createDRS(NAME_5, NAME_1, NAME_6), + createDRS(NAME_6))) + .stream() + .map(DependentResourceSpec::getName) + .collect(Collectors.toList()); assertThat(res) .containsExactlyInAnyOrder(NAME_1, NAME_5, NAME_6, NAME_2, NAME_3, NAME_4) @@ -106,44 +121,58 @@ void orderingMultipleRoots() { @Test void detectsCyclesTrivialCases() { String NAME_3 = "name3"; - Assertions.assertThrows(OperatorException.class, () -> managedWorkflowSupport - .orderAndDetectCycles(List.of(createDRS(NAME_2, NAME_1), createDRS(NAME_1, NAME_2)))); - Assertions.assertThrows(OperatorException.class, - () -> managedWorkflowSupport - .orderAndDetectCycles(List.of(createDRS(NAME_2, NAME_1), createDRS(NAME_1, NAME_3), - createDRS(NAME_3, NAME_2)))); + Assertions.assertThrows( + OperatorException.class, + () -> + managedWorkflowSupport.orderAndDetectCycles( + List.of(createDRS(NAME_2, NAME_1), createDRS(NAME_1, NAME_2)))); + Assertions.assertThrows( + OperatorException.class, + () -> + managedWorkflowSupport.orderAndDetectCycles( + List.of( + createDRS(NAME_2, NAME_1), + createDRS(NAME_1, NAME_3), + createDRS(NAME_3, NAME_2)))); } @Test void detectsCycleOnSubTree() { - Assertions.assertThrows(OperatorException.class, - () -> managedWorkflowSupport.orderAndDetectCycles(List.of(createDRS(NAME_1), - createDRS(NAME_2, NAME_1), - createDRS(NAME_3, NAME_1, NAME_4), - createDRS(NAME_4, NAME_3)))); - - Assertions.assertThrows(OperatorException.class, - () -> managedWorkflowSupport.orderAndDetectCycles(List.of( - createDRS(NAME_1), - createDRS(NAME_2, NAME_1, NAME_4), - createDRS(NAME_3, NAME_2), - createDRS(NAME_4, NAME_3)))); + Assertions.assertThrows( + OperatorException.class, + () -> + managedWorkflowSupport.orderAndDetectCycles( + List.of( + createDRS(NAME_1), + createDRS(NAME_2, NAME_1), + createDRS(NAME_3, NAME_1, NAME_4), + createDRS(NAME_4, NAME_3)))); + + Assertions.assertThrows( + OperatorException.class, + () -> + managedWorkflowSupport.orderAndDetectCycles( + List.of( + createDRS(NAME_1), + createDRS(NAME_2, NAME_1, NAME_4), + createDRS(NAME_3, NAME_2), + createDRS(NAME_4, NAME_3)))); } @Test void createsWorkflow() { - var specs = List.of(createDRS(NAME_1), - createDRS(NAME_2, NAME_1), - createDRS(NAME_3, NAME_1), - createDRS(NAME_4, NAME_3, NAME_2)); + var specs = + List.of( + createDRS(NAME_1), + createDRS(NAME_2, NAME_1), + createDRS(NAME_3, NAME_1), + createDRS(NAME_4, NAME_3, NAME_2)); var workflow = managedWorkflowSupport.createAsDefault(specs); - assertThat(workflow.nodeNames()) - .containsExactlyInAnyOrder(NAME_1, NAME_2, NAME_3, NAME_4); + assertThat(workflow.nodeNames()).containsExactlyInAnyOrder(NAME_1, NAME_2, NAME_3, NAME_4); assertThat(workflow.getTopLevelResources()).containsExactly(NAME_1); assertThat(workflow.getBottomLevelResources()).containsExactly(NAME_4); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java index e634a368d7..1a46cec2f8 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTest.java @@ -36,21 +36,25 @@ void isNotCleanerIfNoDeleter() { @Test void isNotCleanerIfGarbageCollected() { - assertThat(managedWorkflow(createDRSWithTraits(NAME, GarbageCollected.class)) - .hasCleaner()).isFalse(); + assertThat(managedWorkflow(createDRSWithTraits(NAME, GarbageCollected.class)).hasCleaner()) + .isFalse(); } @Test void isCleanerShouldWork() { - assertThat(managedWorkflow( - createDRSWithTraits(NAME, GarbageCollected.class), - createDRSWithTraits("foo", Deleter.class)) - .hasCleaner()).isTrue(); - - assertThat(managedWorkflow( - createDRSWithTraits("foo", Deleter.class), - createDRSWithTraits(NAME, GarbageCollected.class)) - .hasCleaner()).isTrue(); + assertThat( + managedWorkflow( + createDRSWithTraits(NAME, GarbageCollected.class), + createDRSWithTraits("foo", Deleter.class)) + .hasCleaner()) + .isTrue(); + + assertThat( + managedWorkflow( + createDRSWithTraits("foo", Deleter.class), + createDRSWithTraits(NAME, GarbageCollected.class)) + .hasCleaner()) + .isTrue(); } @Test @@ -63,26 +67,25 @@ void isCleanerIfHasDeleter() { ManagedWorkflow managedWorkflow(DependentResourceSpec... specs) { final var configuration = mock(ControllerConfiguration.class); - var ws = new WorkflowSpec() { - @Override - public List getDependentResourceSpecs() { - return List.of(specs); - } - - @Override - public boolean isExplicitInvocation() { - return false; - } - - @Override - public boolean handleExceptionsInReconciler() { - return false; - } - }; + var ws = + new WorkflowSpec() { + @Override + public List getDependentResourceSpecs() { + return List.of(specs); + } + + @Override + public boolean isExplicitInvocation() { + return false; + } + + @Override + public boolean handleExceptionsInReconciler() { + return false; + } + }; when(configuration.getWorkflowSpec()).thenReturn(Optional.of(ws)); - return new BaseConfigurationService().getWorkflowFactory() - .workflowFor(configuration); + return new BaseConfigurationService().getWorkflowFactory().workflowFor(configuration); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java index ae0731b8f5..58ca9a4c6e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/ManagedWorkflowTestUtils.java @@ -18,18 +18,19 @@ public class ManagedWorkflowTestUtils { @SuppressWarnings("unchecked") public static DependentResourceSpec createDRS(String name, String... dependOns) { - return new DependentResourceSpec(EmptyTestDependentResource.class, name, Set.of(dependOns), - null, null, null, null, null); + return new DependentResourceSpec( + EmptyTestDependentResource.class, name, Set.of(dependOns), null, null, null, null, null); } - public static DependentResourceSpec createDRSWithTraits(String name, - Class... dependentResourceTraits) { + public static DependentResourceSpec createDRSWithTraits( + String name, Class... dependentResourceTraits) { final var spy = Mockito.mock(DependentResourceSpec.class); when(spy.getName()).thenReturn(name); Class toMock = DependentResource.class; - final var garbageCollected = dependentResourceTraits != null && - Arrays.asList(dependentResourceTraits).contains(GarbageCollected.class); + final var garbageCollected = + dependentResourceTraits != null + && Arrays.asList(dependentResourceTraits).contains(GarbageCollected.class); if (garbageCollected) { toMock = KubernetesDependentResource.class; } @@ -38,5 +39,4 @@ public static DependentResourceSpec createDRSWithTraits(String name, when(spy.getDependentResourceClass()).thenReturn(dr.getClass()); return spy; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java index b41ee430f7..947cc8c630 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowBuilderTest.java @@ -18,12 +18,12 @@ void workflowIsCleanerIfAtLeastOneDRIsCleaner() { when(deleter.isDeletable()).thenReturn(true); when(deleter.name()).thenReturn("deleter"); - var workflow = new WorkflowBuilder() - .addDependentResource(deleter) - .addDependentResource(dr) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(deleter) + .addDependentResource(dr) + .build(); assertThat(workflow.hasCleaner()).isTrue(); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java index 878cec419c..f8c1b3efad 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowCleanupExecutorTest.java @@ -28,8 +28,10 @@ class WorkflowCleanupExecutorTest extends AbstractWorkflowExecutorTest { protected TestDeleterDependent dd2 = new TestDeleterDependent("DR_DELETER_2"); protected TestDeleterDependent dd3 = new TestDeleterDependent("DR_DELETER_3"); protected TestDeleterDependent dd4 = new TestDeleterDependent("DR_DELETER_4"); + @SuppressWarnings("unchecked") Context mockContext = spy(Context.class); + ExecutorService executorService = Executors.newCachedThreadPool(); @BeforeEach @@ -52,36 +54,43 @@ void setup() { @Test void cleanUpDiamondWorkflow() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dr1).dependsOn(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dr1, dd2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dr1) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dr1, dd2) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciledInOrder(dd3, dd2, dd1).notReconciled(dr1); - Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd1, dd2, - dd3); + Assertions.assertThat(res.getDeleteCalledOnDependents()) + .containsExactlyInAnyOrder(dd1, dd2, dd3); Assertions.assertThat(res.getErroredDependents()).isEmpty(); Assertions.assertThat(res.getPostConditionNotMetDependents()).isEmpty(); } @Test void dontDeleteIfDependentErrored() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dd2) - .addDependentResourceAndConfigure(errorDD).dependsOn(dd2) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dd2) + .addDependentResourceAndConfigure(errorDD) + .dependsOn(dd2) + .withThrowExceptionFurther(false) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); - assertThrows(AggregatedOperatorException.class, - res::throwAggregateExceptionIfErrorsPresent); + assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); assertThat(executionHistory).deleted(dd3, errorDD).notReconciled(dd1, dd2); @@ -90,14 +99,15 @@ void dontDeleteIfDependentErrored() { Assertions.assertThat(res.getPostConditionNotMetDependents()).isEmpty(); } - @Test void cleanupConditionTrivialCase() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .withDeletePostcondition(notMetCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .withDeletePostcondition(notMetCondition) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); @@ -109,10 +119,13 @@ void cleanupConditionTrivialCase() { @Test void cleanupConditionMet() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1).withDeletePostcondition(metCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .withDeletePostcondition(metCondition) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); @@ -125,13 +138,17 @@ void cleanupConditionMet() { @Test void cleanupConditionDiamondWorkflow() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dd1) - .withDeletePostcondition(notMetCondition) - .addDependentResourceAndConfigure(dd4).dependsOn(dd2, dd3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dd1) + .withDeletePostcondition(notMetCondition) + .addDependentResourceAndConfigure(dd4) + .dependsOn(dd2, dd3) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); @@ -140,56 +157,60 @@ void cleanupConditionDiamondWorkflow() { .reconciledInOrder(dd4, dd3) .notReconciled(dr1); - Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd4, dd3, - dd2); + Assertions.assertThat(res.getDeleteCalledOnDependents()) + .containsExactlyInAnyOrder(dd4, dd3, dd2); Assertions.assertThat(res.getErroredDependents()).isEmpty(); Assertions.assertThat(res.getPostConditionNotMetDependents()).containsExactlyInAnyOrder(dd3); } @Test void dontDeleteIfGarbageCollected() { - var workflow = new WorkflowBuilder() - .addDependentResource(gcDeleter) - .build(); + var workflow = + new WorkflowBuilder().addDependentResource(gcDeleter).build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); - assertThat(executionHistory) - .notReconciled(gcDeleter); + assertThat(executionHistory).notReconciled(gcDeleter); Assertions.assertThat(res.getDeleteCalledOnDependents()).isEmpty(); } @Test void ifDependentActiveDependentNormallyDeleted() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dd1) - .withActivationCondition(metCondition) - .addDependentResourceAndConfigure(dd4).dependsOn(dd2, dd3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dd1) + .withActivationCondition(metCondition) + .addDependentResourceAndConfigure(dd4) + .dependsOn(dd2, dd3) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); - assertThat(executionHistory) - .reconciledInOrder(dd4, dd2, dd1) - .reconciledInOrder(dd4, dd3, dd1); + assertThat(executionHistory).reconciledInOrder(dd4, dd2, dd1).reconciledInOrder(dd4, dd3, dd1); - Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd4, dd3, - dd2, dd1); + Assertions.assertThat(res.getDeleteCalledOnDependents()) + .containsExactlyInAnyOrder(dd4, dd3, dd2, dd1); } @Test void ifDependentActiveDeletePostConditionIsChecked() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dd1) - .withDeletePostcondition(notMetCondition) - .withActivationCondition(metCondition) - .addDependentResourceAndConfigure(dd4).dependsOn(dd2, dd3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dd1) + .withDeletePostcondition(notMetCondition) + .withActivationCondition(metCondition) + .addDependentResourceAndConfigure(dd4) + .dependsOn(dd2, dd3) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); @@ -198,60 +219,66 @@ void ifDependentActiveDeletePostConditionIsChecked() { .reconciledInOrder(dd4, dd3) .notReconciled(dr1); - Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd4, dd3, - dd2); + Assertions.assertThat(res.getDeleteCalledOnDependents()) + .containsExactlyInAnyOrder(dd4, dd3, dd2); Assertions.assertThat(res.getErroredDependents()).isEmpty(); Assertions.assertThat(res.getPostConditionNotMetDependents()).containsExactlyInAnyOrder(dd3); } @Test void ifDependentInactiveDeleteIsNotCalled() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dd1) - .withActivationCondition(notMetCondition) - .addDependentResourceAndConfigure(dd4).dependsOn(dd2, dd3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dd1) + .withActivationCondition(notMetCondition) + .addDependentResourceAndConfigure(dd4) + .dependsOn(dd2, dd3) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); - assertThat(executionHistory) - .reconciledInOrder(dd4, dd2, dd1); + assertThat(executionHistory).reconciledInOrder(dd4, dd2, dd1); - Assertions.assertThat(res.getDeleteCalledOnDependents()).containsExactlyInAnyOrder(dd4, - dd2, dd1); + Assertions.assertThat(res.getDeleteCalledOnDependents()) + .containsExactlyInAnyOrder(dd4, dd2, dd1); } @Test void ifDependentInactiveDeletePostConditionNotChecked() { - var workflow = new WorkflowBuilder() - .addDependentResource(dd1) - .addDependentResourceAndConfigure(dd2).dependsOn(dd1) - .addDependentResourceAndConfigure(dd3).dependsOn(dd1) - .withDeletePostcondition(notMetCondition) - .withActivationCondition(notMetCondition) - .addDependentResourceAndConfigure(dd4).dependsOn(dd2, dd3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dd1) + .addDependentResourceAndConfigure(dd2) + .dependsOn(dd1) + .addDependentResourceAndConfigure(dd3) + .dependsOn(dd1) + .withDeletePostcondition(notMetCondition) + .withActivationCondition(notMetCondition) + .addDependentResourceAndConfigure(dd4) + .dependsOn(dd2, dd3) + .build(); var res = workflow.cleanup(new TestCustomResource(), mockContext); - assertThat(executionHistory) - .reconciledInOrder(dd4, dd2, dd1); + assertThat(executionHistory).reconciledInOrder(dd4, dd2, dd1); Assertions.assertThat(res.getPostConditionNotMetDependents()).isEmpty(); } @Test void singleInactiveDependent() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dd1) - .withActivationCondition(notMetCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dd1) + .withActivationCondition(notMetCondition) + .build(); workflow.cleanup(new TestCustomResource(), mockContext); assertThat(executionHistory).notReconciled(dd1); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java index 0aa93fb0f9..f0c5c64e44 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowReconcileExecutorTest.java @@ -28,6 +28,7 @@ class WorkflowReconcileExecutorTest extends AbstractWorkflowExecutorTest { @SuppressWarnings("unchecked") Context mockContext = spy(Context.class); + ExecutorService executorService = Executors.newCachedThreadPool(); TestDependent dr3 = new TestDependent("DR_3"); @@ -45,10 +46,11 @@ void setup(TestInfo testInfo) { @Test void reconcileTopLevelResources() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResource(dr2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResource(dr2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -59,10 +61,12 @@ void reconcileTopLevelResources() { @Test void reconciliationWithSimpleDependsOn() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -76,17 +80,19 @@ void reconciliationWithSimpleDependsOn() { @Test void reconciliationWithTwoTheDependsOns() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .addDependentResourceAndConfigure(dr3).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); - assertThat(executionHistory) - .reconciledInOrder(dr1, dr2).reconciledInOrder(dr1, dr3); + assertThat(executionHistory).reconciledInOrder(dr1, dr2).reconciledInOrder(dr1, dr3); Assertions.assertThat(res.getReconciledDependents()).containsExactlyInAnyOrder(dr1, dr2, dr3); Assertions.assertThat(res.getErroredDependents()).isEmpty(); Assertions.assertThat(res.getNotReadyDependents()).isEmpty(); @@ -94,37 +100,40 @@ void reconciliationWithTwoTheDependsOns() { @Test void diamondShareWorkflowReconcile() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .addDependentResourceAndConfigure(dr3).dependsOn(dr1) - .addDependentResourceAndConfigure(dr4).dependsOn(dr3).dependsOn(dr2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr4) + .dependsOn(dr3) + .dependsOn(dr2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); - assertThat(executionHistory) - .reconciledInOrder(dr1, dr2, dr4) - .reconciledInOrder(dr1, dr3, dr4); + assertThat(executionHistory).reconciledInOrder(dr1, dr2, dr4).reconciledInOrder(dr1, dr3, dr4); - Assertions.assertThat(res.getReconciledDependents()).containsExactlyInAnyOrder(dr1, dr2, dr3, - dr4); + Assertions.assertThat(res.getReconciledDependents()) + .containsExactlyInAnyOrder(dr1, dr2, dr3, dr4); Assertions.assertThat(res.getErroredDependents()).isEmpty(); Assertions.assertThat(res.getNotReadyDependents()).isEmpty(); } @Test void exceptionHandlingSimpleCases() { - var workflow = new WorkflowBuilder() - .addDependentResource(drError) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(drError) + .withThrowExceptionFurther(false) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThrows(AggregatedOperatorException.class, - res::throwAggregateExceptionIfErrorsPresent); + assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); assertThat(executionHistory).reconciled(drError); Assertions.assertThat(res.getErroredDependents()).containsOnlyKeys(drError); @@ -134,16 +143,18 @@ void exceptionHandlingSimpleCases() { @Test void dependentsOnErroredResourceNotReconciled() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(drError).dependsOn(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(drError) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(drError) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(drError) + .withThrowExceptionFurther(false) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThrows(AggregatedOperatorException.class, - res::throwAggregateExceptionIfErrorsPresent); + assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); assertThat(executionHistory).reconciled(dr1, drError).notReconciled(dr2); Assertions.assertThat(res.getErroredDependents()).containsOnlyKeys(drError); @@ -154,17 +165,20 @@ void dependentsOnErroredResourceNotReconciled() { @Test void oneBranchErrorsOtherCompletes() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(drError).dependsOn(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .addDependentResourceAndConfigure(dr3).dependsOn(dr2) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(drError) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr2) + .withThrowExceptionFurther(false) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThrows(AggregatedOperatorException.class, - res::throwAggregateExceptionIfErrorsPresent); + assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); assertThat(executionHistory).reconciledInOrder(dr1, dr2, dr3).reconciledInOrder(dr1, drError); Assertions.assertThat(res.getErroredDependents()).containsOnlyKeys(drError); @@ -174,16 +188,17 @@ void oneBranchErrorsOtherCompletes() { @Test void onlyOneDependsOnErroredResourceNotReconciled() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResource(drError) - .addDependentResourceAndConfigure(dr2).dependsOn(drError, dr1) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResource(drError) + .addDependentResourceAndConfigure(dr2) + .dependsOn(drError, dr1) + .withThrowExceptionFurther(false) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThrows(AggregatedOperatorException.class, - res::throwAggregateExceptionIfErrorsPresent); + assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); assertThat(executionHistory).notReconciled(dr2); Assertions.assertThat(res.getErroredDependents()).containsKey(drError); @@ -194,20 +209,26 @@ void onlyOneDependsOnErroredResourceNotReconciled() { @Test void simpleReconcileCondition() { final var result = "Some error message"; - final var unmetWithResult = new DetailedCondition() { - @Override - public Result detailedIsMet( - DependentResource dependentResource, - TestCustomResource primary, Context context) { - return Result.withResult(false, result); - } - }; - - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).withReconcilePrecondition(unmetWithResult) - .addDependentResourceAndConfigure(dr2).withReconcilePrecondition(metCondition) - .addDependentResourceAndConfigure(drDeleter).withReconcilePrecondition(notMetCondition) - .build(); + final var unmetWithResult = + new DetailedCondition() { + @Override + public Result detailedIsMet( + DependentResource dependentResource, + TestCustomResource primary, + Context context) { + return Result.withResult(false, result); + } + }; + + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReconcilePrecondition(unmetWithResult) + .addDependentResourceAndConfigure(dr2) + .withReconcilePrecondition(metCondition) + .addDependentResourceAndConfigure(drDeleter) + .withReconcilePrecondition(notMetCondition) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -219,15 +240,17 @@ public Result detailedIsMet( .ifPresentOrElse(s -> assertEquals(result, s), org.junit.jupiter.api.Assertions::fail); } - @Test void triangleOnceConditionNotMet() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .addDependentResourceAndConfigure(drDeleter).withReconcilePrecondition(notMetCondition) - .dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .addDependentResourceAndConfigure(drDeleter) + .withReconcilePrecondition(notMetCondition) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -241,15 +264,19 @@ void triangleOnceConditionNotMet() { void reconcileConditionTransitiveDelete() { TestDeleterDependent drDeleter2 = new TestDeleterDependent("DR_DELETER_2"); - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter).dependsOn(dr2) - .withReconcilePrecondition(metCondition) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drDeleter) - .withReconcilePrecondition(metCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter) + .dependsOn(dr2) + .withReconcilePrecondition(metCondition) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drDeleter) + .withReconcilePrecondition(metCondition) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -267,21 +294,21 @@ void reconcileConditionTransitiveDelete() { void reconcileConditionAlsoErrorDependsOn() { TestDeleterDependent drDeleter2 = new TestDeleterDependent("DR_DELETER_2"); - var workflow = new WorkflowBuilder() - .addDependentResource(drError) - .addDependentResourceAndConfigure(drDeleter).withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drError, drDeleter) - .withReconcilePrecondition(metCondition) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(drError) + .addDependentResourceAndConfigure(drDeleter) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drError, drDeleter) + .withReconcilePrecondition(metCondition) + .withThrowExceptionFurther(false) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThrows(AggregatedOperatorException.class, - res::throwAggregateExceptionIfErrorsPresent); + assertThrows(AggregatedOperatorException.class, res::throwAggregateExceptionIfErrorsPresent); - assertThat(executionHistory) - .deleted(drDeleter2, drDeleter) - .reconciled(drError); + assertThat(executionHistory).deleted(drDeleter2, drDeleter).reconciled(drError); Assertions.assertThat(res.getErroredDependents()).containsOnlyKeys(drError); Assertions.assertThat(res.getReconciledDependents()).isEmpty(); @@ -290,11 +317,14 @@ void reconcileConditionAlsoErrorDependsOn() { @Test void oneDependsOnConditionNotMet() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter).dependsOn(dr1, dr2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter) + .dependsOn(dr1, dr2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -309,12 +339,15 @@ void oneDependsOnConditionNotMet() { @Test void deletedIfReconcileConditionNotMet() { TestDeleterDependent drDeleter2 = new TestDeleterDependent("DR_DELETER_2"); - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(drDeleter).dependsOn(dr1) - .withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(dr1, drDeleter) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(drDeleter) + .dependsOn(dr1) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(dr1, drDeleter) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -333,14 +366,19 @@ void deleteDoneInReverseOrder() { TestDeleterDependent drDeleter3 = new TestDeleterDependent("DR_DELETER_3"); TestDeleterDependent drDeleter4 = new TestDeleterDependent("DR_DELETER_4"); - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(drDeleter).withReconcilePrecondition(notMetCondition) - .dependsOn(dr1) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drDeleter) - .addDependentResourceAndConfigure(drDeleter3).dependsOn(drDeleter) - .addDependentResourceAndConfigure(drDeleter4).dependsOn(drDeleter3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(drDeleter) + .withReconcilePrecondition(notMetCondition) + .dependsOn(dr1) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drDeleter) + .addDependentResourceAndConfigure(drDeleter3) + .dependsOn(drDeleter) + .addDependentResourceAndConfigure(drDeleter4) + .dependsOn(drDeleter3) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -360,17 +398,23 @@ void diamondDeleteWithPostConditionInMiddle() { TestDeleterDependent drDeleter3 = new TestDeleterDependent("DR_DELETER_3"); TestDeleterDependent drDeleter4 = new TestDeleterDependent("DR_DELETER_4"); - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(drDeleter).withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drDeleter) - .addDependentResourceAndConfigure(drDeleter3).dependsOn(drDeleter) - .withDeletePostcondition(this.notMetCondition) - .addDependentResourceAndConfigure(drDeleter4).dependsOn(drDeleter3, drDeleter2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(drDeleter) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drDeleter) + .addDependentResourceAndConfigure(drDeleter3) + .dependsOn(drDeleter) + .withDeletePostcondition(this.notMetCondition) + .addDependentResourceAndConfigure(drDeleter4) + .dependsOn(drDeleter3, drDeleter2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThat(executionHistory).notReconciled(drDeleter) + assertThat(executionHistory) + .notReconciled(drDeleter) .reconciledInOrder(drDeleter4, drDeleter2) .reconciledInOrder(drDeleter4, drDeleter3); @@ -384,13 +428,18 @@ void diamondDeleteErrorInMiddle() { TestDeleterDependent drDeleter2 = new TestDeleterDependent("DR_DELETER_2"); TestDeleterDependent drDeleter3 = new TestDeleterDependent("DR_DELETER_3"); - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(drDeleter).withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drDeleter) - .addDependentResourceAndConfigure(errorDD).dependsOn(drDeleter) - .addDependentResourceAndConfigure(drDeleter3).dependsOn(errorDD, drDeleter2) - .withThrowExceptionFurther(false) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(drDeleter) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drDeleter) + .addDependentResourceAndConfigure(errorDD) + .dependsOn(drDeleter) + .addDependentResourceAndConfigure(drDeleter3) + .dependsOn(errorDD, drDeleter2) + .withThrowExceptionFurther(false) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -405,10 +454,13 @@ void diamondDeleteErrorInMiddle() { @Test void readyConditionTrivialCase() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).withReadyPostcondition(metCondition) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReadyPostcondition(metCondition) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -421,14 +473,16 @@ void readyConditionTrivialCase() { @Test void readyConditionNotMetTrivialCase() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).withReadyPostcondition(notMetCondition) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReadyPostcondition(notMetCondition) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); - assertThat(executionHistory).reconciled(dr1).notReconciled(dr2); Assertions.assertThat(res.getErroredDependents()).isEmpty(); @@ -439,11 +493,14 @@ void readyConditionNotMetTrivialCase() { @Test void readyConditionNotMetInOneParent() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).withReadyPostcondition(notMetCondition) - .addDependentResource(dr2) - .addDependentResourceAndConfigure(dr3).dependsOn(dr1, dr2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReadyPostcondition(notMetCondition) + .addDependentResource(dr2) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr1, dr2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -455,19 +512,23 @@ void readyConditionNotMetInOneParent() { @Test void diamondShareWithReadyCondition() { - var workflow = new WorkflowBuilder() - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2) - .dependsOn(dr1) - .withReadyPostcondition(notMetCondition) - .addDependentResourceAndConfigure(dr3).dependsOn(dr1) - .addDependentResourceAndConfigure(dr4).dependsOn(dr2, dr3) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .withReadyPostcondition(notMetCondition) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr4) + .dependsOn(dr2, dr3) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getErroredDependents()).isEmpty(); - assertThat(executionHistory).reconciledInOrder(dr1, dr2) + assertThat(executionHistory) + .reconciledInOrder(dr1, dr2) .reconciledInOrder(dr1, dr3) .notReconciled(dr4); @@ -478,9 +539,11 @@ void diamondShareWithReadyCondition() { @Test void garbageCollectedResourceIsDeletedIfReconcilePreconditionDoesNotHold() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(gcDeleter).withReconcilePrecondition(notMetCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(gcDeleter) + .withReconcilePrecondition(notMetCondition) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -490,10 +553,13 @@ void garbageCollectedResourceIsDeletedIfReconcilePreconditionDoesNotHold() { @Test void garbageCollectedDeepResourceIsDeletedIfReconcilePreconditionDoesNotHold() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).withReconcilePrecondition(notMetCondition) - .addDependentResourceAndConfigure(gcDeleter).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReconcilePrecondition(notMetCondition) + .addDependentResourceAndConfigure(gcDeleter) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -503,11 +569,12 @@ void garbageCollectedDeepResourceIsDeletedIfReconcilePreconditionDoesNotHold() { @Test void notReconciledIfActivationConditionNotMet() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withActivationCondition(notMetCondition) - .addDependentResource(dr2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withActivationCondition(notMetCondition) + .addDependentResource(dr2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr2).notReconciled(dr1); @@ -517,12 +584,14 @@ void notReconciledIfActivationConditionNotMet() { @Test void dependentsOnANonActiveDependentNotReconciled() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withActivationCondition(notMetCondition) - .addDependentResource(dr2) - .addDependentResourceAndConfigure(dr3).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withActivationCondition(notMetCondition) + .addDependentResource(dr2) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); assertThat(executionHistory).reconciled(dr2).notReconciled(dr1, dr3); @@ -532,13 +601,15 @@ void dependentsOnANonActiveDependentNotReconciled() { @Test void readyConditionNotCheckedOnNonActiveDependent() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withActivationCondition(notMetCondition) - .withReadyPostcondition(notMetCondition) - .addDependentResource(dr2) - .addDependentResourceAndConfigure(dr3).dependsOn(dr1) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withActivationCondition(notMetCondition) + .withReadyPostcondition(notMetCondition) + .addDependentResource(dr2) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr1) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); @@ -550,11 +621,12 @@ void readyConditionNotCheckedOnNonActiveDependent() { void reconcilePreconditionNotCheckedOnNonActiveDependent() { var precondition = mock(Condition.class); - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withActivationCondition(notMetCondition) - .withReconcilePrecondition(precondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withActivationCondition(notMetCondition) + .withReconcilePrecondition(precondition) + .build(); workflow.reconcile(new TestCustomResource(), mockContext); @@ -566,20 +638,23 @@ void deletesDependentsOfNonActiveDependentButNotTheNonActive() { TestDeleterDependent drDeleter2 = new TestDeleterDependent("DR_DELETER_2"); TestDeleterDependent drDeleter3 = new TestDeleterDependent("DR_DELETER_3"); - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).withActivationCondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter).dependsOn(dr1) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drDeleter) - .withActivationCondition(notMetCondition) - .addDependentResourceAndConfigure(drDeleter3).dependsOn(drDeleter2) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withActivationCondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter) + .dependsOn(dr1) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drDeleter) + .withActivationCondition(notMetCondition) + .addDependentResourceAndConfigure(drDeleter3) + .dependsOn(drDeleter2) + .build(); var res = workflow.reconcile(new TestCustomResource(), mockContext); Assertions.assertThat(res.getReconciledDependents()).isEmpty(); - assertThat(executionHistory).deleted(drDeleter, drDeleter3) - .notReconciled(dr1, - drDeleter2); + assertThat(executionHistory).deleted(drDeleter, drDeleter3).notReconciled(dr1, drDeleter2); } @Test @@ -589,10 +664,13 @@ void activationConditionOnlyCalledOnceOnDeleteDependents() { var condition = mock(Condition.class); when(condition.isMet(any(), any(), any())).thenReturn(false); - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(drDeleter).withActivationCondition(condition) - .addDependentResourceAndConfigure(drDeleter2).dependsOn(drDeleter) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(drDeleter) + .withActivationCondition(condition) + .addDependentResourceAndConfigure(drDeleter2) + .dependsOn(drDeleter) + .build(); workflow.reconcile(new TestCustomResource(), mockContext); @@ -600,66 +678,75 @@ void activationConditionOnlyCalledOnceOnDeleteDependents() { verify(condition, times(1)).isMet(any(), any(), any()); } - @Test void resultFromReadyConditionShouldBeAvailableIfExisting() { final var result = Integer.valueOf(42); - final var resultCondition = new DetailedCondition<>() { - @Override - public Result detailedIsMet(DependentResource dependentResource, - HasMetadata primary, Context context) { - return new Result<>() { + final var resultCondition = + new DetailedCondition<>() { @Override - public Object getDetail() { - return result; - } - - @Override - public boolean isSuccess() { - return false; // force not ready + public Result detailedIsMet( + DependentResource dependentResource, + HasMetadata primary, + Context context) { + return new Result<>() { + @Override + public Object getDetail() { + return result; + } + + @Override + public boolean isSuccess() { + return false; // force not ready + } + }; } }; - } - }; - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withReadyPostcondition(resultCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReadyPostcondition(resultCondition) + .build(); final var reconcileResult = workflow.reconcile(new TestCustomResource(), mockContext); - assertEquals(result, - reconcileResult.getNotReadyDependentResult(dr1, Integer.class).orElseThrow()); + assertEquals( + result, reconcileResult.getNotReadyDependentResult(dr1, Integer.class).orElseThrow()); } @Test void shouldThrowIllegalArgumentExceptionIfTypesDoNotMatch() { final var result = "FOO"; - final var resultCondition = new DetailedCondition<>() { - @Override - public Result detailedIsMet(DependentResource dependentResource, - HasMetadata primary, Context context) { - return new Result<>() { - @Override - public Object getDetail() { - return result; - } - + final var resultCondition = + new DetailedCondition<>() { @Override - public boolean isSuccess() { - return false; // force not ready + public Result detailedIsMet( + DependentResource dependentResource, + HasMetadata primary, + Context context) { + return new Result<>() { + @Override + public Object getDetail() { + return result; + } + + @Override + public boolean isSuccess() { + return false; // force not ready + } + }; } }; - } - }; - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withReadyPostcondition(resultCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReadyPostcondition(resultCondition) + .build(); final var reconcileResult = workflow.reconcile(new TestCustomResource(), mockContext); final var expectedResultType = Integer.class; - final var e = assertThrows(IllegalArgumentException.class, - () -> reconcileResult.getNotReadyDependentResult(dr1, expectedResultType)); + final var e = + assertThrows( + IllegalArgumentException.class, + () -> reconcileResult.getNotReadyDependentResult(dr1, expectedResultType)); final var message = e.getMessage(); assertTrue(message.contains(dr1.name())); assertTrue(message.contains(expectedResultType.getSimpleName())); @@ -668,10 +755,11 @@ public boolean isSuccess() { @Test void shouldReturnEmptyIfNoConditionResultExists() { - var workflow = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1) - .withReadyPostcondition(notMetCondition) - .build(); + var workflow = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .withReadyPostcondition(notMetCondition) + .build(); final var reconcileResult = workflow.reconcile(new TestCustomResource(), mockContext); assertTrue(reconcileResult.getNotReadyDependentResult(dr1, Integer.class).isEmpty()); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java index 6d268082f5..e2bd4a5b62 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/dependent/workflow/WorkflowTest.java @@ -26,14 +26,18 @@ void zeroTopLevelDRShouldThrowException() { var dr2 = mockDependent("dr2"); var dr3 = mockDependent("dr3"); - var cyclicWorkflowBuilderSetup = new WorkflowBuilder() - .addDependentResourceAndConfigure(dr1).dependsOn() - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .addDependentResourceAndConfigure(dr3).dependsOn(dr2) - .addDependentResourceAndConfigure(dr1).dependsOn(dr2); - - assertThrows(IllegalStateException.class, - cyclicWorkflowBuilderSetup::build); + var cyclicWorkflowBuilderSetup = + new WorkflowBuilder() + .addDependentResourceAndConfigure(dr1) + .dependsOn() + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .addDependentResourceAndConfigure(dr3) + .dependsOn(dr2) + .addDependentResourceAndConfigure(dr1) + .dependsOn(dr2); + + assertThrows(IllegalStateException.class, cyclicWorkflowBuilderSetup::build); } @Test @@ -42,11 +46,13 @@ void calculatesTopLevelResources() { var dr2 = mockDependent("dr2"); var independentDR = mockDependent("independentDR"); - var workflow = new WorkflowBuilder() - .addDependentResource(independentDR) - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .buildAsDefaultWorkflow(); + var workflow = + new WorkflowBuilder() + .addDependentResource(independentDR) + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .buildAsDefaultWorkflow(); Set topResources = workflow.getTopLevelDependentResources().stream() @@ -62,11 +68,13 @@ void calculatesBottomLevelResources() { var dr2 = mockDependent("dr2"); var independentDR = mockDependent("independentDR"); - final var workflow = new WorkflowBuilder() - .addDependentResource(independentDR) - .addDependentResource(dr1) - .addDependentResourceAndConfigure(dr2).dependsOn(dr1) - .buildAsDefaultWorkflow(); + final var workflow = + new WorkflowBuilder() + .addDependentResource(independentDR) + .addDependentResource(dr1) + .addDependentResourceAndConfigure(dr2) + .dependsOn(dr1) + .buildAsDefaultWorkflow(); Set bottomResources = workflow.getBottomLevelDependentResources().stream() @@ -76,7 +84,6 @@ void calculatesBottomLevelResources() { assertThat(bottomResources).containsExactlyInAnyOrder(dr2, independentDR); } - @Test void isDeletableShouldWork() { var dr = mock(DependentResource.class); @@ -91,8 +98,10 @@ void isDeletableShouldWork() { dr = mock(KubernetesDependentResource.class, withSettings().extraInterfaces(Deleter.class)); assertTrue(DefaultWorkflow.isDeletable(dr.getClass())); - dr = mock(KubernetesDependentResource.class, withSettings().extraInterfaces(Deleter.class, - GarbageCollected.class)); + dr = + mock( + KubernetesDependentResource.class, + withSettings().extraInterfaces(Deleter.class, GarbageCollected.class)); assertFalse(DefaultWorkflow.isDeletable(dr.getClass())); } @@ -101,5 +110,4 @@ static DependentResource mockDependent(String name) { when(res.name()).thenReturn(name); return res; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java index 9f7e390c0a..fe2e6e9514 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventProcessorTest.java @@ -66,8 +66,7 @@ class EventProcessorTest { mock(ReconciliationDispatcher.class); private final EventSourceManager eventSourceManagerMock = mock(EventSourceManager.class); private final TimerEventSource retryTimerEventSourceMock = mock(TimerEventSource.class); - private final ControllerEventSource controllerEventSourceMock = - mock(ControllerEventSource.class); + private final ControllerEventSource controllerEventSourceMock = mock(ControllerEventSource.class); private final Metrics metricsMock = mock(Metrics.class); private EventProcessor eventProcessor; private EventProcessor eventProcessorWithRetry; @@ -75,18 +74,23 @@ class EventProcessorTest { @BeforeEach void setup() { - when(eventSourceManagerMock.getControllerEventSource()) - .thenReturn(controllerEventSourceMock); + when(eventSourceManagerMock.getControllerEventSource()).thenReturn(controllerEventSourceMock); eventProcessor = - spy(new EventProcessor(controllerConfiguration(null, rateLimiterMock), - reconciliationDispatcherMock, - eventSourceManagerMock, null)); + spy( + new EventProcessor( + controllerConfiguration(null, rateLimiterMock), + reconciliationDispatcherMock, + eventSourceManagerMock, + null)); eventProcessor.start(); eventProcessorWithRetry = - spy(new EventProcessor( - controllerConfiguration(GenericRetry.defaultLimitedExponentialRetry(), - rateLimiterMock), - reconciliationDispatcherMock, eventSourceManagerMock, null)); + spy( + new EventProcessor( + controllerConfiguration( + GenericRetry.defaultLimitedExponentialRetry(), rateLimiterMock), + reconciliationDispatcherMock, + eventSourceManagerMock, + null)); eventProcessorWithRetry.start(); when(eventProcessor.retryEventSource()).thenReturn(retryTimerEventSourceMock); when(eventProcessorWithRetry.retryEventSource()).thenReturn(retryTimerEventSourceMock); @@ -125,8 +129,7 @@ void ifExecutionInProgressWaitsUntilItsFinished() { void schedulesAnEventRetryOnException() { TestCustomResource customResource = testCustomResource(); - ExecutionScope executionScope = - new ExecutionScope(null); + ExecutionScope executionScope = new ExecutionScope(null); executionScope.setResource(customResource); PostExecutionControl postExecutionControl = PostExecutionControl.exceptionDuringExecution(new RuntimeException("test")); @@ -134,8 +137,8 @@ void schedulesAnEventRetryOnException() { eventProcessorWithRetry.eventProcessingFinished(executionScope, postExecutionControl); verify(retryTimerEventSourceMock, times(1)) - .scheduleOnce(eq(ResourceID.fromResource(customResource)), - eq(GradualRetry.DEFAULT_INITIAL_INTERVAL)); + .scheduleOnce( + eq(ResourceID.fromResource(customResource)), eq(GradualRetry.DEFAULT_INITIAL_INTERVAL)); } @Test @@ -147,11 +150,13 @@ void executesTheControllerInstantlyAfterErrorIfNewEventsReceived() { PostExecutionControl.exceptionDuringExecution(new RuntimeException("test")); when(reconciliationDispatcherMock.handleExecution(any())) - .thenAnswer((Answer) invocationOnMock -> { - // avoid to process the first event before the second submitted - Thread.sleep(50); - return postExecutionControl; - }) + .thenAnswer( + (Answer) + invocationOnMock -> { + // avoid to process the first event before the second submitted + Thread.sleep(50); + return postExecutionControl; + }) .thenReturn(PostExecutionControl.defaultDispatch()); // start processing an event @@ -166,8 +171,8 @@ void executesTheControllerInstantlyAfterErrorIfNewEventsReceived() { List allValues = executionScopeArgumentCaptor.getAllValues(); assertThat(allValues).hasSize(2); verify(retryTimerEventSourceMock, never()) - .scheduleOnce(eq(ResourceID.fromResource(customResource)), - eq(GradualRetry.DEFAULT_INITIAL_INTERVAL)); + .scheduleOnce( + eq(ResourceID.fromResource(customResource)), eq(GradualRetry.DEFAULT_INITIAL_INTERVAL)); } @Test @@ -204,7 +209,6 @@ void successfulExecutionResetsTheRetry() { waitUntilProcessingFinished(eventProcessorWithRetry, event.getRelatedCustomResourceID()); log.info("Finished successfulExecutionResetsTheRetry"); - List executionScopes = executionScopeArgumentCaptor.getAllValues(); assertThat(executionScopes).hasSize(3); @@ -214,9 +218,10 @@ void successfulExecutionResetsTheRetry() { assertThat(executionScopes.get(1).getRetryInfo().isLastAttempt()).isEqualTo(false); } - private void waitUntilProcessingFinished(EventProcessor eventProcessor, - ResourceID relatedCustomResourceID) { - await().atMost(Duration.ofSeconds(3)) + private void waitUntilProcessingFinished( + EventProcessor eventProcessor, ResourceID relatedCustomResourceID) { + await() + .atMost(Duration.ofSeconds(3)) .until(() -> !eventProcessor.isUnderProcessing(relatedCustomResourceID)); } @@ -235,16 +240,20 @@ void scheduleTimedEventIfInstructedByPostExecutionControl() { @Test void reScheduleOnlyIfNotExecutedEventsReceivedMeanwhile() throws InterruptedException { var testDelay = 10000L; - doAnswer(new AnswersWithDelay(FAKE_CONTROLLER_EXECUTION_DURATION, - new Returns(PostExecutionControl.defaultDispatch().withReSchedule(testDelay)))) - .when(reconciliationDispatcherMock).handleExecution(any()); + doAnswer( + new AnswersWithDelay( + FAKE_CONTROLLER_EXECUTION_DURATION, + new Returns(PostExecutionControl.defaultDispatch().withReSchedule(testDelay)))) + .when(reconciliationDispatcherMock) + .handleExecution(any()); var resourceId = new ResourceID("test1", "default"); eventProcessor.handleEvent(prepareCREvent(resourceId)); Thread.sleep(FAKE_CONTROLLER_EXECUTION_DURATION / 3); eventProcessor.handleEvent(prepareCREvent(resourceId)); - verify(retryTimerEventSourceMock, - after((long) (FAKE_CONTROLLER_EXECUTION_DURATION * 1.5)).times(0)) + verify( + retryTimerEventSourceMock, + after((long) (FAKE_CONTROLLER_EXECUTION_DURATION * 1.5)).times(0)) .scheduleOnce((ResourceID) any(), eq(testDelay)); } @@ -261,8 +270,8 @@ void cancelScheduleOnceEventsOnSuccessfulExecution() { var crID = new ResourceID("test-cr", TEST_NAMESPACE); var cr = testCustomResource(crID); - eventProcessor.eventProcessingFinished(new ExecutionScope(null).setResource(cr), - PostExecutionControl.defaultDispatch()); + eventProcessor.eventProcessingFinished( + new ExecutionScope(null).setResource(cr), PostExecutionControl.defaultDispatch()); verify(retryTimerEventSourceMock, times(1)).cancelOnceSchedule(eq(crID)); } @@ -271,12 +280,13 @@ void cancelScheduleOnceEventsOnSuccessfulExecution() { void startProcessedMarkedEventReceivedBefore() { var crID = new ResourceID("test-cr", TEST_NAMESPACE); eventProcessor = - spy(new EventProcessor(controllerConfiguration(null, - LinearRateLimiter.deactivatedRateLimiter()), reconciliationDispatcherMock, - eventSourceManagerMock, - metricsMock)); - when(controllerEventSourceMock.get(eq(crID))) - .thenReturn(Optional.of(testCustomResource())); + spy( + new EventProcessor( + controllerConfiguration(null, LinearRateLimiter.deactivatedRateLimiter()), + reconciliationDispatcherMock, + eventSourceManagerMock, + metricsMock)); + when(controllerEventSourceMock.get(eq(crID))).thenReturn(Optional.of(testCustomResource())); eventProcessor.handleEvent(new Event(crID)); verify(reconciliationDispatcherMock, timeout(100).times(0)).handleExecution(any()); @@ -290,23 +300,20 @@ void startProcessedMarkedEventReceivedBefore() { @Test void notUpdatesEventSourceHandlerIfResourceUpdated() { TestCustomResource customResource = testCustomResource(); - ExecutionScope executionScope = - new ExecutionScope(null).setResource(customResource); + ExecutionScope executionScope = new ExecutionScope(null).setResource(customResource); PostExecutionControl postExecutionControl = PostExecutionControl.customResourceStatusPatched(customResource); eventProcessorWithRetry.eventProcessingFinished(executionScope, postExecutionControl); - verify(controllerEventSourceMock, times(0)).handleRecentResourceUpdate(any(), any(), - any()); + verify(controllerEventSourceMock, times(0)).handleRecentResourceUpdate(any(), any(), any()); } @Test void notReschedulesAfterTheFinalizerRemoveProcessed() { TestCustomResource customResource = testCustomResource(); markForDeletion(customResource); - ExecutionScope executionScope = - new ExecutionScope(null).setResource(customResource); + ExecutionScope executionScope = new ExecutionScope(null).setResource(customResource); PostExecutionControl postExecutionControl = PostExecutionControl.customResourceFinalizerRemoved(customResource); @@ -319,8 +326,7 @@ void notReschedulesAfterTheFinalizerRemoveProcessed() { void skipEventProcessingIfFinalizerRemoveProcessed() { TestCustomResource customResource = testCustomResource(); markForDeletion(customResource); - ExecutionScope executionScope = - new ExecutionScope(null).setResource(customResource); + ExecutionScope executionScope = new ExecutionScope(null).setResource(customResource); PostExecutionControl postExecutionControl = PostExecutionControl.customResourceFinalizerRemoved(customResource); @@ -337,8 +343,7 @@ void skipEventProcessingIfFinalizerRemoveProcessed() { void newResourceAfterMissedDeleteEvent() { TestCustomResource customResource = testCustomResource(); markForDeletion(customResource); - ExecutionScope executionScope = - new ExecutionScope(null).setResource(customResource); + ExecutionScope executionScope = new ExecutionScope(null).setResource(customResource); PostExecutionControl postExecutionControl = PostExecutionControl.customResourceFinalizerRemoved(customResource); var newResource = testCustomResource(); @@ -374,10 +379,8 @@ void rateLimitsReconciliationSubmission() { @Test void schedulesRetryForMarReconciliationInterval() { TestCustomResource customResource = testCustomResource(); - ExecutionScope executionScope = - new ExecutionScope(null).setResource(customResource); - PostExecutionControl postExecutionControl = - PostExecutionControl.defaultDispatch(); + ExecutionScope executionScope = new ExecutionScope(null).setResource(customResource); + PostExecutionControl postExecutionControl = PostExecutionControl.defaultDispatch(); eventProcessorWithRetry.eventProcessingFinished(executionScope, postExecutionControl); @@ -391,13 +394,14 @@ void schedulesRetryForMarReconciliationIntervalIfRetryExhausted() { Retry retry = mock(Retry.class); when(retry.initExecution()).thenReturn(mockRetryExecution); eventProcessorWithRetry = - spy(new EventProcessor(controllerConfiguration(retry, - LinearRateLimiter.deactivatedRateLimiter()), reconciliationDispatcherMock, - eventSourceManagerMock, - metricsMock)); + spy( + new EventProcessor( + controllerConfiguration(retry, LinearRateLimiter.deactivatedRateLimiter()), + reconciliationDispatcherMock, + eventSourceManagerMock, + metricsMock)); eventProcessorWithRetry.start(); - ExecutionScope executionScope = - new ExecutionScope(null).setResource(testCustomResource()); + ExecutionScope executionScope = new ExecutionScope(null).setResource(testCustomResource()); PostExecutionControl postExecutionControl = PostExecutionControl.exceptionDuringExecution(new RuntimeException()); when(eventProcessorWithRetry.retryEventSource()).thenReturn(retryTimerEventSourceMock); @@ -410,30 +414,35 @@ void schedulesRetryForMarReconciliationIntervalIfRetryExhausted() { @Test void executionOfReconciliationShouldNotStartIfProcessorStopped() throws InterruptedException { when(reconciliationDispatcherMock.handleExecution(any())) - .then((Answer) invocationOnMock -> { - Thread.sleep(DISPATCHING_DELAY); - return PostExecutionControl.defaultDispatch(); - }); - - final var configurationService = ConfigurationService.newOverriddenConfigurationService( - new BaseConfigurationService(), - o -> { - o.withConcurrentReconciliationThreads(1); - }); + .then( + (Answer) + invocationOnMock -> { + Thread.sleep(DISPATCHING_DELAY); + return PostExecutionControl.defaultDispatch(); + }); + + final var configurationService = + ConfigurationService.newOverriddenConfigurationService( + new BaseConfigurationService(), + o -> { + o.withConcurrentReconciliationThreads(1); + }); eventProcessor = - spy(new EventProcessor(controllerConfiguration(null, rateLimiterMock, configurationService), - reconciliationDispatcherMock, - eventSourceManagerMock, null)); + spy( + new EventProcessor( + controllerConfiguration(null, rateLimiterMock, configurationService), + reconciliationDispatcherMock, + eventSourceManagerMock, + null)); eventProcessor.start(); - eventProcessor.handleEvent(prepareCREvent(new ResourceID("test1","default"))); - eventProcessor.handleEvent(prepareCREvent(new ResourceID("test1","default"))); + eventProcessor.handleEvent(prepareCREvent(new ResourceID("test1", "default"))); + eventProcessor.handleEvent(prepareCREvent(new ResourceID("test1", "default"))); eventProcessor.stop(); // wait until both event should be handled Thread.sleep(TIME_TO_WAIT_AFTER_SUBMISSION_BEFORE_EXECUTION + 2 * DISPATCHING_DELAY); - verify(reconciliationDispatcherMock, atMostOnce()) - .handleExecution(any()); + verify(reconciliationDispatcherMock, atMostOnce()).handleExecution(any()); } @Test @@ -441,9 +450,12 @@ void cleansUpForDeleteEventEvenIfProcessorNotStarted() { ResourceID resourceID = new ResourceID("test1", "default"); eventProcessor = - spy(new EventProcessor(controllerConfiguration(null, rateLimiterMock), - reconciliationDispatcherMock, - eventSourceManagerMock, null)); + spy( + new EventProcessor( + controllerConfiguration(null, rateLimiterMock), + reconciliationDispatcherMock, + eventSourceManagerMock, + null)); eventProcessor.handleEvent(prepareCREvent(resourceID)); eventProcessor.handleEvent(new ResourceEvent(ResourceAction.DELETED, resourceID, null)); @@ -454,10 +466,11 @@ void cleansUpForDeleteEventEvenIfProcessorNotStarted() { private ResourceID eventAlreadyUnderProcessing() { when(reconciliationDispatcherMock.handleExecution(any())) .then( - (Answer) invocationOnMock -> { - Thread.sleep(FAKE_CONTROLLER_EXECUTION_DURATION); - return PostExecutionControl.defaultDispatch(); - }); + (Answer) + invocationOnMock -> { + Thread.sleep(FAKE_CONTROLLER_EXECUTION_DURATION); + return PostExecutionControl.defaultDispatch(); + }); Event event = prepareCREvent(); eventProcessor.handleEvent(event); return event.getRelatedCustomResourceID(); @@ -470,16 +483,15 @@ private ResourceEvent prepareCREvent() { private ResourceEvent prepareCREvent(HasMetadata hasMetadata) { when(controllerEventSourceMock.get(eq(ResourceID.fromResource(hasMetadata)))) .thenReturn(Optional.of(hasMetadata)); - return new ResourceEvent(ResourceAction.UPDATED, - ResourceID.fromResource(hasMetadata), hasMetadata); + return new ResourceEvent( + ResourceAction.UPDATED, ResourceID.fromResource(hasMetadata), hasMetadata); } private ResourceEvent prepareCREvent(ResourceID resourceID) { TestCustomResource customResource = testCustomResource(resourceID); - when(controllerEventSourceMock.get(eq(resourceID))) - .thenReturn(Optional.of(customResource)); - return new ResourceEvent(ResourceAction.UPDATED, - ResourceID.fromResource(customResource), customResource); + when(controllerEventSourceMock.get(eq(resourceID))).thenReturn(Optional.of(customResource)); + return new ResourceEvent( + ResourceAction.UPDATED, ResourceID.fromResource(customResource), customResource); } private Event nonCREvent(ResourceID relatedCustomResourceUid) { @@ -495,8 +507,8 @@ ControllerConfiguration controllerConfiguration(Retry retry, RateLimiter rateLim return controllerConfiguration(retry, rateLimiter, new BaseConfigurationService()); } - ControllerConfiguration controllerConfiguration(Retry retry, RateLimiter rateLimiter, - ConfigurationService configurationService) { + ControllerConfiguration controllerConfiguration( + Retry retry, RateLimiter rateLimiter, ConfigurationService configurationService) { ControllerConfiguration res = mock(ControllerConfiguration.class); when(res.getName()).thenReturn("Test"); when(res.getRetry()).thenReturn(retry); @@ -505,5 +517,4 @@ ControllerConfiguration controllerConfiguration(Retry retry, RateLimiter rateLim when(res.getConfigurationService()).thenReturn(configurationService); return res; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourceManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourceManagerTest.java index 9ddb877f07..7592512cb3 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourceManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourceManagerTest.java @@ -119,8 +119,8 @@ void notPossibleAddEventSourcesForSameName() { when(eventSource.name()).thenReturn(name); final var source = eventSource; - final var exception = assertThrows(OperatorException.class, - () -> manager.registerEventSource(source)); + final var exception = + assertThrows(OperatorException.class, () -> manager.registerEventSource(source)); final var cause = exception.getCause(); assertInstanceOf(IllegalArgumentException.class, cause); assertThat(cause.getMessage()).contains("is already registered with name"); @@ -135,21 +135,20 @@ void retrievingAnEventSourceWhenMultipleAreRegisteredForATypeShouldRequireAQuali when(eventSource.name()).thenReturn("name1"); manager.registerEventSource(eventSource); - ManagedInformerEventSource eventSource2 = mock(ManagedInformerEventSource.class); when(eventSource2.name()).thenReturn("name2"); when(eventSource2.resourceType()).thenReturn(TestCustomResource.class); manager.registerEventSource(eventSource2); - final var exception = assertThrows(IllegalArgumentException.class, - () -> manager.getEventSourceFor(TestCustomResource.class)); + final var exception = + assertThrows( + IllegalArgumentException.class, + () -> manager.getEventSourceFor(TestCustomResource.class)); assertTrue(exception.getMessage().contains("name1")); assertTrue(exception.getMessage().contains("name2")); - assertEquals(manager.getEventSourceFor(TestCustomResource.class, "name2"), - eventSource2); - assertEquals(manager.getEventSourceFor(TestCustomResource.class, "name1"), - eventSource); + assertEquals(manager.getEventSourceFor(TestCustomResource.class, "name2"), eventSource2); + assertEquals(manager.getEventSourceFor(TestCustomResource.class, "name1"), eventSource); } @Test @@ -161,8 +160,9 @@ void changesNamespacesOnControllerAndInformerEventSources() { final var configService = new BaseConfigurationService(); when(configuration.getConfigurationService()).thenReturn(configService); - final Controller controller = new Controller(mock(Reconciler.class), configuration, - MockKubernetesClient.client(HasMetadata.class)); + final Controller controller = + new Controller( + mock(Reconciler.class), configuration, MockKubernetesClient.client(HasMetadata.class)); EventSources eventSources = spy(new EventSources()); var controllerResourceEventSourceMock = mock(ControllerEventSource.class); @@ -190,8 +190,9 @@ private EventSourceManager initManager() { final var configService = new BaseConfigurationService(); when(configuration.getConfigurationService()).thenReturn(configService); - final Controller controller = new Controller(mock(Reconciler.class), configuration, - MockKubernetesClient.client(ConfigMap.class)); + final Controller controller = + new Controller( + mock(Reconciler.class), configuration, MockKubernetesClient.client(ConfigMap.class)); return new EventSourceManager(controller); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourcesTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourcesTest.java index 12f0cc7331..6e62840dd4 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourcesTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/EventSourcesTest.java @@ -40,9 +40,11 @@ void cannotAddTwoDifferentEventSourcesWithSameName() { when(es2.resourceType()).thenReturn(EventSource.class); eventSources.add(es1); - assertThrows(IllegalArgumentException.class, () -> { - eventSources.add(es2); - }); + assertThrows( + IllegalArgumentException.class, + () -> { + eventSources.add(es2); + }); } @Test @@ -65,9 +67,8 @@ void eventSourcesStreamShouldNotReturnControllerEventSource() { eventSources.add(source); - assertThat(eventSources.additionalEventSources()).containsExactly( - eventSources.retryEventSource(), - source); + assertThat(eventSources.additionalEventSources()) + .containsExactly(eventSources.retryEventSource(), source); } @Test @@ -78,9 +79,8 @@ void additionalEventSourcesShouldNotContainNamedEventSources() { when(source.resourceType()).thenReturn(EventSource.class); eventSources.add(source); - - assertThat(eventSources.additionalEventSources()).containsExactly( - eventSources.retryEventSource(), source); + assertThat(eventSources.additionalEventSources()) + .containsExactly(eventSources.retryEventSource(), source); } @Test @@ -88,24 +88,22 @@ void checkControllerEventSource() { final var eventSources = new EventSources(); final var configuration = MockControllerConfiguration.forResource(HasMetadata.class); when(configuration.getConfigurationService()).thenReturn(new BaseConfigurationService()); - final var controller = new Controller(mock(Reconciler.class), configuration, - MockKubernetesClient.client(HasMetadata.class)); + final var controller = + new Controller( + mock(Reconciler.class), configuration, MockKubernetesClient.client(HasMetadata.class)); eventSources.createControllerEventSource(controller); final var controllerEventSource = eventSources.controllerEventSource(); assertNotNull(controllerEventSource); assertEquals(HasMetadata.class, controllerEventSource.resourceType()); - assertEquals(controllerEventSource, - eventSources.controllerEventSource()); + assertEquals(controllerEventSource, eventSources.controllerEventSource()); } @Test void flatMappedSourcesShouldReturnOnlyUserRegisteredEventSources() { final var eventSources = new EventSources(); - final var mock1 = - eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); - final var mock2 = - eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); + final var mock1 = eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); + final var mock2 = eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); final var mock3 = eventSourceMockWithName(EventSource.class, "name3", ConfigMap.class); eventSources.add(mock1); @@ -118,10 +116,8 @@ void flatMappedSourcesShouldReturnOnlyUserRegisteredEventSources() { @Test void clearShouldWork() { final var eventSources = new EventSources(); - final var mock1 = - eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); - final var mock2 = - eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); + final var mock1 = eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); + final var mock2 = eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); final var mock3 = eventSourceMockWithName(EventSource.class, "name3", ConfigMap.class); eventSources.add(mock1); @@ -135,10 +131,8 @@ void clearShouldWork() { @Test void getShouldWork() { final var eventSources = new EventSources(); - final var mock1 = - eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); - final var mock2 = - eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); + final var mock1 = eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); + final var mock2 = eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); final var mock3 = eventSourceMockWithName(EventSource.class, "name3", ConfigMap.class); eventSources.add(mock1); @@ -150,10 +144,9 @@ void getShouldWork() { assertEquals(mock3, eventSources.get(ConfigMap.class, "name3")); assertEquals(mock3, eventSources.get(ConfigMap.class, null)); - assertThrows(IllegalArgumentException.class, () -> eventSources.get(HasMetadata.class, null)); - assertThrows(IllegalArgumentException.class, - () -> eventSources.get(ConfigMap.class, "unknown")); + assertThrows( + IllegalArgumentException.class, () -> eventSources.get(ConfigMap.class, "unknown")); assertThrows(IllegalArgumentException.class, () -> eventSources.get(null, null)); assertThrows(IllegalArgumentException.class, () -> eventSources.get(HasMetadata.class, null)); } @@ -161,10 +154,8 @@ void getShouldWork() { @Test void getEventSourcesShouldWork() { final var eventSources = new EventSources(); - final var mock1 = - eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); - final var mock2 = - eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); + final var mock1 = eventSourceMockWithName(EventSource.class, "name1", HasMetadata.class); + final var mock2 = eventSourceMockWithName(EventSource.class, "name2", HasMetadata.class); final var mock3 = eventSourceMockWithName(EventSource.class, "name3", ConfigMap.class); eventSources.add(mock1); @@ -190,25 +181,31 @@ void testConcurrentAddRemoveAndGet() throws InterruptedException { for (int i = 0; i < 1000 && !concurrentExceptionFound.get(); i++) { final var eventSources = new EventSources(); var eventSourceList = - IntStream.range(1, 20).mapToObj(n -> eventSourceMockWithName(EventSource.class, - "name" + n, HasMetadata.class)).toList(); + IntStream.range(1, 20) + .mapToObj( + n -> eventSourceMockWithName(EventSource.class, "name" + n, HasMetadata.class)) + .toList(); IntStream.range(1, 10).forEach(n -> eventSources.add(eventSourceList.get(n - 1))); var phaser = new Phaser(2); - var t1 = new Thread(() -> { - phaser.arriveAndAwaitAdvance(); - IntStream.range(11, 20).forEach(n -> eventSources.add(eventSourceList.get(n - 1))); - }); - var t2 = new Thread(() -> { - phaser.arriveAndAwaitAdvance(); - try { - eventSources.getEventSources(eventSourceList.get(0).resourceType()); - } catch (ConcurrentModificationException e) { - concurrentExceptionFound.set(true); - } - }); + var t1 = + new Thread( + () -> { + phaser.arriveAndAwaitAdvance(); + IntStream.range(11, 20).forEach(n -> eventSources.add(eventSourceList.get(n - 1))); + }); + var t2 = + new Thread( + () -> { + phaser.arriveAndAwaitAdvance(); + try { + eventSources.getEventSources(eventSourceList.get(0).resourceType()); + } catch (ConcurrentModificationException e) { + concurrentExceptionFound.set(true); + } + }); t1.start(); t2.start(); t1.join(); @@ -220,12 +217,11 @@ void testConcurrentAddRemoveAndGet() throws InterruptedException { .isFalse(); } - EventSource eventSourceMockWithName(Class clazz, String name, - Class resourceType) { + EventSource eventSourceMockWithName( + Class clazz, String name, Class resourceType) { var mockedES = mock(clazz); when(mockedES.name()).thenReturn(name); when(mockedES.resourceType()).thenReturn(resourceType); return mockedES; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java index f8f0c59845..89f3655356 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ReconciliationDispatcherTest.java @@ -82,30 +82,37 @@ static void initConfigService(boolean useSSA, boolean noCloning) { * implemented on TestCustomResourceSpec or TestCustomResourceStatus */ configurationService = - ConfigurationService.newOverriddenConfigurationService(new BaseConfigurationService(), - overrider -> overrider.checkingCRDAndValidateLocalModel(false) - - .withResourceCloner(new Cloner() { - @Override - public R clone(R object) { - if (noCloning) { - return object; - } else { - return new KubernetesSerialization().clone(object); - } - } - }) - .withUseSSAToPatchPrimaryResource(useSSA)); - } - - private ReconciliationDispatcher init(R customResource, - Reconciler reconciler, ControllerConfiguration configuration, - CustomResourceFacade customResourceFacade, boolean useFinalizer) { + ConfigurationService.newOverriddenConfigurationService( + new BaseConfigurationService(), + overrider -> + overrider + .checkingCRDAndValidateLocalModel(false) + .withResourceCloner( + new Cloner() { + @Override + public R clone(R object) { + if (noCloning) { + return object; + } else { + return new KubernetesSerialization().clone(object); + } + } + }) + .withUseSSAToPatchPrimaryResource(useSSA)); + } + + private ReconciliationDispatcher init( + R customResource, + Reconciler reconciler, + ControllerConfiguration configuration, + CustomResourceFacade customResourceFacade, + boolean useFinalizer) { final Class resourceClass = (Class) customResource.getClass(); - configuration = configuration == null - ? MockControllerConfiguration.forResource(resourceClass, configurationService) - : configuration; + configuration = + configuration == null + ? MockControllerConfiguration.forResource(resourceClass, configurationService) + : configuration; when(configuration.getConfigurationService()).thenReturn(configurationService); when(configuration.getFinalizerName()).thenReturn(DEFAULT_FINALIZER); @@ -118,13 +125,13 @@ private ReconciliationDispatcher init(R customResourc when(configuration.maxReconciliationInterval()) .thenReturn(Optional.of(Duration.ofHours(RECONCILIATION_MAX_INTERVAL))); - Controller controller = new Controller<>(reconciler, configuration, - MockKubernetesClient.client(resourceClass)) { - @Override - public boolean useFinalizer() { - return useFinalizer; - } - }; + Controller controller = + new Controller<>(reconciler, configuration, MockKubernetesClient.client(resourceClass)) { + @Override + public boolean useFinalizer() { + return useFinalizer; + } + }; controller.start(); return new ReconciliationDispatcher<>(controller, customResourceFacade); @@ -134,8 +141,7 @@ public boolean useFinalizer() { void addFinalizerOnNewResource() { assertFalse(testCustomResource.hasFinalizer(DEFAULT_FINALIZER)); reconciliationDispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); - verify(reconciler, never()) - .reconcile(ArgumentMatchers.eq(testCustomResource), any()); + verify(reconciler, never()).reconcile(ArgumentMatchers.eq(testCustomResource), any()); verify(customResourceFacade, times(1)) .patchResourceWithSSA( argThat(testCustomResource -> testCustomResource.hasFinalizer(DEFAULT_FINALIZER))); @@ -149,8 +155,7 @@ void addFinalizerOnNewResourceWithoutSSA() { assertFalse(testCustomResource.hasFinalizer(DEFAULT_FINALIZER)); dispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); - verify(reconciler, never()) - .reconcile(ArgumentMatchers.eq(testCustomResource), any()); + verify(reconciler, never()).reconcile(ArgumentMatchers.eq(testCustomResource), any()); verify(customResourceFacade, times(1)) .patchResource( argThat(testCustomResource -> testCustomResource.hasFinalizer(DEFAULT_FINALIZER)), @@ -162,8 +167,7 @@ void addFinalizerOnNewResourceWithoutSSA() { void callCreateOrUpdateOnNewResourceIfFinalizerSet() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); reconciliationDispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); - verify(reconciler, times(1)) - .reconcile(ArgumentMatchers.eq(testCustomResource), any()); + verify(reconciler, times(1)).reconcile(ArgumentMatchers.eq(testCustomResource), any()); } @Test @@ -197,8 +201,7 @@ void callCreateOrUpdateOnModifiedResourceIfFinalizerSet() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); reconciliationDispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); - verify(reconciler, times(1)) - .reconcile(ArgumentMatchers.eq(testCustomResource), any()); + verify(reconciler, times(1)).reconcile(ArgumentMatchers.eq(testCustomResource), any()); } @Test @@ -275,11 +278,9 @@ void throwsExceptionIfFinalizerRemovalRetryExceeded() { assertThat(postExecControl.isFinalizerRemoved()).isFalse(); assertThat(postExecControl.getRuntimeException()).isPresent(); - assertThat(postExecControl.getRuntimeException().get()) - .isInstanceOf(OperatorException.class); + assertThat(postExecControl.getRuntimeException().get()).isInstanceOf(OperatorException.class); verify(customResourceFacade, times(MAX_UPDATE_RETRY)).patchResourceWithoutSSA(any(), any()); - verify(customResourceFacade, times(MAX_UPDATE_RETRY - 1)).getResource(any(), - any()); + verify(customResourceFacade, times(MAX_UPDATE_RETRY - 1)).getResource(any(), any()); } @Test @@ -356,8 +357,7 @@ void doesNotUpdateTheResourceIfNoUpdateUpdateControlIfFinalizerSet() { void addsFinalizerIfNotMarkedForDeletionAndEmptyCustomResourceReturned() { removeFinalizers(testCustomResource); reconciler.reconcile = (r, c) -> UpdateControl.noUpdate(); - when(customResourceFacade.patchResourceWithSSA(any())) - .thenReturn(testCustomResource); + when(customResourceFacade.patchResourceWithSSA(any())).thenReturn(testCustomResource); var postExecControl = reconciliationDispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); @@ -394,22 +394,21 @@ void propagatesRetryInfoToContextIfFinalizerSet() { reconciliationDispatcher.handleExecution( new ExecutionScope( - new RetryInfo() { - @Override - public int getAttemptCount() { - return 2; - } - - @Override - public boolean isLastAttempt() { - return true; - } - }).setResource(testCustomResource)); - - ArgumentCaptor contextArgumentCaptor = - ArgumentCaptor.forClass(Context.class); - verify(reconciler, times(1)) - .reconcile(any(), contextArgumentCaptor.capture()); + new RetryInfo() { + @Override + public int getAttemptCount() { + return 2; + } + + @Override + public boolean isLastAttempt() { + return true; + } + }) + .setResource(testCustomResource)); + + ArgumentCaptor contextArgumentCaptor = ArgumentCaptor.forClass(Context.class); + verify(reconciler, times(1)).reconcile(any(), contextArgumentCaptor.capture()); Context context = contextArgumentCaptor.getValue(); final var retryInfo = context.getRetryInfo().orElseGet(() -> fail("Missing optional")); assertThat(retryInfo.getAttemptCount()).isEqualTo(2); @@ -453,13 +452,12 @@ void doesNotUpdatesObservedGenerationIfStatusIsNotPatchedWhenUsingSSA() throws E final var config = MockControllerConfiguration.forResource(ObservedGenCustomResource.class); CustomResourceFacade facade = mock(CustomResourceFacade.class); when(config.isGenerationAware()).thenReturn(true); - when(reconciler.reconcile(any(), any())) - .thenReturn(UpdateControl.noUpdate()); + when(reconciler.reconcile(any(), any())).thenReturn(UpdateControl.noUpdate()); when(facade.patchStatus(any(), any())).thenReturn(observedGenResource); var dispatcher = init(observedGenResource, reconciler, config, facade, true); - PostExecutionControl control = dispatcher.handleExecution( - executionScopeWithCREvent(observedGenResource)); + PostExecutionControl control = + dispatcher.handleExecution(executionScopeWithCREvent(observedGenResource)); assertThat(control.getUpdatedCustomResource()).isEmpty(); } @@ -476,8 +474,7 @@ void doesNotPatchObservedGenerationOnCustomResourcePatch() throws Exception { when(facade.patchResource(any(), any())).thenReturn(observedGenResource); var dispatcher = init(observedGenResource, reconciler, config, facade, false); - dispatcher.handleExecution( - executionScopeWithCREvent(observedGenResource)); + dispatcher.handleExecution(executionScopeWithCREvent(observedGenResource)); verify(facade, never()).patchStatus(any(), any()); } @@ -486,69 +483,75 @@ void doesNotPatchObservedGenerationOnCustomResourcePatch() throws Exception { void callErrorStatusHandlerIfImplemented() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; - reconciler.errorHandler = () -> { - testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); - return ErrorStatusUpdateControl.patchStatus(testCustomResource); - }; + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; + reconciler.errorHandler = + () -> { + testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); + return ErrorStatusUpdateControl.patchStatus(testCustomResource); + }; reconciliationDispatcher.handleExecution( new ExecutionScope( - new RetryInfo() { - @Override - public int getAttemptCount() { - return 2; - } - - @Override - public boolean isLastAttempt() { - return true; - } - }).setResource(testCustomResource)); + new RetryInfo() { + @Override + public int getAttemptCount() { + return 2; + } + + @Override + public boolean isLastAttempt() { + return true; + } + }) + .setResource(testCustomResource)); verify(customResourceFacade, times(1)).patchStatus(eq(testCustomResource), any()); - verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), - any(), any()); + verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), any(), any()); } @Test void callErrorStatusHandlerEvenOnFirstError() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; - reconciler.errorHandler = () -> { - testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); - return ErrorStatusUpdateControl.patchStatus(testCustomResource); - }; + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; + reconciler.errorHandler = + () -> { + testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); + return ErrorStatusUpdateControl.patchStatus(testCustomResource); + }; - var postExecControl = reconciliationDispatcher.handleExecution( - new ExecutionScope(null).setResource(testCustomResource)); + var postExecControl = + reconciliationDispatcher.handleExecution( + new ExecutionScope(null).setResource(testCustomResource)); verify(customResourceFacade, times(1)).patchStatus(eq(testCustomResource), any()); - verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), - any(), any()); + verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), any(), any()); assertThat(postExecControl.exceptionDuringExecution()).isTrue(); } @Test void errorHandlerCanInstructNoRetryWithUpdate() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; - reconciler.errorHandler = () -> { - testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); - return ErrorStatusUpdateControl.patchStatus(testCustomResource).withNoRetry(); - }; - - var postExecControl = reconciliationDispatcher.handleExecution( - new ExecutionScope(null).setResource(testCustomResource)); + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; + reconciler.errorHandler = + () -> { + testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); + return ErrorStatusUpdateControl.patchStatus(testCustomResource).withNoRetry(); + }; - verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), - any(), any()); + var postExecControl = + reconciliationDispatcher.handleExecution( + new ExecutionScope(null).setResource(testCustomResource)); + + verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), any(), any()); verify(customResourceFacade, times(1)).patchStatus(eq(testCustomResource), any()); assertThat(postExecControl.exceptionDuringExecution()).isFalse(); } @@ -556,19 +559,21 @@ void errorHandlerCanInstructNoRetryWithUpdate() { @Test void errorHandlerCanInstructNoRetryNoUpdate() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; - reconciler.errorHandler = () -> { - testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); - return ErrorStatusUpdateControl.noStatusUpdate().withNoRetry(); - }; - - var postExecControl = reconciliationDispatcher.handleExecution( - new ExecutionScope(null).setResource(testCustomResource)); + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; + reconciler.errorHandler = + () -> { + testCustomResource.getStatus().setConfigMapStatus(ERROR_MESSAGE); + return ErrorStatusUpdateControl.noStatusUpdate().withNoRetry(); + }; + + var postExecControl = + reconciliationDispatcher.handleExecution( + new ExecutionScope(null).setResource(testCustomResource)); - verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), - any(), any()); + verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), any(), any()); verify(customResourceFacade, times(0)).patchStatus(eq(testCustomResource), any()); assertThat(postExecControl.exceptionDuringExecution()).isFalse(); } @@ -576,43 +581,47 @@ void errorHandlerCanInstructNoRetryNoUpdate() { @Test void errorStatusHandlerCanPatchResource() { testCustomResource.addFinalizer(DEFAULT_FINALIZER); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; - reconciler.errorHandler = - () -> ErrorStatusUpdateControl.patchStatus(testCustomResource); + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; + reconciler.errorHandler = () -> ErrorStatusUpdateControl.patchStatus(testCustomResource); reconciliationDispatcher.handleExecution( new ExecutionScope(null).setResource(testCustomResource)); verify(customResourceFacade, times(1)).patchStatus(eq(testCustomResource), any()); - verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), - any(), any()); + verify(reconciler, times(1)).updateErrorStatus(eq(testCustomResource), any(), any()); } @Test void ifRetryLimitedToZeroMaxAttemptsErrorHandlerGetsCorrectLastAttempt() { var configuration = - MockControllerConfiguration - .forResource((Class) testCustomResource.getClass()); + MockControllerConfiguration.forResource( + (Class) testCustomResource.getClass()); when(configuration.getRetry()).thenReturn(new GenericRetry().setMaxAttempts(0)); reconciliationDispatcher = init(testCustomResource, reconciler, configuration, customResourceFacade, false); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; reconciler.errorHandler = () -> ErrorStatusUpdateControl.noStatusUpdate(); reconciliationDispatcher.handleExecution( new ExecutionScope(null).setResource(testCustomResource)); - verify(reconciler, times(1)).updateErrorStatus(any(), - ArgumentMatchers.argThat(context -> { - var retryInfo = context.getRetryInfo().orElseThrow(); - return retryInfo.isLastAttempt(); - }), any()); + verify(reconciler, times(1)) + .updateErrorStatus( + any(), + ArgumentMatchers.argThat( + context -> { + var retryInfo = context.getRetryInfo().orElseThrow(); + return retryInfo.isLastAttempt(); + }), + any()); } @Test @@ -641,10 +650,12 @@ void retriesAddingFinalizerWithoutSSA() { .thenThrow(new KubernetesClientException(null, 409, null)) .thenReturn(testCustomResource); when(customResourceFacade.getResource(any(), any())) - .then((Answer) invocationOnMock -> { - testCustomResource.getFinalizers().clear(); - return testCustomResource; - }); + .then( + (Answer) + invocationOnMock -> { + testCustomResource.getFinalizers().clear(); + return testCustomResource; + }); reconciliationDispatcher.handleExecution(executionScopeWithCREvent(testCustomResource)); @@ -655,15 +666,16 @@ void retriesAddingFinalizerWithoutSSA() { void reSchedulesFromErrorHandler() { var delay = 1000L; testCustomResource.addFinalizer(DEFAULT_FINALIZER); - reconciler.reconcile = (r, c) -> { - throw new IllegalStateException("Error Status Test"); - }; + reconciler.reconcile = + (r, c) -> { + throw new IllegalStateException("Error Status Test"); + }; reconciler.errorHandler = - () -> ErrorStatusUpdateControl.noStatusUpdate() - .rescheduleAfter(delay); + () -> ErrorStatusUpdateControl.noStatusUpdate().rescheduleAfter(delay); - var res = reconciliationDispatcher.handleExecution( - new ExecutionScope(null).setResource(testCustomResource)); + var res = + reconciliationDispatcher.handleExecution( + new ExecutionScope(null).setResource(testCustomResource)); assertThat(res.getReScheduleDelay()).contains(delay); assertThat(res.getRuntimeException()).isEmpty(); @@ -722,8 +734,8 @@ private class TestReconciler private Supplier errorHandler; @Override - public UpdateControl reconcile(TestCustomResource resource, - Context context) { + public UpdateControl reconcile( + TestCustomResource resource, Context context) { if (reconcile != null && resource.equals(testCustomResource)) { return reconcile.apply(resource, context); } @@ -740,10 +752,8 @@ public DeleteControl cleanup(TestCustomResource resource, Context context) { @Override public ErrorStatusUpdateControl updateErrorStatus( - TestCustomResource resource, - Context context, Exception e) { - return errorHandler != null ? errorHandler.get() - : ErrorStatusUpdateControl.noStatusUpdate(); + TestCustomResource resource, Context context, Exception e) { + return errorHandler != null ? errorHandler.get() : ErrorStatusUpdateControl.noStatusUpdate(); } } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java index 1cc925fd01..2c4d9fa4f3 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/ResourceStateManagerTest.java @@ -23,7 +23,6 @@ void init() { state2 = manager.getOrCreate(sampleResourceID2); } - @Test public void returnsNoEventPresentIfNotMarkedYet() { assertThat(state.noEventPresent()).isTrue(); @@ -69,10 +68,12 @@ public void cleansUp() { @Test public void cannotMarkEventAfterDeleteEventReceived() { - Assertions.assertThrows(IllegalStateException.class, () -> { - state.markDeleteEventReceived(); - state.markEventReceived(); - }); + Assertions.assertThrows( + IllegalStateException.class, + () -> { + state.markDeleteEventReceived(); + state.markEventReceived(); + }); } @Test @@ -86,5 +87,4 @@ public void listsResourceIDSWithEventsPresent() { assertThat(res).hasSize(1); assertThat(res.get(0).getId()).isEqualTo(sampleResourceID2); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiterTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiterTest.java index e4aa0a7123..ea3d619d13 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiterTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/rate/LinearRateLimiterTest.java @@ -64,5 +64,4 @@ void rateLimitCanBeTurnedOff() { assertThat(res).isEmpty(); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSourceTestBase.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSourceTestBase.java index 41b8b76623..9cea4790b0 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSourceTestBase.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/AbstractEventSourceTestBase.java @@ -21,7 +21,6 @@ public void setUpSource(S source) { setUpSource(source, true); } - public void setUpSource(S source, boolean start, ControllerConfiguration configurationService) { setUpSource(source, (T) mock(EventHandler.class), start, configurationService); } @@ -39,8 +38,8 @@ public void setUpSource(S source, T eventHandler, boolean start) { setUpSource(source, eventHandler, start, mock(ControllerConfiguration.class)); } - public void setUpSource(S source, T eventHandler, boolean start, - ControllerConfiguration controllerConfiguration) { + public void setUpSource( + S source, T eventHandler, boolean start, ControllerConfiguration controllerConfiguration) { this.eventHandler = eventHandler; this.source = source; diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java index 3055975a98..675935d003 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/ExternalResourceCachingEventSourceTest.java @@ -13,8 +13,9 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.*; -class ExternalResourceCachingEventSourceTest extends - AbstractEventSourceTestBase, EventHandler> { +class ExternalResourceCachingEventSourceTest + extends AbstractEventSourceTestBase< + ExternalResourceCachingEventSource, EventHandler> { @BeforeEach public void setup() { @@ -200,5 +201,4 @@ public TestExternalCachingEventSource() { super(SampleExternalResource.class, SampleExternalResource::getName); } } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java index 6f45090b35..86eccbc57b 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/SampleExternalResource.java @@ -56,10 +56,8 @@ public SampleExternalResource setValue(String value) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; SampleExternalResource that = (SampleExternalResource) o; return Objects.equals(name, that.name) && Objects.equals(value, that.value); } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java index 9381aedd0d..14414ddc64 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/BoundedItemStoreTest.java @@ -20,18 +20,19 @@ class BoundedItemStoreTest { private BoundedItemStore boundedItemStore; + @SuppressWarnings("unchecked") private final BoundedCache boundedCache = mock(BoundedCache.class); + @SuppressWarnings("unchecked") private final ResourceFetcher resourceFetcher = mock(ResourceFetcher.class); @BeforeEach void setup() { - boundedItemStore = new BoundedItemStore<>(boundedCache, - TestCustomResource.class, - namespaceKeyFunc(), - resourceFetcher); + boundedItemStore = + new BoundedItemStore<>( + boundedCache, TestCustomResource.class, namespaceKeyFunc(), resourceFetcher); } @Test @@ -44,10 +45,8 @@ void shouldNotFetchResourcesFromServerIfNotKnown() { @Test void getsResourceFromServerIfNotInCache() { - boundedItemStore.put(testRes1Key(), - TestUtils.testCustomResource1()); - when(resourceFetcher.fetchResource(testRes1Key())) - .thenReturn(TestUtils.testCustomResource1()); + boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); + when(resourceFetcher.fetchResource(testRes1Key())).thenReturn(TestUtils.testCustomResource1()); var res = boundedItemStore.get(testRes1Key()); @@ -57,10 +56,8 @@ void getsResourceFromServerIfNotInCache() { @Test void removesResourcesNotFoundOnServerFromStore() { - boundedItemStore.put(testRes1Key(), - TestUtils.testCustomResource1()); - when(resourceFetcher.fetchResource(testRes1Key())) - .thenReturn(null); + boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); + when(resourceFetcher.fetchResource(testRes1Key())).thenReturn(null); var res = boundedItemStore.get(testRes1Key()); @@ -70,8 +67,7 @@ void removesResourcesNotFoundOnServerFromStore() { @Test void removesResourceFromCache() { - boundedItemStore.put(testRes1Key(), - TestUtils.testCustomResource1()); + boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); boundedItemStore.remove(testRes1Key()); @@ -83,8 +79,7 @@ void removesResourceFromCache() { @Test void readingKeySetDoesNotReadFromBoundedCache() { - boundedItemStore.put(testRes1Key(), - TestUtils.testCustomResource1()); + boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); boundedItemStore.keySet(); @@ -93,8 +88,7 @@ void readingKeySetDoesNotReadFromBoundedCache() { @Test void readingValuesDoesNotReadFromBoundedCache() { - boundedItemStore.put(testRes1Key(), - TestUtils.testCustomResource1()); + boundedItemStore.put(testRes1Key(), TestUtils.testCustomResource1()); boundedItemStore.values(); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java index 1158e00295..86ecb0d729 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/cache/KubernetesResourceFetcherTest.java @@ -31,18 +31,17 @@ void inverseKeyFunction() { private HasMetadata namespacedResource() { var cm = new ConfigMap(); - cm.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(DEFAULT_NAMESPACE) - .build()); + cm.setMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(DEFAULT_NAMESPACE) + .build()); return cm; } private HasMetadata clusterScopedResource() { var cm = new CustomResourceDefinition(); - cm.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + cm.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return cm; } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSourceTest.java index 4ba2a5184c..6548bbddc7 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/controller/ControllerEventSourceTest.java @@ -27,8 +27,8 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -class ControllerEventSourceTest extends - AbstractEventSourceTestBase, EventHandler> { +class ControllerEventSourceTest + extends AbstractEventSourceTestBase, EventHandler> { public static final String FINALIZER = ReconcilerUtils.getDefaultFinalizerName(TestCustomResource.class); @@ -87,8 +87,7 @@ void normalExecutionIfGenerationChanges() { @Test void handlesAllEventIfNotGenerationAware() { - source = - new ControllerEventSource<>(new TestController(false)); + source = new ControllerEventSource<>(new TestController(false)); setup(); TestCustomResource customResource1 = TestUtils.testCustomResource(); @@ -116,8 +115,8 @@ void callsBroadcastsOnResourceEvents() { source.eventReceived(ResourceAction.UPDATED, customResource1, customResource1); verify(testController.getEventSourceManager(), times(1)) - .broadcastOnResourceEvent(eq(ResourceAction.UPDATED), eq(customResource1), - eq(customResource1)); + .broadcastOnResourceEvent( + eq(ResourceAction.UPDATED), eq(customResource1), eq(customResource1)); } @Test @@ -126,9 +125,7 @@ void filtersOutEventsOnAddAndUpdate() { OnAddFilter onAddFilter = (res) -> false; OnUpdateFilter onUpdatePredicate = (res, res2) -> false; - source = - new ControllerEventSource<>( - new TestController(onAddFilter, onUpdatePredicate, null)); + source = new ControllerEventSource<>(new TestController(onAddFilter, onUpdatePredicate, null)); setUpSource(source, true, controllerConfig); source.eventReceived(ResourceAction.ADDED, cr, null); @@ -141,8 +138,7 @@ void filtersOutEventsOnAddAndUpdate() { void genericFilterFiltersOutAddUpdateAndDeleteEvents() { TestCustomResource cr = TestUtils.testCustomResource(); - source = - new ControllerEventSource<>(new TestController(null, null, res -> false)); + source = new ControllerEventSource<>(new TestController(null, null, res -> false)); setUpSource(source, true, controllerConfig); source.eventReceived(ResourceAction.ADDED, cr, null); @@ -161,15 +157,20 @@ private static class TestController extends Controller { private final EventSourceManager eventSourceManager = mock(EventSourceManager.class); - public TestController(OnAddFilter onAddFilter, + public TestController( + OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, GenericFilter genericFilter) { - super(reconciler, new TestConfiguration(true, onAddFilter, onUpdateFilter, genericFilter), + super( + reconciler, + new TestConfiguration(true, onAddFilter, onUpdateFilter, genericFilter), MockKubernetesClient.client(TestCustomResource.class)); } public TestController(boolean generationAware) { - super(reconciler, new TestConfiguration(generationAware, null, null, null), + super( + reconciler, + new TestConfiguration(generationAware, null, null, null), MockKubernetesClient.client(TestCustomResource.class)); } @@ -184,10 +185,12 @@ public boolean useFinalizer() { } } - private static class TestConfiguration extends - ResolvedControllerConfiguration { + private static class TestConfiguration + extends ResolvedControllerConfiguration { - public TestConfiguration(boolean generationAware, OnAddFilter onAddFilter, + public TestConfiguration( + boolean generationAware, + OnAddFilter onAddFilter, OnUpdateFilter onUpdateFilter, GenericFilter genericFilter) { super( @@ -201,8 +204,10 @@ public TestConfiguration(boolean generationAware, OnAddFilter, EventHandler> { +class CachingInboundEventSourceTest + extends AbstractEventSourceTestBase< + CachingInboundEventSource, EventHandler> { @SuppressWarnings("unchecked") - private final CachingInboundEventSource.ResourceFetcher supplier = - mock( - CachingInboundEventSource.ResourceFetcher.class); + private final CachingInboundEventSource.ResourceFetcher< + SampleExternalResource, TestCustomResource> + supplier = mock(CachingInboundEventSource.ResourceFetcher.class); + private final TestCustomResource testCustomResource = TestUtils.testCustomResource(); private final CacheKeyMapper cacheKeyMapper = r -> r.getName() + "#" + r.getValue(); @BeforeEach public void setup() { - when(supplier.fetchResources(any())) - .thenReturn(Set.of(SampleExternalResource.testResource1())); + when(supplier.fetchResources(any())).thenReturn(Set.of(SampleExternalResource.testResource1())); - setUpSource(new CachingInboundEventSource<>(supplier, - SampleExternalResource.class, cacheKeyMapper)); + setUpSource( + new CachingInboundEventSource<>(supplier, SampleExternalResource.class, cacheKeyMapper)); } @Test void getSecondaryResourceFromCacheOrSupplier() throws InterruptedException { - when(supplier.fetchResources(any())) - .thenReturn(Set.of(SampleExternalResource.testResource1())); + when(supplier.fetchResources(any())).thenReturn(Set.of(SampleExternalResource.testResource1())); var value = source.getSecondaryResources(testCustomResource); @@ -59,7 +59,8 @@ void getSecondaryResourceFromCacheOrSupplier() throws InterruptedException { verify(supplier, times(1)).fetchResources(eq(testCustomResource)); verify(eventHandler, never()).handleEvent(any()); - source.handleResourceEvent(ResourceID.fromResource(testCustomResource), + source.handleResourceEvent( + ResourceID.fromResource(testCustomResource), Set.of(SampleExternalResource.testResource1(), SampleExternalResource.testResource2())); verify(supplier, times(1)).fetchResources(eq(testCustomResource)); @@ -69,11 +70,13 @@ void getSecondaryResourceFromCacheOrSupplier() throws InterruptedException { @Test void propagateEventOnDeletedResource() throws InterruptedException { - source.handleResourceEvent(ResourceID.fromResource(testCustomResource), - SampleExternalResource.testResource1()); - source.handleResourceDeleteEvent(ResourceID.fromResource(testCustomResource), + source.handleResourceEvent( + ResourceID.fromResource(testCustomResource), SampleExternalResource.testResource1()); + source.handleResourceDeleteEvent( + ResourceID.fromResource(testCustomResource), cacheKeyMapper.keyFor(SampleExternalResource.testResource1())); - source.handleResourceDeleteEvent(ResourceID.fromResource(testCustomResource), + source.handleResourceDeleteEvent( + ResourceID.fromResource(testCustomResource), cacheKeyMapper.keyFor(SampleExternalResource.testResource2())); verify(eventHandler, times(2)).handleEvent(any()); @@ -81,7 +84,8 @@ void propagateEventOnDeletedResource() throws InterruptedException { @Test void propagateEventOnUpdateResources() throws InterruptedException { - source.handleResourceEvent(ResourceID.fromResource(testCustomResource), + source.handleResourceEvent( + ResourceID.fromResource(testCustomResource), Set.of(SampleExternalResource.testResource1(), SampleExternalResource.testResource2())); verify(eventHandler, times(1)).handleEvent(any()); diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSourceTest.java index ce027846f0..3205bca523 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/InformerEventSourceTest.java @@ -93,8 +93,8 @@ void skipsAddEventPropagationViaAnnotation() { @Test void skipsUpdateEventPropagationViaAnnotation() { - informerEventSource.onUpdate(testDeployment(), - informerEventSource.addPreviousAnnotation("1", testDeployment())); + informerEventSource.onUpdate( + testDeployment(), informerEventSource.addPreviousAnnotation("1", testDeployment())); verify(eventHandlerMock, never()).handleEvent(any()); } @@ -108,9 +108,12 @@ void processEventPropagationWithoutAnnotation() { @Test void processEventPropagationWithIncorrectAnnotation() { - informerEventSource.onAdd(new DeploymentBuilder(testDeployment()).editMetadata() - .addToAnnotations(InformerEventSource.PREVIOUS_ANNOTATION_KEY, "invalid") - .endMetadata().build()); + informerEventSource.onAdd( + new DeploymentBuilder(testDeployment()) + .editMetadata() + .addToAnnotations(InformerEventSource.PREVIOUS_ANNOTATION_KEY, "invalid") + .endMetadata() + .build()); verify(eventHandlerMock, times(1)).handleEvent(any()); } @@ -122,7 +125,6 @@ void propagateEventAndRemoveResourceFromTempCacheIfResourceVersionMismatch() { when(temporaryResourceCacheMock.getResourceFromCache(any())) .thenReturn(Optional.of(cachedDeployment)); - informerEventSource.onUpdate(cachedDeployment, testDeployment()); verify(eventHandlerMock, times(1)).handleEvent(any()); @@ -132,8 +134,7 @@ void propagateEventAndRemoveResourceFromTempCacheIfResourceVersionMismatch() { @Test void genericFilterForEvents() { informerEventSource.setGenericFilter(r -> false); - when(temporaryResourceCacheMock.getResourceFromCache(any())) - .thenReturn(Optional.empty()); + when(temporaryResourceCacheMock.getResourceFromCache(any())).thenReturn(Optional.empty()); informerEventSource.onAdd(testDeployment()); informerEventSource.onUpdate(testDeployment(), testDeployment()); @@ -145,8 +146,7 @@ void genericFilterForEvents() { @Test void filtersOnAddEvents() { informerEventSource.setOnAddFilter(r -> false); - when(temporaryResourceCacheMock.getResourceFromCache(any())) - .thenReturn(Optional.empty()); + when(temporaryResourceCacheMock.getResourceFromCache(any())).thenReturn(Optional.empty()); informerEventSource.onAdd(testDeployment()); @@ -156,8 +156,7 @@ void filtersOnAddEvents() { @Test void filtersOnUpdateEvents() { informerEventSource.setOnUpdateFilter((r1, r2) -> false); - when(temporaryResourceCacheMock.getResourceFromCache(any())) - .thenReturn(Optional.empty()); + when(temporaryResourceCacheMock.getResourceFromCache(any())).thenReturn(Optional.empty()); informerEventSource.onUpdate(testDeployment(), testDeployment()); @@ -167,8 +166,7 @@ void filtersOnUpdateEvents() { @Test void filtersOnDeleteEvents() { informerEventSource.setOnDeleteFilter((r, b) -> false); - when(temporaryResourceCacheMock.getResourceFromCache(any())) - .thenReturn(Optional.empty()); + when(temporaryResourceCacheMock.getResourceFromCache(any())).thenReturn(Optional.empty()); informerEventSource.onDelete(testDeployment(), true); @@ -180,16 +178,21 @@ void informerStoppedHandlerShouldBeCalledWhenInformerStops() { final var exception = new RuntimeException("Informer stopped exceptionally!"); final var informerStoppedHandler = mock(InformerStoppedHandler.class); var configuration = - ConfigurationService.newOverriddenConfigurationService(new BaseConfigurationService(), + ConfigurationService.newOverriddenConfigurationService( + new BaseConfigurationService(), o -> o.withInformerStoppedHandler(informerStoppedHandler)); var mockControllerConfig = mock(ControllerConfiguration.class); when(mockControllerConfig.getConfigurationService()).thenReturn(configuration); - informerEventSource = new InformerEventSource<>(informerEventSourceConfiguration, - MockKubernetesClient.client(Deployment.class, unused -> { - throw exception; - })); + informerEventSource = + new InformerEventSource<>( + informerEventSourceConfiguration, + MockKubernetesClient.client( + Deployment.class, + unused -> { + throw exception; + })); informerEventSource.setControllerConfiguration(mockControllerConfig); // by default informer fails to start if there is an exception in the client on start. @@ -205,5 +208,4 @@ Deployment testDeployment() { deployment.getMetadata().setName("test"); return deployment; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/MappersTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/MappersTest.java index 12e7b54706..fe091c9698 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/MappersTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/MappersTest.java @@ -17,7 +17,6 @@ class MappersTest { - @Test void secondaryToPrimaryMapperFromOwnerReference() { var primary = TestUtils.testCustomResource(); @@ -25,8 +24,32 @@ void secondaryToPrimaryMapperFromOwnerReference() { var secondary = getConfigMap(primary); secondary.addOwnerReference(primary); - var res = Mappers.fromOwnerReferences(TestCustomResource.class) - .toPrimaryResourceIDs(secondary); + var res = Mappers.fromOwnerReferences(TestCustomResource.class).toPrimaryResourceIDs(secondary); + + assertThat(res).contains(ResourceID.fromResource(primary)); + } + + @Test + void secondaryToPrimaryMapperFromOwnerReferenceWhereGroupIdIsEmpty() { + var primary = + new ConfigMapBuilder() + .withNewMetadata() + .withName("test") + .withNamespace("default") + .endMetadata() + .build(); + primary.getMetadata().setUid(UUID.randomUUID().toString()); + var secondary = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName("test1") + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + secondary.addOwnerReference(primary); + + var res = Mappers.fromOwnerReferences(ConfigMap.class).toPrimaryResourceIDs(secondary); assertThat(res).contains(ResourceID.fromResource(primary)); } @@ -38,19 +61,20 @@ void secondaryToPrimaryMapperFromOwnerReferenceFiltersByType() { var secondary = getConfigMap(primary); secondary.addOwnerReference(primary); - var res = Mappers.fromOwnerReferences(TestCustomResourceOtherV1.class) - .toPrimaryResourceIDs(secondary); + var res = + Mappers.fromOwnerReferences(TestCustomResourceOtherV1.class) + .toPrimaryResourceIDs(secondary); assertThat(res).isEmpty(); } - private static ConfigMap getConfigMap(TestCustomResource primary) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName("test1") - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName("test1") + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .build(); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/PrimaryToSecondaryIndexTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/PrimaryToSecondaryIndexTest.java index 6793b09550..7343b1e581 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/PrimaryToSecondaryIndexTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/PrimaryToSecondaryIndexTest.java @@ -20,6 +20,7 @@ class PrimaryToSecondaryIndexTest { @SuppressWarnings("unchecked") private final SecondaryToPrimaryMapper secondaryToPrimaryMapperMock = mock(SecondaryToPrimaryMapper.class); + private final PrimaryToSecondaryIndex primaryToSecondaryIndex = new DefaultPrimaryToSecondaryIndex<>(secondaryToPrimaryMapperMock); @@ -59,10 +60,10 @@ void indexesAdditionalResources() { var secondaryResources1 = primaryToSecondaryIndex.getSecondaryResources(primaryID1); var secondaryResources2 = primaryToSecondaryIndex.getSecondaryResources(primaryID2); - assertThat(secondaryResources1).containsOnly(ResourceID.fromResource(secondary1), - ResourceID.fromResource(secondary2)); - assertThat(secondaryResources2).containsOnly(ResourceID.fromResource(secondary1), - ResourceID.fromResource(secondary2)); + assertThat(secondaryResources1) + .containsOnly(ResourceID.fromResource(secondary1), ResourceID.fromResource(secondary2)); + assertThat(secondaryResources2) + .containsOnly(ResourceID.fromResource(secondary1), ResourceID.fromResource(secondary2)); } @Test diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryPrimaryResourceCacheTest.java similarity index 87% rename from operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java rename to operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryPrimaryResourceCacheTest.java index 60eb7245a9..e62888832f 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryResourceCacheTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TemporaryPrimaryResourceCacheTest.java @@ -19,11 +19,13 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -class TemporaryResourceCacheTest { +class TemporaryPrimaryResourceCacheTest { public static final String RESOURCE_VERSION = "2"; + @SuppressWarnings("unchecked") private InformerEventSource informerEventSource; + private TemporaryResourceCache temporaryResourceCache; @BeforeEach @@ -99,8 +101,12 @@ void resourceVersionParsing() { ConfigMap testResource = propagateTestResourceToCache(); // an event with a newer version will not remove - temporaryResourceCache.onAddOrUpdateEvent(new ConfigMapBuilder(testResource).editMetadata() - .withResourceVersion("1").endMetadata().build()); + temporaryResourceCache.onAddOrUpdateEvent( + new ConfigMapBuilder(testResource) + .editMetadata() + .withResourceVersion("1") + .endMetadata() + .build()); assertThat(temporaryResourceCache.isKnownResourceVersion(testResource)).isTrue(); assertThat(temporaryResourceCache.getResourceFromCache(ResourceID.fromResource(testResource))) @@ -118,8 +124,13 @@ void rapidDeletion() { var testResource = testResource(); temporaryResourceCache.onAddOrUpdateEvent(testResource); - temporaryResourceCache.onDeleteEvent(new ConfigMapBuilder(testResource).editMetadata() - .withResourceVersion("3").endMetadata().build(), false); + temporaryResourceCache.onDeleteEvent( + new ConfigMapBuilder(testResource) + .editMetadata() + .withResourceVersion("3") + .endMetadata() + .build(), + false); temporaryResourceCache.putAddedResource(testResource); assertThat(temporaryResourceCache.getResourceFromCache(ResourceID.fromResource(testResource))) @@ -146,10 +157,13 @@ void expirationCacheTtl() { cache.add(1); cache.add(2); - Awaitility.await().atMost(1, TimeUnit.SECONDS).untilAsserted(() -> { - assertThat(cache.contains(1)).isFalse(); - assertThat(cache.contains(2)).isFalse(); - }); + Awaitility.await() + .atMost(1, TimeUnit.SECONDS) + .untilAsserted( + () -> { + assertThat(cache.contains(1)).isFalse(); + assertThat(cache.contains(2)).isFalse(); + }); } private ConfigMap propagateTestResourceToCache() { @@ -163,14 +177,11 @@ private ConfigMap propagateTestResourceToCache() { ConfigMap testResource() { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withLabels(Map.of("k", "v")) - .build()); + configMap.setMetadata(new ObjectMetaBuilder().withLabels(Map.of("k", "v")).build()); configMap.getMetadata().setName("test"); configMap.getMetadata().setNamespace("default"); configMap.getMetadata().setResourceVersion(RESOURCE_VERSION); configMap.getMetadata().setUid("test-uid"); return configMap; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStoreTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStoreTest.java index 3bebc79094..ae25d16d9a 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStoreTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/informer/TransformingItemStoreTest.java @@ -14,10 +14,12 @@ class TransformingItemStoreTest { @Test void cachedObjectTransformed() { - TransformingItemStore transformingItemStore = new TransformingItemStore<>(r -> { - r.getMetadata().setLabels(null); - return r; - }); + TransformingItemStore transformingItemStore = + new TransformingItemStore<>( + r -> { + r.getMetadata().setLabels(null); + return r; + }); var cm = configMap(); cm.getMetadata().setLabels(Map.of("k", "v")); @@ -29,12 +31,14 @@ void cachedObjectTransformed() { @Test void preservesSelectedAttributes() { - TransformingItemStore transformingItemStore = new TransformingItemStore<>(r -> { - r.getMetadata().setName(null); - r.getMetadata().setNamespace(null); - r.getMetadata().setResourceVersion(null); - return r; - }); + TransformingItemStore transformingItemStore = + new TransformingItemStore<>( + r -> { + r.getMetadata().setName(null); + r.getMetadata().setNamespace(null); + r.getMetadata().setResourceVersion(null); + return r; + }); var cm = configMap(); transformingItemStore.put(metaNamespaceKeyFunc(cm), cm); @@ -43,17 +47,18 @@ void preservesSelectedAttributes() { assertThat(transformingItemStore.get(metaNamespaceKeyFunc(cm)).getMetadata().getNamespace()) .isNotNull(); assertThat( - transformingItemStore.get(metaNamespaceKeyFunc(cm)).getMetadata().getResourceVersion()) + transformingItemStore.get(metaNamespaceKeyFunc(cm)).getMetadata().getResourceVersion()) .isNotNull(); } ConfigMap configMap() { var cm = new ConfigMap(); - cm.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .withNamespace("default").withResourceVersion("1") - .build()); + cm.setMetadata( + new ObjectMetaBuilder() + .withName("test1") + .withNamespace("default") + .withResourceVersion("1") + .build()); return cm; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java index fd5b85aa16..c85f40d5ad 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PerResourcePollingEventSourceTest.java @@ -23,64 +23,76 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -class PerResourcePollingEventSourceTest extends - AbstractEventSourceTestBase, EventHandler> { +class PerResourcePollingEventSourceTest + extends AbstractEventSourceTestBase< + PerResourcePollingEventSource, EventHandler> { public static final int PERIOD = 150; + @SuppressWarnings("unchecked") - private final PerResourcePollingEventSource.ResourceFetcher supplier = - mock(PerResourcePollingEventSource.ResourceFetcher.class); + private final PerResourcePollingEventSource.ResourceFetcher< + SampleExternalResource, TestCustomResource> + supplier = mock(PerResourcePollingEventSource.ResourceFetcher.class); + @SuppressWarnings("unchecked") private final IndexerResourceCache resourceCache = mock(IndexerResourceCache.class); + private final TestCustomResource testCustomResource = TestUtils.testCustomResource(); private final EventSourceContext context = mock(EventSourceContext.class); @BeforeEach public void setup() { when(resourceCache.get(any())).thenReturn(Optional.of(testCustomResource)); - when(supplier.fetchResources(any())) - .thenReturn(Set.of(SampleExternalResource.testResource1())); + when(supplier.fetchResources(any())).thenReturn(Set.of(SampleExternalResource.testResource1())); when(context.getPrimaryCache()).thenReturn(resourceCache); - setUpSource(new PerResourcePollingEventSource<>(SampleExternalResource.class, context, - new PerResourcePollingConfigurationBuilder<>(supplier, Duration.ofMillis(PERIOD)) - .withCacheKeyMapper(r -> r.getName() + "#" + r.getValue()) - .build())); + setUpSource( + new PerResourcePollingEventSource<>( + SampleExternalResource.class, + context, + new PerResourcePollingConfigurationBuilder<>(supplier, Duration.ofMillis(PERIOD)) + .withCacheKeyMapper(r -> r.getName() + "#" + r.getValue()) + .build())); } @Test void pollsTheResourceAfterAwareOfIt() { source.onResourceCreated(testCustomResource); - await().pollDelay(Duration.ofMillis(3 * PERIOD)).untilAsserted(() -> { - verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); - verify(supplier, atLeast(2)).fetchDelay(any(), eq(testCustomResource)); - verify(eventHandler, times(1)).handleEvent(any()); - }); + await() + .pollDelay(Duration.ofMillis(3 * PERIOD)) + .untilAsserted( + () -> { + verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); + verify(supplier, atLeast(2)).fetchDelay(any(), eq(testCustomResource)); + verify(eventHandler, times(1)).handleEvent(any()); + }); } @Test void registeringTaskOnAPredicate() { - setUpSource(new PerResourcePollingEventSource<>(SampleExternalResource.class, context, - new PerResourcePollingConfigurationBuilder<>( - supplier, Duration.ofMillis(PERIOD)) - .withRegisterPredicate( - testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1) - .withCacheKeyMapper(CacheKeyMapper.singleResourceCacheKeyMapper()) - .build())); + setUpSource( + new PerResourcePollingEventSource<>( + SampleExternalResource.class, + context, + new PerResourcePollingConfigurationBuilder<>(supplier, Duration.ofMillis(PERIOD)) + .withRegisterPredicate( + testCustomResource -> testCustomResource.getMetadata().getGeneration() > 1) + .withCacheKeyMapper(CacheKeyMapper.singleResourceCacheKeyMapper()) + .build())); source.onResourceCreated(testCustomResource); - - await().pollDelay(Duration.ofMillis(2 * PERIOD)) + await() + .pollDelay(Duration.ofMillis(2 * PERIOD)) .untilAsserted(() -> verify(supplier, times(0)).fetchResources(eq(testCustomResource))); testCustomResource.getMetadata().setGeneration(2L); source.onResourceUpdated(testCustomResource, testCustomResource); - - await().pollDelay(Duration.ofMillis(2 * PERIOD)) + await() + .pollDelay(Duration.ofMillis(2 * PERIOD)) .untilAsserted(() -> verify(supplier, atLeast(1)).fetchResources(eq(testCustomResource))); } @@ -91,10 +103,13 @@ void propagateEventOnDeletedResource() { .thenReturn(Set.of(SampleExternalResource.testResource1())) .thenReturn(Collections.emptySet()); - await().pollDelay(Duration.ofMillis(3 * PERIOD)).untilAsserted(() -> { - verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); - verify(eventHandler, times(2)).handleEvent(any()); - }); + await() + .pollDelay(Duration.ofMillis(3 * PERIOD)) + .untilAsserted( + () -> { + verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); + verify(eventHandler, times(2)).handleEvent(any()); + }); } @Test @@ -117,11 +132,14 @@ void getSecondaryResourceInitiatesFetchJustForFirstTime() { verify(supplier, times(1)).fetchResources(eq(testCustomResource)); verify(eventHandler, never()).handleEvent(any()); - await().pollDelay(Duration.ofMillis(PERIOD * 2)).untilAsserted(() -> { - verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); - var val = source.getSecondaryResources(testCustomResource); - assertThat(val).hasSize(2); - }); + await() + .pollDelay(Duration.ofMillis(PERIOD * 2)) + .untilAsserted( + () -> { + verify(supplier, atLeast(2)).fetchResources(eq(testCustomResource)); + var val = source.getSecondaryResources(testCustomResource); + assertThat(val).hasSize(2); + }); } @Test @@ -131,60 +149,71 @@ void getsValueFromCacheOrSupplier() { .thenReturn(Collections.emptySet()) .thenReturn(Set.of(SampleExternalResource.testResource1())); - await().pollDelay(Duration.ofMillis(PERIOD / 3)).untilAsserted(() -> { - var value = source.getSecondaryResources(testCustomResource); - verify(eventHandler, times(0)).handleEvent(any()); - assertThat(value).isEmpty(); - }); - - await().pollDelay(Duration.ofMillis(PERIOD * 2)).untilAsserted(() -> { - var value2 = source.getSecondaryResources(testCustomResource); - assertThat(value2).hasSize(1); - verify(eventHandler, times(1)).handleEvent(any()); - }); + await() + .pollDelay(Duration.ofMillis(PERIOD / 3)) + .untilAsserted( + () -> { + var value = source.getSecondaryResources(testCustomResource); + verify(eventHandler, times(0)).handleEvent(any()); + assertThat(value).isEmpty(); + }); + + await() + .pollDelay(Duration.ofMillis(PERIOD * 2)) + .untilAsserted( + () -> { + var value2 = source.getSecondaryResources(testCustomResource); + assertThat(value2).hasSize(1); + verify(eventHandler, times(1)).handleEvent(any()); + }); } @Test void supportsDynamicPollingDelay() { - when(supplier.fetchResources(any())) - .thenReturn(Set.of(SampleExternalResource.testResource1())); - when(supplier.fetchDelay(any(),any())) - .thenReturn(Optional.of(Duration.ofMillis(PERIOD))) - .thenReturn(Optional.of(Duration.ofMillis(PERIOD*2))); + when(supplier.fetchResources(any())).thenReturn(Set.of(SampleExternalResource.testResource1())); + when(supplier.fetchDelay(any(), any())) + .thenReturn(Optional.of(Duration.ofMillis(PERIOD))) + .thenReturn(Optional.of(Duration.ofMillis(PERIOD * 2))); source.onResourceCreated(testCustomResource); - await().pollDelay(Duration.ofMillis(PERIOD)).atMost(Duration.ofMillis((long) (1.5 * PERIOD))) - .pollInterval(Duration.ofMillis(20)) - .untilAsserted(() -> verify(supplier,times(1)).fetchResources(any())); + await() + .pollDelay(Duration.ofMillis(PERIOD)) + .atMost(Duration.ofMillis((long) (1.5 * PERIOD))) + .pollInterval(Duration.ofMillis(20)) + .untilAsserted(() -> verify(supplier, times(1)).fetchResources(any())); // verifying that it is not called as with normal interval - await().pollDelay(Duration.ofMillis(PERIOD)).atMost(Duration.ofMillis((long) (1.5*PERIOD))) - .pollInterval(Duration.ofMillis(20)) - .untilAsserted(() -> verify(supplier,times(1)).fetchResources(any())); - await().pollDelay(Duration.ofMillis(PERIOD)).atMost(Duration.ofMillis(2 * PERIOD)) - .pollInterval(Duration.ofMillis(20)) - .untilAsserted(() -> verify(supplier,times(2)).fetchResources(any())); + await() + .pollDelay(Duration.ofMillis(PERIOD)) + .atMost(Duration.ofMillis((long) (1.5 * PERIOD))) + .pollInterval(Duration.ofMillis(20)) + .untilAsserted(() -> verify(supplier, times(1)).fetchResources(any())); + await() + .pollDelay(Duration.ofMillis(PERIOD)) + .atMost(Duration.ofMillis(2 * PERIOD)) + .pollInterval(Duration.ofMillis(20)) + .untilAsserted(() -> verify(supplier, times(2)).fetchResources(any())); } @Test void deleteEventCancelsTheScheduling() { - when(supplier.fetchResources(any())) - .thenReturn(Set.of(SampleExternalResource.testResource1())); + when(supplier.fetchResources(any())).thenReturn(Set.of(SampleExternalResource.testResource1())); source.onResourceCreated(testCustomResource); - await().pollDelay(Duration.ofMillis(PERIOD)) - .atMost(Duration.ofMillis((2* PERIOD))) - .pollInterval(Duration.ofMillis(20)) - .untilAsserted(() -> verify(supplier,times(1)).fetchResources(any())); + await() + .pollDelay(Duration.ofMillis(PERIOD)) + .atMost(Duration.ofMillis((2 * PERIOD))) + .pollInterval(Duration.ofMillis(20)) + .untilAsserted(() -> verify(supplier, times(1)).fetchResources(any())); when(resourceCache.get(any())).thenReturn(Optional.empty()); source.onResourceDeleted(testCustomResource); // check if not called again - await().pollDelay(Duration.ofMillis(2*PERIOD)) - .atMost(Duration.ofMillis((4* PERIOD))) - .untilAsserted(() -> verify(supplier,times(1)).fetchResources(any())); + await() + .pollDelay(Duration.ofMillis(2 * PERIOD)) + .atMost(Duration.ofMillis((4 * PERIOD))) + .untilAsserted(() -> verify(supplier, times(1)).fetchResources(any())); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java index ced96e9b7d..0f7d26446d 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/polling/PollingEventSourceTest.java @@ -21,8 +21,8 @@ import static org.mockito.Mockito.*; class PollingEventSourceTest - extends - AbstractEventSourceTestBase, EventHandler> { + extends AbstractEventSourceTestBase< + PollingEventSource, EventHandler> { public static final int DEFAULT_WAIT_PERIOD = 100; public static final Duration POLL_PERIOD = Duration.ofMillis(30L); @@ -30,9 +30,14 @@ class PollingEventSourceTest @SuppressWarnings("unchecked") private final PollingEventSource.GenericResourceFetcher resourceFetcher = mock(PollingEventSource.GenericResourceFetcher.class); + private final PollingEventSource pollingEventSource = - new PollingEventSource<>(SampleExternalResource.class, - new PollingConfiguration<>(null, resourceFetcher, POLL_PERIOD, + new PollingEventSource<>( + SampleExternalResource.class, + new PollingConfiguration<>( + null, + resourceFetcher, + POLL_PERIOD, (SampleExternalResource er) -> er.getName() + "#" + er.getValue())); @BeforeEach @@ -51,7 +56,8 @@ void pollsAndProcessesEvents() throws InterruptedException { @Test void propagatesEventForRemovedResources() throws InterruptedException { - when(resourceFetcher.fetchResources()).thenReturn(testResponseWithTwoValues()) + when(resourceFetcher.fetchResources()) + .thenReturn(testResponseWithTwoValues()) .thenReturn(testResponseWithOneValue()); pollingEventSource.start(); Thread.sleep(DEFAULT_WAIT_PERIOD); @@ -82,8 +88,7 @@ void propagatesEventOnNewResourceForPrimary() throws InterruptedException { @Test void updatesHealthIndicatorBasedOnExceptionsInFetcher() throws InterruptedException { - when(resourceFetcher.fetchResources()) - .thenReturn(testResponseWithOneValue()); + when(resourceFetcher.fetchResources()).thenReturn(testResponseWithOneValue()); pollingEventSource.start(); assertThat(pollingEventSource.getStatus()).isEqualTo(Status.HEALTHY); @@ -93,8 +98,10 @@ void updatesHealthIndicatorBasedOnExceptionsInFetcher() throws InterruptedExcept .thenThrow(new RuntimeException("test exception")) .thenReturn(testResponseWithOneValue()); - await().pollInterval(POLL_PERIOD).untilAsserted( - () -> assertThat(pollingEventSource.getStatus()).isEqualTo(Status.UNHEALTHY)); + await() + .pollInterval(POLL_PERIOD) + .untilAsserted( + () -> assertThat(pollingEventSource.getStatus()).isEqualTo(Status.UNHEALTHY)); await() .untilAsserted(() -> assertThat(pollingEventSource.getStatus()).isEqualTo(Status.HEALTHY)); @@ -118,5 +125,4 @@ private Map> testResponseWithTwoValues() res.put(primaryID2(), Set.of(testResource2())); return res; } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSourceTest.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSourceTest.java index 3fe3a5db58..9396411777 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSourceTest.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/processing/event/source/timer/TimerEventSourceTest.java @@ -23,13 +23,12 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; class TimerEventSourceTest - extends - AbstractEventSourceTestBase, CapturingEventHandler> { + extends AbstractEventSourceTestBase< + TimerEventSource, CapturingEventHandler> { public static final int INITIAL_DELAY = 50; public static final int PERIOD = 50; - @BeforeEach public void setup() { setUpSource(new TimerEventSource<>(), new CapturingEventHandler()); @@ -80,8 +79,8 @@ public void eventNotRegisteredIfStopped() throws IOException { var resourceID = ResourceID.fromResource(TestUtils.testCustomResource()); source.stop(); - assertThatExceptionOfType(IllegalStateException.class).isThrownBy( - () -> source.scheduleOnce(resourceID, PERIOD)); + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> source.scheduleOnce(resourceID, PERIOD)); } @Test diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenCustomResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenCustomResource.java index 74f58b795f..f06c0035d3 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenCustomResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenCustomResource.java @@ -6,8 +6,7 @@ @Group("sample.javaoperatorsdk.io") @Version("v1") -public class ObservedGenCustomResource - extends CustomResource { +public class ObservedGenCustomResource extends CustomResource { @Override protected ObservedGenSpec initSpec() { diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenSpec.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenSpec.java index 181204bc1c..30fb495c56 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenSpec.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenSpec.java @@ -14,8 +14,6 @@ public void setValue(String value) { @Override public String toString() { - return "TestCustomResourceSpec{" + - "value='" + value + '\'' + - '}'; + return "TestCustomResourceSpec{" + "value='" + value + '\'' + '}'; } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenStatus.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenStatus.java index 81ce9a435d..0f685d5b05 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenStatus.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/observedgeneration/ObservedGenStatus.java @@ -1,5 +1,3 @@ package io.javaoperatorsdk.operator.sample.observedgeneration; -public class ObservedGenStatus { - -} +public class ObservedGenStatus {} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/NamespacedTestCustomResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/NamespacedTestCustomResource.java index 45bd9cb6ce..761d91dc04 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/NamespacedTestCustomResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/NamespacedTestCustomResource.java @@ -9,5 +9,4 @@ @Version("v1") public class NamespacedTestCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconciler.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconciler.java index be2c80667e..1d7535c9d3 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconciler.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconciler.java @@ -35,8 +35,7 @@ public TestCustomReconciler(KubernetesClient kubernetesClient, boolean updateSta } @Override - public DeleteControl cleanup( - TestCustomResource resource, Context context) { + public DeleteControl cleanup(TestCustomResource resource, Context context) { var statusDetails = kubernetesClient .configMaps() diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconcilerOtherV1.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconcilerOtherV1.java index 97f2fb9098..5327b30a79 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconcilerOtherV1.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomReconcilerOtherV1.java @@ -9,8 +9,8 @@ public class TestCustomReconcilerOtherV1 implements Reconciler { @Override - public UpdateControl reconcile(TestCustomResourceOtherV1 resource, - Context context) { + public UpdateControl reconcile( + TestCustomResourceOtherV1 resource, Context context) { return UpdateControl.noUpdate(); } } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java index 59d9d4fb91..d01bd3c747 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResource.java @@ -18,5 +18,4 @@ protected TestCustomResourceSpec initSpec() { protected TestCustomResourceStatus initStatus() { return new TestCustomResourceStatus(); } - } diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceOtherV1.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceOtherV1.java index 90e226abc8..6bb572ca4e 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceOtherV1.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceOtherV1.java @@ -9,6 +9,4 @@ @Version("v1") @Kind("TestCustomResourceOtherV1") // this is needed to override the automatically generated kind public class TestCustomResourceOtherV1 - extends CustomResource { - -} + extends CustomResource {} diff --git a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java index 5c23cc4c95..69a6b107b2 100644 --- a/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java +++ b/operator-framework-core/src/test/java/io/javaoperatorsdk/operator/sample/simple/TestCustomResourceSpec.java @@ -58,8 +58,9 @@ public boolean equals(Object o) { return false; } TestCustomResourceSpec that = (TestCustomResourceSpec) o; - return Objects.equals(configMapName, that.configMapName) && Objects.equals( - key, that.key) && Objects.equals(value, that.value); + return Objects.equals(configMapName, that.configMapName) + && Objects.equals(key, that.key) + && Objects.equals(value, that.value); } @Override diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/configmap.empty-owner-reference-desired.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/configmap.empty-owner-reference-desired.yaml index 3a9d018266..01d27e39b3 100644 --- a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/configmap.empty-owner-reference-desired.yaml +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/configmap.empty-owner-reference-desired.yaml @@ -10,5 +10,3 @@ metadata: uid: 1ef74cb4-dbbd-45ef-9caf-aa76186594ea data: key1: "val1" - - diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/deployment-with-managed-fields-additional-controller.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/deployment-with-managed-fields-additional-controller.yaml index d82b5c8933..38358a16c0 100644 --- a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/deployment-with-managed-fields-additional-controller.yaml +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/deployment-with-managed-fields-additional-controller.yaml @@ -25,6 +25,7 @@ metadata: f:image: {} f:name: {} f:ports: + .: {} k:{"containerPort":80,"protocol":"TCP"}: .: {} f:containerPort: {} diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod-desired.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod-desired.yaml index 92ece6df00..e400532fad 100644 --- a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod-desired.yaml +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod-desired.yaml @@ -18,4 +18,4 @@ spec: - name: shared-data mountPath: /data command: ["/bin/sh"] - args: ["-c", "echo Level Up Blue Team! > /data/index.html"] \ No newline at end of file + args: ["-c", "echo Level Up Blue Team! > /data/index.html"] diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod.yaml index e1334117b6..6a5f2d82b4 100644 --- a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod.yaml +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/multi-container-pod.yaml @@ -211,4 +211,4 @@ status: podIPs: - ip: 10.244.0.3 qosClass: BestEffort - startTime: "2023-06-08T11:50:59Z" \ No newline at end of file + startTime: "2023-06-08T11:50:59Z" diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-desired.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-desired.yaml new file mode 100644 index 0000000000..29f9866592 --- /dev/null +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-desired.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: Secret +metadata: + name: test1 + namespace: default +data: + key1: "dmFsMQ==" diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-with-finalizer-desired.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-with-finalizer-desired.yaml new file mode 100644 index 0000000000..f0e64b1a60 --- /dev/null +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-with-finalizer-desired.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + finalizers: + - test-finalizer + name: test1 + namespace: default +data: + key1: "dmFsMQ==" diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-with-finalizer.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-with-finalizer.yaml new file mode 100644 index 0000000000..fa9ffc13a0 --- /dev/null +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret-with-finalizer.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +data: + key1: "dmFsMQ==" +kind: Secret +metadata: + creationTimestamp: "2023-06-07T11:08:34Z" + finalizers: + - test-finalizer + managedFields: + - apiVersion: v1 + fieldsType: FieldsV1 + fieldsV1: + f:data: + f:key1: {} + f:metadata: + f:finalizers: + .: {} + v:"test-finalizer": {} + manager: controller + operation: Apply + time: "2023-06-07T11:08:34Z" + name: test1 + namespace: default + resourceVersion: "400" + uid: 1d47f98f-ff1e-46d8-bbb5-6658ec488ae2 diff --git a/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret.yaml b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret.yaml new file mode 100644 index 0000000000..a6dc3b3c3e --- /dev/null +++ b/operator-framework-core/src/test/resources/io/javaoperatorsdk/operator/processing/dependent/kubernetes/secret.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +data: + key1: "dmFsMQ==" +kind: Secret +metadata: + creationTimestamp: "2023-06-07T11:08:34Z" + managedFields: + - apiVersion: v1 + fieldsType: FieldsV1 + fieldsV1: + f:data: + f:key1: {} + manager: controller + operation: Apply + time: "2023-06-07T11:08:34Z" + name: test1 + namespace: default + resourceVersion: "400" + uid: 1d47f98f-ff1e-46d8-bbb5-6658ec488ae2 diff --git a/operator-framework-junit5/pom.xml b/operator-framework-junit5/pom.xml index d2075303be..111fdf3b1b 100644 --- a/operator-framework-junit5/pom.xml +++ b/operator-framework-junit5/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT operator-framework-junit-5 diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java index d46987caa6..00bf7e8380 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/AbstractOperatorExtension.java @@ -25,11 +25,12 @@ import io.fabric8.kubernetes.client.utils.Utils; import io.javaoperatorsdk.operator.api.config.ConfigurationServiceOverrider; -public abstract class AbstractOperatorExtension implements HasKubernetesClient, - BeforeAllCallback, - BeforeEachCallback, - AfterAllCallback, - AfterEachCallback { +public abstract class AbstractOperatorExtension + implements HasKubernetesClient, + BeforeAllCallback, + BeforeEachCallback, + AfterAllCallback, + AfterEachCallback { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractOperatorExtension.class); public static final int MAX_NAMESPACE_NAME_LENGTH = 63; @@ -57,8 +58,8 @@ protected AbstractOperatorExtension( KubernetesClient kubernetesClient, Function namespaceNameSupplier, Function perClassNamespaceNameSupplier) { - this.kubernetesClient = kubernetesClient != null ? kubernetesClient - : new KubernetesClientBuilder().build(); + this.kubernetesClient = + kubernetesClient != null ? kubernetesClient : new KubernetesClientBuilder().build(); this.infrastructure = infrastructure; this.infrastructureTimeout = infrastructureTimeout; this.oneNamespacePerClass = oneNamespacePerClass; @@ -68,7 +69,6 @@ protected AbstractOperatorExtension( this.perClassNamespaceNameSupplier = perClassNamespaceNameSupplier; } - @Override public void beforeAll(ExtensionContext context) { beforeAllImpl(context); @@ -98,8 +98,8 @@ public String getNamespace() { return namespace; } - public NonNamespaceOperation, Resource> resources( - Class type) { + public + NonNamespaceOperation, Resource> resources(Class type) { return kubernetesClient.resources(type).inNamespace(namespace); } @@ -140,13 +140,12 @@ protected void before(ExtensionContext context) { kubernetesClient .namespaces() .resource( - new NamespaceBuilder().withMetadata(new ObjectMetaBuilder().withName(namespace).build()) + new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(namespace).build()) .build()) .serverSideApply(); - kubernetesClient - .resourceList(infrastructure) - .serverSideApply(); + kubernetesClient.resourceList(infrastructure).serverSideApply(); kubernetesClient .resourceList(infrastructure) .waitUntilReady(infrastructureTimeout.toMillis(), TimeUnit.MILLISECONDS); @@ -189,7 +188,7 @@ protected void deleteOperator() { } @SuppressWarnings("unchecked") - public static abstract class AbstractBuilder> { + public abstract static class AbstractBuilder> { protected final List infrastructure; protected Duration infrastructureTimeout; protected boolean preserveNamespaceOnError; @@ -206,21 +205,18 @@ protected AbstractBuilder() { this.infrastructure = new ArrayList<>(); this.infrastructureTimeout = Duration.ofMinutes(1); - this.preserveNamespaceOnError = Utils.getSystemPropertyOrEnvVar( - "josdk.it.preserveNamespaceOnError", - false); + this.preserveNamespaceOnError = + Utils.getSystemPropertyOrEnvVar("josdk.it.preserveNamespaceOnError", false); - this.waitForNamespaceDeletion = Utils.getSystemPropertyOrEnvVar( - "josdk.it.waitForNamespaceDeletion", - true); + this.waitForNamespaceDeletion = + Utils.getSystemPropertyOrEnvVar("josdk.it.waitForNamespaceDeletion", true); - this.oneNamespacePerClass = Utils.getSystemPropertyOrEnvVar( - "josdk.it.oneNamespacePerClass", - false); + this.oneNamespacePerClass = + Utils.getSystemPropertyOrEnvVar("josdk.it.oneNamespacePerClass", false); - this.namespaceDeleteTimeout = Utils.getSystemPropertyOrEnvVar( - "josdk.it.namespaceDeleteTimeout", - DEFAULT_NAMESPACE_DELETE_TIMEOUT); + this.namespaceDeleteTimeout = + Utils.getSystemPropertyOrEnvVar( + "josdk.it.namespaceDeleteTimeout", DEFAULT_NAMESPACE_DELETE_TIMEOUT); } public T preserveNamespaceOnError(boolean value) { diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/ClusterDeployedOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/ClusterDeployedOperatorExtension.java index b2ec773fcc..3fc49d4575 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/ClusterDeployedOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/ClusterDeployedOperatorExtension.java @@ -41,10 +41,15 @@ private ClusterDeployedOperatorExtension( KubernetesClient kubernetesClient, Function namespaceNameSupplier, Function perClassNamespaceNameSupplier) { - super(infrastructure, infrastructureTimeout, oneNamespacePerClass, + super( + infrastructure, + infrastructureTimeout, + oneNamespacePerClass, preserveNamespaceOnError, waitForNamespaceDeletion, - kubernetesClient, namespaceNameSupplier, perClassNamespaceNameSupplier); + kubernetesClient, + namespaceNameSupplier, + perClassNamespaceNameSupplier); this.operatorDeployment = operatorDeployment; this.operatorDeploymentTimeout = operatorDeploymentTimeout; } @@ -65,8 +70,9 @@ protected void before(ExtensionContext context) { final var crdSuffix = "-v1.yml"; final var kubernetesClient = getKubernetesClient(); - for (var crdFile : Objects - .requireNonNull(new File(crdPath).listFiles((ignored, name) -> name.endsWith(crdSuffix)))) { + for (var crdFile : + Objects.requireNonNull( + new File(crdPath).listFiles((ignored, name) -> name.endsWith(crdSuffix)))) { try (InputStream is = new FileInputStream(crdFile)) { final var crd = kubernetesClient.load(is); crd.createOrReplace(); @@ -81,20 +87,18 @@ protected void before(ExtensionContext context) { } LOGGER.debug("Deploying the operator into Kubernetes. Target namespace: {}", namespace); - operatorDeployment.forEach(hm -> { - hm.getMetadata().setNamespace(namespace); - if (hm.getKind().toLowerCase(Locale.ROOT).equals("clusterrolebinding")) { - var crb = (ClusterRoleBinding) hm; - for (var subject : crb.getSubjects()) { - subject.setNamespace(namespace); - } - } - }); - - kubernetesClient - .resourceList(operatorDeployment) - .inNamespace(namespace) - .createOrReplace(); + operatorDeployment.forEach( + hm -> { + hm.getMetadata().setNamespace(namespace); + if (hm.getKind().toLowerCase(Locale.ROOT).equals("clusterrolebinding")) { + var crb = (ClusterRoleBinding) hm; + for (var subject : crb.getSubjects()) { + subject.setNamespace(namespace); + } + } + }); + + kubernetesClient.resourceList(operatorDeployment).inNamespace(namespace).createOrReplace(); kubernetesClient .resourceList(operatorDeployment) .waitUntilReady(operatorDeploymentTimeout.toMillis(), TimeUnit.MILLISECONDS); @@ -123,8 +127,8 @@ public Builder withDeploymentTimeout(Duration value) { return this; } - public Builder withOperatorDeployment(List hm, - Consumer> modifications) { + public Builder withOperatorDeployment( + List hm, Consumer> modifications) { modifications.accept(hm); operatorDeployment.addAll(hm); return this; diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplier.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplier.java index 3041ed0f31..7e496ad2e2 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplier.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplier.java @@ -25,24 +25,30 @@ public class DefaultNamespaceNameSupplier implements Function MAX_NAMESPACE_NAME_LENGTH) { + if (classPart.length() + methodPart.length() + DELIMITERS_LENGTH + RANDOM_SUFFIX_LENGTH + > MAX_NAMESPACE_NAME_LENGTH) { if (classPart.length() > PART_RESERVED_NAME_LENGTH) { int classPartMaxLength = - methodPart.length() > PART_RESERVED_NAME_LENGTH ? PART_RESERVED_NAME_LENGTH + methodPart.length() > PART_RESERVED_NAME_LENGTH + ? PART_RESERVED_NAME_LENGTH : MAX_NAME_LENGTH_TOGETHER - methodPart.length(); classPart = classPart.substring(0, Math.min(classPartMaxLength, classPart.length())); } if (methodPart.length() > PART_RESERVED_NAME_LENGTH) { int methodPartMaxLength = - classPart.length() > PART_RESERVED_NAME_LENGTH ? PART_RESERVED_NAME_LENGTH + classPart.length() > PART_RESERVED_NAME_LENGTH + ? PART_RESERVED_NAME_LENGTH : MAX_NAME_LENGTH_TOGETHER - classPart.length(); methodPart = methodPart.substring(0, Math.min(methodPartMaxLength, methodPart.length())); } } - String namespace = classPart + DELIMITER + methodPart + DELIMITER + UUID.randomUUID().toString() - .substring(0, RANDOM_SUFFIX_LENGTH); + String namespace = + classPart + + DELIMITER + + methodPart + + DELIMITER + + UUID.randomUUID().toString().substring(0, RANDOM_SUFFIX_LENGTH); namespace = KubernetesResourceUtil.sanitizeName(namespace).toLowerCase(Locale.US); return namespace; } diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplier.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplier.java index b184a3fc3d..48f0ae9660 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplier.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplier.java @@ -21,7 +21,8 @@ public class DefaultPerClassNamespaceNameSupplier implements Function MAX_CLASS_NAME_LENGTH ? className.substring(0, MAX_CLASS_NAME_LENGTH) + className.length() > MAX_CLASS_NAME_LENGTH + ? className.substring(0, MAX_CLASS_NAME_LENGTH) : className; namespace += DELIMITER; namespace += UUID.randomUUID().toString().substring(0, RANDOM_SUFFIX_LENGTH); diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/InClusterCurl.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/InClusterCurl.java index dd9748383b..5ce0ace80b 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/InClusterCurl.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/InClusterCurl.java @@ -27,32 +27,42 @@ public String checkUrl(String url) { public String checkUrl(String... args) { String podName = KubernetesResourceUtil.sanitizeName("curl-" + UUID.randomUUID()); try { - Pod curlPod = client.run().inNamespace(namespace) - .withRunConfig(new RunConfigBuilder() - .withArgs(args) - .withName(podName) - .withImage("curlimages/curl:7.78.0") - .withRestartPolicy("Never") - .build()) - .done(); - await("wait-for-curl-pod-run").atMost(2, MINUTES) - .until(() -> { - String phase = - client.pods().inNamespace(namespace).withName(podName).get() - .getStatus().getPhase(); - return phase.equals("Succeeded") || phase.equals("Failed"); - }); + Pod curlPod = + client + .run() + .inNamespace(namespace) + .withRunConfig( + new RunConfigBuilder() + .withArgs(args) + .withName(podName) + .withImage("curlimages/curl:7.78.0") + .withRestartPolicy("Never") + .build()) + .done(); + await("wait-for-curl-pod-run") + .atMost(2, MINUTES) + .until( + () -> { + String phase = + client + .pods() + .inNamespace(namespace) + .withName(podName) + .get() + .getStatus() + .getPhase(); + return phase.equals("Succeeded") || phase.equals("Failed"); + }); String curlOutput = - client.pods().inNamespace(namespace) - .withName(curlPod.getMetadata().getName()).getLog(); + client.pods().inNamespace(namespace).withName(curlPod.getMetadata().getName()).getLog(); return curlOutput; } finally { client.pods().inNamespace(namespace).withName(podName).delete(); - await("wait-for-curl-pod-stop").atMost(1, MINUTES) - .until(() -> client.pods().inNamespace(namespace).withName(podName) - .get() == null); + await("wait-for-curl-pod-stop") + .atMost(1, MINUTES) + .until(() -> client.pods().inNamespace(namespace).withName(podName).get() == null); } } } diff --git a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java index 14bc096ff2..3e6ad35e52 100644 --- a/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java +++ b/operator-framework-junit5/src/main/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtension.java @@ -42,7 +42,7 @@ public class LocallyRunOperatorExtension extends AbstractOperatorExtension { private static final Logger LOGGER = LoggerFactory.getLogger(LocallyRunOperatorExtension.class); - private static final int CRD_DELETE_TIMEOUT = 1000; + private static final int CRD_DELETE_TIMEOUT = 5000; private static final Set appliedCRDs = new HashSet<>(); private static final boolean deleteCRDs = Boolean.parseBoolean(System.getProperty("testsuite.deleteCRDs", "true")); @@ -82,29 +82,31 @@ private LocallyRunOperatorExtension( this.portForwards = portForwards; this.localPortForwards = new ArrayList<>(portForwards.size()); this.additionalCustomResourceDefinitions = additionalCustomResourceDefinitions; - configurationServiceOverrider = configurationServiceOverrider != null - ? configurationServiceOverrider - .andThen(overrider -> overrider.withKubernetesClient(kubernetesClient)) - : overrider -> overrider.withKubernetesClient(kubernetesClient); + configurationServiceOverrider = + configurationServiceOverrider != null + ? configurationServiceOverrider.andThen( + overrider -> overrider.withKubernetesClient(kubernetesClient)) + : overrider -> overrider.withKubernetesClient(kubernetesClient); this.operator = new Operator(configurationServiceOverrider); this.registeredControllers = new HashMap<>(); crdMappings = getAdditionalCRDsFromFiles(additionalCrds, getKubernetesClient()); } - static Map getAdditionalCRDsFromFiles(Iterable additionalCrds, - KubernetesClient client) { + static Map getAdditionalCRDsFromFiles( + Iterable additionalCrds, KubernetesClient client) { Map crdMappings = new HashMap<>(); - additionalCrds.forEach(p -> { - try (InputStream is = new FileInputStream(p)) { - client.load(is).items().stream() - // only consider CRDs to avoid applying random resources to the cluster - .filter(CustomResourceDefinition.class::isInstance) - .map(CustomResourceDefinition.class::cast) - .forEach(crd -> crdMappings.put(crd.getMetadata().getName(), p)); - } catch (Exception e) { - throw new RuntimeException("Couldn't load CRD at " + p, e); - } - }); + additionalCrds.forEach( + p -> { + try (InputStream is = new FileInputStream(p)) { + client.load(is).items().stream() + // only consider CRDs to avoid applying random resources to the cluster + .filter(CustomResourceDefinition.class::isInstance) + .map(CustomResourceDefinition.class::cast) + .forEach(crd -> crdMappings.put(crd.getMetadata().getName(), p)); + } catch (Exception e) { + throw new RuntimeException("Couldn't load CRD at " + p, e); + } + }); return crdMappings; } @@ -164,8 +166,8 @@ private static void applyCrd(String crdString, String path, KubernetesClient cli /** * Applies the CRD associated with the specified custom resource, first checking if a CRD has been * manually specified using {@link Builder#withAdditionalCRD}, otherwise assuming that its CRD - * should be found in the standard location as explained in - * {@link LocallyRunOperatorExtension#applyCrd(String, KubernetesClient)} + * should be found in the standard location as explained in {@link + * LocallyRunOperatorExtension#applyCrd(String, KubernetesClient)} * * @param crClass the custom resource class for which we want to apply the CRD */ @@ -176,12 +178,12 @@ public void applyCrd(Class crClass) { /** * Applies the CRD associated with the specified resource type name, first checking if a CRD has * been manually specified using {@link Builder#withAdditionalCRD}, otherwise assuming that its - * CRD should be found in the standard location as explained in - * {@link LocallyRunOperatorExtension#applyCrd(String, KubernetesClient)} + * CRD should be found in the standard location as explained in {@link + * LocallyRunOperatorExtension#applyCrd(String, KubernetesClient)} * * @param resourceTypeName the resource type name associated with the CRD to be applied, - * typically, given a resource type, its name would be obtained using - * {@link ReconcilerUtils#getResourceTypeName(Class)} + * typically, given a resource type, its name would be obtained using {@link + * ReconcilerUtils#getResourceTypeName(Class)} */ public void applyCrd(String resourceTypeName) { // first attempt to use a manually defined CRD @@ -238,17 +240,23 @@ protected void before(ExtensionContext context) { final var kubernetesClient = getKubernetesClient(); for (var ref : portForwards) { - String podName = kubernetesClient.pods() - .inNamespace(ref.getNamespace()) - .withLabel(ref.getLabelKey(), ref.getLabelValue()) - .list() - .getItems() - .get(0) - .getMetadata() - .getName(); - - localPortForwards.add(kubernetesClient.pods().inNamespace(ref.getNamespace()) - .withName(podName).portForward(ref.getPort(), ref.getLocalPort())); + String podName = + kubernetesClient + .pods() + .inNamespace(ref.getNamespace()) + .withLabel(ref.getLabelKey(), ref.getLabelValue()) + .list() + .getItems() + .get(0) + .getMetadata() + .getName(); + + localPortForwards.add( + kubernetesClient + .pods() + .inNamespace(ref.getNamespace()) + .withName(podName) + .portForward(ref.getPort(), ref.getLocalPort())); } additionalCustomResourceDefinitions.forEach(this::applyCrd); @@ -278,15 +286,16 @@ protected void before(ExtensionContext context) { var registeredController = this.operator.register(ref.reconciler, oconfig.build()); registeredControllers.put(ref.reconciler, registeredController); } - crdMappings.forEach((crdName, path) -> { - final String crdString; - try { - crdString = Files.readString(Path.of(path)); - } catch (IOException e) { - throw new IllegalArgumentException("Couldn't read CRD located at " + path, e); - } - applyCrd(crdString, path, getKubernetesClient()); - }); + crdMappings.forEach( + (crdName, path) -> { + final String crdString; + try { + crdString = Files.readString(Path.of(path)); + } catch (IOException e) { + throw new IllegalArgumentException("Couldn't read CRD located at " + path, e); + } + applyCrd(crdString, path, getKubernetesClient()); + }); crdMappings.clear(); LOGGER.debug("Starting the operator locally"); @@ -334,7 +343,8 @@ private void deleteCrd(AppliedCRD appliedCRD, KubernetesClient client) { crd.withTimeoutInMillis(CRD_DELETE_TIMEOUT).delete(); LOGGER.debug("Deleted CRD with path: {}", appliedCRD.path); } catch (Exception ex) { - throw new IllegalStateException("Cannot delete CRD yaml: " + appliedCRD.path, ex); + LOGGER.warn( + "Cannot delete CRD yaml: {}. You might need to delete it manually.", appliedCRD.path, ex); } } @@ -390,8 +400,8 @@ public Builder withReconciler(Class value) { return this; } - public Builder withPortForward(String namespace, String labelKey, String labelValue, int port, - int localPort) { + public Builder withPortForward( + String namespace, String labelKey, String labelValue, int port, int localPort) { portForwards.add(new PortForwardSpec(namespace, labelKey, labelValue, port, localPort)); return this; } @@ -425,7 +435,8 @@ public LocallyRunOperatorExtension build() { waitForNamespaceDeletion, oneNamespacePerClass, kubernetesClient, - configurationServiceOverrider, namespaceNameSupplier, + configurationServiceOverrider, + namespaceNameSupplier, perClassNamespaceNameSupplier, additionalCRDs); } @@ -438,8 +449,8 @@ private static class PortForwardSpec { final int port; final int localPort; - public PortForwardSpec(String namespace, String labelKey, String labelValue, int port, - int localPort) { + public PortForwardSpec( + String namespace, String labelKey, String labelValue, int port, int localPort) { this.namespace = namespace; this.labelKey = labelKey; this.labelValue = labelValue; diff --git a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplierTest.java b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplierTest.java index cd1dce1a51..18f021c24c 100644 --- a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplierTest.java +++ b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultNamespaceNameSupplierTest.java @@ -9,7 +9,6 @@ class DefaultNamespaceNameSupplierTest { - DefaultNamespaceNameSupplier supplier = new DefaultNamespaceNameSupplier(); @Test @@ -40,17 +39,17 @@ void methodPartLonger() { void methodPartAndClassPartLonger() { String ns = supplier.apply(mockExtensionContext(LONG_CLASS_NAME, LONG_METHOD_NAME)); - assertThat(ns).startsWith(LONG_CLASS_NAME.substring(0, PART_RESERVED_NAME_LENGTH) + DELIMITER - + LONG_METHOD_NAME.substring(0, PART_RESERVED_NAME_LENGTH) - + DELIMITER); + assertThat(ns) + .startsWith( + LONG_CLASS_NAME.substring(0, PART_RESERVED_NAME_LENGTH) + + DELIMITER + + LONG_METHOD_NAME.substring(0, PART_RESERVED_NAME_LENGTH) + + DELIMITER); shortEnoughAndEndsWithRandomString(ns); } - private static void shortEnoughAndEndsWithRandomString(String ns) { assertThat(ns.length()).isLessThanOrEqualTo(MAX_NAMESPACE_NAME_LENGTH); assertThat(ns.split("-")[2]).hasSize(RANDOM_SUFFIX_LENGTH); } - - } diff --git a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplierTest.java b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplierTest.java index 40e240cbd1..5e46b15551 100644 --- a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplierTest.java +++ b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/DefaultPerClassNamespaceNameSupplierTest.java @@ -40,5 +40,4 @@ private static void shortEnoughAndEndsWithRandomString(String ns) { assertThat(ns.length()).isLessThanOrEqualTo(MAX_NAMESPACE_NAME_LENGTH); assertThat(ns.split("-")[1]).hasSize(RANDOM_SUFFIX_LENGTH); } - } diff --git a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtensionTest.java b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtensionTest.java index e5b57fa173..04ac7a91ae 100644 --- a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtensionTest.java +++ b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/LocallyRunOperatorExtensionTest.java @@ -15,9 +15,10 @@ class LocallyRunOperatorExtensionTest { void getAdditionalCRDsFromFiles() { System.out.println(Path.of("").toAbsolutePath()); System.out.println(Path.of("src/test/crd/test.crd").toAbsolutePath()); - final var crds = LocallyRunOperatorExtension.getAdditionalCRDsFromFiles( - List.of("src/test/resources/crd/test.crd", "src/test/crd/test.crd"), - new KubernetesClientBuilder().build()); + final var crds = + LocallyRunOperatorExtension.getAdditionalCRDsFromFiles( + List.of("src/test/resources/crd/test.crd", "src/test/crd/test.crd"), + new KubernetesClientBuilder().build()); assertNotNull(crds); assertEquals(2, crds.size()); assertEquals("src/test/crd/test.crd", crds.get("externals.crd.example")); diff --git a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/NamespaceNamingTestUtils.java b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/NamespaceNamingTestUtils.java index 0443d0e983..72a6c66883 100644 --- a/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/NamespaceNamingTestUtils.java +++ b/operator-framework-junit5/src/test/java/io/javaoperatorsdk/operator/junit/NamespaceNamingTestUtils.java @@ -12,12 +12,13 @@ public class NamespaceNamingTestUtils { public static final String SHORT_CLASS_NAME = Method.class.getSimpleName().toLowerCase(); public static final String SHORT_METHOD_NAME = "short"; public static final String LONG_METHOD_NAME = "longmethodnametotestifistruncatedcorrectly"; - public static final String LONG_CLASS_NAME = VeryLongClassNameForSakeOfThisTestIfItWorks.class - .getSimpleName().toLowerCase(); + public static final String LONG_CLASS_NAME = + VeryLongClassNameForSakeOfThisTestIfItWorks.class.getSimpleName().toLowerCase(); // longer then 63 public static final String VERY_LONG_CLASS_NAME = VeryVeryVeryVeryVeryVeryLongClassNameForSakeOfThisTestIfItWorks.class - .getSimpleName().toLowerCase(); + .getSimpleName() + .toLowerCase(); public static ExtensionContext mockExtensionContext(String className, String methodName) { ExtensionContext extensionContext = mock(ExtensionContext.class); @@ -41,13 +42,7 @@ public static ExtensionContext mockExtensionContext(String className, String met return extensionContext; } + public static class VeryVeryVeryVeryVeryVeryLongClassNameForSakeOfThisTestIfItWorks {} - public static class VeryVeryVeryVeryVeryVeryLongClassNameForSakeOfThisTestIfItWorks { - - } - - public static class VeryLongClassNameForSakeOfThisTestIfItWorks { - - } - + public static class VeryLongClassNameForSakeOfThisTestIfItWorks {} } diff --git a/operator-framework/pom.xml b/operator-framework/pom.xml index e2aae87401..0d60152818 100644 --- a/operator-framework/pom.xml +++ b/operator-framework/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT operator-framework @@ -125,6 +125,15 @@ + + org.apache.maven.plugins + maven-jar-plugin + + + META-INF/fabric8/*.yml + + + org.apache.maven.plugins maven-surefire-plugin diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AccumulativeMappingWriter.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AccumulativeMappingWriter.java index 6f37572e24..6e70a60f21 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AccumulativeMappingWriter.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/AccumulativeMappingWriter.java @@ -56,8 +56,8 @@ public AccumulativeMappingWriter add(String key, String value) { } /** - * Generates or override the resource file with the given path - * ({@link AccumulativeMappingWriter#resourcePath}) + * Generates or override the resource file with the given path ({@link + * AccumulativeMappingWriter#resourcePath}) */ public void flush() { PrintWriter printWriter = null; diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ClassMappingProvider.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ClassMappingProvider.java index d3559a9201..0bc2525a86 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ClassMappingProvider.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ClassMappingProvider.java @@ -25,7 +25,7 @@ static Map provide(final String resourcePath, T key, V value) { try { final var classLoader = Thread.currentThread().getContextClassLoader(); final Enumeration resourcesMetadataList = classLoader.getResources(resourcePath); - for (Iterator it = resourcesMetadataList.asIterator(); it.hasNext();) { + for (Iterator it = resourcesMetadataList.asIterator(); it.hasNext(); ) { URL url = it.next(); List classNamePairs = retrieveClassNamePairs(url); @@ -36,8 +36,7 @@ static Map provide(final String resourcePath, T key, V value) { if (classNames.length != 2) { throw new IllegalStateException( String.format( - "%s is not valid Mapping metadata, defined in %s", - clazzPair, url)); + "%s is not valid Mapping metadata, defined in %s", clazzPair, url)); } result.put( @@ -56,8 +55,7 @@ static Map provide(final String resourcePath, T key, V value) { private static List retrieveClassNamePairs(URL url) throws IOException { try (BufferedReader br = new BufferedReader(new InputStreamReader(url.openStream()))) { - return br.lines() - .collect(Collectors.toList()); + return br.lines().collect(Collectors.toList()); } } } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java index 6440ec78a0..a2df87fefb 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/ControllerConfigurationAnnotationProcessor.java @@ -70,9 +70,7 @@ private TypeParameterResolver initializeResolver(ProcessingEnvironment processin processingEnv .getTypeUtils() .getDeclaredType( - processingEnv - .getElementUtils() - .getTypeElement(Reconciler.class.getCanonicalName()), + processingEnv.getElementUtils().getTypeElement(Reconciler.class.getCanonicalName()), processingEnv.getTypeUtils().getWildcardType(null, null)); return new TypeParameterResolver(resourceControllerType, 0); } diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/RuntimeControllerMetadata.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/RuntimeControllerMetadata.java index 068d762c03..0fda410406 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/RuntimeControllerMetadata.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/RuntimeControllerMetadata.java @@ -9,7 +9,8 @@ public class RuntimeControllerMetadata { public static final String RECONCILERS_RESOURCE_PATH = "javaoperatorsdk/reconcilers"; - private static final Map, Class> controllerToCustomResourceMappings; + private static final Map, Class> + controllerToCustomResourceMappings; static { controllerToCustomResourceMappings = diff --git a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/TypeParameterResolver.java b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/TypeParameterResolver.java index 1ecf8ffb81..ef4af729f1 100644 --- a/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/TypeParameterResolver.java +++ b/operator-framework/src/main/java/io/javaoperatorsdk/operator/config/runtime/TypeParameterResolver.java @@ -30,11 +30,11 @@ public TypeParameterResolver(DeclaredType interestedClass, int interestedTypeArg /** * @param typeUtils Type utilities, During the annotation processing processingEnv.getTypeUtils() - * can be passed. + * can be passed. * @param declaredType Class or Interface which extends or implements the interestedClass, and the - * interest is getting the actual declared type is used. + * interest is getting the actual declared type is used. * @return the type of the parameter if it can be resolved from the given declareType, otherwise - * it returns null + * it returns null */ public TypeMirror resolve(Types typeUtils, DeclaredType declaredType) { final var chain = findChain(typeUtils, declaredType); @@ -138,18 +138,18 @@ private List findChainOfInterfaces(Types typeUtils, DeclaredType p var matchingInterfaces = ((TypeElement) parentInterface.asElement()) .getInterfaces().stream() - .filter(i -> typeUtils.isAssignable(i, interestedClass)) - .map(i -> (DeclaredType) i) - .collect(Collectors.toList()); + .filter(i -> typeUtils.isAssignable(i, interestedClass)) + .map(i -> (DeclaredType) i) + .collect(Collectors.toList()); while (matchingInterfaces.size() > 0) { result.addAll(matchingInterfaces); final var lastFoundInterface = matchingInterfaces.get(matchingInterfaces.size() - 1); matchingInterfaces = ((TypeElement) lastFoundInterface.asElement()) .getInterfaces().stream() - .filter(i -> typeUtils.isAssignable(i, interestedClass)) - .map(i -> (DeclaredType) i) - .collect(Collectors.toList()); + .filter(i -> typeUtils.isAssignable(i, interestedClass)) + .map(i -> (DeclaredType) i) + .collect(Collectors.toList()); } return result; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java index e0535381fa..9153ae4ff5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/CRDMappingInTestExtensionIT.java @@ -34,14 +34,25 @@ public class CRDMappingInTestExtensionIT { @Test void correctlyAppliesManuallySpecifiedCRD() { final var crdClient = client.apiextensions().v1().customResourceDefinitions(); - await().pollDelay(Duration.ofMillis(150)) - .untilAsserted(() -> { - final var actual = crdClient.withName("tests.crd.example").get(); - assertThat(actual).isNotNull(); - assertThat(actual.getSpec().getVersions().get(0).getSchema().getOpenAPIV3Schema() - .getProperties().containsKey("foo")).isTrue(); - }); - await().pollDelay(Duration.ofMillis(150)) + await() + .pollDelay(Duration.ofMillis(150)) + .untilAsserted( + () -> { + final var actual = crdClient.withName("tests.crd.example").get(); + assertThat(actual).isNotNull(); + assertThat( + actual + .getSpec() + .getVersions() + .get(0) + .getSchema() + .getOpenAPIV3Schema() + .getProperties() + .containsKey("foo")) + .isTrue(); + }); + await() + .pollDelay(Duration.ofMillis(150)) .untilAsserted( () -> assertThat(crdClient.withName("externals.crd.example").get()).isNotNull()); } @@ -49,8 +60,7 @@ void correctlyAppliesManuallySpecifiedCRD() { @Group("crd.example") @Version("v1") @Kind("Test") - private static class TestCR extends CustomResource implements Namespaced { - } + private static class TestCR extends CustomResource implements Namespaced {} @ControllerConfiguration private static class TestReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java index 0f2a976731..a4c3abfad4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/IntegrationTestConstants.java @@ -3,5 +3,4 @@ public class IntegrationTestConstants { public static final int GARBAGE_COLLECTION_TIMEOUT_SECONDS = 60; - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java index eb60907e12..80fb09095d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ConcurrencyIT.java @@ -42,9 +42,9 @@ void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedException { .untilAsserted( () -> { List items = - operator.resources(ConfigMap.class) - .withLabel( - "managedBy", TestReconciler.class.getSimpleName()) + operator + .resources(ConfigMap.class) + .withLabel("managedBy", TestReconciler.class.getSimpleName()) .list() .getItems(); assertThat(items).hasSize(NUMBER_OF_RESOURCES_CREATED); @@ -54,11 +54,9 @@ void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedException { // update some resources for (int i = 0; i < NUMBER_OF_RESOURCES_UPDATED; i++) { TestCustomResource tcr = - operator.get(TestCustomResource.class, - TestUtils.TEST_CUSTOM_RESOURCE_PREFIX + i); + operator.get(TestCustomResource.class, TestUtils.TEST_CUSTOM_RESOURCE_PREFIX + i); tcr.getSpec().setValue(i + UPDATED_SUFFIX); - operator.resources(TestCustomResource.class).resource(tcr) - .createOrReplace(); + operator.resources(TestCustomResource.class).resource(tcr).createOrReplace(); } // sleep for a short time to make variability to the test, so some updates are not // executed before delete @@ -75,9 +73,9 @@ void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedException { .untilAsserted( () -> { List items = - operator.resources(ConfigMap.class) - .withLabel( - "managedBy", TestReconciler.class.getSimpleName()) + operator + .resources(ConfigMap.class) + .withLabel("managedBy", TestReconciler.class.getSimpleName()) .list() .getItems(); // reducing configmaps to names only - better for debugging @@ -89,11 +87,8 @@ void manyResourcesGetCreatedUpdatedAndDeleted() throws InterruptedException { .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); List crs = - operator.resources(TestCustomResource.class) - .list() - .getItems(); - assertThat(crs) - .hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); + operator.resources(TestCustomResource.class).list().getItems(); + assertThat(crs).hasSize(NUMBER_OF_RESOURCES_CREATED - NUMBER_OF_RESOURCES_DELETED); }); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java index b18e9b6763..18a107d9b2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/InformerErrorHandlerStartIT.java @@ -16,22 +16,21 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; class InformerErrorHandlerStartIT { - /** - * Test showcases that the operator starts even if there is no access right for some resource. - */ + /** Test showcases that the operator starts even if there is no access right for some resource. */ @Test @Timeout(5) void operatorStart() { - KubernetesClient client = new KubernetesClientBuilder() - .withConfig(new ConfigBuilder() - .withImpersonateUsername("user-with-no-rights") - .build()) - .build(); + KubernetesClient client = + new KubernetesClientBuilder() + .withConfig(new ConfigBuilder().withImpersonateUsername("user-with-no-rights").build()) + .build(); - Operator operator = new Operator(o -> o - .withKubernetesClient(client) - .withStopOnInformerErrorDuringStartup(false) - .withCacheSyncTimeout(Duration.ofSeconds(2))); + Operator operator = + new Operator( + o -> + o.withKubernetesClient(client) + .withStopOnInformerErrorDuringStartup(false) + .withCacheSyncTimeout(Duration.ofSeconds(2))); operator.register(new ConfigMapReconciler()); operator.start(); } @@ -44,5 +43,4 @@ public UpdateControl reconcile(ConfigMap resource, Context return UpdateControl.noUpdate(); } } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java index 2523e15423..51b1f4b3d5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/LeaderElectionPermissionIT.java @@ -30,27 +30,27 @@ void operatorStopsIfNoLeaderElectionPermission() { applyRole(); applyRoleBinding(); - var client = new KubernetesClientBuilder().withConfig(new ConfigBuilder() - .withImpersonateUsername("leader-elector-stop-noaccess") - .build()).build(); + var client = + new KubernetesClientBuilder() + .withConfig( + new ConfigBuilder().withImpersonateUsername("leader-elector-stop-noaccess").build()) + .build(); - var operator = new Operator(o -> { - o.withKubernetesClient(client); - o.withLeaderElectionConfiguration( - new LeaderElectionConfiguration("lease1", "default")); - o.withStopOnInformerErrorDuringStartup(false); - }); + var operator = + new Operator( + o -> { + o.withKubernetesClient(client); + o.withLeaderElectionConfiguration( + new LeaderElectionConfiguration("lease1", "default")); + o.withStopOnInformerErrorDuringStartup(false); + }); operator.register(new TestReconciler(), o -> o.settingNamespace("default")); - OperatorException exception = assertThrows( - OperatorException.class, - operator::start); + OperatorException exception = assertThrows(OperatorException.class, operator::start); - assertThat(exception.getCause().getMessage()) - .contains(NO_PERMISSION_TO_LEASE_RESOURCE_MESSAGE); + assertThat(exception.getCause().getMessage()).contains(NO_PERMISSION_TO_LEASE_RESOURCE_MESSAGE); } - @ControllerConfiguration public static class TestReconciler implements Reconciler { @Override @@ -61,15 +61,16 @@ public UpdateControl reconcile(ConfigMap resource, Context } private void applyRoleBinding() { - var clusterRoleBinding = ReconcilerUtils - .loadYaml(RoleBinding.class, this.getClass(), - "leader-elector-stop-noaccess-role-binding.yaml"); + var clusterRoleBinding = + ReconcilerUtils.loadYaml( + RoleBinding.class, this.getClass(), "leader-elector-stop-noaccess-role-binding.yaml"); adminClient.resource(clusterRoleBinding).createOrReplace(); } private void applyRole() { - var role = ReconcilerUtils - .loadYaml(Role.class, this.getClass(), "leader-elector-stop-role-noaccess.yaml"); + var role = + ReconcilerUtils.loadYaml( + Role.class, this.getClass(), "leader-elector-stop-role-noaccess.yaml"); adminClient.resource(role).createOrReplace(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java index 3867711f7f..9d914d080c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerIT.java @@ -33,26 +33,38 @@ class BuiltInResourceCleanerIT { void cleanerIsCalledOnBuiltInResource() { var service = operator.create(testService()); - await().untilAsserted(() -> { - assertThat(operator.getReconcilerOfType(BuiltInResourceCleanerReconciler.class) - .getReconcileCount()).isPositive(); - var actualService = operator.get(Service.class, service.getMetadata().getName()); - assertThat(actualService.getMetadata().getFinalizers()).isNotEmpty(); - }); + await() + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(BuiltInResourceCleanerReconciler.class) + .getReconcileCount()) + .isPositive(); + var actualService = operator.get(Service.class, service.getMetadata().getName()); + assertThat(actualService.getMetadata().getFinalizers()).isNotEmpty(); + }); operator.delete(service); - await().untilAsserted(() -> { - assertThat(operator.getReconcilerOfType(BuiltInResourceCleanerReconciler.class) - .getCleanCount()).isPositive(); - }); + await() + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(BuiltInResourceCleanerReconciler.class) + .getCleanCount()) + .isPositive(); + }); } Service testService() { - Service service = ReconcilerUtils.loadYaml(Service.class, StandaloneDependentResourceIT.class, - "/io/javaoperatorsdk/operator/service-template.yaml"); + Service service = + ReconcilerUtils.loadYaml( + Service.class, + StandaloneDependentResourceIT.class, + "/io/javaoperatorsdk/operator/service-template.yaml"); service.getMetadata().setLabels(Map.of("builtintest", "true")); return service; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerReconciler.java index 79150eb745..5b42e795c3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/builtinresourcecleaner/BuiltInResourceCleanerReconciler.java @@ -7,16 +7,13 @@ import io.javaoperatorsdk.operator.api.reconciler.*; @ControllerConfiguration(informer = @Informer(labelSelector = "builtintest=true")) -public class BuiltInResourceCleanerReconciler - implements Reconciler, Cleaner { +public class BuiltInResourceCleanerReconciler implements Reconciler, Cleaner { private final AtomicInteger reconciled = new AtomicInteger(0); private final AtomicInteger cleaned = new AtomicInteger(0); @Override - public UpdateControl reconcile( - Service resource, - Context context) { + public UpdateControl reconcile(Service resource, Context context) { reconciled.addAndGet(1); return UpdateControl.noUpdate(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java index 1f6806aed7..67f65c64ca 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceIT.java @@ -30,7 +30,8 @@ class ChangeNamespaceIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new ChangeNamespaceTestReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new ChangeNamespaceTestReconciler()) .build(); @BeforeEach @@ -56,15 +57,14 @@ void addNewAndRemoveOldNamespaceTest() { // adding additional namespace RegisteredController registeredController = operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class); - registeredController - .changeNamespaces(Set.of(operator.getNamespace(), ADDITIONAL_TEST_NAMESPACE)); + registeredController.changeNamespaces( + Set.of(operator.getNamespace(), ADDITIONAL_TEST_NAMESPACE)); assertReconciled(reconciler, resourceInAdditionalTestNamespace); // removing a namespace registeredController.changeNamespaces(Set.of(ADDITIONAL_TEST_NAMESPACE)); - var newResourceInDefaultNamespace = operator.create(customResource(TEST_RESOURCE_NAME_3)); assertNotReconciled(reconciler, newResourceInDefaultNamespace); @@ -84,8 +84,7 @@ void changeToWatchAllNamespaces() { var registeredController = operator.getRegisteredControllerForReconcile(ChangeNamespaceTestReconciler.class); - registeredController - .changeNamespaces(Set.of(Constants.WATCH_ALL_NAMESPACES)); + registeredController.changeNamespaces(Set.of(Constants.WATCH_ALL_NAMESPACES)); assertReconciled(reconciler, resourceInAdditionalTestNamespace); @@ -97,20 +96,29 @@ void changeToWatchAllNamespaces() { assertNotReconciled(reconciler, resource2InAdditionalResource); } - private static void assertReconciled(ChangeNamespaceTestReconciler reconciler, + private static void assertReconciled( + ChangeNamespaceTestReconciler reconciler, ChangeNamespaceTestCustomResource resourceInAdditionalTestNamespace) { - await().untilAsserted( - () -> assertThat( - reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace)) - .isEqualTo(2)); + await() + .untilAsserted( + () -> + assertThat( + reconciler.numberOfResourceReconciliations( + resourceInAdditionalTestNamespace)) + .isEqualTo(2)); } - private static void assertNotReconciled(ChangeNamespaceTestReconciler reconciler, + private static void assertNotReconciled( + ChangeNamespaceTestReconciler reconciler, ChangeNamespaceTestCustomResource resourceInAdditionalTestNamespace) { - await().pollDelay(Duration.ofMillis(200)).untilAsserted( - () -> assertThat( - reconciler.numberOfResourceReconciliations(resourceInAdditionalTestNamespace)) - .isZero()); + await() + .pollDelay(Duration.ofMillis(200)) + .untilAsserted( + () -> + assertThat( + reconciler.numberOfResourceReconciliations( + resourceInAdditionalTestNamespace)) + .isZero()); } private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace() { @@ -119,7 +127,8 @@ private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace() private ChangeNamespaceTestCustomResource createResourceInAdditionalNamespace(String name) { var res = customResource(name); - return client().resources(ChangeNamespaceTestCustomResource.class) + return client() + .resources(ChangeNamespaceTestCustomResource.class) .inNamespace(ADDITIONAL_TEST_NAMESPACE) .resource(res) .create(); @@ -130,15 +139,14 @@ private KubernetesClient client() { } private Namespace additionalTestNamespace() { - return new NamespaceBuilder().withMetadata(new ObjectMetaBuilder() - .withName(ADDITIONAL_TEST_NAMESPACE) - .build()).build(); + return new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(ADDITIONAL_TEST_NAMESPACE).build()) + .build(); } private ChangeNamespaceTestCustomResource customResource(String name) { ChangeNamespaceTestCustomResource customResource = new ChangeNamespaceTestCustomResource(); - customResource.setMetadata( - new ObjectMetaBuilder().withName(name).build()); + customResource.setMetadata(new ObjectMetaBuilder().withName(name).build()); return customResource; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestCustomResource.java index eff8a4bb3f..853d10e433 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestCustomResource.java @@ -8,7 +8,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") public class ChangeNamespaceTestCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestReconciler.java index fc14b0951f..fdbac6fe00 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/changenamespace/ChangeNamespaceTestReconciler.java @@ -25,8 +25,8 @@ public List> prepareEventSourc InformerEventSource configMapES = new InformerEventSource<>( - InformerEventSourceConfiguration - .from(ConfigMap.class, ChangeNamespaceTestCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, ChangeNamespaceTestCustomResource.class) .build(), context); @@ -40,7 +40,10 @@ public UpdateControl reconcile( var actualConfigMap = context.getSecondaryResource(ConfigMap.class); if (actualConfigMap.isEmpty()) { - context.getClient().configMaps().inNamespace(primary.getMetadata().getNamespace()) + context + .getClient() + .configMaps() + .inNamespace(primary.getMetadata().getNamespace()) .resource(configMap(primary)) .create(); } @@ -51,9 +54,10 @@ public UpdateControl reconcile( increaseNumberOfResourceExecutions(primary); var statusPatchResource = new ChangeNamespaceTestCustomResource(); - statusPatchResource.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) + statusPatchResource.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) .build()); statusPatchResource.setStatus(new ChangeNamespaceTestCustomResourceStatus()); var statusUpdates = primary.getStatus().getNumberOfStatusUpdates(); @@ -73,9 +77,11 @@ public int numberOfResourceReconciliations(ChangeNamespaceTestCustomResource pri private ConfigMap configMap(ChangeNamespaceTestCustomResource primary) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder().withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of("data", primary.getMetadata().getName())); configMap.addOwnerReference(primary); return configMap; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerCustomResource.java index 4367d6d089..d4f8aa6b0c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerCustomResource.java @@ -11,7 +11,5 @@ @Version("v1") @Kind("CleanerForReconcilerCustomResource") @ShortNames("cfr") -public class CleanerForReconcilerCustomResource - extends CustomResource - implements Namespaced { -} +public class CleanerForReconcilerCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java index 04ea2f9646..6ccb34f0e3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerIT.java @@ -15,10 +15,10 @@ class CleanerForReconcilerIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new CleanerForReconcilerTestReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new CleanerForReconcilerTestReconciler()) .build(); - @Test void addsFinalizerAndCallsCleanupIfCleanerImplemented() { CleanerForReconcilerTestReconciler reconciler = @@ -28,13 +28,21 @@ void addsFinalizerAndCallsCleanupIfCleanerImplemented() { var testResource = createTestResource(); operator.create(testResource); - await().until(() -> !operator.get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) - .getMetadata().getFinalizers().isEmpty()); + await() + .until( + () -> + !operator + .get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) + .getMetadata() + .getFinalizers() + .isEmpty()); operator.delete(testResource); - await().until( - () -> operator.get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) == null); + await() + .until( + () -> + operator.get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) == null); assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); assertThat(reconciler.getNumberOfCleanupExecutions()).isEqualTo(1); @@ -49,17 +57,26 @@ void reSchedulesCleanupIfInstructed() { var testResource = createTestResource(); operator.create(testResource); - await().until(() -> !operator.get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) - .getMetadata().getFinalizers().isEmpty()); + await() + .until( + () -> + !operator + .get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) + .getMetadata() + .getFinalizers() + .isEmpty()); operator.delete(testResource); - await().untilAsserted( - () -> assertThat(reconciler.getNumberOfCleanupExecutions()).isGreaterThan(5)); + await() + .untilAsserted( + () -> assertThat(reconciler.getNumberOfCleanupExecutions()).isGreaterThan(5)); reconciler.setReScheduleCleanup(false); - await().until( - () -> operator.get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) == null); + await() + .until( + () -> + operator.get(CleanerForReconcilerCustomResource.class, TEST_RESOURCE_NAME) == null); } private CleanerForReconcilerCustomResource createTestResource() { @@ -68,5 +85,4 @@ private CleanerForReconcilerCustomResource createTestResource() { cr.getMetadata().setName(TEST_RESOURCE_NAME); return cr; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerTestReconciler.java index 8bb5fa1062..4c19e41952 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanerforreconciler/CleanerForReconcilerTestReconciler.java @@ -8,8 +8,8 @@ @ControllerConfiguration public class CleanerForReconcilerTestReconciler implements Reconciler, - Cleaner, - TestExecutionInfoProvider { + Cleaner, + TestExecutionInfoProvider { public static final int RESCHEDULE_DELAY = 150; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -34,7 +34,8 @@ public int getNumberOfCleanupExecutions() { } @Override - public DeleteControl cleanup(CleanerForReconcilerCustomResource resource, + public DeleteControl cleanup( + CleanerForReconcilerCustomResource resource, Context context) { if (reScheduleCleanup) { numberOfCleanupExecutions.addAndGet(1); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictCustomResource.java index 82f51c1f83..58a118761d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictCustomResource.java @@ -12,7 +12,4 @@ @Kind("CleanupConflictCustomResource") @ShortNames("ccc") public class CleanupConflictCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java index 4d79d6d1d3..19ef6df9a0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictIT.java @@ -19,8 +19,7 @@ class CleanupConflictIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new CleanupConflictReconciler()) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(new CleanupConflictReconciler()).build(); @Test void cleanupRemovesFinalizerWithoutConflict() throws InterruptedException { @@ -28,9 +27,14 @@ void cleanupRemovesFinalizerWithoutConflict() throws InterruptedException { testResource.addFinalizer(ADDITIONAL_FINALIZER); testResource = operator.create(testResource); - await().untilAsserted( - () -> assertThat(operator.getReconcilerOfType(CleanupConflictReconciler.class) - .getNumberReconcileExecutions()).isEqualTo(1)); + await() + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(CleanupConflictReconciler.class) + .getNumberReconcileExecutions()) + .isEqualTo(1)); operator.delete(testResource); Thread.sleep(WAIT_TIME / 2); @@ -39,9 +43,15 @@ void cleanupRemovesFinalizerWithoutConflict() throws InterruptedException { testResource.getMetadata().setResourceVersion(null); operator.replace(testResource); - await().pollDelay(Duration.ofMillis(WAIT_TIME * 2)).untilAsserted( - () -> assertThat(operator.getReconcilerOfType(CleanupConflictReconciler.class) - .getNumberOfCleanupExecutions()).isEqualTo(1)); + await() + .pollDelay(Duration.ofMillis(WAIT_TIME * 2)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(CleanupConflictReconciler.class) + .getNumberOfCleanupExecutions()) + .isEqualTo(1)); } private CleanupConflictCustomResource createTestResource() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictReconciler.java index 1c66fde159..7f2bb64896 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/cleanupconflict/CleanupConflictReconciler.java @@ -14,15 +14,14 @@ public class CleanupConflictReconciler @Override public UpdateControl reconcile( - CleanupConflictCustomResource resource, - Context context) { + CleanupConflictCustomResource resource, Context context) { numberReconcileExecutions.addAndGet(1); return UpdateControl.noUpdate(); } @Override - public DeleteControl cleanup(CleanupConflictCustomResource resource, - Context context) { + public DeleteControl cleanup( + CleanupConflictCustomResource resource, Context context) { numberOfCleanupExecutions.addAndGet(1); try { Thread.sleep(WAIT_TIME); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResource.java index 0e26316cfb..11250ed2b3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResource.java @@ -9,7 +9,4 @@ @Version("v1") @ShortNames("csc") public class ClusterScopedCustomResource - extends CustomResource { - - -} + extends CustomResource {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResourceReconciler.java index dd3316f8fc..e8b12d6eba 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedCustomResourceReconciler.java @@ -32,11 +32,13 @@ public UpdateControl reconcile( var optionalConfigMap = context.getSecondaryResource(ConfigMap.class); final var client = context.getClient(); - optionalConfigMap.ifPresentOrElse(cm -> { - if (!resource.getSpec().getData().equals(cm.getData().get(DATA_KEY))) { - client.configMaps().resource(desired(resource)).replace(); - } - }, () -> client.configMaps().resource(desired(resource)).create()); + optionalConfigMap.ifPresentOrElse( + cm -> { + if (!resource.getSpec().getData().equals(cm.getData().get(DATA_KEY))) { + client.configMaps().resource(desired(resource)).replace(); + } + }, + () -> client.configMaps().resource(desired(resource)).create()); resource.setStatus(new ClusterScopedCustomResourceStatus()); resource.getStatus().setCreated(true); @@ -44,14 +46,16 @@ public UpdateControl reconcile( } private ConfigMap desired(ClusterScopedCustomResource resource) { - var cm = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getSpec().getTargetNamespace()) - .withLabels(Map.of(TEST_LABEL_KEY, TEST_LABEL_VALUE)) - .build()) - .withData(Map.of(DATA_KEY, resource.getSpec().getData())) - .build(); + var cm = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getSpec().getTargetNamespace()) + .withLabels(Map.of(TEST_LABEL_KEY, TEST_LABEL_VALUE)) + .build()) + .withData(Map.of(DATA_KEY, resource.getSpec().getData())) + .build(); cm.addOwnerReference(resource); return cm; } @@ -59,13 +63,15 @@ private ConfigMap desired(ClusterScopedCustomResource resource) { @Override public List> prepareEventSources( EventSourceContext context) { - var ies = new InformerEventSource<>( - InformerEventSourceConfiguration.from(ConfigMap.class, ClusterScopedCustomResource.class) - .withSecondaryToPrimaryMapper( - Mappers.fromOwnerReferences(context.getPrimaryResourceClass(), true)) - .withLabelSelector(TEST_LABEL_KEY + "=" + TEST_LABEL_VALUE) - .build(), - context); + var ies = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, ClusterScopedCustomResource.class) + .withSecondaryToPrimaryMapper( + Mappers.fromOwnerReferences(context.getPrimaryResourceClass(), true)) + .withLabelSelector(TEST_LABEL_KEY + "=" + TEST_LABEL_VALUE) + .build(), + context); return List.of(ies); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java index e923084425..4c92fbada7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/clusterscopedresource/ClusterScopedResourceIT.java @@ -18,50 +18,53 @@ class ClusterScopedResourceIT { public static final String TEST_NAME = "test1"; public static final String INITIAL_DATA = "initialData"; public static final String UPDATED_DATA = "updatedData"; + @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() - .withReconciler(new ClusterScopedCustomResourceReconciler()).build(); + .withReconciler(new ClusterScopedCustomResourceReconciler()) + .build(); @Test void crudOperationOnClusterScopedCustomResource() { var resource = operator.create(testResource()); - await().untilAsserted(() -> { - var res = operator.get(ClusterScopedCustomResource.class, TEST_NAME); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getCreated()).isTrue(); - var cm = operator.get(ConfigMap.class, TEST_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData().get(ClusterScopedCustomResourceReconciler.DATA_KEY)) - .isEqualTo(INITIAL_DATA); - }); + await() + .untilAsserted( + () -> { + var res = operator.get(ClusterScopedCustomResource.class, TEST_NAME); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getCreated()).isTrue(); + var cm = operator.get(ConfigMap.class, TEST_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData().get(ClusterScopedCustomResourceReconciler.DATA_KEY)) + .isEqualTo(INITIAL_DATA); + }); resource.getSpec().setData(UPDATED_DATA); operator.replace(resource); - await().untilAsserted(() -> { - var cm = operator.get(ConfigMap.class, TEST_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData().get(ClusterScopedCustomResourceReconciler.DATA_KEY)) - .isEqualTo(UPDATED_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = operator.get(ConfigMap.class, TEST_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData().get(ClusterScopedCustomResourceReconciler.DATA_KEY)) + .isEqualTo(UPDATED_DATA); + }); operator.delete(resource); - await().atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)) + await() + .atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)) .untilAsserted(() -> assertThat(operator.get(ConfigMap.class, TEST_NAME)).isNull()); } - ClusterScopedCustomResource testResource() { var res = new ClusterScopedCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_NAME).build()); res.setSpec(new ClusterScopedCustomResourceSpec()); res.getSpec().setTargetNamespace(operator.getNamespace()); res.getSpec().setData(INITIAL_DATA); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalCustomResource.java new file mode 100644 index 0000000000..3f9b61d1a8 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalCustomResource.java @@ -0,0 +1,13 @@ +package io.javaoperatorsdk.operator.baseapi.concurrentfinalizerremoval; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("cfr") +public class ConcurrentFinalizerRemovalCustomResource + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java new file mode 100644 index 0000000000..da166ce2c1 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalIT.java @@ -0,0 +1,68 @@ +package io.javaoperatorsdk.operator.baseapi.concurrentfinalizerremoval; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; +import io.javaoperatorsdk.operator.processing.retry.GenericRetry; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class ConcurrentFinalizerRemovalIT { + + private static final Logger log = LoggerFactory.getLogger(ConcurrentFinalizerRemovalIT.class); + public static final String TEST_RESOURCE_NAME = "test"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + // should work without a retry, thus not retry the whole reconciliation but to retry + // finalizer removal only. + .withReconciler( + new ConcurrentFinalizerRemovalReconciler1(), + o -> + o.withRetry(GenericRetry.noRetry()).withFinalizer("reconciler1.sample/finalizer")) + .withReconciler( + new ConcurrentFinalizerRemovalReconciler2(), + o -> + o.withRetry(GenericRetry.noRetry()).withFinalizer("reconciler2.sample/finalizer")) + .build(); + + @Test + void concurrentFinalizerRemoval() { + for (int i = 0; i < 10; i++) { + var resource = extension.create(createResource()); + await() + .untilAsserted( + () -> { + var res = + extension.get( + ConcurrentFinalizerRemovalCustomResource.class, TEST_RESOURCE_NAME); + assertThat(res.getMetadata().getFinalizers()).hasSize(2); + }); + resource.getMetadata().setResourceVersion(null); + extension.delete(resource); + + await() + .untilAsserted( + () -> { + var res = + extension.get( + ConcurrentFinalizerRemovalCustomResource.class, TEST_RESOURCE_NAME); + assertThat(res).isNull(); + }); + } + } + + public ConcurrentFinalizerRemovalCustomResource createResource() { + ConcurrentFinalizerRemovalCustomResource res = new ConcurrentFinalizerRemovalCustomResource(); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); + res.setSpec(new ConcurrentFinalizerRemovalSpec()); + res.getSpec().setNumber(0); + return res; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalReconciler1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalReconciler1.java new file mode 100644 index 0000000000..b789255cb5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalReconciler1.java @@ -0,0 +1,29 @@ +package io.javaoperatorsdk.operator.baseapi.concurrentfinalizerremoval; + +import io.javaoperatorsdk.operator.api.reconciler.Cleaner; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; + +@ControllerConfiguration +public class ConcurrentFinalizerRemovalReconciler1 + implements Reconciler, + Cleaner { + + @Override + public UpdateControl reconcile( + ConcurrentFinalizerRemovalCustomResource resource, + Context context) { + return UpdateControl.noUpdate(); + } + + @Override + public DeleteControl cleanup( + ConcurrentFinalizerRemovalCustomResource resource, + Context context) + throws Exception { + return DeleteControl.defaultDelete(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalReconciler2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalReconciler2.java new file mode 100644 index 0000000000..0b8993a8f5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalReconciler2.java @@ -0,0 +1,29 @@ +package io.javaoperatorsdk.operator.baseapi.concurrentfinalizerremoval; + +import io.javaoperatorsdk.operator.api.reconciler.Cleaner; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.DeleteControl; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; + +@ControllerConfiguration +public class ConcurrentFinalizerRemovalReconciler2 + implements Reconciler, + Cleaner { + + @Override + public UpdateControl reconcile( + ConcurrentFinalizerRemovalCustomResource resource, + Context context) { + return UpdateControl.noUpdate(); + } + + @Override + public DeleteControl cleanup( + ConcurrentFinalizerRemovalCustomResource resource, + Context context) + throws Exception { + return DeleteControl.defaultDelete(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalSpec.java new file mode 100644 index 0000000000..d740721f30 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/concurrentfinalizerremoval/ConcurrentFinalizerRemovalSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.baseapi.concurrentfinalizerremoval; + +public class ConcurrentFinalizerRemovalSpec { + + private int number; + + public int getNumber() { + return number; + } + + public ConcurrentFinalizerRemovalSpec setNumber(int number) { + this.number = number; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResource.java index 31ef818cd6..9b56bbc650 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResource.java @@ -10,8 +10,5 @@ @Version("v1") @ShortNames("cue") public class CreateUpdateEventFilterTestCustomResource - extends - CustomResource - implements Namespaced { - -} + extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResourceSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResourceSpec.java index 4575e68db0..fb38fa9f39 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResourceSpec.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestCustomResourceSpec.java @@ -12,5 +12,4 @@ public CreateUpdateEventFilterTestCustomResourceSpec setValue(String value) { this.value = value; return this; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java index 4ec65f5673..664e75a950 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateEventFilterTestReconciler.java @@ -33,7 +33,8 @@ public UpdateControl reconcile( numberOfExecutions.incrementAndGet(); ConfigMap configMap = - context.getClient() + context + .getClient() .configMaps() .inNamespace(resource.getMetadata().getNamespace()) .withName(resource.getMetadata().getName()) @@ -70,14 +71,12 @@ private ConfigMap createConfigMap(CreateUpdateEventFilterTestCustomResource reso public List> prepareEventSources( EventSourceContext context) { InformerEventSourceConfiguration informerConfiguration = - InformerEventSourceConfiguration - .from(ConfigMap.class, CreateUpdateEventFilterTestCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, CreateUpdateEventFilterTestCustomResource.class) .withLabelSelector("integrationtest = " + this.getClass().getSimpleName()) .build(); - final var informerEventSource = - new InformerEventSource<>( - informerConfiguration, context); + final var informerEventSource = new InformerEventSource<>(informerConfiguration, context); this.configMapDR.setEventSource(informerEventSource); return List.of(informerEventSource); @@ -88,8 +87,8 @@ public int getNumberOfExecutions() { } private static final class DirectConfigMapDependentResource - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, CreateUpdateEventFilterTestCustomResource> { private ConfigMap desired; @@ -98,14 +97,17 @@ private DirectConfigMapDependentResource(Class resourceType) { } @Override - protected ConfigMap desired(CreateUpdateEventFilterTestCustomResource primary, + protected ConfigMap desired( + CreateUpdateEventFilterTestCustomResource primary, Context context) { return desired; } @Override public void setEventSource( - io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource eventSource) { + io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource< + ConfigMap, CreateUpdateEventFilterTestCustomResource> + eventSource) { super.setEventSource(eventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java index 9c69087e74..2d9a7db573 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/CreateUpdateInformerEventSourceEventFilterIT.java @@ -25,36 +25,40 @@ class CreateUpdateInformerEventSourceEventFilterIT { void updateEventNotReceivedAfterCreateOrUpdate() { CreateUpdateEventFilterTestCustomResource resource = CreateUpdateInformerEventSourceEventFilterIT.prepareTestResource(); - var createdResource = - operator.create(resource); + var createdResource = operator.create(resource); assertData(operator, createdResource, 1, 1); CreateUpdateEventFilterTestCustomResource actualCreatedResource = - operator.get(CreateUpdateEventFilterTestCustomResource.class, - resource.getMetadata().getName()); + operator.get( + CreateUpdateEventFilterTestCustomResource.class, resource.getMetadata().getName()); actualCreatedResource.getSpec().setValue("2"); operator.replace(actualCreatedResource); assertData(operator, actualCreatedResource, 2, 2); } - static void assertData(LocallyRunOperatorExtension operator, - CreateUpdateEventFilterTestCustomResource resource, int minExecutions, int maxExecutions) { + static void assertData( + LocallyRunOperatorExtension operator, + CreateUpdateEventFilterTestCustomResource resource, + int minExecutions, + int maxExecutions) { await() .atMost(Duration.ofSeconds(1)) - .until(() -> { - var cm = operator.get(ConfigMap.class, resource.getMetadata().getName()); - if (cm == null) { - return false; - } - return cm.getData() - .get(CONFIG_MAP_TEST_DATA_KEY) - .equals(resource.getSpec().getValue()); - }); + .until( + () -> { + var cm = operator.get(ConfigMap.class, resource.getMetadata().getName()); + if (cm == null) { + return false; + } + return cm.getData() + .get(CONFIG_MAP_TEST_DATA_KEY) + .equals(resource.getSpec().getValue()); + }); - int numberOfExecutions = ((CreateUpdateEventFilterTestReconciler) operator.getFirstReconciler()) - .getNumberOfExecutions(); + int numberOfExecutions = + ((CreateUpdateEventFilterTestReconciler) operator.getFirstReconciler()) + .getNumberOfExecutions(); assertThat(numberOfExecutions).isGreaterThanOrEqualTo(minExecutions); assertThat(numberOfExecutions).isLessThanOrEqualTo(maxExecutions); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java index 209c5fd15c..b5554493ee 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/createupdateeventfilter/PreviousAnnotationDisabledIT.java @@ -19,18 +19,16 @@ class PreviousAnnotationDisabledIT { void updateEventReceivedAfterCreateOrUpdate() { CreateUpdateEventFilterTestCustomResource resource = CreateUpdateInformerEventSourceEventFilterIT.prepareTestResource(); - var createdResource = - operator.create(resource); + var createdResource = operator.create(resource); CreateUpdateInformerEventSourceEventFilterIT.assertData(operator, createdResource, 1, 2); CreateUpdateEventFilterTestCustomResource actualCreatedResource = - operator.get(CreateUpdateEventFilterTestCustomResource.class, - resource.getMetadata().getName()); + operator.get( + CreateUpdateEventFilterTestCustomResource.class, resource.getMetadata().getName()); actualCreatedResource.getSpec().setValue("2"); operator.replace(actualCreatedResource); CreateUpdateInformerEventSourceEventFilterIT.assertData(operator, actualCreatedResource, 2, 4); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/DeploymentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/DeploymentReconciler.java index ef04ca644a..27ea60852b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/DeploymentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/DeploymentReconciler.java @@ -18,8 +18,7 @@ @ControllerConfiguration( informer = @Informer(labelSelector = "test=KubernetesResourceStatusUpdateIT")) -public class DeploymentReconciler - implements Reconciler, TestExecutionInfoProvider { +public class DeploymentReconciler implements Reconciler, TestExecutionInfoProvider { public static final String STATUS_MESSAGE = "Reconciled by DeploymentReconciler"; @@ -27,8 +26,7 @@ public class DeploymentReconciler private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override - public UpdateControl reconcile( - Deployment resource, Context context) { + public UpdateControl reconcile(Deployment resource, Context context) { log.info("Reconcile deployment: {}", resource); numberOfExecutions.incrementAndGet(); @@ -42,15 +40,15 @@ public UpdateControl reconcile( var condition = conditions.stream().filter(c -> c.getMessage().equals(STATUS_MESSAGE)).findFirst(); if (condition.isEmpty()) { - conditions.add(new DeploymentCondition(null, null, STATUS_MESSAGE, null, - "unknown", "DeploymentReconciler")); + conditions.add( + new DeploymentCondition( + null, null, STATUS_MESSAGE, null, "unknown", "DeploymentReconciler")); return UpdateControl.patchStatus(resource); } else { return UpdateControl.noUpdate(); } } - @Override public int getNumberOfExecutions() { return numberOfExecutions.get(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java index dff60df5cd..5fe9bfa939 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/deployment/KubernetesResourceStatusUpdateIT.java @@ -32,18 +32,22 @@ class KubernetesResourceStatusUpdateIT { @Test void testReconciliationOfNonCustomResourceAndStatusUpdate() { var deployment = operator.create(testDeployment()); - await().atMost(120, TimeUnit.SECONDS).untilAsserted(() -> { - var d = operator.get(Deployment.class, deployment.getMetadata().getName()); - assertThat(d.getStatus()).isNotNull(); - assertThat(d.getStatus().getConditions()).isNotNull(); - // wait until the pod is ready, if not this is causing some test stability issues with - // namespace cleanup in k8s version 1.22 - assertThat(d.getStatus().getReadyReplicas()).isGreaterThanOrEqualTo(1); - assertThat( - d.getStatus().getConditions().stream().filter(c -> c.getMessage().equals(STATUS_MESSAGE)) - .count()) - .isEqualTo(1); - }); + await() + .atMost(120, TimeUnit.SECONDS) + .untilAsserted( + () -> { + var d = operator.get(Deployment.class, deployment.getMetadata().getName()); + assertThat(d.getStatus()).isNotNull(); + assertThat(d.getStatus().getConditions()).isNotNull(); + // wait until the pod is ready, if not this is causing some test stability issues with + // namespace cleanup in k8s version 1.22 + assertThat(d.getStatus().getReadyReplicas()).isGreaterThanOrEqualTo(1); + assertThat( + d.getStatus().getConditions().stream() + .filter(c -> c.getMessage().equals(STATUS_MESSAGE)) + .count()) + .isEqualTo(1); + }); } private Deployment testDeployment() { @@ -51,10 +55,7 @@ private Deployment testDeployment() { Map labels = new HashMap<>(); labels.put("test", "KubernetesResourceStatusUpdateIT"); resource.setMetadata( - new ObjectMetaBuilder() - .withName("test-deployment") - .withLabels(labels) - .build()); + new ObjectMetaBuilder().withName("test-deployment").withLabels(labels).build()); DeploymentSpec spec = new DeploymentSpec(); resource.setSpec(spec); spec.setReplicas(1); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationCustomResource.java index 339f14434a..df1273c4d9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationCustomResource.java @@ -9,8 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("dger") -public class DynamicGenericEventSourceRegistrationCustomResource - extends CustomResource - implements Namespaced { - -} +public class DynamicGenericEventSourceRegistrationCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java index b00573e334..5c05850b4a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationIT.java @@ -16,6 +16,7 @@ class DynamicGenericEventSourceRegistrationIT { public static final String TEST_RESOURCE_NAME = "test1"; + @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() @@ -28,12 +29,15 @@ void registersEventSourcesDynamically() { extension.getReconcilerOfType(DynamicGenericEventSourceRegistrationReconciler.class); extension.create(testResource()); - await().pollDelay(Duration.ofMillis(150)).untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - var secret = extension.get(Secret.class, TEST_RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(secret).isNotNull(); - }); + await() + .pollDelay(Duration.ofMillis(150)) + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + var secret = extension.get(Secret.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(secret).isNotNull(); + }); var executions = reconciler.getNumberOfExecutions(); assertThat(reconciler.getNumberOfEventSources()).isEqualTo(2); assertThat(executions).isLessThanOrEqualTo(3); @@ -43,18 +47,17 @@ void registersEventSourcesDynamically() { extension.replace(cm); // triggers the reconciliation - await().untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions() - executions).isEqualTo(2); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions() - executions).isEqualTo(2); + }); assertThat(reconciler.getNumberOfEventSources()).isEqualTo(2); } - DynamicGenericEventSourceRegistrationCustomResource testResource() { var res = new DynamicGenericEventSourceRegistrationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return res; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java index 0abf91f32f..1d018e55dc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/dynamicgenericeventsourceregistration/DynamicGenericEventSourceRegistrationReconciler.java @@ -28,51 +28,59 @@ public UpdateControl reconc numberOfExecutions.addAndGet(1); - context.eventSourceRetriever().dynamicallyRegisterEventSource( - genericInformerFor(ConfigMap.class, context)); - context.eventSourceRetriever().dynamicallyRegisterEventSource( - genericInformerFor(Secret.class, context)); + context + .eventSourceRetriever() + .dynamicallyRegisterEventSource(genericInformerFor(ConfigMap.class, context)); + context + .eventSourceRetriever() + .dynamicallyRegisterEventSource(genericInformerFor(Secret.class, context)); context.getClient().resource(secret(primary)).createOr(NonDeletingOperation::update); context.getClient().resource(configMap(primary)).createOr(NonDeletingOperation::update); - numberOfEventSources.set(context.eventSourceRetriever() - .getEventSourcesFor(GenericKubernetesResource.class).size()); + numberOfEventSources.set( + context.eventSourceRetriever().getEventSourcesFor(GenericKubernetesResource.class).size()); return UpdateControl.noUpdate(); } private Secret secret(DynamicGenericEventSourceRegistrationCustomResource primary) { - var secret = new SecretBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) - .withData(Map.of("key", Base64.getEncoder().encodeToString("val".getBytes()))) - .build(); + var secret = + new SecretBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withData(Map.of("key", Base64.getEncoder().encodeToString("val".getBytes()))) + .build(); secret.addOwnerReference(primary); return secret; } private ConfigMap configMap(DynamicGenericEventSourceRegistrationCustomResource primary) { - var cm = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) - .withData(Map.of("key", "val")) - .build(); + var cm = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withData(Map.of("key", "val")) + .build(); cm.addOwnerReference(primary); return cm; } - private InformerEventSource genericInformerFor( - Class clazz, - Context context) { + private InformerEventSource< + GenericKubernetesResource, DynamicGenericEventSourceRegistrationCustomResource> + genericInformerFor( + Class clazz, + Context context) { return new InformerEventSource<>( - InformerEventSourceConfiguration - .from(GroupVersionKind.gvkFor(clazz), + InformerEventSourceConfiguration.from( + GroupVersionKind.gvkFor(clazz), DynamicGenericEventSourceRegistrationCustomResource.class) .withName(clazz.getSimpleName()) .build(), diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java index 6e69a50e82..b888143d2d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerIT.java @@ -20,14 +20,13 @@ class ErrorStatusHandlerIT { @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() - .withReconciler(reconciler, - new GenericRetry().setMaxAttempts(MAX_RETRY_ATTEMPTS).withLinearRetry()) + .withReconciler( + reconciler, new GenericRetry().setMaxAttempts(MAX_RETRY_ATTEMPTS).withLinearRetry()) .build(); @Test void testErrorMessageSetEventually() { - ErrorStatusHandlerTestCustomResource resource = - operator.create(createCustomResource()); + ErrorStatusHandlerTestCustomResource resource = operator.create(createCustomResource()); await() .atMost(10, TimeUnit.SECONDS) @@ -35,8 +34,8 @@ void testErrorMessageSetEventually() { .untilAsserted( () -> { ErrorStatusHandlerTestCustomResource res = - operator.get(ErrorStatusHandlerTestCustomResource.class, - resource.getMetadata().getName()); + operator.get( + ErrorStatusHandlerTestCustomResource.class, resource.getMetadata().getName()); assertThat(res.getStatus()).isNotNull(); for (int i = 0; i < MAX_RETRY_ATTEMPTS + 1; i++) { assertThat(res.getStatus().getMessages()) @@ -54,5 +53,4 @@ public ErrorStatusHandlerTestCustomResource createCustomResource() { .build()); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestCustomResource.java index 0606115f5f..2e4664a5de 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestCustomResource.java @@ -13,5 +13,4 @@ @ShortNames("esh") public class ErrorStatusHandlerTestCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestReconciler.java index e51286ef5f..d917faab93 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/errorstatushandler/ErrorStatusHandlerTestReconciler.java @@ -25,7 +25,10 @@ public UpdateControl reconcile( if (context.getRetryInfo().isPresent()) { retryAttempt = context.getRetryInfo().get().getAttemptCount(); } - log.info("Number of execution: {} retry attempt: {} , resource: {}", number, retryAttempt, + log.info( + "Number of execution: {} retry attempt: {} , resource: {}", + number, + retryAttempt, resource); throw new IllegalStateException(); } @@ -45,10 +48,13 @@ public int getNumberOfExecutions() { @Override public ErrorStatusUpdateControl updateErrorStatus( ErrorStatusHandlerTestCustomResource resource, - Context context, Exception e) { + Context context, + Exception e) { log.info("Setting status."); ensureStatusExists(resource); - resource.getStatus().getMessages() + resource + .getStatus() + .getMessages() .add(ERROR_STATUS_MESSAGE + context.getRetryInfo().orElseThrow().getAttemptCount()); return ErrorStatusUpdateControl.patchStatus(resource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java index 227adaeadc..eaa881709b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceIT.java @@ -16,7 +16,8 @@ class EventSourceIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(EventSourceTestCustomReconciler.class) + LocallyRunOperatorExtension.builder() + .withReconciler(EventSourceTestCustomReconciler.class) .build(); @Test @@ -27,11 +28,9 @@ void receivingPeriodicEvents() { await() .atMost(5, TimeUnit.SECONDS) - .pollInterval( - EventSourceTestCustomReconciler.TIMER_PERIOD / 2, TimeUnit.MILLISECONDS) + .pollInterval(EventSourceTestCustomReconciler.TIMER_PERIOD / 2, TimeUnit.MILLISECONDS) .untilAsserted( - () -> assertThat(TestUtils.getNumberOfExecutions(operator)) - .isGreaterThanOrEqualTo(4)); + () -> assertThat(TestUtils.getNumberOfExecutions(operator)).isGreaterThanOrEqualTo(4)); } public EventSourceTestCustomResource createTestCustomResource(String id) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomReconciler.java index 59f27e5723..2993a29103 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomReconciler.java @@ -7,8 +7,7 @@ @ControllerConfiguration public class EventSourceTestCustomReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { public static final int TIMER_PERIOD = 500; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResource.java index 2493fc138b..b5f50df476 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResource.java @@ -13,5 +13,4 @@ @ShortNames("es") public class EventSourceTestCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResourceStatus.java index bb4c9cd5f1..c602dd5db4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/event/EventSourceTestCustomResourceStatus.java @@ -14,6 +14,7 @@ public EventSourceTestCustomResourceStatus setState(State state) { } public enum State { - SUCCESS, ERROR + SUCCESS, + ERROR } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java index 634873ec86..1e51ad5ab0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterIT.java @@ -19,52 +19,71 @@ class FilterIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(FilterTestReconciler.class) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(FilterTestReconciler.class).build(); @Test void filtersControllerResourceUpdate() { var res = operator.create(createResource()); // One for CR create event other for ConfigMap event - await().pollDelay(Duration.ofMillis(POLL_DELAY)) - .untilAsserted(() -> assertThat(operator.getReconcilerOfType(FilterTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(2)); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(FilterTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(2)); res.getSpec().setValue(FilterTestReconciler.CUSTOM_RESOURCE_FILTER_VALUE); operator.replace(res); // not more reconciliation with the filtered value - await().pollDelay(Duration.ofMillis(POLL_DELAY)) - .untilAsserted(() -> assertThat(operator.getReconcilerOfType(FilterTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(2)); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(FilterTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(2)); } @Test void filtersSecondaryResourceUpdate() { var res = operator.create(createResource()); // One for CR create event other for ConfigMap event - await().pollDelay(Duration.ofMillis(POLL_DELAY)) - .untilAsserted(() -> assertThat(operator.getReconcilerOfType(FilterTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(2)); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(FilterTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(2)); res.getSpec().setValue(CONFIG_MAP_FILTER_VALUE); operator.replace(res); // the CM event filtered out - await().pollDelay(Duration.ofMillis(POLL_DELAY)) - .untilAsserted(() -> assertThat(operator.getReconcilerOfType(FilterTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(3)); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(FilterTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(3)); } - FilterTestCustomResource createResource() { FilterTestCustomResource resource = new FilterTestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); resource.setSpec(new FilterTestResourceSpec()); resource.getSpec().setValue("value1"); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestCustomResource.java index a9f560b9bb..83a34deeb9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestCustomResource.java @@ -10,8 +10,7 @@ @Version("v1") @ShortNames("ftc") public class FilterTestCustomResource - extends CustomResource - implements Namespaced { + extends CustomResource implements Namespaced { public String getConfigMapName(int id) { return "configmap" + id; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestReconciler.java index add0075a3b..eeb988b143 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestReconciler.java @@ -17,8 +17,7 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; @ControllerConfiguration(informer = @Informer(onUpdateFilter = UpdateFilter.class)) -public class FilterTestReconciler - implements Reconciler { +public class FilterTestReconciler implements Reconciler { public static final String CONFIG_MAP_FILTER_VALUE = "config_map_skip_this"; public static final String CUSTOM_RESOURCE_FILTER_VALUE = "custom_resource_skip_this"; @@ -28,10 +27,12 @@ public class FilterTestReconciler @Override public UpdateControl reconcile( - FilterTestCustomResource resource, - Context context) { + FilterTestCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); - context.getClient().configMaps().inNamespace(resource.getMetadata().getNamespace()) + context + .getClient() + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) .resource(createConfigMap(resource)) .createOrReplace(); return UpdateControl.noUpdate(); @@ -39,16 +40,16 @@ public UpdateControl reconcile( private ConfigMap createConfigMap(FilterTestCustomResource resource) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); configMap.addOwnerReference(resource); configMap.setData(Map.of(CM_VALUE_KEY, resource.getSpec().getValue())); return configMap; } - public int getNumberOfExecutions() { return numberOfExecutions.get(); } @@ -57,12 +58,12 @@ public int getNumberOfExecutions() { public List> prepareEventSources( EventSourceContext context) { - final var informerConfiguration = InformerEventSourceConfiguration - .from(ConfigMap.class, FilterTestCustomResource.class) - .withOnUpdateFilter((newCM, - oldCM) -> !newCM.getData().get(CM_VALUE_KEY) - .equals(CONFIG_MAP_FILTER_VALUE)) - .build(); + final var informerConfiguration = + InformerEventSourceConfiguration.from(ConfigMap.class, FilterTestCustomResource.class) + .withOnUpdateFilter( + (newCM, oldCM) -> + !newCM.getData().get(CM_VALUE_KEY).equals(CONFIG_MAP_FILTER_VALUE)) + .build(); InformerEventSource configMapES = new InformerEventSource<>(informerConfiguration, context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestResourceStatus.java index 1714a2135d..9d72abadf7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/FilterTestResourceStatus.java @@ -1,5 +1,3 @@ package io.javaoperatorsdk.operator.baseapi.filter; -public class FilterTestResourceStatus { - -} +public class FilterTestResourceStatus {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/UpdateFilter.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/UpdateFilter.java index 7f697d32a1..62d7ceaa76 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/UpdateFilter.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/filter/UpdateFilter.java @@ -4,8 +4,7 @@ import static io.javaoperatorsdk.operator.baseapi.filter.FilterTestReconciler.CUSTOM_RESOURCE_FILTER_VALUE; -public class UpdateFilter - implements OnUpdateFilter { +public class UpdateFilter implements OnUpdateFilter { @Override public boolean accept(FilterTestCustomResource resource, FilterTestCustomResource oldResource) { return !resource.getSpec().getValue().equals(CUSTOM_RESOURCE_FILTER_VALUE); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingCustomResource.java index 45f424b8eb..07baef441a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingCustomResource.java @@ -11,6 +11,4 @@ @Version("v1") @ShortNames("gkrr") public class GenericKubernetesResourceHandlingCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java index 0e4700a482..3b2fccfe59 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingIT.java @@ -24,12 +24,9 @@ public LocallyRunOperatorExtension extension() { @Override public GenericKubernetesResourceHandlingCustomResource testResource(String name, String data) { var resource = new GenericKubernetesResourceHandlingCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(name).build()); resource.setSpec(new GenericKubernetesDependentSpec()); resource.getSpec().setValue(INITIAL_DATA); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java index 2d7cf217bc..fda6833afa 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/generickubernetesresourcehandling/GenericKubernetesResourceHandlingReconciler.java @@ -17,7 +17,6 @@ public class GenericKubernetesResourceHandlingReconciler implements Reconciler { - public static final String VERSION = "v1"; public static final String KIND = "ConfigMap"; public static final String KEY = "key"; @@ -29,13 +28,23 @@ public UpdateControl reconcile( var secondary = context.getSecondaryResource(GenericKubernetesResource.class); - secondary.ifPresentOrElse(r -> { - var desired = desiredConfigMap(primary, context); - if (!matches(r, desired)) { - context.getClient().genericKubernetesResources(VERSION, KIND).resource(desired).update(); - } - }, () -> context.getClient().genericKubernetesResources(VERSION, KIND) - .resource(desiredConfigMap(primary, context)).create()); + secondary.ifPresentOrElse( + r -> { + var desired = desiredConfigMap(primary, context); + if (!matches(r, desired)) { + context + .getClient() + .genericKubernetesResources(VERSION, KIND) + .resource(desired) + .update(); + } + }, + () -> + context + .getClient() + .genericKubernetesResources(VERSION, KIND) + .resource(desiredConfigMap(primary, context)) + .create()); return UpdateControl.noUpdate(); } @@ -63,15 +72,17 @@ GenericKubernetesResource desiredConfigMap( } } - @Override public List> prepareEventSources( EventSourceContext context) { - var informerEventSource = new InformerEventSource<>(InformerEventSourceConfiguration.from( - new GroupVersionKind("", VERSION, KIND), - GenericKubernetesResourceHandlingCustomResource.class).build(), - context); + var informerEventSource = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + new GroupVersionKind("", VERSION, KIND), + GenericKubernetesResourceHandlingCustomResource.class) + .build(), + context); return List.of(informerEventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java index a28d6da07d..4f499b256b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopIT.java @@ -19,8 +19,10 @@ public class GracefulStopIT { @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() - .withConfigurationService(o -> o.withCloseClientOnStop(false) - .withReconciliationTerminationTimeout(Duration.ofMillis(RECONCILER_SLEEP))) + .withConfigurationService( + o -> + o.withCloseClientOnStop(false) + .withReconciliationTerminationTimeout(Duration.ofMillis(RECONCILER_SLEEP))) .withReconciler(new GracefulStopTestReconciler()) .build(); @@ -31,41 +33,49 @@ void stopsGracefullyWithTimeoutConfiguration() { private void testGracefulStop(String resourceName, int expectedFinalGeneration) { var testRes = operator.create(testResource(resourceName)); - await().untilAsserted(() -> { - var r = operator.get(GracefulStopTestCustomResource.class, resourceName); - assertThat(r.getStatus()).isNotNull(); - assertThat(r.getStatus().getObservedGeneration()).isEqualTo(1); - assertThat(operator.getReconcilerOfType(GracefulStopTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + var r = operator.get(GracefulStopTestCustomResource.class, resourceName); + assertThat(r.getStatus()).isNotNull(); + assertThat(r.getStatus().getObservedGeneration()).isEqualTo(1); + assertThat( + operator + .getReconcilerOfType(GracefulStopTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + }); testRes.getSpec().setValue(2); operator.replace(testRes); - await().pollDelay(Duration.ofMillis(50)).untilAsserted( - () -> assertThat(operator.getReconcilerOfType(GracefulStopTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(2)); + await() + .pollDelay(Duration.ofMillis(50)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(GracefulStopTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(2)); operator.getOperator().stop(); - await().untilAsserted(() -> { - var r = operator.get(GracefulStopTestCustomResource.class, resourceName); - assertThat(r.getStatus()).isNotNull(); - assertThat(r.getStatus().getObservedGeneration()).isEqualTo(expectedFinalGeneration); - }); + await() + .untilAsserted( + () -> { + var r = operator.get(GracefulStopTestCustomResource.class, resourceName); + assertThat(r.getStatus()).isNotNull(); + assertThat(r.getStatus().getObservedGeneration()).isEqualTo(expectedFinalGeneration); + }); } public GracefulStopTestCustomResource testResource(String name) { - GracefulStopTestCustomResource resource = - new GracefulStopTestCustomResource(); + GracefulStopTestCustomResource resource = new GracefulStopTestCustomResource(); resource.setMetadata( - new ObjectMetaBuilder() - .withName(name) - .withNamespace(operator.getNamespace()) - .build()); + new ObjectMetaBuilder().withName(name).withNamespace(operator.getNamespace()).build()); resource.setSpec(new GracefulStopTestCustomResourceSpec()); resource.getSpec().setValue(1); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestCustomResource.java index 0a3f0c680f..3c21246c41 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestCustomResource.java @@ -11,6 +11,4 @@ @ShortNames("gst") public class GracefulStopTestCustomResource extends CustomResource - implements Namespaced { - -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestReconciler.java index 7b33c68722..64c527196e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/gracefulstop/GracefulStopTestReconciler.java @@ -8,8 +8,7 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; @ControllerConfiguration -public class GracefulStopTestReconciler - implements Reconciler { +public class GracefulStopTestReconciler implements Reconciler { public static final int RECONCILER_SLEEP = 1000; @@ -17,8 +16,8 @@ public class GracefulStopTestReconciler @Override public UpdateControl reconcile( - GracefulStopTestCustomResource resource, - Context context) throws InterruptedException { + GracefulStopTestCustomResource resource, Context context) + throws InterruptedException { numberOfExecutions.addAndGet(1); resource.setStatus(new GracefulStopTestCustomResourceStatus()); @@ -31,5 +30,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java index 81e0a0febe..c1a857c22c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceIT.java @@ -33,8 +33,7 @@ class InformerEventSourceIT { void testUsingInformerToWatchChangesOfConfigMap() { var customResource = initialCustomResource(); customResource = operator.create(customResource); - ConfigMap configMap = - operator.create(relatedConfigMap(customResource.getMetadata().getName())); + ConfigMap configMap = operator.create(relatedConfigMap(customResource.getMetadata().getName())); waitForCRStatusValue(INITIAL_STATUS_MESSAGE); configMap.getData().put(TARGET_CONFIG_MAP_KEY, UPDATE_STATUS_MESSAGE); @@ -48,8 +47,7 @@ void deletingSecondaryResource() { var customResource = initialCustomResource(); customResource = operator.create(customResource); waitForCRStatusValue(MISSING_CONFIG_MAP); - ConfigMap configMap = - operator.create(relatedConfigMap(customResource.getMetadata().getName())); + ConfigMap configMap = operator.create(relatedConfigMap(customResource.getMetadata().getName())); waitForCRStatusValue(INITIAL_STATUS_MESSAGE); boolean res = operator.delete(configMap); @@ -58,8 +56,9 @@ void deletingSecondaryResource() { } waitForCRStatusValue(MISSING_CONFIG_MAP); - assertThat(((InformerEventSourceTestCustomReconciler) operator.getReconcilers().get(0)) - .getNumberOfExecutions()) + assertThat( + ((InformerEventSourceTestCustomReconciler) operator.getReconcilers().get(0)) + .getNumberOfExecutions()) .isEqualTo(3); } @@ -86,12 +85,13 @@ private InformerEventSourceTestCustomResource initialCustomResource() { } private void waitForCRStatusValue(String value) { - await().atMost(10, TimeUnit.SECONDS).untilAsserted(() -> { - var cr = - operator.get(InformerEventSourceTestCustomResource.class, RESOURCE_NAME); - assertThat(cr.getStatus()).isNotNull(); - assertThat(cr.getStatus().getConfigMapValue()).isEqualTo(value); - }); + await() + .atMost(10, TimeUnit.SECONDS) + .untilAsserted( + () -> { + var cr = operator.get(InformerEventSourceTestCustomResource.class, RESOURCE_NAME); + assertThat(cr.getStatus()).isNotNull(); + assertThat(cr.getStatus().getConfigMapValue()).isEqualTo(value); + }); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomReconciler.java index 5028d6a0f4..1f0ecccb8c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomReconciler.java @@ -37,10 +37,12 @@ public List> prepareEventS EventSourceContext context) { InformerEventSourceConfiguration config = - InformerEventSourceConfiguration - .from(ConfigMap.class, InformerEventSourceTestCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, InformerEventSourceTestCustomResource.class) .withSecondaryToPrimaryMapper( - Mappers.fromAnnotation(RELATED_RESOURCE_NAME, RELATED_RESOURCE_TYPE, + Mappers.fromAnnotation( + RELATED_RESOURCE_NAME, + RELATED_RESOURCE_TYPE, InformerEventSourceTestCustomResource.class)) .build(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomResource.java index 4d1077c64e..cb63ac4754 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informereventsource/InformerEventSourceTestCustomResource.java @@ -11,8 +11,6 @@ @Version("v1") @Kind("Informereventsourcesample") @ShortNames("ies") -public class InformerEventSourceTestCustomResource extends - CustomResource - implements Namespaced { - -} +public class InformerEventSourceTestCustomResource + extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterCustomResource.java index c7e6ee43b4..79011f1448 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterCustomResource.java @@ -10,5 +10,4 @@ @Version("v1") @ShortNames("irc") public class InformerRemoteClusterCustomResource - extends CustomResource implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java index 3ad274d954..3cd5ddccbc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterIT.java @@ -39,50 +39,59 @@ class InformerRemoteClusterIT { void testRemoteClusterInformer() { var r = extension.create(testCustomResource()); - var cm = kubernetesClient.configMaps() - .resource(remoteConfigMap(r.getMetadata().getName(), - r.getMetadata().getNamespace())) - .create(); + var cm = + kubernetesClient + .configMaps() + .resource(remoteConfigMap(r.getMetadata().getName(), r.getMetadata().getNamespace())) + .create(); // config map does not exist on the primary resource cluster - assertThat(extension.getKubernetesClient().configMaps() - .inNamespace(CM_NAMESPACE) - .withName(CONFIG_MAP_NAME).get()).isNull(); - - await().untilAsserted(() -> { - var cr = extension.get(InformerRemoteClusterCustomResource.class, NAME); - assertThat(cr.getStatus()).isNotNull(); - assertThat(cr.getStatus().getRemoteConfigMapMessage()).isEqualTo(INITIAL_VALUE); - }); + assertThat( + extension + .getKubernetesClient() + .configMaps() + .inNamespace(CM_NAMESPACE) + .withName(CONFIG_MAP_NAME) + .get()) + .isNull(); + + await() + .untilAsserted( + () -> { + var cr = extension.get(InformerRemoteClusterCustomResource.class, NAME); + assertThat(cr.getStatus()).isNotNull(); + assertThat(cr.getStatus().getRemoteConfigMapMessage()).isEqualTo(INITIAL_VALUE); + }); cm.getData().put(DATA_KEY, CHANGED_VALUE); kubernetesClient.configMaps().resource(cm).update(); - await().untilAsserted(() -> { - var cr = extension.get(InformerRemoteClusterCustomResource.class, NAME); - assertThat(cr.getStatus().getRemoteConfigMapMessage()).isEqualTo(CHANGED_VALUE); - }); + await() + .untilAsserted( + () -> { + var cr = extension.get(InformerRemoteClusterCustomResource.class, NAME); + assertThat(cr.getStatus().getRemoteConfigMapMessage()).isEqualTo(CHANGED_VALUE); + }); } InformerRemoteClusterCustomResource testCustomResource() { var res = new InformerRemoteClusterCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(NAME).build()); return res; } ConfigMap remoteConfigMap(String ownerName, String ownerNamespace) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(CONFIG_MAP_NAME) - .withNamespace(CM_NAMESPACE) - .withAnnotations(Map.of( - Mappers.DEFAULT_ANNOTATION_FOR_NAME, ownerName, - Mappers.DEFAULT_ANNOTATION_FOR_NAMESPACE, ownerNamespace)) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(CONFIG_MAP_NAME) + .withNamespace(CM_NAMESPACE) + .withAnnotations( + Map.of( + Mappers.DEFAULT_ANNOTATION_FOR_NAME, ownerName, + Mappers.DEFAULT_ANNOTATION_FOR_NAMESPACE, ownerNamespace)) + .build()) .withData(Map.of(DATA_KEY, INITIAL_VALUE)) .build(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterReconciler.java index 30735dd880..08c861df96 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/informerremotecluster/InformerRemoteClusterReconciler.java @@ -30,34 +30,43 @@ public InformerRemoteClusterReconciler(KubernetesClient remoteClient) { @Override public UpdateControl reconcile( InformerRemoteClusterCustomResource resource, - Context context) throws Exception { + Context context) + throws Exception { - return context.getSecondaryResource(ConfigMap.class).map(cm -> { - var r = new InformerRemoteClusterCustomResource(); - r.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); - r.setStatus(new InformerRemoteClusterStatus()); - r.getStatus().setRemoteConfigMapMessage(cm.getData().get(DATA_KEY)); - return UpdateControl.patchStatus(r); - }).orElseGet(UpdateControl::noUpdate); + return context + .getSecondaryResource(ConfigMap.class) + .map( + cm -> { + var r = new InformerRemoteClusterCustomResource(); + r.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); + r.setStatus(new InformerRemoteClusterStatus()); + r.getStatus().setRemoteConfigMapMessage(cm.getData().get(DATA_KEY)); + return UpdateControl.patchStatus(r); + }) + .orElseGet(UpdateControl::noUpdate); } @Override public List> prepareEventSources( EventSourceContext context) { - var es = new InformerEventSource<>(InformerEventSourceConfiguration - .from(ConfigMap.class, InformerRemoteClusterCustomResource.class) - // owner references do not work cross cluster, using - // annotations here to reference primary resource - .withSecondaryToPrimaryMapper( - Mappers.fromDefaultAnnotations(InformerRemoteClusterCustomResource.class)) - // setting remote client for informer - .withKubernetesClient(remoteClient) - .withWatchAllNamespaces() - .build(), context); + var es = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, InformerRemoteClusterCustomResource.class) + // owner references do not work cross cluster, using + // annotations here to reference primary resource + .withSecondaryToPrimaryMapper( + Mappers.fromDefaultAnnotations(InformerRemoteClusterCustomResource.class)) + // setting remote client for informer + .withKubernetesClient(remoteClient) + .withWatchAllNamespaces() + .build(), + context); return List.of(es); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java index 41411fa16f..b073bee248 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorIT.java @@ -19,7 +19,8 @@ class LabelSelectorIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new LabelSelectorTestReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new LabelSelectorTestReconciler()) .build(); @Test @@ -27,21 +28,25 @@ void filtersCustomResourceByLabel() { operator.create(resource("r1", true)); operator.create(resource("r2", false)); - await().pollDelay(Duration.ofMillis(150)).untilAsserted(() -> { - assertThat( - operator.getReconcilerOfType(LabelSelectorTestReconciler.class).getNumberOfExecutions()) - .isEqualTo(1); - }); + await() + .pollDelay(Duration.ofMillis(150)) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(LabelSelectorTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + }); } LabelSelectorTestCustomResource resource(String name, boolean addLabel) { var res = new LabelSelectorTestCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(name) - .withLabels(addLabel ? Map.of(LABEL_KEY, LABEL_VALUE) - : Collections.emptyMap()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(name) + .withLabels(addLabel ? Map.of(LABEL_KEY, LABEL_VALUE) : Collections.emptyMap()) + .build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestCustomResource.java index 321c684eef..6659af0404 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("lst") -public class LabelSelectorTestCustomResource - extends CustomResource - implements Namespaced { -} +public class LabelSelectorTestCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestReconciler.java index a225f6a7be..4800f758fb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/labelselector/LabelSelectorTestReconciler.java @@ -9,8 +9,7 @@ import static io.javaoperatorsdk.operator.baseapi.labelselector.LabelSelectorTestReconciler.LABEL_KEY; import static io.javaoperatorsdk.operator.baseapi.labelselector.LabelSelectorTestReconciler.LABEL_VALUE; -@ControllerConfiguration( - informer = @Informer(labelSelector = LABEL_KEY + "=" + LABEL_VALUE)) +@ControllerConfiguration(informer = @Informer(labelSelector = LABEL_KEY + "=" + LABEL_VALUE)) public class LabelSelectorTestReconciler implements Reconciler, TestExecutionInfoProvider { @@ -30,5 +29,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceCustomResource.java index 7b4a38d429..421ab2b5ce 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("lcn") -public class LeaderElectionChangeNamespaceCustomResource - extends CustomResource - implements Namespaced { -} +public class LeaderElectionChangeNamespaceCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java index dab495ba18..f6194439e2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceIT.java @@ -27,8 +27,8 @@ public class LeaderElectionChangeNamespaceIT { @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() - .withConfigurationService(o -> o.withLeaderElectionConfiguration( - new LeaderElectionConfiguration(LEASE_NAME))) + .withConfigurationService( + o -> o.withLeaderElectionConfiguration(new LeaderElectionConfiguration(LEASE_NAME))) .withReconciler(new LeaderElectionChangeNamespaceReconciler()) .build(); @@ -50,46 +50,47 @@ void noReconcileOnChangeNamespace() { extension.create(testResource()); var reconciler = extension.getReconcilerOfType(LeaderElectionChangeNamespaceReconciler.class); - await().pollDelay(Duration.ofSeconds(1)) + await() + .pollDelay(Duration.ofSeconds(1)) .timeout(Duration.ofSeconds(3)) - .untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isEqualTo(0); - }); + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(0); + }); - extension.getRegisteredControllerForReconcile(LeaderElectionChangeNamespaceReconciler.class) + extension + .getRegisteredControllerForReconcile(LeaderElectionChangeNamespaceReconciler.class) .changeNamespaces("default", extension.getNamespace()); - await().pollDelay(Duration.ofSeconds(1)) + await() + .pollDelay(Duration.ofSeconds(1)) .timeout(Duration.ofSeconds(3)) - .untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isEqualTo(0); - }); + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(0); + }); } - LeaderElectionChangeNamespaceCustomResource testResource() { var resource = new LeaderElectionChangeNamespaceCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName("test1").build()); return resource; } static Lease lease() { var lease = new Lease(); - lease.setMetadata(new ObjectMetaBuilder() - .withName(LEASE_NAME) - .withNamespace("default") - .build()); + lease.setMetadata( + new ObjectMetaBuilder().withName(LEASE_NAME).withNamespace("default").build()); var time = ZonedDateTime.now(); - lease.setSpec(new LeaseSpecBuilder() - .withAcquireTime(ZonedDateTime.now()) - .withRenewTime(time) - .withAcquireTime(time) - .withHolderIdentity("non-operator-identity") - .withLeaseTransitions(0) - .withLeaseDurationSeconds(30) - .build()); + lease.setSpec( + new LeaseSpecBuilder() + .withAcquireTime(ZonedDateTime.now()) + .withRenewTime(time) + .withAcquireTime(time) + .withHolderIdentity("non-operator-identity") + .withLeaseTransitions(0) + .withLeaseDurationSeconds(30) + .build()); return lease; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceReconciler.java index 70b45035b7..c98900a694 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/leaderelectionchangenamespace/LeaderElectionChangeNamespaceReconciler.java @@ -25,5 +25,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationCustomResource.java index d33f9dd5b3..386940857f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationCustomResource.java @@ -11,5 +11,4 @@ @ShortNames("mog") public class ManualObservedGenerationCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java index 743f73742d..f90fda5c5e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationIT.java @@ -12,38 +12,44 @@ public class ManualObservedGenerationIT { public static final String RESOURCE_NAME = "test1"; + @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new ManualObservedGenerationReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new ManualObservedGenerationReconciler()) .build(); @Test void observedGenerationUpdated() { extension.create(testResource()); - await().untilAsserted(() -> { - var r = extension.get(ManualObservedGenerationCustomResource.class, RESOURCE_NAME); - assertThat(r).isNotNull(); - assertThat(r.getStatus().getObservedGeneration()).isEqualTo(1); - assertThat(r.getStatus().getObservedGeneration()).isEqualTo(r.getMetadata().getGeneration()); - }); + await() + .untilAsserted( + () -> { + var r = extension.get(ManualObservedGenerationCustomResource.class, RESOURCE_NAME); + assertThat(r).isNotNull(); + assertThat(r.getStatus().getObservedGeneration()).isEqualTo(1); + assertThat(r.getStatus().getObservedGeneration()) + .isEqualTo(r.getMetadata().getGeneration()); + }); var changed = testResource(); changed.getSpec().setValue("changed value"); extension.replace(changed); - await().untilAsserted(() -> { - var r = extension.get(ManualObservedGenerationCustomResource.class, RESOURCE_NAME); - assertThat(r.getStatus().getObservedGeneration()).isEqualTo(2); - assertThat(r.getStatus().getObservedGeneration()).isEqualTo(r.getMetadata().getGeneration()); - }); + await() + .untilAsserted( + () -> { + var r = extension.get(ManualObservedGenerationCustomResource.class, RESOURCE_NAME); + assertThat(r.getStatus().getObservedGeneration()).isEqualTo(2); + assertThat(r.getStatus().getObservedGeneration()) + .isEqualTo(r.getMetadata().getGeneration()); + }); } ManualObservedGenerationCustomResource testResource() { var res = new ManualObservedGenerationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); res.setSpec(new ManualObservedGenerationSpec()); res.getSpec().setValue("Initial Value"); return res; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationReconciler.java index 865d099766..f69ac6a37b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/manualobservedgeneration/ManualObservedGenerationReconciler.java @@ -18,9 +18,11 @@ public UpdateControl reconcile( Context context) { numberOfExecutions.addAndGet(1); var resourceForStatusPatch = resourceForStatusPatch(resource); - if (!Objects.equals(resource.getMetadata().getGeneration(), + if (!Objects.equals( + resource.getMetadata().getGeneration(), resourceForStatusPatch.getStatus().getObservedGeneration())) { - resourceForStatusPatch.getStatus() + resourceForStatusPatch + .getStatus() .setObservedGeneration(resource.getMetadata().getGeneration()); return UpdateControl.patchStatus(resourceForStatusPatch); } else { @@ -31,10 +33,11 @@ public UpdateControl reconcile( private ManualObservedGenerationCustomResource resourceForStatusPatch( ManualObservedGenerationCustomResource original) { var res = new ManualObservedGenerationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(original.getMetadata().getName()) - .withNamespace(original.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(original.getMetadata().getName()) + .withNamespace(original.getMetadata().getNamespace()) + .build()); res.setStatus(original.getStatus()); if (res.getStatus() == null) { res.setStatus(new ManualObservedGenerationStatus()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java index 460b374071..af7cdbe8ec 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalIT.java @@ -27,9 +27,12 @@ void reconciliationTriggeredBasedOnMaxInterval() { .pollInterval(50, TimeUnit.MILLISECONDS) .atMost(500, TimeUnit.MILLISECONDS) .untilAsserted( - () -> assertThat(operator.getReconcilerOfType(MaxIntervalTestReconciler.class) - .getNumberOfExecutions()) - .isGreaterThan(3)); + () -> + assertThat( + operator + .getReconcilerOfType(MaxIntervalTestReconciler.class) + .getNumberOfExecutions()) + .isGreaterThan(3)); } private MaxIntervalTestCustomResource createTestResource() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestCustomResource.java index d9f7cb74ba..ad4af2df74 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestCustomResource.java @@ -11,7 +11,5 @@ @Version("v1") @Kind("MaxIntervalTestCustomResource") @ShortNames("mit") -public class MaxIntervalTestCustomResource - extends CustomResource - implements Namespaced { -} +public class MaxIntervalTestCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestReconciler.java index ce9197cff5..d017a9d2e1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxinterval/MaxIntervalTestReconciler.java @@ -10,8 +10,9 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@ControllerConfiguration(maxReconciliationInterval = @MaxReconciliationInterval(interval = 50, - timeUnit = TimeUnit.MILLISECONDS)) +@ControllerConfiguration( + maxReconciliationInterval = + @MaxReconciliationInterval(interval = 50, timeUnit = TimeUnit.MILLISECONDS)) public class MaxIntervalTestReconciler implements Reconciler, TestExecutionInfoProvider { @@ -27,5 +28,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java index ce53c483cc..bc55fa6035 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryIT.java @@ -16,7 +16,8 @@ class MaxIntervalAfterRetryIT { @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() - .withReconciler(new MaxIntervalAfterRetryTestReconciler()).build(); + .withReconciler(new MaxIntervalAfterRetryTestReconciler()) + .build(); @Test void reconciliationTriggeredBasedOnMaxInterval() { @@ -28,8 +29,12 @@ void reconciliationTriggeredBasedOnMaxInterval() { .pollInterval(50, TimeUnit.MILLISECONDS) .atMost(1, TimeUnit.SECONDS) .untilAsserted( - () -> assertThat(operator.getReconcilerOfType(MaxIntervalAfterRetryTestReconciler.class) - .getNumberOfExecutions()).isGreaterThan(5)); + () -> + assertThat( + operator + .getReconcilerOfType(MaxIntervalAfterRetryTestReconciler.class) + .getNumberOfExecutions()) + .isGreaterThan(5)); } private MaxIntervalAfterRetryTestCustomResource createTestResource() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestCustomResource.java index 85328a26a3..b854d05560 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("mir") -public class MaxIntervalAfterRetryTestCustomResource - extends CustomResource - implements Namespaced { -} +public class MaxIntervalAfterRetryTestCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestReconciler.java index cf9a42f0e2..ec1bbc99d5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/maxintervalafterretry/MaxIntervalAfterRetryTestReconciler.java @@ -11,8 +11,9 @@ import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; @GradualRetry(maxAttempts = 1, initialInterval = 100) -@ControllerConfiguration(maxReconciliationInterval = @MaxReconciliationInterval(interval = 50, - timeUnit = TimeUnit.MILLISECONDS)) +@ControllerConfiguration( + maxReconciliationInterval = + @MaxReconciliationInterval(interval = 50, timeUnit = TimeUnit.MILLISECONDS)) public class MaxIntervalAfterRetryTestReconciler implements Reconciler, TestExecutionInfoProvider { @@ -33,5 +34,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeCustomResource.java index d7e3fa5c37..1d8062366f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("mrst") public class MultipleReconcilerSameTypeCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java index 57e4ad795b..81cfa7fd07 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeIT.java @@ -13,6 +13,7 @@ public class MultipleReconcilerSameTypeIT { public static final String TEST_RESOURCE_1 = "test1"; public static final String TEST_RESOURCE_2 = "test2"; + @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() @@ -20,39 +21,44 @@ public class MultipleReconcilerSameTypeIT { .withReconciler(MultipleReconcilerSameTypeReconciler2.class) .build(); - @Test void multipleReconcilersBasedOnLeaderElection() { extension.create(testResource(TEST_RESOURCE_1, true)); extension.create(testResource(TEST_RESOURCE_2, false)); + await() + .untilAsserted( + () -> { + assertThat( + extension + .getReconcilerOfType(MultipleReconcilerSameTypeReconciler1.class) + .getNumberOfExecutions()) + .isEqualTo(1); + assertThat( + extension + .getReconcilerOfType(MultipleReconcilerSameTypeReconciler2.class) + .getNumberOfExecutions()) + .isEqualTo(1); - await().untilAsserted(() -> { - assertThat(extension.getReconcilerOfType(MultipleReconcilerSameTypeReconciler1.class) - .getNumberOfExecutions()).isEqualTo(1); - assertThat(extension.getReconcilerOfType(MultipleReconcilerSameTypeReconciler2.class) - .getNumberOfExecutions()).isEqualTo(1); - - var res1 = extension.get(MultipleReconcilerSameTypeCustomResource.class, TEST_RESOURCE_1); - var res2 = extension.get(MultipleReconcilerSameTypeCustomResource.class, TEST_RESOURCE_2); - assertThat(res1).isNotNull(); - assertThat(res2).isNotNull(); - assertThat(res1.getStatus().getReconciledBy()) - .isEqualTo(MultipleReconcilerSameTypeReconciler1.class.getSimpleName()); - assertThat(res2.getStatus().getReconciledBy()) - .isEqualTo(MultipleReconcilerSameTypeReconciler2.class.getSimpleName()); - }); + var res1 = + extension.get(MultipleReconcilerSameTypeCustomResource.class, TEST_RESOURCE_1); + var res2 = + extension.get(MultipleReconcilerSameTypeCustomResource.class, TEST_RESOURCE_2); + assertThat(res1).isNotNull(); + assertThat(res2).isNotNull(); + assertThat(res1.getStatus().getReconciledBy()) + .isEqualTo(MultipleReconcilerSameTypeReconciler1.class.getSimpleName()); + assertThat(res2.getStatus().getReconciledBy()) + .isEqualTo(MultipleReconcilerSameTypeReconciler2.class.getSimpleName()); + }); } MultipleReconcilerSameTypeCustomResource testResource(String name, boolean type1) { var res = new MultipleReconcilerSameTypeCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(name).build()); if (type1) { res.getMetadata().getLabels().put("reconciler", "1"); } return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler1.java index a3ffd9667e..42aa52f9e1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler1.java @@ -26,5 +26,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler2.java index ee446a7cf4..fc61f0624a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplereconcilersametype/MultipleReconcilerSameTypeReconciler2.java @@ -29,5 +29,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceCustomResource.java index d142f1f0d1..7c3f88ae74 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceCustomResource.java @@ -11,7 +11,5 @@ @Version("v1") @Kind("MultipleSecondaryEventSourceCustomResource") @ShortNames("mses") -public class MultipleSecondaryEventSourceCustomResource - extends CustomResource - implements Namespaced { -} +public class MultipleSecondaryEventSourceCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java index 856f6d8a6f..18a937040f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceIT.java @@ -14,6 +14,7 @@ class MultipleSecondaryEventSourceIT { public static final String TEST_RESOURCE_NAME = "testresource"; + @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() @@ -28,26 +29,30 @@ void receivingPeriodicEvents() { var reconciler = operator.getReconcilerOfType(MultipleSecondaryEventSourceReconciler.class); - await().pollDelay(Duration.ofMillis(300)) - .until(() -> reconciler.getNumberOfExecutions() <= 3); + await().pollDelay(Duration.ofMillis(300)).until(() -> reconciler.getNumberOfExecutions() <= 3); int numberOfInitialExecutions = reconciler.getNumberOfExecutions(); updateConfigMap(resource, 1); - await().pollDelay(Duration.ofMillis(300)) + await() + .pollDelay(Duration.ofMillis(300)) .until(() -> reconciler.getNumberOfExecutions() == numberOfInitialExecutions + 1); updateConfigMap(resource, 2); - await().pollDelay(Duration.ofMillis(300)) + await() + .pollDelay(Duration.ofMillis(300)) .until(() -> reconciler.getNumberOfExecutions() == numberOfInitialExecutions + 2); } private void updateConfigMap(MultipleSecondaryEventSourceCustomResource resource, int number) { - ConfigMap map1 = operator.get(ConfigMap.class, - number == 1 ? MultipleSecondaryEventSourceReconciler.getName1(resource) - : MultipleSecondaryEventSourceReconciler.getName2(resource)); + ConfigMap map1 = + operator.get( + ConfigMap.class, + number == 1 + ? MultipleSecondaryEventSourceReconciler.getName1(resource) + : MultipleSecondaryEventSourceReconciler.getName2(resource)); map1.getData().put("value2", "value2"); operator.replace(map1); } @@ -62,5 +67,4 @@ public MultipleSecondaryEventSourceCustomResource createTestCustomResource() { .build()); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java index 12ebf45c8c..18432cc86b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiplesecondaryeventsource/MultipleSecondaryEventSourceReconciler.java @@ -39,15 +39,27 @@ public UpdateControl reconcile( numberOfExecutions.addAndGet(1); final var client = context.getClient(); - if (client.configMaps().inNamespace(resource.getMetadata().getNamespace()) - .withName(getName1(resource)).get() == null) { - client.configMaps().inNamespace(resource.getMetadata().getNamespace()) + if (client + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(getName1(resource)) + .get() + == null) { + client + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) .resource(configMap(getName1(resource), resource)) .createOrReplace(); } - if (client.configMaps().inNamespace(resource.getMetadata().getNamespace()) - .withName(getName2(resource)).get() == null) { - client.configMaps().inNamespace(resource.getMetadata().getNamespace()) + if (client + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(getName2(resource)) + .get() + == null) { + client + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) .resource(configMap(getName2(resource), resource)) .createOrReplace(); } @@ -68,17 +80,22 @@ public int getNumberOfExecutions() { public List> prepareEventSources( EventSourceContext context) { - var config = InformerEventSourceConfiguration - .from(ConfigMap.class, MultipleSecondaryEventSourceCustomResource.class) - .withNamespacesInheritedFromController() - .withLabelSelector("multisecondary") - .withSecondaryToPrimaryMapper(s -> { - var name = - s.getMetadata().getName().subSequence(0, s.getMetadata().getName().length() - 1); - return Set.of(new ResourceID(name.toString(), s.getMetadata().getNamespace())); - }).build(); - InformerEventSource configMapEventSource = - new InformerEventSource<>(config, context); + var config = + InformerEventSourceConfiguration.from( + ConfigMap.class, MultipleSecondaryEventSourceCustomResource.class) + .withNamespacesInheritedFromController() + .withLabelSelector("multisecondary") + .withSecondaryToPrimaryMapper( + s -> { + var name = + s.getMetadata() + .getName() + .subSequence(0, s.getMetadata().getName().length() - 1); + return Set.of(new ResourceID(name.toString(), s.getMetadata().getNamespace())); + }) + .build(); + InformerEventSource + configMapEventSource = new InformerEventSource<>(config, context); return List.of(configMapEventSource); } 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 687ea3c581..9e0c6eb1fb 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 @@ -55,21 +55,33 @@ public void reset() { @SuppressWarnings("rawtypes") public void onStop(SharedIndexInformer informer, Throwable ex) { if (ex instanceof WatcherException watcherEx) { - watcherEx.getRawWatchMessage().ifPresent(raw -> { - try { - // extract the resource at which the version is attempted to be created (i.e. the stored - // version) - final var unmarshal = Serialization.jsonMapper().readTree(raw); - final var object = unmarshal.get("object"); - resourceCreateAsVersion = acceptOnlyIfUnsetOrEqualToAlreadySet(resourceCreateAsVersion, - object.get("apiVersion").asText()); - // extract the asked resource version - failedResourceVersion = acceptOnlyIfUnsetOrEqualToAlreadySet(failedResourceVersion, - object.get("metadata").get("managedFields").get(0).get("apiVersion").asText()); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - }); + watcherEx + .getRawWatchMessage() + .ifPresent( + raw -> { + try { + // extract the resource at which the version is attempted to be created (i.e. + // the stored + // version) + final var unmarshal = Serialization.jsonMapper().readTree(raw); + final var object = unmarshal.get("object"); + resourceCreateAsVersion = + acceptOnlyIfUnsetOrEqualToAlreadySet( + resourceCreateAsVersion, object.get("apiVersion").asText()); + // extract the asked resource version + failedResourceVersion = + acceptOnlyIfUnsetOrEqualToAlreadySet( + failedResourceVersion, + object + .get("metadata") + .get("managedFields") + .get(0) + .get("apiVersion") + .asText()); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + }); // extract error message errorMessage = @@ -82,10 +94,16 @@ public void onStop(SharedIndexInformer informer, Throwable ex) { resourceClassName = acceptOnlyIfUnsetOrEqualToAlreadySet(resourceClassName, apiTypeClass.getName()); - log.debug("API Type Class: " + apiTypeClass.getName() - + " - resource class name: " + resourceClassName); - log.info("Informer for " + HasMetadata.getFullResourceName(apiTypeClass) - + " stopped due to: " + ex.getMessage()); + log.debug( + "API Type Class: " + + apiTypeClass.getName() + + " - resource class name: " + + resourceClassName); + log.info( + "Informer for " + + HasMetadata.getFullResourceName(apiTypeClass) + + " stopped due to: " + + ex.getMessage()); } public String getResourceClassName() { @@ -109,7 +127,7 @@ private String acceptOnlyIfUnsetOrEqualToAlreadySet(String existing, String newV } } - private final static TestInformerStoppedHandler informerStoppedHandler = + private static final TestInformerStoppedHandler informerStoppedHandler = new TestInformerStoppedHandler(); @Test @@ -146,29 +164,28 @@ void invalidEventsShouldStopInformerAndCallInformerStoppedHandler() { await() .atMost(Duration.ofSeconds(10)) .pollInterval(Duration.ofMillis(50)) - .untilAsserted(() -> { - // v1 is the stored version so trying to create a v2 version should fail because we cannot - // convert a String (as defined by the spec of the v2 CRD) to an int (which is what the - // spec of the v1 CRD defines) - assertThat(informerStoppedHandler.getResourceCreateAsVersion()) - .isEqualTo(HasMetadata.getApiVersion( - MultiVersionCRDTestCustomResource1.class)); - assertThat(informerStoppedHandler.getResourceClassName()) - .isEqualTo(MultiVersionCRDTestCustomResource1.class.getName()); - assertThat(informerStoppedHandler.getFailedResourceVersion()) - .isEqualTo(HasMetadata.getApiVersion( - MultiVersionCRDTestCustomResource2.class)); - assertThat(informerStoppedHandler.getErrorMessage()).contains( - "Cannot deserialize value of type `int` from String \"string value\": not a valid `int` value"); - }); - assertThat( - operator - .get(MultiVersionCRDTestCustomResource2.class, CR_V2_NAME) - .getStatus()) + .untilAsserted( + () -> { + // v1 is the stored version so trying to create a v2 version should fail because we + // cannot + // convert a String (as defined by the spec of the v2 CRD) to an int (which is what + // the + // spec of the v1 CRD defines) + assertThat(informerStoppedHandler.getResourceCreateAsVersion()) + .isEqualTo(HasMetadata.getApiVersion(MultiVersionCRDTestCustomResource1.class)); + assertThat(informerStoppedHandler.getResourceClassName()) + .isEqualTo(MultiVersionCRDTestCustomResource1.class.getName()); + assertThat(informerStoppedHandler.getFailedResourceVersion()) + .isEqualTo(HasMetadata.getApiVersion(MultiVersionCRDTestCustomResource2.class)); + assertThat(informerStoppedHandler.getErrorMessage()) + .contains( + "Cannot deserialize value of type `int` from String \"string value\": not a" + + " valid `int` value"); + }); + assertThat(operator.get(MultiVersionCRDTestCustomResource2.class, CR_V2_NAME).getStatus()) .isNull(); } - MultiVersionCRDTestCustomResource1 createTestResourceV1WithoutLabel() { MultiVersionCRDTestCustomResource1 cr = new MultiVersionCRDTestCustomResource1(); cr.setMetadata(new ObjectMeta()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource1.java index cdc56026eb..f376cba7f0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource1.java @@ -12,9 +12,6 @@ @Kind("MultiVersionCRDTestCustomResource") @ShortNames("mvc") public class MultiVersionCRDTestCustomResource1 - extends - CustomResource - implements Namespaced { - - -} + extends CustomResource< + MultiVersionCRDTestCustomResourceSpec1, MultiVersionCRDTestCustomResourceStatus1> + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource2.java index ecf3f0c7e2..87fd47064d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResource2.java @@ -12,8 +12,6 @@ @Kind("MultiVersionCRDTestCustomResource") @ShortNames("mvc") public class MultiVersionCRDTestCustomResource2 - extends - CustomResource - implements Namespaced { - -} + extends CustomResource< + MultiVersionCRDTestCustomResourceSpec2, MultiVersionCRDTestCustomResourceStatus2> + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResourceSpec1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResourceSpec1.java index da6b415cee..d870d4d315 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResourceSpec1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestCustomResourceSpec1.java @@ -12,5 +12,4 @@ public MultiVersionCRDTestCustomResourceSpec1 setValue(int value) { this.value = value; return this; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler1.java index a9b3af7586..870ba979c4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler1.java @@ -19,8 +19,7 @@ public class MultiVersionCRDTestReconciler1 public UpdateControl reconcile( MultiVersionCRDTestCustomResource1 resource, Context context) { - log.info("Reconcile MultiVersionCRDTestCustomResource1: {}", - resource.getMetadata().getName()); + log.info("Reconcile MultiVersionCRDTestCustomResource1: {}", resource.getMetadata().getName()); if (resource.getStatus() == null) { resource.setStatus(new MultiVersionCRDTestCustomResourceStatus1()); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler2.java index 29d7e830fa..a0457ccf1e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/multiversioncrd/MultiVersionCRDTestReconciler2.java @@ -19,8 +19,7 @@ public class MultiVersionCRDTestReconciler2 public UpdateControl reconcile( MultiVersionCRDTestCustomResource2 resource, Context context) { - log.info("Reconcile MultiVersionCRDTestCustomResource2: {}", - resource.getMetadata().getName()); + log.info("Reconcile MultiVersionCRDTestCustomResource2: {}", resource.getMetadata().getName()); if (resource.getStatus() == null) { resource.setStatus(new MultiVersionCRDTestCustomResourceStatus2()); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentCustomResource.java index 5a54443393..2b1ddbcbec 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentCustomResource.java @@ -10,9 +10,4 @@ @Version("v1") @ShortNames("nri") public class NextReconciliationImminentCustomResource - extends CustomResource - implements Namespaced { - - - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java index 5f7933610f..03bd6b0a7a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentIT.java @@ -15,8 +15,7 @@ public class NextReconciliationImminentIT { - private static final Logger log = - LoggerFactory.getLogger(NextReconciliationImminentIT.class); + private static final Logger log = LoggerFactory.getLogger(NextReconciliationImminentIT.class); public static final int WAIT_FOR_EVENT = 300; public static final String TEST_RESOURCE_NAME = "test1"; @@ -46,19 +45,22 @@ void skippingStatusUpdateWithNextReconciliationImminent() throws InterruptedExce await().untilAsserted(() -> assertThat(reconciler.isReconciliationWaiting()).isTrue()); reconciler.allowReconciliationToProceed(); - await().pollDelay(Duration.ofMillis(WAIT_FOR_EVENT)).untilAsserted(() -> { - assertThat(extension.get(NextReconciliationImminentCustomResource.class, TEST_RESOURCE_NAME) - .getStatus().getUpdateNumber()).isEqualTo(1); - }); + await() + .pollDelay(Duration.ofMillis(WAIT_FOR_EVENT)) + .untilAsserted( + () -> { + assertThat( + extension + .get(NextReconciliationImminentCustomResource.class, TEST_RESOURCE_NAME) + .getStatus() + .getUpdateNumber()) + .isEqualTo(1); + }); } - NextReconciliationImminentCustomResource testResource() { var res = new NextReconciliationImminentCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentReconciler.java index d3de766869..7e8b5a49fd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/nextreconciliationimminent/NextReconciliationImminentReconciler.java @@ -24,7 +24,8 @@ public class NextReconciliationImminentReconciler @Override public UpdateControl reconcile( NextReconciliationImminentCustomResource resource, - Context context) throws InterruptedException { + Context context) + throws InterruptedException { log.info("started reconciliation"); reconciliationWaiting = true; // wait long enough to get manually allowed diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSACustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSACustomResource.java index 45f5543024..9411f43e25 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSACustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSACustomResource.java @@ -13,5 +13,4 @@ @ShortNames("du") public class PatchResourceAndStatusNoSSACustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java index de4ba09b23..cd63c708e9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAIT.java @@ -15,7 +15,6 @@ class PatchResourceAndStatusNoSSAIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder() .withConfigurationService(o -> o.withUseSSAToPatchPrimaryResource(false)) .withReconciler(PatchResourceAndStatusNoSSAReconciler.class) @@ -31,19 +30,17 @@ void updatesSubResourceStatus() { TestUtils.waitXms(300); PatchResourceAndStatusNoSSACustomResource customResource = - operator - .get(PatchResourceAndStatusNoSSACustomResource.class, - resource.getMetadata().getName()); + operator.get( + PatchResourceAndStatusNoSSACustomResource.class, resource.getMetadata().getName()); - assertThat(TestUtils.getNumberOfExecutions(operator)) - .isEqualTo(1); + assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(1); assertThat(customResource.getStatus().getState()) .isEqualTo(PatchResourceAndStatusNoSSAStatus.State.SUCCESS); assertThat( - customResource - .getMetadata() - .getAnnotations() - .get(PatchResourceAndStatusNoSSAReconciler.TEST_ANNOTATION)) + customResource + .getMetadata() + .getAnnotations() + .get(PatchResourceAndStatusNoSSAReconciler.TEST_ANNOTATION)) .isNotNull(); } @@ -54,10 +51,8 @@ void awaitStatusUpdated(String name) { () -> { PatchResourceAndStatusNoSSACustomResource cr = operator.get(PatchResourceAndStatusNoSSACustomResource.class, name); - assertThat(cr) - .isNotNull(); - assertThat(cr.getStatus()) - .isNotNull(); + assertThat(cr).isNotNull(); + assertThat(cr.getStatus()).isNotNull(); assertThat(cr.getStatus().getState()) .isEqualTo(PatchResourceAndStatusNoSSAStatus.State.SUCCESS); }); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAStatus.java index bad4568700..1f1bb5db9b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourceandstatusnossa/PatchResourceAndStatusNoSSAStatus.java @@ -14,6 +14,7 @@ public PatchResourceAndStatusNoSSAStatus setState(State state) { } public enum State { - SUCCESS, ERROR + SUCCESS, + ERROR } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java index b8746dda3f..f395706092 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAIT.java @@ -8,5 +8,4 @@ public class PatchResourceAndStatusWithSSAIT extends PatchWithSSAITBase { protected Reconciler reconciler() { return new PatchResourceAndStatusWithSSAReconciler(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAReconciler.java index 2d599a9039..f3449c4d05 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceAndStatusWithSSAReconciler.java @@ -6,7 +6,7 @@ @ControllerConfiguration public class PatchResourceAndStatusWithSSAReconciler implements Reconciler, - Cleaner { + Cleaner { public static final String ADDED_VALUE = "Added Value"; @@ -16,10 +16,11 @@ public UpdateControl reconcile( Context context) { var res = new PatchResourceWithSSACustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); res.setSpec(new PatchResourceWithSSASpec()); res.getSpec().setControllerManagedValue(ADDED_VALUE); @@ -30,7 +31,8 @@ public UpdateControl reconcile( } @Override - public DeleteControl cleanup(PatchResourceWithSSACustomResource resource, + public DeleteControl cleanup( + PatchResourceWithSSACustomResource resource, Context context) { return DeleteControl.defaultDelete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSACustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSACustomResource.java index 0c789127ac..8203f0a85e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSACustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSACustomResource.java @@ -11,6 +11,4 @@ @ShortNames("prs") public class PatchResourceWithSSACustomResource extends CustomResource - implements Namespaced { - -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java index 6ceaea2867..d512665d06 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAIT.java @@ -1,14 +1,11 @@ package io.javaoperatorsdk.operator.baseapi.patchresourcewithssa; - import io.javaoperatorsdk.operator.api.reconciler.Reconciler; - public class PatchResourceWithSSAIT extends PatchWithSSAITBase { @Override protected Reconciler reconciler() { return new PatchResourceWithSSAReconciler(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAReconciler.java index f5f53ee6c5..7bc6c2b42a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchResourceWithSSAReconciler.java @@ -6,7 +6,7 @@ @ControllerConfiguration public class PatchResourceWithSSAReconciler implements Reconciler, - Cleaner { + Cleaner { public static final String ADDED_VALUE = "Added Value"; @@ -16,10 +16,11 @@ public UpdateControl reconcile( Context context) { var res = new PatchResourceWithSSACustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); // first update the spec with missing value, then status in next reconciliation if (resource.getSpec().getControllerManagedValue() == null) { @@ -34,7 +35,8 @@ public UpdateControl reconcile( } @Override - public DeleteControl cleanup(PatchResourceWithSSACustomResource resource, + public DeleteControl cleanup( + PatchResourceWithSSACustomResource resource, Context context) { return DeleteControl.defaultDelete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchWithSSAITBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchWithSSAITBase.java index 50b005fef7..4fdb35d800 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchWithSSAITBase.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/patchresourcewithssa/PatchWithSSAITBase.java @@ -17,38 +17,42 @@ public abstract class PatchWithSSAITBase { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder() - .withReconciler(reconciler()) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(reconciler()).build(); @Test void reconcilerPatchesResourceWithSSA() { extension.create(testResource()); - await().untilAsserted(() -> { - var actualResource = extension.get(PatchResourceWithSSACustomResource.class, RESOURCE_NAME); - - assertThat(actualResource.getSpec().getInitValue()).isEqualTo(INIT_VALUE); - assertThat(actualResource.getSpec().getControllerManagedValue()) - .isEqualTo(PatchResourceWithSSAReconciler.ADDED_VALUE); - // finalizer is added to the SSA patch in the background by the framework - assertThat(actualResource.getMetadata().getFinalizers()).isNotEmpty(); - assertThat(actualResource.getStatus().isSuccessfullyReconciled()).isTrue(); - // one for resource, one for subresource - assertThat(actualResource.getMetadata().getManagedFields().stream() - .filter(mf -> mf.getManager() - .equals(reconciler().getClass().getSimpleName().toLowerCase())) - .toList()).hasSize(2); - }); + await() + .untilAsserted( + () -> { + var actualResource = + extension.get(PatchResourceWithSSACustomResource.class, RESOURCE_NAME); + + assertThat(actualResource.getSpec().getInitValue()).isEqualTo(INIT_VALUE); + assertThat(actualResource.getSpec().getControllerManagedValue()) + .isEqualTo(PatchResourceWithSSAReconciler.ADDED_VALUE); + // finalizer is added to the SSA patch in the background by the framework + assertThat(actualResource.getMetadata().getFinalizers()).isNotEmpty(); + assertThat(actualResource.getStatus().isSuccessfullyReconciled()).isTrue(); + // one for resource, one for subresource + assertThat( + actualResource.getMetadata().getManagedFields().stream() + .filter( + mf -> + mf.getManager() + .equals( + reconciler().getClass().getSimpleName().toLowerCase())) + .toList()) + .hasSize(2); + }); } protected abstract Reconciler reconciler(); PatchResourceWithSSACustomResource testResource() { var res = new PatchResourceWithSSACustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); res.setSpec(new PatchResourceWithSSASpec()); res.getSpec().setInitValue(INIT_VALUE); return res; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourceEventSourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourceEventSourceCustomResource.java index 74817da86d..3efae6a309 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourceEventSourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourceEventSourceCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("pres") -public class PerResourceEventSourceCustomResource - extends CustomResource - implements Namespaced { -} +public class PerResourceEventSourceCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java index 299109bdd1..20f89e0d39 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceIT.java @@ -23,7 +23,7 @@ class PerResourcePollingEventSourceIT { /** * This is kinda some test to verify that the implementation of PerResourcePollingEventSource * works with the underling mechanisms in event source manager and other parts of the system. - **/ + */ @Test void fetchedAndReconciledMultipleTimes() { operator.create(resource(NAME_1)); @@ -31,20 +31,19 @@ void fetchedAndReconciledMultipleTimes() { var reconciler = operator.getReconcilerOfType(PerResourcePollingEventSourceTestReconciler.class); - await().untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions(NAME_1)).isGreaterThan(2); - assertThat(reconciler.getNumberOfFetchExecution(NAME_1)).isGreaterThan(2); - assertThat(reconciler.getNumberOfExecutions(NAME_2)).isGreaterThan(2); - assertThat(reconciler.getNumberOfFetchExecution(NAME_2)).isGreaterThan(2); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions(NAME_1)).isGreaterThan(2); + assertThat(reconciler.getNumberOfFetchExecution(NAME_1)).isGreaterThan(2); + assertThat(reconciler.getNumberOfExecutions(NAME_2)).isGreaterThan(2); + assertThat(reconciler.getNumberOfFetchExecution(NAME_2)).isGreaterThan(2); + }); } private PerResourceEventSourceCustomResource resource(String name) { var res = new PerResourceEventSourceCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(name).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java index 8d1cbcc37f..b34c6ef863 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/perresourceeventsource/PerResourcePollingEventSourceTestReconciler.java @@ -27,7 +27,8 @@ public class PerResourcePollingEventSourceTestReconciler @Override public UpdateControl reconcile( PerResourceEventSourceCustomResource resource, - Context context) throws Exception { + Context context) + throws Exception { numberOfExecutions.putIfAbsent(resource.getMetadata().getName(), 0); numberOfExecutions.compute(resource.getMetadata().getName(), (s, v) -> v + 1); return UpdateControl.noUpdate(); @@ -37,14 +38,18 @@ public UpdateControl reconcile( public List> prepareEventSources( EventSourceContext context) { PerResourcePollingEventSource eventSource = - new PerResourcePollingEventSource<>(String.class, context, + new PerResourcePollingEventSource<>( + String.class, + context, new PerResourcePollingConfigurationBuilder<>( - (PerResourceEventSourceCustomResource resource) -> { - numberOfFetchExecutions.putIfAbsent(resource.getMetadata().getName(), 0); - numberOfFetchExecutions.compute(resource.getMetadata().getName(), - (s, v) -> v + 1); - return Set.of(UUID.randomUUID().toString()); - }, Duration.ofMillis(POLL_PERIOD)).build()); + (PerResourceEventSourceCustomResource resource) -> { + numberOfFetchExecutions.putIfAbsent(resource.getMetadata().getName(), 0); + numberOfFetchExecutions.compute( + resource.getMetadata().getName(), (s, v) -> v + 1); + return Set.of(UUID.randomUUID().toString()); + }, + Duration.ofMillis(POLL_PERIOD)) + .build()); return List.of(eventSource); } @@ -56,5 +61,4 @@ public int getNumberOfExecutions(String name) { public int getNumberOfFetchExecution(String name) { return numberOfFetchExecutions.get(name); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/AbstractPrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/AbstractPrimaryIndexerTestReconciler.java index ac2e7e1534..9294a597dd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/AbstractPrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/AbstractPrimaryIndexerTestReconciler.java @@ -12,8 +12,8 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; @ControllerConfiguration -public class AbstractPrimaryIndexerTestReconciler implements - Reconciler { +public class AbstractPrimaryIndexerTestReconciler + implements Reconciler { public static final String CONFIG_MAP_NAME = "common-config-map"; @@ -36,5 +36,4 @@ public UpdateControl reconcile( public Map getNumberOfExecutions() { return numberOfExecutions; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java index cb810dcd35..9063ef0fcb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerIT.java @@ -18,11 +18,11 @@ public class PrimaryIndexerIT { public static final String RESOURCE_NAME1 = "test1"; public static final String RESOURCE_NAME2 = "test2"; - @RegisterExtension - LocallyRunOperatorExtension operator = buildOperator(); + @RegisterExtension LocallyRunOperatorExtension operator = buildOperator(); protected LocallyRunOperatorExtension buildOperator() { - return LocallyRunOperatorExtension.builder().withReconciler(new PrimaryIndexerTestReconciler()) + return LocallyRunOperatorExtension.builder() + .withReconciler(new PrimaryIndexerTestReconciler()) .build(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResource.java index f670a4be64..2b338c3263 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResource.java @@ -12,7 +12,6 @@ @Kind("PrimaryIndexerTestCustomResource") @ShortNames("pi") public class PrimaryIndexerTestCustomResource - extends - CustomResource - implements Namespaced { -} + extends CustomResource< + PrimaryIndexerTestCustomResourceSpec, PrimaryIndexerTestCustomResourceStatus> + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResourceStatus.java index ebcfe347e5..313e2b6a68 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestCustomResourceStatus.java @@ -1,7 +1,3 @@ package io.javaoperatorsdk.operator.baseapi.primaryindexer; -public class PrimaryIndexerTestCustomResourceStatus { - - - -} +public class PrimaryIndexerTestCustomResourceStatus {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestReconciler.java index d03cc0fcb0..d7ece6fe3c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primaryindexer/PrimaryIndexerTestReconciler.java @@ -12,8 +12,7 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; @ControllerConfiguration -public class PrimaryIndexerTestReconciler - extends AbstractPrimaryIndexerTestReconciler { +public class PrimaryIndexerTestReconciler extends AbstractPrimaryIndexerTestReconciler { @Override public List> prepareEventSources( @@ -22,17 +21,17 @@ public List> prepareEventSource context.getPrimaryCache().addIndexer(CONFIG_MAP_RELATION_INDEXER, indexer); var informerConfiguration = - InformerEventSourceConfiguration - .from(ConfigMap.class, PrimaryIndexerTestCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, PrimaryIndexerTestCustomResource.class) .withSecondaryToPrimaryMapper( - (ConfigMap secondaryResource) -> context - .getPrimaryCache() - .byIndex( - CONFIG_MAP_RELATION_INDEXER, - secondaryResource.getMetadata().getName()) - .stream() - .map(ResourceID::fromResource) - .collect(Collectors.toSet())) + (ConfigMap secondaryResource) -> + context + .getPrimaryCache() + .byIndex( + CONFIG_MAP_RELATION_INDEXER, secondaryResource.getMetadata().getName()) + .stream() + .map(ResourceID::fromResource) + .collect(Collectors.toSet())) .build(); return List.of(new InformerEventSource<>(informerConfiguration, context)); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Cluster.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Cluster.java index 18190ae1fa..d0be7738a6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Cluster.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Cluster.java @@ -9,7 +9,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("clu") -public class Cluster - extends CustomResource - implements Namespaced { -} +public class Cluster extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Job.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Job.java index 3215fd8538..611898bd52 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Job.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/Job.java @@ -9,7 +9,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("cjo") -public class Job - extends CustomResource - implements Namespaced { -} +public class Job extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/JobReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/JobReconciler.java index fdca997f7d..1855f89b77 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/JobReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/JobReconciler.java @@ -18,8 +18,7 @@ * intended to be a reusable code as it is, rather serves for deeper understanding of the problem. */ @ControllerConfiguration() -public class JobReconciler - implements Reconciler { +public class JobReconciler implements Reconciler { private static final String JOB_CLUSTER_INDEX = "job-cluster-index"; @@ -38,20 +37,22 @@ public JobReconciler(boolean addPrimaryToSecondaryMapper) { } @Override - public UpdateControl reconcile( - Job resource, Context context) { + public UpdateControl reconcile(Job resource, Context context) { if (!getResourceDirectlyFromCache) { // this is only possible when there is primary to secondary mapper - context.getSecondaryResource(Cluster.class) + context + .getSecondaryResource(Cluster.class) .orElseThrow(() -> new IllegalStateException("Secondary resource should be present")); } else { // reading the resource from cache as alternative, works without primary to secondary mapper - var informerEventSource = (InformerEventSource) context.eventSourceRetriever() - .getEventSourceFor(Cluster.class); + var informerEventSource = + (InformerEventSource) + context.eventSourceRetriever().getEventSourceFor(Cluster.class); informerEventSource - .get(new ResourceID(resource.getSpec().getClusterName(), - resource.getMetadata().getNamespace())) + .get( + new ResourceID( + resource.getSpec().getClusterName(), resource.getMetadata().getNamespace())) .orElseThrow( () -> new IllegalStateException("Secondary resource cannot be read from cache")); } @@ -61,21 +62,39 @@ public UpdateControl reconcile( @Override public List> prepareEventSources(EventSourceContext context) { - context.getPrimaryCache().addIndexer(JOB_CLUSTER_INDEX, (job -> List - .of(indexKey(job.getSpec().getClusterName(), job.getMetadata().getNamespace())))); + context + .getPrimaryCache() + .addIndexer( + JOB_CLUSTER_INDEX, + (job -> + List.of( + indexKey(job.getSpec().getClusterName(), job.getMetadata().getNamespace())))); InformerEventSourceConfiguration.Builder informerConfiguration = InformerEventSourceConfiguration.from(Cluster.class, Job.class) - .withSecondaryToPrimaryMapper(cluster -> context.getPrimaryCache() - .byIndex(JOB_CLUSTER_INDEX, indexKey(cluster.getMetadata().getName(), - cluster.getMetadata().getNamespace())) - .stream().map(ResourceID::fromResource).collect(Collectors.toSet())) + .withSecondaryToPrimaryMapper( + cluster -> + context + .getPrimaryCache() + .byIndex( + JOB_CLUSTER_INDEX, + indexKey( + cluster.getMetadata().getName(), + cluster.getMetadata().getNamespace())) + .stream() + .map(ResourceID::fromResource) + .collect(Collectors.toSet())) .withNamespacesInheritedFromController(); if (addPrimaryToSecondaryMapper) { - informerConfiguration = informerConfiguration.withPrimaryToSecondaryMapper( - (PrimaryToSecondaryMapper) primary -> Set.of(new ResourceID( - primary.getSpec().getClusterName(), primary.getMetadata().getNamespace()))); + informerConfiguration = + informerConfiguration.withPrimaryToSecondaryMapper( + (PrimaryToSecondaryMapper) + primary -> + Set.of( + new ResourceID( + primary.getSpec().getClusterName(), + primary.getMetadata().getNamespace()))); } return List.of(new InformerEventSource<>(informerConfiguration.build(), context)); @@ -90,8 +109,8 @@ public int getNumberOfExecutions() { } @Override - public ErrorStatusUpdateControl updateErrorStatus(Job resource, Context context, - Exception e) { + public ErrorStatusUpdateControl updateErrorStatus( + Job resource, Context context, Exception e) { errorOccurred = true; return ErrorStatusUpdateControl.noStatusUpdate(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java index cfcb2854bb..9344cc787f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryIT.java @@ -29,16 +29,18 @@ void readsSecondaryInManyToOneCases() throws InterruptedException { Thread.sleep(MIN_DELAY); operator.create(job()); - await().pollDelay(Duration.ofMillis(300)).untilAsserted( - () -> assertThat(operator.getReconcilerOfType(JobReconciler.class).getNumberOfExecutions()) - .isEqualTo(1)); + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> + assertThat( + operator.getReconcilerOfType(JobReconciler.class).getNumberOfExecutions()) + .isEqualTo(1)); } public static Job job() { var job = new Job(); - job.setMetadata(new ObjectMetaBuilder() - .withName("job1") - .build()); + job.setMetadata(new ObjectMetaBuilder().withName("job1").build()); job.setSpec(new JobSpec()); job.getSpec().setClusterName(CLUSTER_NAME); return job; @@ -46,10 +48,7 @@ public static Job job() { public static Cluster cluster() { Cluster cluster = new Cluster(); - cluster.setMetadata(new ObjectMetaBuilder() - .withName(CLUSTER_NAME) - .build()); + cluster.setMetadata(new ObjectMetaBuilder().withName(CLUSTER_NAME).build()); return cluster; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java index 4e1908f282..84e6910b35 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/primarytosecondary/PrimaryToSecondaryMissingIT.java @@ -30,10 +30,12 @@ void missingPrimaryToSecondaryCausesIssueAccessingSecondary() throws Interrupted Thread.sleep(300); operator.create(job()); - await().untilAsserted(() -> { - assertThat(reconciler.isErrorOccurred()).isTrue(); - assertThat(reconciler.getNumberOfExecutions()).isZero(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.isErrorOccurred()).isTrue(); + assertThat(reconciler.getNumberOfExecutions()).isZero(); + }); } @Test @@ -44,10 +46,11 @@ void accessingDirectlyTheCacheWorksWithoutPToSMapper() throws InterruptedExcepti Thread.sleep(300); operator.create(job()); - await().untilAsserted(() -> { - assertThat(reconciler.isErrorOccurred()).isFalse(); - assertThat(reconciler.getNumberOfExecutions()).isPositive(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.isErrorOccurred()).isFalse(); + assertThat(reconciler.getNumberOfExecutions()).isPositive(); + }); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResource.java index 79732a199b..1ecd97f05e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResource.java @@ -9,8 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("rlc") -public class RateLimitCustomResource - extends CustomResource - implements Namespaced { - -} +public class RateLimitCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResourceStatus.java index a33142805b..e7b24ae8e0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitCustomResourceStatus.java @@ -1,5 +1,3 @@ package io.javaoperatorsdk.operator.baseapi.ratelimit; -public class RateLimitCustomResourceStatus { - -} +public class RateLimitCustomResourceStatus {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java index 1f09b75b59..8b04fd0095 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitIT.java @@ -17,43 +17,50 @@ class RateLimitIT { - private final static Logger log = LoggerFactory.getLogger(RateLimitIT.class); + private static final Logger log = LoggerFactory.getLogger(RateLimitIT.class); @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder() - .withReconciler(new RateLimitReconciler()) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(new RateLimitReconciler()).build(); @Test void rateLimitsExecution() { var res = operator.create(createResource()); - IntStream.rangeClosed(1, 5).forEach(i -> { - log.debug("replacing resource version: {}", i); - var resource = createResource(); - resource.getSpec().setNumber(i); - operator.replace(resource); - }); - await().pollInterval(Duration.ofMillis(100)) + IntStream.rangeClosed(1, 5) + .forEach( + i -> { + log.debug("replacing resource version: {}", i); + var resource = createResource(); + resource.getSpec().setNumber(i); + operator.replace(resource); + }); + await() + .pollInterval(Duration.ofMillis(100)) .pollDelay(Duration.ofMillis(REFRESH_PERIOD / 2)) - .untilAsserted(() -> assertThat( - operator.getReconcilerOfType(RateLimitReconciler.class).getNumberOfExecutions()) - .isEqualTo(1)); - - await().pollDelay(Duration.ofMillis(REFRESH_PERIOD)) - .untilAsserted(() -> assertThat( - operator.getReconcilerOfType(RateLimitReconciler.class).getNumberOfExecutions()) - .isEqualTo(2)); + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(RateLimitReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1)); + + await() + .pollDelay(Duration.ofMillis(REFRESH_PERIOD)) + .untilAsserted( + () -> + assertThat( + operator + .getReconcilerOfType(RateLimitReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(2)); } public RateLimitCustomResource createResource() { RateLimitCustomResource res = new RateLimitCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName("test") - .build()); + res.setMetadata(new ObjectMetaBuilder().withName("test").build()); res.setSpec(new RateLimitCustomResourceSpec()); res.getSpec().setNumber(0); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitReconciler.java index 75a70f94d1..d16fcb837b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/ratelimit/RateLimitReconciler.java @@ -9,12 +9,12 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; import io.javaoperatorsdk.operator.processing.event.rate.RateLimited; -@RateLimited(maxReconciliations = 1, +@RateLimited( + maxReconciliations = 1, within = RateLimitReconciler.REFRESH_PERIOD, unit = TimeUnit.MILLISECONDS) @ControllerConfiguration -public class RateLimitReconciler - implements Reconciler { +public class RateLimitReconciler implements Reconciler { public static final int REFRESH_PERIOD = 3000; @@ -22,8 +22,7 @@ public class RateLimitReconciler @Override public UpdateControl reconcile( - RateLimitCustomResource resource, - Context context) { + RateLimitCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); return UpdateControl.noUpdate(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java index e1610dc000..410d34a390 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryIT.java @@ -24,11 +24,12 @@ class RetryIT { LocallyRunOperatorExtension.builder() .withReconciler( new RetryTestCustomReconciler(NUMBER_FAILED_EXECUTIONS), - new GenericRetry().setInitialInterval(RETRY_INTERVAL).withLinearRetry() + new GenericRetry() + .setInitialInterval(RETRY_INTERVAL) + .withLinearRetry() .setMaxAttempts(MAX_RETRY_ATTEMPTS)) .build(); - @Test void retryFailedExecution() { RetryTestCustomResource resource = createTestCustomResource("1"); @@ -36,32 +37,24 @@ void retryFailedExecution() { operator.create(resource); await("cr status updated") - .pollDelay( - RETRY_INTERVAL * (NUMBER_FAILED_EXECUTIONS + 2), - TimeUnit.MILLISECONDS) - .pollInterval( - RETRY_INTERVAL, - TimeUnit.MILLISECONDS) + .pollDelay(RETRY_INTERVAL * (NUMBER_FAILED_EXECUTIONS + 2), TimeUnit.MILLISECONDS) + .pollInterval(RETRY_INTERVAL, TimeUnit.MILLISECONDS) .atMost(5, TimeUnit.SECONDS) - .untilAsserted(() -> { - assertThat( - TestUtils.getNumberOfExecutions(operator)) - .isEqualTo(NUMBER_FAILED_EXECUTIONS + 1); - - RetryTestCustomResource finalResource = - operator.get(RetryTestCustomResource.class, - resource.getMetadata().getName()); - assertThat(finalResource.getStatus().getState()) - .isEqualTo(RetryTestCustomResourceStatus.State.SUCCESS); - }); + .untilAsserted( + () -> { + assertThat(TestUtils.getNumberOfExecutions(operator)) + .isEqualTo(NUMBER_FAILED_EXECUTIONS + 1); + + RetryTestCustomResource finalResource = + operator.get(RetryTestCustomResource.class, resource.getMetadata().getName()); + assertThat(finalResource.getStatus().getState()) + .isEqualTo(RetryTestCustomResourceStatus.State.SUCCESS); + }); } public static RetryTestCustomResource createTestCustomResource(String id) { RetryTestCustomResource resource = new RetryTestCustomResource(); - resource.setMetadata( - new ObjectMetaBuilder() - .withName("retrysource-" + id) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName("retrysource-" + id).build()); resource.setKind("retrysample"); resource.setSpec(new RetryTestCustomResourceSpec()); resource.getSpec().setValue(id); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java index bedfd5cff5..f57a70201f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryMaxAttemptIT.java @@ -20,12 +20,14 @@ class RetryMaxAttemptIT { @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() - .withReconciler(reconciler, - new GenericRetry().setInitialInterval(RETRY_INTERVAL).withLinearRetry() + .withReconciler( + reconciler, + new GenericRetry() + .setInitialInterval(RETRY_INTERVAL) + .withLinearRetry() .setMaxAttempts(MAX_RETRY_ATTEMPTS)) .build(); - @Test void retryFailedExecution() throws InterruptedException { RetryTestCustomResource resource = createTestCustomResource("max-retry"); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomReconciler.java index b9fee4769a..af156736f4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomReconciler.java @@ -15,20 +15,18 @@ public class RetryTestCustomReconciler implements Reconciler, TestExecutionInfoProvider { - private static final Logger log = - LoggerFactory.getLogger(RetryTestCustomReconciler.class); + private static final Logger log = LoggerFactory.getLogger(RetryTestCustomReconciler.class); private final AtomicInteger numberOfExecutions = new AtomicInteger(0); private final AtomicInteger numberOfExecutionFails; - public RetryTestCustomReconciler(int numberOfExecutionFails) { this.numberOfExecutionFails = new AtomicInteger(numberOfExecutionFails); } @Override - public UpdateControl reconcile(RetryTestCustomResource resource, - Context context) { + public UpdateControl reconcile( + RetryTestCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); log.info("Value: " + resource.getSpec().getValue()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResource.java index 0b3e1244b6..492d8b77bb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResource.java @@ -13,5 +13,4 @@ @ShortNames("rs") public class RetryTestCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResourceStatus.java index b2d8f3ba56..363195c34f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/retry/RetryTestCustomResourceStatus.java @@ -14,6 +14,7 @@ public RetryTestCustomResourceStatus setState(State state) { } public enum State { - SUCCESS, ERROR + SUCCESS, + ERROR } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java index 727930d8e6..cbd8de4459 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/ReconcilerExecutorIT.java @@ -63,9 +63,12 @@ void cleanupExecuted() { awaitStatusUpdated(); operator.delete(resource); - await().atMost(Duration.ofSeconds(1)) - .until(() -> ((TestReconciler) operator.getFirstReconciler()) - .getNumberOfCleanupExecutions() == 1); + await() + .atMost(Duration.ofSeconds(1)) + .until( + () -> + ((TestReconciler) operator.getFirstReconciler()).getNumberOfCleanupExecutions() + == 1); } void awaitResourcesCreatedOrUpdated() { @@ -73,8 +76,7 @@ void awaitResourcesCreatedOrUpdated() { .atMost(5, TimeUnit.SECONDS) .untilAsserted( () -> { - ConfigMap configMap = - operator.get(ConfigMap.class, "test-config-map"); + ConfigMap configMap = operator.get(ConfigMap.class, "test-config-map"); assertThat(configMap).isNotNull(); assertThat(configMap.getData().get("test-key")).isEqualTo("test-value"); }); @@ -90,8 +92,7 @@ void awaitStatusUpdated(int timeout) { .untilAsserted( () -> { TestCustomResource cr = - operator.get(TestCustomResource.class, - TestUtils.TEST_CUSTOM_RESOURCE_NAME); + operator.get(TestCustomResource.class, TestUtils.TEST_CUSTOM_RESOURCE_NAME); assertThat(cr).isNotNull(); assertThat(cr.getStatus()).isNotNull(); assertThat(cr.getStatus().getConfigMapStatus()).isEqualTo("ConfigMap Ready"); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestCustomResource.java index 5728746573..2e2e53b948 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestCustomResource.java @@ -13,5 +13,4 @@ @ShortNames("cs") public class TestCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestReconciler.java index 50cfd334bf..975d5afd9a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/simple/TestReconciler.java @@ -16,8 +16,9 @@ @ControllerConfiguration(generationAwareEventProcessing = false) public class TestReconciler - implements Reconciler, Cleaner, - TestExecutionInfoProvider { + implements Reconciler, + Cleaner, + TestExecutionInfoProvider { private static final Logger log = LoggerFactory.getLogger(TestReconciler.class); @@ -28,7 +29,6 @@ public class TestReconciler private final AtomicInteger numberOfCleanupExecutions = new AtomicInteger(0); private volatile boolean updateStatus; - public TestReconciler(boolean updateStatus) { this.updateStatus = updateStatus; } @@ -38,15 +38,16 @@ public void setUpdateStatus(boolean updateStatus) { } @Override - public DeleteControl cleanup( - TestCustomResource resource, Context context) { + public DeleteControl cleanup(TestCustomResource resource, Context context) { numberOfCleanupExecutions.incrementAndGet(); - var statusDetail = context.getClient() - .configMaps() - .inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getSpec().getConfigMapName()) - .delete(); + var statusDetail = + context + .getClient() + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getSpec().getConfigMapName()) + .delete(); if (statusDetail.size() == 1 && statusDetail.get(0).getCauses().isEmpty()) { log.info( @@ -100,15 +101,17 @@ public UpdateControl reconcile( .build(); kubernetesClient .configMaps() - .inNamespace(resource.getMetadata().getNamespace()).resource(newConfigMap) + .inNamespace(resource.getMetadata().getNamespace()) + .resource(newConfigMap) .createOrReplace(); } if (updateStatus) { var statusUpdateResource = new TestCustomResource(); - statusUpdateResource.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); + statusUpdateResource.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); resource.setStatus(new TestCustomResourceStatus()); resource.getStatus().setConfigMapStatus("ConfigMap Ready"); return UpdateControl.patchStatus(resource); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/PeriodicTriggerEventSource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/PeriodicTriggerEventSource.java new file mode 100644 index 0000000000..366777409a --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/PeriodicTriggerEventSource.java @@ -0,0 +1,52 @@ +package io.javaoperatorsdk.operator.baseapi.statuscache; + +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.javaoperatorsdk.operator.OperatorException; +import io.javaoperatorsdk.operator.processing.event.Event; +import io.javaoperatorsdk.operator.processing.event.ResourceID; +import io.javaoperatorsdk.operator.processing.event.source.AbstractEventSource; +import io.javaoperatorsdk.operator.processing.event.source.IndexerResourceCache; + +public class PeriodicTriggerEventSource

+ extends AbstractEventSource { + + public static final int DEFAULT_PERIOD = 30; + private final Timer timer = new Timer(); + private final IndexerResourceCache

primaryCache; + private final int period; + + public PeriodicTriggerEventSource(IndexerResourceCache

primaryCache) { + this(primaryCache, DEFAULT_PERIOD); + } + + public PeriodicTriggerEventSource(IndexerResourceCache

primaryCache, int period) { + super(Void.class); + this.primaryCache = primaryCache; + this.period = period; + } + + @Override + public Set getSecondaryResources(P primary) { + return Set.of(); + } + + @Override + public void start() throws OperatorException { + super.start(); + timer.schedule( + new TimerTask() { + @Override + public void run() { + primaryCache + .list() + .forEach(r -> getEventHandler().handleEvent(new Event(ResourceID.fromResource(r)))); + } + }, + 0, + period); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockCustomResource.java new file mode 100644 index 0000000000..8ab742a975 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockCustomResource.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.baseapi.statuscache; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("spwl") +public class StatusPatchCacheWithLockCustomResource + extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockIT.java new file mode 100644 index 0000000000..c5752f4aae --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockIT.java @@ -0,0 +1,48 @@ +package io.javaoperatorsdk.operator.baseapi.statuscache; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +public class StatusPatchCacheWithLockIT { + + public static final String TEST_1 = "test1"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + .withReconciler(StatusPatchCacheWithLockReconciler.class) + .build(); + + @Test + void testStatusAlwaysUpToDate() { + var reconciler = extension.getReconcilerOfType(StatusPatchCacheWithLockReconciler.class); + + extension.create(testResource()); + + // the reconciliation is periodically triggered, the status values should be increasing + // monotonically + await() + .pollDelay(Duration.ofSeconds(1)) + .pollInterval(Duration.ofMillis(30)) + .untilAsserted( + () -> { + assertThat(reconciler.errorPresent).isFalse(); + assertThat(reconciler.latestValue).isGreaterThan(10); + }); + } + + StatusPatchCacheWithLockCustomResource testResource() { + var res = new StatusPatchCacheWithLockCustomResource(); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_1).build()); + res.setSpec(new StatusPatchCacheWithLockSpec()); + return res; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockReconciler.java new file mode 100644 index 0000000000..364f8e9ff5 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockReconciler.java @@ -0,0 +1,71 @@ +package io.javaoperatorsdk.operator.baseapi.statuscache; + +import java.util.List; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.EventSourceContext; +import io.javaoperatorsdk.operator.api.reconciler.PrimaryUpdateAndCacheUtils; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.processing.event.source.EventSource; + +@ControllerConfiguration +public class StatusPatchCacheWithLockReconciler + implements Reconciler { + + public volatile int latestValue = 0; + public volatile boolean errorPresent = false; + + @Override + public UpdateControl reconcile( + StatusPatchCacheWithLockCustomResource resource, + Context context) { + + if (resource.getStatus() != null && resource.getStatus().getValue() != latestValue) { + errorPresent = true; + throw new IllegalStateException( + "status is not up to date. Latest value: " + + latestValue + + " status values: " + + resource.getStatus().getValue()); + } + + // test also resource update happening meanwhile reconciliation + resource.getSpec().setCounter(resource.getSpec().getCounter() + 1); + context.getClient().resource(resource).update(); + + var freshCopy = createFreshCopy(resource); + + freshCopy + .getStatus() + .setValue(resource.getStatus() == null ? 1 : resource.getStatus().getValue() + 1); + + var updated = + PrimaryUpdateAndCacheUtils.ssaPatchStatusAndCacheResource(resource, freshCopy, context); + latestValue = updated.getStatus().getValue(); + + return UpdateControl.noUpdate(); + } + + @Override + public List> prepareEventSources( + EventSourceContext context) { + // periodic event triggering for testing purposes + return List.of(new PeriodicTriggerEventSource<>(context.getPrimaryCache())); + } + + private StatusPatchCacheWithLockCustomResource createFreshCopy( + StatusPatchCacheWithLockCustomResource resource) { + var res = new StatusPatchCacheWithLockCustomResource(); + res.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); + res.setStatus(new StatusPatchCacheWithLockStatus()); + + return res; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockSpec.java new file mode 100644 index 0000000000..ebbabd49a0 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockSpec.java @@ -0,0 +1,14 @@ +package io.javaoperatorsdk.operator.baseapi.statuscache; + +public class StatusPatchCacheWithLockSpec { + + private int counter = 0; + + public int getCounter() { + return counter; + } + + public void setCounter(int counter) { + this.counter = counter; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockStatus.java new file mode 100644 index 0000000000..5f2d8f5a6f --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuscache/StatusPatchCacheWithLockStatus.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.baseapi.statuscache; + +public class StatusPatchCacheWithLockStatus { + + private Integer value = 0; + + public Integer getValue() { + return value; + } + + public StatusPatchCacheWithLockStatus setValue(Integer value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingCustomResource.java index 61d4c7f1ac..7e7dc00c9c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingCustomResource.java @@ -10,8 +10,6 @@ @Version("v1") @ShortNames("spl") public class StatusPatchLockingCustomResource - extends - CustomResource - implements Namespaced { - -} + extends CustomResource< + StatusPatchLockingCustomResourceSpec, StatusPatchLockingCustomResourceStatus> + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingReconciler.java index 46f0e63331..79d69ff50b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchLockingReconciler.java @@ -8,8 +8,7 @@ import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; @ControllerConfiguration -public class StatusPatchLockingReconciler - implements Reconciler { +public class StatusPatchLockingReconciler implements Reconciler { public static final String MESSAGE = "message"; public static final long WAIT_TIME = 500L; @@ -33,5 +32,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java index 2363a86392..4913b900a0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchNotLockingForNonSSAIT.java @@ -20,7 +20,8 @@ class StatusPatchNotLockingForNonSSAIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(StatusPatchLockingReconciler.class) + LocallyRunOperatorExtension.builder() + .withReconciler(StatusPatchLockingReconciler.class) .withConfigurationService(o -> o.withUseSSAToPatchPrimaryResource(false)) .build(); @@ -31,17 +32,19 @@ void noOptimisticLockingDoneOnStatusUpdate() throws InterruptedException { resource.getMetadata().setAnnotations(Map.of("key", "value")); operator.replace(resource); - await().pollDelay(Duration.ofMillis(WAIT_TIME)).untilAsserted(() -> { - assertThat( - operator.getReconcilerOfType(StatusPatchLockingReconciler.class).getNumberOfExecutions()) - .isEqualTo(1); - var actual = operator.get(StatusPatchLockingCustomResource.class, - TEST_RESOURCE_NAME); - assertThat(actual - .getStatus().getValue()).isEqualTo(1); - assertThat(actual.getMetadata().getGeneration()) - .isEqualTo(1); - }); + await() + .pollDelay(Duration.ofMillis(WAIT_TIME)) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(StatusPatchLockingReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + var actual = operator.get(StatusPatchLockingCustomResource.class, TEST_RESOURCE_NAME); + assertThat(actual.getStatus().getValue()).isEqualTo(1); + assertThat(actual.getMetadata().getGeneration()).isEqualTo(1); + }); } // see https://github.com/fabric8io/kubernetes-client/issues/4158 @@ -49,24 +52,27 @@ void noOptimisticLockingDoneOnStatusUpdate() throws InterruptedException { void valuesAreDeletedIfSetToNull() { var resource = operator.create(createResource()); - await().untilAsserted(() -> { - var actual = operator.get(StatusPatchLockingCustomResource.class, - TEST_RESOURCE_NAME); - assertThat(actual.getStatus()).isNotNull(); - assertThat(actual.getStatus().getMessage()).isEqualTo(MESSAGE); - }); + await() + .untilAsserted( + () -> { + var actual = operator.get(StatusPatchLockingCustomResource.class, TEST_RESOURCE_NAME); + assertThat(actual.getStatus()).isNotNull(); + assertThat(actual.getStatus().getMessage()).isEqualTo(MESSAGE); + }); // resource needs to be read again to we don't replace the with wrong managed fields resource = operator.get(StatusPatchLockingCustomResource.class, TEST_RESOURCE_NAME); resource.getSpec().setMessageInStatus(false); operator.replace(resource); - await().timeout(Duration.ofMinutes(3)).untilAsserted(() -> { - var actual = operator.get(StatusPatchLockingCustomResource.class, - TEST_RESOURCE_NAME); - assertThat(actual.getStatus()).isNotNull(); - assertThat(actual.getStatus().getMessage()).isNull(); - }); + await() + .timeout(Duration.ofMinutes(3)) + .untilAsserted( + () -> { + var actual = operator.get(StatusPatchLockingCustomResource.class, TEST_RESOURCE_NAME); + assertThat(actual.getStatus()).isNotNull(); + assertThat(actual.getStatus().getMessage()).isNull(); + }); } StatusPatchLockingCustomResource createResource() { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java index 0a5c947c21..a301e9f61a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statuspatchnonlocking/StatusPatchSSAMigrationIT.java @@ -26,9 +26,9 @@ public class StatusPatchSSAMigrationIT { @BeforeEach void beforeEach(TestInfo testInfo) { - LocallyRunOperatorExtension.applyCrd(StatusPatchLockingCustomResource.class, - client); - testInfo.getTestMethod() + LocallyRunOperatorExtension.applyCrd(StatusPatchLockingCustomResource.class, client); + testInfo + .getTestMethod() .ifPresent(method -> testNamespace = KubernetesResourceUtil.sanitizeName(method.getName())); client.namespaces().resource(testNamespace(testNamespace)).create(); } @@ -36,48 +36,57 @@ void beforeEach(TestInfo testInfo) { @AfterEach void afterEach() { client.namespaces().withName(testNamespace).delete(); - await().untilAsserted(() -> { - var ns = client.namespaces().withName(testNamespace).get(); - assertThat(ns).isNull(); - }); + await() + .untilAsserted( + () -> { + var ns = client.namespaces().withName(testNamespace).get(); + assertThat(ns).isNull(); + }); client.close(); } - @Test void testMigratingToSSA() { var operator = startOperator(false); var testResource = client.resource(testResource()).create(); - await().untilAsserted(() -> { - var res = client.resource(testResource).get(); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); - assertThat(res.getStatus().getValue()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()) + .isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(1); + }); operator.stop(); // start operator with SSA operator = startOperator(true); - await().untilAsserted(() -> { - var res = client.resource(testResource).get(); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); - assertThat(res.getStatus().getValue()).isEqualTo(2); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()) + .isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(2); + }); var actualResource = client.resource(testResource()).get(); actualResource.getSpec().setMessageInStatus(false); client.resource(actualResource).update(); - await().untilAsserted(() -> { - var res = client.resource(testResource).get(); - assertThat(res.getStatus()).isNotNull(); - // !!! This is wrong, the message should be null, - // see issue in Kubernetes: https://github.com/kubernetes/kubernetes/issues/99003 - assertThat(res.getStatus().getMessage()).isNotNull(); - assertThat(res.getStatus().getValue()).isEqualTo(3); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + // !!! This is wrong, the message should be null, + // see issue in Kubernetes: https://github.com/kubernetes/kubernetes/issues/99003 + assertThat(res.getStatus().getMessage()).isNotNull(); + assertThat(res.getStatus().getValue()).isEqualTo(3); + }); client.resource(testResource()).delete(); operator.stop(); @@ -88,48 +97,60 @@ void workaroundMigratingFromToSSA() { var operator = startOperator(false); var testResource = client.resource(testResource()).create(); - await().untilAsserted(() -> { - var res = client.resource(testResource).get(); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); - assertThat(res.getStatus().getValue()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()) + .isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(1); + }); operator.stop(); // start operator with SSA operator = startOperator(true); - await().untilAsserted(() -> { - var res = client.resource(testResource).get(); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getMessage()).isEqualTo(StatusPatchLockingReconciler.MESSAGE); - assertThat(res.getStatus().getValue()).isEqualTo(2); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()) + .isEqualTo(StatusPatchLockingReconciler.MESSAGE); + assertThat(res.getStatus().getValue()).isEqualTo(2); + }); var actualResource = client.resource(testResource()).get(); actualResource.getSpec().setMessageInStatus(false); // removing the managed field entry for former method works - actualResource.getMetadata().setManagedFields(actualResource.getMetadata().getManagedFields() - .stream().filter(r -> !r.getOperation().equals("Update") && r.getSubresource() != null) - .toList()); + actualResource + .getMetadata() + .setManagedFields( + actualResource.getMetadata().getManagedFields().stream() + .filter(r -> !r.getOperation().equals("Update") && r.getSubresource() != null) + .toList()); client.resource(actualResource).update(); - await().untilAsserted(() -> { - var res = client.resource(testResource).get(); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getMessage()).isNull(); - assertThat(res.getStatus().getValue()).isEqualTo(3); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(testResource).get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getMessage()).isNull(); + assertThat(res.getStatus().getValue()).isEqualTo(3); + }); client.resource(testResource()).delete(); operator.stop(); } - private Operator startOperator(boolean patchStatusWithSSA) { - var operator = new Operator(o -> o.withCloseClientOnStop(false) - .withUseSSAToPatchPrimaryResource(patchStatusWithSSA)); - operator.register(new StatusPatchLockingReconciler(), - o -> o.settingNamespaces(testNamespace)); + var operator = + new Operator( + o -> + o.withCloseClientOnStop(false) + .withUseSSAToPatchPrimaryResource(patchStatusWithSSA)); + operator.register(new StatusPatchLockingReconciler(), o -> o.settingNamespaces(testNamespace)); operator.start(); return operator; @@ -138,16 +159,14 @@ private Operator startOperator(boolean patchStatusWithSSA) { StatusPatchLockingCustomResource testResource() { StatusPatchLockingCustomResource res = new StatusPatchLockingCustomResource(); res.setSpec(new StatusPatchLockingCustomResourceSpec()); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(testNamespace) - .build()); + res.setMetadata( + new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).withNamespace(testNamespace).build()); return res; } private Namespace testNamespace(String name) { - return new NamespaceBuilder().withMetadata(new ObjectMetaBuilder() - .withName(name) - .build()).build(); + return new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(name).build()) + .build(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingCustomResource.java index fb638ef5a0..8f97242215 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingCustomResource.java @@ -12,7 +12,4 @@ @Kind("StatusUpdateLockingCustomResource") @ShortNames("sul") public class StatusUpdateLockingCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java index 881b0b01fc..ad51d82059 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingIT.java @@ -31,15 +31,23 @@ void noOptimisticLockingDoneOnStatusPatch() throws InterruptedException { resource.getMetadata().setAnnotations(Map.of("key", "value")); operator.replace(resource); - await().pollDelay(Duration.ofMillis(WAIT_TIME)).timeout(Duration.ofSeconds(460)) - .untilAsserted(() -> { - assertThat( - operator.getReconcilerOfType(StatusUpdateLockingReconciler.class) - .getNumberOfExecutions()) - .isEqualTo(1); - assertThat(operator.get(StatusUpdateLockingCustomResource.class, TEST_RESOURCE_NAME) - .getStatus().getValue()).isEqualTo(1); - }); + await() + .pollDelay(Duration.ofMillis(WAIT_TIME)) + .timeout(Duration.ofSeconds(460)) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(StatusUpdateLockingReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + assertThat( + operator + .get(StatusUpdateLockingCustomResource.class, TEST_RESOURCE_NAME) + .getStatus() + .getValue()) + .isEqualTo(1); + }); } StatusUpdateLockingCustomResource createResource() { @@ -47,5 +55,4 @@ StatusUpdateLockingCustomResource createResource() { res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingReconciler.java index 31dc727dde..d6332634fa 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/statusupdatelocking/StatusUpdateLockingReconciler.java @@ -26,5 +26,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomReconciler.java index 51ae9e9330..9c7cfe6609 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomReconciler.java @@ -19,11 +19,9 @@ public class SubResourceTestCustomReconciler public static final int RECONCILER_MIN_EXEC_TIME = 300; - private static final Logger log = - LoggerFactory.getLogger(SubResourceTestCustomReconciler.class); + private static final Logger log = LoggerFactory.getLogger(SubResourceTestCustomReconciler.class); private final AtomicInteger numberOfExecutions = new AtomicInteger(0); - @Override public UpdateControl reconcile( SubResourceTestCustomResource resource, Context context) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResource.java index 976af2c97f..0bd59fc7fe 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResource.java @@ -15,5 +15,4 @@ @ShortNames("ss") public class SubResourceTestCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResourceStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResourceStatus.java index 46080b784e..d427e432cd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResourceStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceTestCustomResourceStatus.java @@ -14,6 +14,7 @@ public SubResourceTestCustomResourceStatus setState(State state) { } public enum State { - SUCCESS, ERROR + SUCCESS, + ERROR } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java index 8a73cadf3f..7544e3b791 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/subresource/SubResourceUpdateIT.java @@ -21,7 +21,8 @@ class SubResourceUpdateIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(SubResourceTestCustomReconciler.class) + LocallyRunOperatorExtension.builder() + .withReconciler(SubResourceTestCustomReconciler.class) .build(); @Test @@ -33,8 +34,7 @@ void updatesSubResourceStatus() { // wait for sure, there are no more events waitXms(WAIT_AFTER_EXECUTION); // there is no event on status update processed - assertThat(TestUtils.getNumberOfExecutions(operator)) - .isEqualTo(2); + assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(2); } @Test @@ -48,8 +48,7 @@ void updatesSubResourceStatusNoFinalizer() { // wait for sure, there are no more events waitXms(WAIT_AFTER_EXECUTION); // there is no event on status update processed - assertThat(TestUtils.getNumberOfExecutions(operator)) - .isEqualTo(2); + assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(2); } /** Note that we check on controller impl if there is finalizer on execution. */ @@ -63,8 +62,7 @@ void ifNoFinalizerPresentFirstAddsTheFinalizerThenExecutesControllerAgain() { // wait for sure, there are no more events waitXms(WAIT_AFTER_EXECUTION); // there is no event on status update processed - assertThat(TestUtils.getNumberOfExecutions(operator)) - .isEqualTo(2); + assertThat(TestUtils.getNumberOfExecutions(operator)).isEqualTo(2); } /** @@ -99,17 +97,13 @@ void awaitStatusUpdated(String name) { operator.get(SubResourceTestCustomResource.class, name); assertThat(cr).isNotNull(); assertThat(cr.getStatus()).isNotNull(); - assertThat(cr.getStatus().getState()) - .isEqualTo(SUCCESS); + assertThat(cr.getStatus().getState()).isEqualTo(SUCCESS); }); } public SubResourceTestCustomResource createTestCustomResource(String id) { SubResourceTestCustomResource resource = new SubResourceTestCustomResource(); - resource.setMetadata( - new ObjectMetaBuilder() - .withName("subresource-" + id) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName("subresource-" + id).build()); resource.setKind("SubresourceSample"); resource.setSpec(new SubResourceTestCustomResourceSpec()); resource.getSpec().setValue(id); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java index 7b0d757f82..74846f9a3f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartCustomResource.java @@ -10,7 +10,4 @@ @Version("v1") @ShortNames("udp") public class UnmodifiableDependentPartCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java index a80905c23a..735255c54c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartIT.java @@ -28,33 +28,33 @@ public class UnmodifiableDependentPartIT { void partConfigMapDataUnmodifiable() { var resource = operator.create(testResource()); - await().untilAsserted(() -> { - var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA); - assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, INITIAL_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA); + assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, INITIAL_DATA); + }); resource.getSpec().setData(UPDATED_DATA); operator.replace(resource); - await().untilAsserted(() -> { - var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA); - assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, UPDATED_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(UNMODIFIABLE_INITIAL_DATA_KEY, INITIAL_DATA); + assertThat(cm.getData()).containsEntry(ACTUAL_DATA_KEY, UPDATED_DATA); + }); } - UnmodifiableDependentPartCustomResource testResource() { var res = new UnmodifiableDependentPartCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new UnmodifiableDependentPartSpec()); res.getSpec().setData(INITIAL_DATA); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java index 9c7f6fb9c1..6defde1d32 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiableDependentPartReconciler.java @@ -24,5 +24,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java index d913c0cecd..c6f0759410 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/unmodifiabledependentpart/UnmodifiablePartConfigMapDependent.java @@ -14,25 +14,28 @@ public class UnmodifiablePartConfigMapDependent public static final String UNMODIFIABLE_INITIAL_DATA_KEY = "initialDataKey"; public static final String ACTUAL_DATA_KEY = "actualDataKey"; - public UnmodifiablePartConfigMapDependent() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(UnmodifiableDependentPartCustomResource primary, + protected ConfigMap desired( + UnmodifiableDependentPartCustomResource primary, Context context) { var actual = context.getSecondaryResource(ConfigMap.class); - ConfigMap res = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) - .build(); - res.setData(Map.of(ACTUAL_DATA_KEY, primary.getSpec().getData(), - // setting the old data if available - UNMODIFIABLE_INITIAL_DATA_KEY, - actual.map(cm -> cm.getData().get(UNMODIFIABLE_INITIAL_DATA_KEY)) - .orElse(primary.getSpec().getData()))); + ConfigMap res = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .build(); + res.setData( + Map.of( + ACTUAL_DATA_KEY, + primary.getSpec().getData(), + // setting the old data if available + UNMODIFIABLE_INITIAL_DATA_KEY, + actual + .map(cm -> cm.getData().get(UNMODIFIABLE_INITIAL_DATA_KEY)) + .orElse(primary.getSpec().getData()))); return res; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomResource.java index de877ceb90..b3f260b5f1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomResource.java @@ -10,8 +10,5 @@ @Version("v1") @ShortNames("usc") public class UpdateStatusInCleanupAndRescheduleCustomResource - extends - CustomResource - implements Namespaced { - -} + extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomStatus.java index dcd9d7dd6c..b053235f11 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleCustomStatus.java @@ -12,5 +12,4 @@ public UpdateStatusInCleanupAndRescheduleCustomStatus setCleanupAttempt(Integer this.cleanupAttempt = cleanupAttempt; return this; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java index 81dc6dd448..b2a2b463ba 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleIT.java @@ -12,42 +12,47 @@ public class UpdateStatusInCleanupAndRescheduleIT { public static final String TEST_RESOURCE = "test1"; + @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() .withReconciler(UpdateStatusInCleanupAndRescheduleReconciler.class) .build(); - @Test void testRescheduleAfterPatch() { var res = extension.create(testResource()); - await().untilAsserted(() -> { - var resource = - extension.get(UpdateStatusInCleanupAndRescheduleCustomResource.class, TEST_RESOURCE); - assertThat(resource.getMetadata().getFinalizers()).isNotEmpty(); - }); + await() + .untilAsserted( + () -> { + var resource = + extension.get( + UpdateStatusInCleanupAndRescheduleCustomResource.class, TEST_RESOURCE); + assertThat(resource.getMetadata().getFinalizers()).isNotEmpty(); + }); extension.delete(res); - await().untilAsserted(() -> { - var resource = - extension.get(UpdateStatusInCleanupAndRescheduleCustomResource.class, TEST_RESOURCE); - assertThat(resource).isNull(); - }); - - assertThat(extension.getReconcilerOfType(UpdateStatusInCleanupAndRescheduleReconciler.class) - .getRescheduleDelayWorked()) + await() + .untilAsserted( + () -> { + var resource = + extension.get( + UpdateStatusInCleanupAndRescheduleCustomResource.class, TEST_RESOURCE); + assertThat(resource).isNull(); + }); + + assertThat( + extension + .getReconcilerOfType(UpdateStatusInCleanupAndRescheduleReconciler.class) + .getRescheduleDelayWorked()) .isTrue(); } UpdateStatusInCleanupAndRescheduleCustomResource testResource() { var resource = new UpdateStatusInCleanupAndRescheduleCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE).build()); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleReconciler.java index ed38df7172..29b60bdf57 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/baseapi/updatestatusincleanupandreschedule/UpdateStatusInCleanupAndRescheduleReconciler.java @@ -13,7 +13,7 @@ @ControllerConfiguration public class UpdateStatusInCleanupAndRescheduleReconciler implements Reconciler, - Cleaner { + Cleaner { public static final Integer DELAY = 150; @@ -30,7 +30,8 @@ public UpdateControl reconcile } @Override - public DeleteControl cleanup(UpdateStatusInCleanupAndRescheduleCustomResource resource, + public DeleteControl cleanup( + UpdateStatusInCleanupAndRescheduleCustomResource resource, Context context) { var status = resource.getStatus(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java index d239122314..25926e6405 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/BaseConfigurationServiceTest.java @@ -54,19 +54,21 @@ class BaseConfigurationServiceTest { // subclass to expose configFor method to this test class - private final static class TestConfigurationService extends BaseConfigurationService { + private static final class TestConfigurationService extends BaseConfigurationService { @Override - protected

io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( - Reconciler

reconciler) { + protected

+ io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( + Reconciler

reconciler) { return super.configFor(reconciler); } } private final TestConfigurationService configurationService = new TestConfigurationService(); - private

io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( - Reconciler

reconciler) { + private

+ io.javaoperatorsdk.operator.api.config.ControllerConfiguration

configFor( + Reconciler

reconciler) { // ensure that a new configuration is created each time return configurationService.configFor(reconciler); } @@ -137,7 +139,9 @@ void missingAnnotationCreatesDefaultConfig() { @SuppressWarnings("rawtypes") private DependentResourceSpec findByName( List dependentResourceSpecList, String name) { - return dependentResourceSpecList.stream().filter(d -> d.getName().equals(name)).findFirst() + return dependentResourceSpecList.stream() + .filter(d -> d.getName().equals(name)) + .findFirst() .orElseThrow(); } @@ -158,9 +162,11 @@ void addingDuplicatedDependentsWithNameShouldWork() { var config = configFor(new NamedDuplicatedDepReconciler()); var dependents = config.getWorkflowSpec().orElseThrow().getDependentResourceSpecs(); assertEquals(2, dependents.size()); - assertTrue(findByNameOptional(dependents, NamedDuplicatedDepReconciler.NAME).isPresent() - && findByNameOptional(dependents, DependentResource.defaultNameFor(ReadOnlyDependent.class)) - .isPresent()); + assertTrue( + findByNameOptional(dependents, NamedDuplicatedDepReconciler.NAME).isPresent() + && findByNameOptional( + dependents, DependentResource.defaultNameFor(ReadOnlyDependent.class)) + .isPresent()); } @Test @@ -200,8 +206,7 @@ void configuringRateLimitAndGradualRetryViaSuperClassShouldWork() { final var retry = config.getRetry(); final var testRetry = assertInstanceOf(GenericRetry.class, retry); assertEquals( - BaseClassWithGradualRetryAndRateLimited.RETRY_MAX_ATTEMPTS, - testRetry.getMaxAttempts()); + BaseClassWithGradualRetryAndRateLimited.RETRY_MAX_ATTEMPTS, testRetry.getMaxAttempts()); final var rateLimiter = assertInstanceOf(LinearRateLimiter.class, config.getRateLimiter()); assertEquals( @@ -217,10 +222,11 @@ void checkingRetryingGraduallyWorks() { var config = configFor(new CheckRetryingGraduallyConfiguration()); final var retry = config.getRetry(); final var genericRetry = assertInstanceOf(GenericRetry.class, retry); - assertEquals(CheckRetryingGraduallyConfiguration.INITIAL_INTERVAL, - genericRetry.getInitialInterval()); + assertEquals( + CheckRetryingGraduallyConfiguration.INITIAL_INTERVAL, genericRetry.getInitialInterval()); assertEquals(CheckRetryingGraduallyConfiguration.MAX_ATTEMPTS, genericRetry.getMaxAttempts()); - assertEquals(CheckRetryingGraduallyConfiguration.INTERVAL_MULTIPLIER, + assertEquals( + CheckRetryingGraduallyConfiguration.INTERVAL_MULTIPLIER, genericRetry.getIntervalMultiplier()); assertEquals(CheckRetryingGraduallyConfiguration.MAX_INTERVAL, genericRetry.getMaxInterval()); } @@ -247,8 +253,9 @@ void excludedResourceClassesShouldNotUseSSAByDefault() { final var kubernetesDependentResourceConfig = extractDependentKubernetesResourceConfig(config, 1); assertNotNull(kubernetesDependentResourceConfig); - assertFalse(configurationService.shouldUseSSA(ReadOnlyDependent.class, ConfigMap.class, - kubernetesDependentResourceConfig)); + assertFalse( + configurationService.shouldUseSSA( + ReadOnlyDependent.class, ConfigMap.class, kubernetesDependentResourceConfig)); } @Test @@ -261,8 +268,11 @@ void excludedResourceClassesShouldUseSSAIfAnnotatedToDoSo() { extractDependentKubernetesResourceConfig(config, 0); assertNotNull(kubernetesDependentResourceConfig); assertTrue(kubernetesDependentResourceConfig.useSSA()); - assertTrue(configurationService.shouldUseSSA(SelectorReconciler.WithAnnotation.class, - ConfigMap.class, kubernetesDependentResourceConfig)); + assertTrue( + configurationService.shouldUseSSA( + SelectorReconciler.WithAnnotation.class, + ConfigMap.class, + kubernetesDependentResourceConfig)); } @Test @@ -272,26 +282,32 @@ void dependentsShouldUseSSAByDefaultIfNotExcluded() { var kubernetesDependentResourceConfig = extractDependentKubernetesResourceConfig(config, 0); assertNotNull(kubernetesDependentResourceConfig); - assertTrue(configurationService.shouldUseSSA( - DefaultSSAForDependentsReconciler.DefaultDependent.class, ConfigMapReader.class, - kubernetesDependentResourceConfig)); + assertTrue( + configurationService.shouldUseSSA( + DefaultSSAForDependentsReconciler.DefaultDependent.class, + ConfigMapReader.class, + kubernetesDependentResourceConfig)); kubernetesDependentResourceConfig = extractDependentKubernetesResourceConfig(config, 1); assertNotNull(kubernetesDependentResourceConfig); assertFalse(kubernetesDependentResourceConfig.useSSA()); - assertFalse(configurationService - .shouldUseSSA(DefaultSSAForDependentsReconciler.NonSSADependent.class, Service.class, + assertFalse( + configurationService.shouldUseSSA( + DefaultSSAForDependentsReconciler.NonSSADependent.class, + Service.class, kubernetesDependentResourceConfig)); } @Test void shouldUseSSAShouldAlsoWorkWithManualConfiguration() { var reconciler = new DependentSSAReconciler(true); - assertEquals(reconciler.isUseSSA(), + assertEquals( + reconciler.isUseSSA(), configurationService.shouldUseSSA(reconciler.getSsaConfigMapDependent())); reconciler = new DependentSSAReconciler(false); - assertEquals(reconciler.isUseSSA(), + assertEquals( + reconciler.isUseSSA(), configurationService.shouldUseSSA(reconciler.getSsaConfigMapDependent())); } @@ -304,8 +320,8 @@ private static int getValue( } @ControllerConfiguration( - maxReconciliationInterval = @MaxReconciliationInterval(interval = 50, - timeUnit = TimeUnit.SECONDS)) + maxReconciliationInterval = + @MaxReconciliationInterval(interval = 50, timeUnit = TimeUnit.SECONDS)) private static class MaxIntervalReconciler implements Reconciler { @Override @@ -315,15 +331,14 @@ public UpdateControl reconcile(ConfigMap resource, Context } @Workflow(dependents = @Dependent(type = ReadOnlyDependent.class)) - @ControllerConfiguration( - informer = @Informer(namespaces = OneDepReconciler.CONFIGURED_NS)) + @ControllerConfiguration(informer = @Informer(namespaces = OneDepReconciler.CONFIGURED_NS)) private static class OneDepReconciler implements Reconciler { private static final String CONFIGURED_NS = "foo"; @Override - public UpdateControl reconcile(ConfigMapReader resource, - Context context) { + public UpdateControl reconcile( + ConfigMapReader resource, Context context) { return null; } } @@ -335,38 +350,40 @@ private static class NamedDepReconciler implements Reconciler { private static final String NAME = "foo"; @Override - public UpdateControl reconcile(ConfigMapReader resource, - Context context) { + public UpdateControl reconcile( + ConfigMapReader resource, Context context) { return null; } } - @Workflow(dependents = { - @Dependent(type = ReadOnlyDependent.class), - @Dependent(type = ReadOnlyDependent.class) - }) + @Workflow( + dependents = { + @Dependent(type = ReadOnlyDependent.class), + @Dependent(type = ReadOnlyDependent.class) + }) @ControllerConfiguration private static class DuplicatedDepReconciler implements Reconciler { @Override - public UpdateControl reconcile(ConfigMapReader resource, - Context context) { + public UpdateControl reconcile( + ConfigMapReader resource, Context context) { return null; } } - @Workflow(dependents = { - @Dependent(type = ReadOnlyDependent.class, name = NamedDuplicatedDepReconciler.NAME), - @Dependent(type = ReadOnlyDependent.class) - }) + @Workflow( + dependents = { + @Dependent(type = ReadOnlyDependent.class, name = NamedDuplicatedDepReconciler.NAME), + @Dependent(type = ReadOnlyDependent.class) + }) @ControllerConfiguration private static class NamedDuplicatedDepReconciler implements Reconciler { private static final String NAME = "duplicated"; @Override - public UpdateControl reconcile(ConfigMapReader resource, - Context context) { + public UpdateControl reconcile( + ConfigMapReader resource, Context context) { return null; } } @@ -375,33 +392,29 @@ public UpdateControl reconcile(ConfigMapReader resource, private static class NoDepReconciler implements Reconciler { @Override - public UpdateControl reconcile(ConfigMapReader resource, - Context context) { + public UpdateControl reconcile( + ConfigMapReader resource, Context context) { return null; } } - @Workflow(dependents = { - @Dependent(type = SelectorReconciler.WithAnnotation.class), - @Dependent(type = ReadOnlyDependent.class) - }) + @Workflow( + dependents = { + @Dependent(type = SelectorReconciler.WithAnnotation.class), + @Dependent(type = ReadOnlyDependent.class) + }) @ControllerConfiguration public static class SelectorReconciler implements Reconciler { @Override - public UpdateControl reconcile(ConfigMapReader resource, - Context context) { + public UpdateControl reconcile( + ConfigMapReader resource, Context context) { return null; } @KubernetesDependent(useSSA = BooleanWithUndefined.TRUE) public static class WithAnnotation - extends CRUDKubernetesDependentResource { - - public WithAnnotation() { - super(ConfigMap.class); - } - } + extends CRUDKubernetesDependentResource {} } public static class MissingAnnotationReconciler implements Reconciler { @@ -414,8 +427,8 @@ public UpdateControl reconcile(ConfigMap resource, Context @Workflow( dependents = { - @Dependent(type = DefaultSSAForDependentsReconciler.DefaultDependent.class), - @Dependent(type = DefaultSSAForDependentsReconciler.NonSSADependent.class) + @Dependent(type = DefaultSSAForDependentsReconciler.DefaultDependent.class), + @Dependent(type = DefaultSSAForDependentsReconciler.NonSSADependent.class) }) private static class DefaultSSAForDependentsReconciler implements Reconciler { @@ -425,18 +438,10 @@ public UpdateControl reconcile(ConfigMap resource, Context } private static class DefaultDependent - extends KubernetesDependentResource { - public DefaultDependent() { - super(ConfigMapReader.class); - } - } + extends KubernetesDependentResource {} @KubernetesDependent(useSSA = BooleanWithUndefined.FALSE) - private static class NonSSADependent extends KubernetesDependentResource { - public NonSSADependent() { - super(Service.class); - } - } + private static class NonSSADependent extends KubernetesDependentResource {} } public static class TestRetry implements Retry, AnnotationConfigurable { @@ -499,8 +504,7 @@ public UpdateControl reconcile(ConfigMap resource, Context @ControllerConfiguration private static class GradualRetryAndRateLimitedOnSuperClass - extends BaseClassWithGradualRetryAndRateLimited - implements Reconciler { + extends BaseClassWithGradualRetryAndRateLimited implements Reconciler { @Override public UpdateControl reconcile(ConfigMap resource, Context context) { @@ -519,9 +523,7 @@ private static class BaseClassWithGradualRetryAndRateLimited { public static final int RETRY_MAX_ATTEMPTS = 3; } - private static class ControllerConfigurationOnSuperClass extends BaseClass { - - } + private static class ControllerConfigurationOnSuperClass extends BaseClass {} @ControllerConfiguration private static class BaseClass implements Reconciler { @@ -532,10 +534,11 @@ public UpdateControl reconcile(ConfigMap resource, Context } } - @Workflow(dependents = { - @Dependent(type = CustomAnnotatedDep.class), - @Dependent(type = ChildCustomAnnotatedDep.class) - }) + @Workflow( + dependents = { + @Dependent(type = CustomAnnotatedDep.class), + @Dependent(type = ChildCustomAnnotatedDep.class) + }) @ControllerConfiguration() private static class CustomAnnotationReconciler implements Reconciler { @@ -546,10 +549,13 @@ public UpdateControl reconcile(ConfigMap resource, Context } @CustomAnnotation(value = CustomAnnotatedDep.PROVIDED_VALUE) - @Configured(by = CustomAnnotation.class, with = CustomConfig.class, + @Configured( + by = CustomAnnotation.class, + with = CustomConfig.class, converter = CustomConfigConverter.class) - private static class CustomAnnotatedDep implements DependentResource, - ConfiguredDependentResource { + private static class CustomAnnotatedDep + implements DependentResource, + ConfiguredDependentResource { public static final int PROVIDED_VALUE = 42; private CustomConfig config; @@ -575,9 +581,7 @@ public Optional configuration() { } } - private static class ChildCustomAnnotatedDep extends CustomAnnotatedDep { - - } + private static class ChildCustomAnnotatedDep extends CustomAnnotatedDep {} @Retention(RetentionPolicy.RUNTIME) private @interface CustomAnnotation { @@ -593,7 +597,8 @@ private static class CustomConfigConverter static final int CONVERTER_PROVIDED_DEFAULT = 7; @Override - public CustomConfig configFrom(CustomAnnotation configAnnotation, + public CustomConfig configFrom( + CustomAnnotation configAnnotation, DependentResourceSpec spec, io.javaoperatorsdk.operator.api.config.ControllerConfiguration parentConfiguration) { if (configAnnotation == null) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java index 88fd33e946..5a9599840f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/DefaultConfigurationServiceTest.java @@ -22,8 +22,8 @@ class DefaultConfigurationServiceTest { void returnsValuesFromControllerAnnotationFinalizer() { final var reconciler = new TestCustomReconciler(); final var configuration = configurationService.getConfigurationFor(reconciler); - assertEquals(CustomResource.getCRDName(TestCustomResource.class), - configuration.getResourceTypeName()); + assertEquals( + CustomResource.getCRDName(TestCustomResource.class), configuration.getResourceTypeName()); assertEquals( ReconcilerUtils.getDefaultFinalizerName(TestCustomResource.class), configuration.getFinalizerName()); @@ -59,8 +59,7 @@ public UpdateControl reconcil @Group("test.crd") @Version("v1") - public static class InnerCustomResource extends CustomResource { - } + public static class InnerCustomResource extends CustomResource {} } @ControllerConfiguration(name = NotAutomaticallyCreated.NAME) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/TestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/TestCustomResource.java index 0f94ae92e4..14956f470d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/TestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/config/runtime/TestCustomResource.java @@ -7,5 +7,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") -class TestCustomResource extends CustomResource implements Namespaced { -} +class TestCustomResource extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java index be7365951e..108607283b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentDeleterIT.java @@ -9,7 +9,8 @@ public class BulkDependentDeleterIT extends BulkDependentTestBase { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new ManagedDeleterBulkReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new ManagedDeleterBulkReconciler()) .build(); @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestBase.java index 0441e31205..49c3b0bb76 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestBase.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestBase.java @@ -47,29 +47,43 @@ public void updatesData() { private void assertNumberOfConfigMaps(int n) { // this test was failing with a lower timeout on GitHub, probably the garbage collection was // slower there. - await().atMost(Duration.ofSeconds(30)) - .untilAsserted(() -> { - var cms = - extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) - .withLabel(LABEL_KEY, LABEL_VALUE) - .list().getItems(); - assertThat(cms).withFailMessage("Number of items is still: " + cms.size()) - .hasSize(n); - }); + await() + .atMost(Duration.ofSeconds(30)) + .untilAsserted( + () -> { + var cms = + extension() + .getKubernetesClient() + .configMaps() + .inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list() + .getItems(); + assertThat(cms).withFailMessage("Number of items is still: " + cms.size()).hasSize(n); + }); } private void assertAdditionalDataOnConfigMaps(String expectedValue) { - await().atMost(Duration.ofSeconds(30)) - .untilAsserted(() -> { - var cms = - extension().getKubernetesClient().configMaps().inNamespace(extension().getNamespace()) - .withLabel(LABEL_KEY, LABEL_VALUE) - .list().getItems(); - cms.forEach(cm -> { - assertThat(cm.getData().get(ConfigMapDeleterBulkDependentResource.ADDITIONAL_DATA_KEY)) - .isEqualTo(expectedValue); - }); - }); + await() + .atMost(Duration.ofSeconds(30)) + .untilAsserted( + () -> { + var cms = + extension() + .getKubernetesClient() + .configMaps() + .inNamespace(extension().getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list() + .getItems(); + cms.forEach( + cm -> { + assertThat( + cm.getData() + .get(ConfigMapDeleterBulkDependentResource.ADDITIONAL_DATA_KEY)) + .isEqualTo(expectedValue); + }); + }); } public static BulkDependentTestCustomResource testResource() { @@ -88,8 +102,8 @@ private void updateSpecWithNewAdditionalData(String data) { extension().replace(resource); } - public static void updateSpecWithNewAdditionalData(LocallyRunOperatorExtension extension, - String data) { + public static void updateSpecWithNewAdditionalData( + LocallyRunOperatorExtension extension, String data) { var resource = testResource(); resource.getSpec().setAdditionalData(data); extension.replace(resource); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestCustomResource.java index f7451e06d6..f9d527ccd0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/BulkDependentTestCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("sbd") public class BulkDependentTestCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/CRUDConfigMapBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/CRUDConfigMapBulkDependentResource.java index 236118c2c7..07652e90dc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/CRUDConfigMapBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/CRUDConfigMapBulkDependentResource.java @@ -3,5 +3,4 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.GarbageCollected; public class CRUDConfigMapBulkDependentResource extends ConfigMapDeleterBulkDependentResource - implements GarbageCollected { -} + implements GarbageCollected {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java index cba3db3835..cf3c96b82a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/ConfigMapDeleterBulkDependentResource.java @@ -11,12 +11,9 @@ import io.javaoperatorsdk.operator.processing.dependent.*; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; -/** - * Not using CRUDKubernetesDependentResource so the delete functionality can be tested. - */ +/** Not using CRUDKubernetesDependentResource so the delete functionality can be tested. */ public class ConfigMapDeleterBulkDependentResource - extends - KubernetesDependentResource + extends KubernetesDependentResource implements CRUDBulkDependentResource { public static final String LABEL_KEY = "bulk"; @@ -24,13 +21,9 @@ public class ConfigMapDeleterBulkDependentResource public static final String ADDITIONAL_DATA_KEY = "additionalData"; public static final String INDEX_DELIMITER = "-"; - public ConfigMapDeleterBulkDependentResource() { - super(ConfigMap.class); - } - @Override - public Map desiredResources(BulkDependentTestCustomResource primary, - Context context) { + public Map desiredResources( + BulkDependentTestCustomResource primary, Context context) { var number = primary.getSpec().getNumberOfResources(); Map res = new HashMap<>(); for (int i = 0; i < number; i++) { @@ -42,24 +35,27 @@ public Map desiredResources(BulkDependentTestCustomResource p public ConfigMap desired(BulkDependentTestCustomResource primary, String key) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName() + INDEX_DELIMITER + key) - .withNamespace(primary.getMetadata().getNamespace()) - .withLabels(Map.of(LABEL_KEY, LABEL_VALUE)) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName() + INDEX_DELIMITER + key) + .withNamespace(primary.getMetadata().getNamespace()) + .withLabels(Map.of(LABEL_KEY, LABEL_VALUE)) + .build()); configMap.setData( Map.of("number", key, ADDITIONAL_DATA_KEY, primary.getSpec().getAdditionalData())); return configMap; } @Override - public Map getSecondaryResources(BulkDependentTestCustomResource primary, - Context context) { - return context.getSecondaryResourcesAsStream(ConfigMap.class) + public Map getSecondaryResources( + BulkDependentTestCustomResource primary, Context context) { + return context + .getSecondaryResourcesAsStream(ConfigMap.class) .filter(cm -> getName(cm).startsWith(primary.getMetadata().getName())) - .collect(Collectors.toMap( - cm -> getName(cm).substring(getName(cm).lastIndexOf(INDEX_DELIMITER) + 1), - Function.identity())); + .collect( + Collectors.toMap( + cm -> getName(cm).substring(getName(cm).lastIndexOf(INDEX_DELIMITER) + 1), + Function.identity())); } private static String getName(ConfigMap cm) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java index d4b857bf0c..eb3a6e7368 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/BulkDependentWithConditionIT.java @@ -26,20 +26,25 @@ void handlesBulkDependentWithPrecondition() { var resource = testResource(); extension.create(resource); - await().untilAsserted(() -> { - var res = extension.get(BulkDependentTestCustomResource.class, - testResource().getMetadata().getName()); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getReady()).isTrue(); - - var cms = extension.getKubernetesClient().configMaps().inNamespace(extension.getNamespace()) - .withLabel(LABEL_KEY, LABEL_VALUE) - .list().getItems(); - assertThat(cms).hasSize(INITIAL_NUMBER_OF_CONFIG_MAPS); - - }); + await() + .untilAsserted( + () -> { + var res = + extension.get( + BulkDependentTestCustomResource.class, + testResource().getMetadata().getName()); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getReady()).isTrue(); + + var cms = + extension + .getKubernetesClient() + .configMaps() + .inNamespace(extension.getNamespace()) + .withLabel(LABEL_KEY, LABEL_VALUE) + .list() + .getItems(); + assertThat(cms).hasSize(INITIAL_NUMBER_OF_CONFIG_MAPS); + }); } - - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/ManagedBulkDependentWithReadyConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/ManagedBulkDependentWithReadyConditionReconciler.java index 1dc48386cb..dca83304d1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/ManagedBulkDependentWithReadyConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/ManagedBulkDependentWithReadyConditionReconciler.java @@ -8,8 +8,11 @@ import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestStatus; import io.javaoperatorsdk.operator.dependent.bulkdependent.CRUDConfigMapBulkDependentResource; -@Workflow(dependents = @Dependent(readyPostcondition = SampleBulkCondition.class, - type = CRUDConfigMapBulkDependentResource.class)) +@Workflow( + dependents = + @Dependent( + readyPostcondition = SampleBulkCondition.class, + type = CRUDConfigMapBulkDependentResource.class)) @ControllerConfiguration() public class ManagedBulkDependentWithReadyConditionReconciler implements Reconciler { @@ -18,14 +21,16 @@ public class ManagedBulkDependentWithReadyConditionReconciler @Override public UpdateControl reconcile( - BulkDependentTestCustomResource resource, - Context context) throws Exception { + BulkDependentTestCustomResource resource, Context context) + throws Exception { numberOfExecutions.incrementAndGet(); - var ready = context.managedWorkflowAndDependentResourceContext().getWorkflowReconcileResult() - .orElseThrow() - .allDependentResourcesReady(); - + var ready = + context + .managedWorkflowAndDependentResourceContext() + .getWorkflowReconcileResult() + .orElseThrow() + .allDependentResourcesReady(); resource.setStatus(new BulkDependentTestStatus()); resource.getStatus().setReady(ready); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/SampleBulkCondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/SampleBulkCondition.java index c6e64b7413..b626742ff8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/SampleBulkCondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/condition/SampleBulkCondition.java @@ -7,8 +7,7 @@ import io.javaoperatorsdk.operator.dependent.bulkdependent.CRUDConfigMapBulkDependentResource; import io.javaoperatorsdk.operator.processing.dependent.workflow.Condition; -public class SampleBulkCondition - implements Condition { +public class SampleBulkCondition implements Condition { // We use ConfigMaps here just to show how to check some properties of resources managed by a // BulkDependentResource. In real life example this would be rather based on some status of those @@ -20,8 +19,9 @@ public boolean isMet( BulkDependentTestCustomResource primary, Context context) { - var resources = ((CRUDConfigMapBulkDependentResource) dependentResource) - .getSecondaryResources(primary, context); + var resources = + ((CRUDConfigMapBulkDependentResource) dependentResource) + .getSecondaryResources(primary, context); return resources.values().stream().noneMatch(cm -> cm.getData().isEmpty()); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java index d25f92d2be..4f7d425729 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/BulkExternalDependentIT.java @@ -13,7 +13,8 @@ class BulkExternalDependentIT { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new ExternalBulkResourceReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new ExternalBulkResourceReconciler()) .build(); ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); @@ -33,7 +34,6 @@ void managesExternalBulkResources() { assertResourceNumberAndData(0, INITIAL_ADDITIONAL_DATA); } - @Test void handlesResourceUpdates() { extension.create(testResource()); @@ -44,11 +44,12 @@ void handlesResourceUpdates() { } private void assertResourceNumberAndData(int n, String data) { - await().untilAsserted(() -> { - var resources = externalServiceMock.listResources(); - assertThat(resources).hasSize(n); - assertThat(resources).allMatch(r -> r.getData().equals(data)); - }); + await() + .untilAsserted( + () -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(n); + assertThat(resources).allMatch(r -> r.getData().equals(data)); + }); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java index 5112b09be1..89aa6c4ff3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalBulkDependentResource.java @@ -18,9 +18,9 @@ public class ExternalBulkDependentResource extends PollingDependentResource implements BulkDependentResource, - Creator, - Deleter, - BulkUpdater { + Creator, + Deleter, + BulkUpdater { public static final String EXTERNAL_RESOURCE_NAME_DELIMITER = "#"; @@ -34,30 +34,38 @@ public ExternalBulkDependentResource() { public Map> fetchResources() { Map> result = new HashMap<>(); var resources = externalServiceMock.listResources(); - resources.forEach(er -> { - var resourceID = toResourceID(er); - result.putIfAbsent(resourceID, new HashSet<>()); - result.get(resourceID).add(er); - }); + resources.forEach( + er -> { + var resourceID = toResourceID(er); + result.putIfAbsent(resourceID, new HashSet<>()); + result.get(resourceID).add(er); + }); return result; } @Override - public ExternalResource create(ExternalResource desired, BulkDependentTestCustomResource primary, + public ExternalResource create( + ExternalResource desired, + BulkDependentTestCustomResource primary, Context context) { return externalServiceMock.create(desired); } @Override - public ExternalResource update(ExternalResource actual, ExternalResource desired, - BulkDependentTestCustomResource primary, Context context) { + public ExternalResource update( + ExternalResource actual, + ExternalResource desired, + BulkDependentTestCustomResource primary, + Context context) { return externalServiceMock.update(desired); } private static String toExternalResourceId(BulkDependentTestCustomResource primary, String i) { - return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + - primary.getMetadata().getNamespace() + - EXTERNAL_RESOURCE_NAME_DELIMITER + i; + return primary.getMetadata().getName() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + i; } private ResourceID toResourceID(ExternalResource externalResource) { @@ -66,34 +74,44 @@ private ResourceID toResourceID(ExternalResource externalResource) { } @Override - public Map desiredResources(BulkDependentTestCustomResource primary, - Context context) { + public Map desiredResources( + BulkDependentTestCustomResource primary, Context context) { var number = primary.getSpec().getNumberOfResources(); Map res = new HashMap<>(); for (int i = 0; i < number; i++) { var key = Integer.toString(i); - res.put(key, new ExternalResource(toExternalResourceId(primary, key), - primary.getSpec().getAdditionalData())); + res.put( + key, + new ExternalResource( + toExternalResourceId(primary, key), primary.getSpec().getAdditionalData())); } return res; } @Override public Map getSecondaryResources( - BulkDependentTestCustomResource primary, - Context context) { - return context.getSecondaryResourcesAsStream(resourceType()) - .filter(r -> r.getId() - .startsWith(primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + - primary.getMetadata().getNamespace() + - EXTERNAL_RESOURCE_NAME_DELIMITER)) - .collect(Collectors.toMap( - r -> r.getId().substring(r.getId().lastIndexOf(EXTERNAL_RESOURCE_NAME_DELIMITER) + 1), - r -> r)); + BulkDependentTestCustomResource primary, Context context) { + return context + .getSecondaryResourcesAsStream(resourceType()) + .filter( + r -> + r.getId() + .startsWith( + primary.getMetadata().getName() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER)) + .collect( + Collectors.toMap( + r -> + r.getId() + .substring(r.getId().lastIndexOf(EXTERNAL_RESOURCE_NAME_DELIMITER) + 1), + r -> r)); } @Override - public void deleteTargetResource(BulkDependentTestCustomResource primary, + public void deleteTargetResource( + BulkDependentTestCustomResource primary, ExternalResource resource, String key, Context context) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java index dfe8468fff..41c5fa5095 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/external/ExternalResource.java @@ -22,10 +22,8 @@ public String getData() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; ExternalResource that = (ExternalResource) o; return Objects.equals(id, that.id) && Objects.equals(data, that.data); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java index 9f3d763f1b..61b7f62a24 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentIT.java @@ -9,10 +9,10 @@ public class ManagedBulkDependentIT extends BulkDependentTestBase { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new ManagedBulkDependentReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new ManagedBulkDependentReconciler()) .build(); - @Override public LocallyRunOperatorExtension extension() { return extension; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentReconciler.java index be323949aa..b0361a8edf 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedBulkDependentReconciler.java @@ -9,15 +9,14 @@ @Workflow(dependents = @Dependent(type = CRUDConfigMapBulkDependentResource.class)) @ControllerConfiguration -public class ManagedBulkDependentReconciler - implements Reconciler { +public class ManagedBulkDependentReconciler implements Reconciler { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override public UpdateControl reconcile( - BulkDependentTestCustomResource resource, - Context context) throws Exception { + BulkDependentTestCustomResource resource, Context context) + throws Exception { numberOfExecutions.addAndGet(1); return UpdateControl.noUpdate(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedDeleterBulkReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedDeleterBulkReconciler.java index 7de2b3898f..48fb5dcfce 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedDeleterBulkReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/managed/ManagedDeleterBulkReconciler.java @@ -10,8 +10,7 @@ public class ManagedDeleterBulkReconciler implements Reconciler { @Override public UpdateControl reconcile( - BulkDependentTestCustomResource resource, - Context context) + BulkDependentTestCustomResource resource, Context context) throws Exception { return UpdateControl.noUpdate(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java index c84ad38de0..422360b7b2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentIT.java @@ -21,51 +21,52 @@ public class ReadOnlyBulkDependentIT { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder() - .withReconciler(new ReadOnlyBulkReconciler()) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(new ReadOnlyBulkReconciler()).build(); @Test void readOnlyBulkDependent() { var primary = extension.create(testCustomResource()); - await().pollDelay(Duration.ofMillis(150)).untilAsserted(() -> { - var actualPrimary = extension.get(BulkDependentTestCustomResource.class, TEST); + await() + .pollDelay(Duration.ofMillis(150)) + .untilAsserted( + () -> { + var actualPrimary = extension.get(BulkDependentTestCustomResource.class, TEST); - assertThat(actualPrimary.getStatus()).isNotNull(); - assertThat(actualPrimary.getStatus().getReady()).isFalse(); - }); + assertThat(actualPrimary.getStatus()).isNotNull(); + assertThat(actualPrimary.getStatus().getReady()).isFalse(); + }); var configMap1 = createConfigMap(1, primary); extension.create(configMap1); var configMap2 = createConfigMap(2, primary); extension.create(configMap2); - await().untilAsserted(() -> { - var actualPrimary = extension.get(BulkDependentTestCustomResource.class, TEST); - assertThat(actualPrimary.getStatus().getReady()).isTrue(); - }); + await() + .untilAsserted( + () -> { + var actualPrimary = extension.get(BulkDependentTestCustomResource.class, TEST); + assertThat(actualPrimary.getStatus().getReady()).isTrue(); + }); } private ConfigMap createConfigMap(int i, BulkDependentTestCustomResource primary) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(TEST + ReadOnlyBulkDependentResource.INDEX_DELIMITER + i) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(TEST + ReadOnlyBulkDependentResource.INDEX_DELIMITER + i) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.addOwnerReference(primary); return configMap; } BulkDependentTestCustomResource testCustomResource() { BulkDependentTestCustomResource customResource = new BulkDependentTestCustomResource(); - customResource.setMetadata(new ObjectMetaBuilder() - .withName(TEST) - .build()); + customResource.setMetadata(new ObjectMetaBuilder().withName(TEST).build()); customResource.setSpec(new BulkDependentTestSpec()); customResource.getSpec().setNumberOfResources(EXPECTED_NUMBER_OF_RESOURCES); return customResource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java index e655d96d6e..1eab400888 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkDependentResource.java @@ -15,28 +15,24 @@ import io.javaoperatorsdk.operator.processing.event.source.SecondaryToPrimaryMapper; import io.javaoperatorsdk.operator.processing.event.source.informer.Mappers; - @KubernetesDependent public class ReadOnlyBulkDependentResource - extends - KubernetesDependentResource + extends KubernetesDependentResource implements BulkDependentResource, - SecondaryToPrimaryMapper { + SecondaryToPrimaryMapper { public static final String INDEX_DELIMITER = "-"; - public ReadOnlyBulkDependentResource() { - super(ConfigMap.class); - } - @Override - public Map getSecondaryResources(BulkDependentTestCustomResource primary, - Context context) { - return context.getSecondaryResourcesAsStream(ConfigMap.class) + public Map getSecondaryResources( + BulkDependentTestCustomResource primary, Context context) { + return context + .getSecondaryResourcesAsStream(ConfigMap.class) .filter(cm -> getName(cm).startsWith(primary.getMetadata().getName())) - .collect(Collectors.toMap( - cm -> getName(cm).substring(getName(cm).lastIndexOf(INDEX_DELIMITER) + 1), - Function.identity())); + .collect( + Collectors.toMap( + cm -> getName(cm).substring(getName(cm).lastIndexOf(INDEX_DELIMITER) + 1), + Function.identity())); } private static String getName(ConfigMap cm) { @@ -48,5 +44,4 @@ public Set toPrimaryResourceIDs(ConfigMap resource) { return Mappers.fromOwnerReferences(BulkDependentTestCustomResource.class, false) .toPrimaryResourceIDs(resource); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java index 9be51eb1f6..a7fb40fcc0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReadyPostCondition.java @@ -12,7 +12,8 @@ public class ReadOnlyBulkReadyPostCondition @Override public boolean isMet( DependentResource dependentResource, - BulkDependentTestCustomResource primary, Context context) { + BulkDependentTestCustomResource primary, + Context context) { var minResourceNumber = primary.getSpec().getNumberOfResources(); @SuppressWarnings("unchecked") var secondaryResources = diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReconciler.java index bec0823914..7d43777f12 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/readonly/ReadOnlyBulkReconciler.java @@ -6,8 +6,11 @@ import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestCustomResource; import io.javaoperatorsdk.operator.dependent.bulkdependent.BulkDependentTestStatus; -@Workflow(dependents = @Dependent(type = ReadOnlyBulkDependentResource.class, - readyPostcondition = ReadOnlyBulkReadyPostCondition.class)) +@Workflow( + dependents = + @Dependent( + type = ReadOnlyBulkDependentResource.class, + readyPostcondition = ReadOnlyBulkReadyPostCondition.class)) @ControllerConfiguration public class ReadOnlyBulkReconciler implements Reconciler { @Override @@ -15,16 +18,18 @@ public UpdateControl reconcile( BulkDependentTestCustomResource resource, Context context) { var nonReadyDependents = - context.managedWorkflowAndDependentResourceContext().getWorkflowReconcileResult() + context + .managedWorkflowAndDependentResourceContext() + .getWorkflowReconcileResult() .orElseThrow() .getNotReadyDependents(); - BulkDependentTestCustomResource customResource = new BulkDependentTestCustomResource(); - customResource.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); + customResource.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); var status = new BulkDependentTestStatus(); status.setReady(nonReadyDependents.isEmpty()); customResource.setStatus(status); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java index 5e0ded6100..a74102db0d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentIT.java @@ -9,7 +9,8 @@ class StandaloneBulkDependentIT extends BulkDependentTestBase { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new StandaloneBulkDependentReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new StandaloneBulkDependentReconciler()) .build(); @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentReconciler.java index ab74c41906..6aa87737d4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/bulkdependent/standalone/StandaloneBulkDependentReconciler.java @@ -24,8 +24,7 @@ public StandaloneBulkDependentReconciler() { @Override public UpdateControl reconcile( - BulkDependentTestCustomResource resource, - Context context) { + BulkDependentTestCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); dependent.reconcile(resource, context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentCustomResource.java index c5cd602ba5..279cf96d2e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentCustomResource.java @@ -11,7 +11,5 @@ @Version("v1") @Kind("CleanerForReconcilerCustomResource") @ShortNames("cfr") -public class CleanerForManagedDependentCustomResource - extends CustomResource - implements Namespaced { -} +public class CleanerForManagedDependentCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java index 000fe2b28e..b0b716e8e2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentResourcesOnlyIT.java @@ -19,21 +19,27 @@ class CleanerForManagedDependentResourcesOnlyIT { .withReconciler(new CleanerForManagedDependentTestReconciler()) .build(); - @Test void addsFinalizerAndCallsCleanupIfCleanerImplemented() { var testResource = createTestResource(); operator.create(testResource); - await().until( - () -> !operator.get(CleanerForManagedDependentCustomResource.class, TEST_RESOURCE_NAME) - .getMetadata().getFinalizers().isEmpty()); + await() + .until( + () -> + !operator + .get(CleanerForManagedDependentCustomResource.class, TEST_RESOURCE_NAME) + .getMetadata() + .getFinalizers() + .isEmpty()); operator.delete(testResource); - await().until( - () -> operator.get(CleanerForManagedDependentCustomResource.class, - TEST_RESOURCE_NAME) == null); + await() + .until( + () -> + operator.get(CleanerForManagedDependentCustomResource.class, TEST_RESOURCE_NAME) + == null); CleanerForManagedDependentTestReconciler reconciler = (CleanerForManagedDependentTestReconciler) operator.getFirstReconciler(); @@ -48,5 +54,4 @@ private CleanerForManagedDependentCustomResource createTestResource() { cr.getMetadata().setName(TEST_RESOURCE_NAME); return cr; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java index bf9a45e29e..d3181d62f1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/CleanerForManagedDependentTestReconciler.java @@ -9,8 +9,7 @@ @Workflow(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) @ControllerConfiguration public class CleanerForManagedDependentTestReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -25,5 +24,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/ConfigMapDependentResource.java index fbb964f196..3c94775045 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/cleanermanageddependent/ConfigMapDependentResource.java @@ -11,20 +11,17 @@ import io.javaoperatorsdk.operator.processing.dependent.Updater; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; -public class ConfigMapDependentResource extends - KubernetesDependentResource +public class ConfigMapDependentResource + extends KubernetesDependentResource implements Creator, - Updater, - Deleter { + Updater, + Deleter { private static final AtomicInteger numberOfCleanupExecutions = new AtomicInteger(0); - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(CleanerForManagedDependentCustomResource primary, + protected ConfigMap desired( + CleanerForManagedDependentCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); @@ -38,7 +35,8 @@ protected ConfigMap desired(CleanerForManagedDependentCustomResource primary, } @Override - public void delete(CleanerForManagedDependentCustomResource primary, + public void delete( + CleanerForManagedDependentCustomResource primary, Context context) { super.delete(primary, context); numberOfCleanupExecutions.incrementAndGet(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/ConfigMapDependentResource.java index 7d7c62e6b4..67532ee159 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/ConfigMapDependentResource.java @@ -7,21 +7,20 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; -public class ConfigMapDependentResource extends - CRUDKubernetesDependentResource { - - public ConfigMapDependentResource() { - super(ConfigMap.class); - } +public class ConfigMapDependentResource + extends CRUDKubernetesDependentResource< + ConfigMap, CreateOnlyIfNotExistingDependentWithSSACustomResource> { @Override - protected ConfigMap desired(CreateOnlyIfNotExistingDependentWithSSACustomResource primary, + protected ConfigMap desired( + CreateOnlyIfNotExistingDependentWithSSACustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of("drkey", "v")); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSACustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSACustomResource.java index 3e53f422bd..0b2e8b1ef6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSACustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSACustomResource.java @@ -8,6 +8,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") public class CreateOnlyIfNotExistingDependentWithSSACustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java index 82e41c4a1d..5c1923fa55 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAIT.java @@ -25,31 +25,30 @@ class CreateOnlyIfNotExistingDependentWithSSAIT { .withReconciler(new CreateOnlyIfNotExistingDependentWithSSAReconciler()) .build(); - @Test void createsResourceOnlyIfNotExisting() { - var cm = new ConfigMapBuilder().withMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()) - .withData(Map.of(KEY, "val")) - .build(); + var cm = + new ConfigMapBuilder() + .withMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()) + .withData(Map.of(KEY, "val")) + .build(); extension.create(cm); extension.create(testResource()); - await().pollDelay(Duration.ofMillis(200)).untilAsserted(() -> { - var currentCM = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(currentCM.getData()).containsKey(KEY); - }); + await() + .pollDelay(Duration.ofMillis(200)) + .untilAsserted( + () -> { + var currentCM = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(currentCM.getData()).containsKey(KEY); + }); } CreateOnlyIfNotExistingDependentWithSSACustomResource testResource() { var res = new CreateOnlyIfNotExistingDependentWithSSACustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java index 3699cd1a78..fbfc7e1a6d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/createonlyifnotexistsdependentwithssa/CreateOnlyIfNotExistingDependentWithSSAReconciler.java @@ -5,8 +5,7 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class)}) +@Workflow(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) @ControllerConfiguration() public class CreateOnlyIfNotExistingDependentWithSSAReconciler implements Reconciler { @@ -24,7 +23,4 @@ public UpdateControl reco public int getNumberOfExecutions() { return numberOfExecutions.get(); } - - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java index 8f2f966a22..466837de4e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperIT.java @@ -31,7 +31,8 @@ void mapsSecondaryByAnnotation() { var reconciler = operator.getReconcilerOfType(DependentAnnotationSecondaryMapperReconciler.class); - await().pollDelay(Duration.ofMillis(150)) + await() + .pollDelay(Duration.ofMillis(150)) .untilAsserted(() -> assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1)); var configMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); @@ -46,17 +47,14 @@ void mapsSecondaryByAnnotation() { configMap.getData().put("additional_data", "data"); operator.replace(configMap); - await().pollDelay(Duration.ofMillis(150)) + await() + .pollDelay(Duration.ofMillis(150)) .untilAsserted(() -> assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2)); } - DependentAnnotationSecondaryMapperResource testResource() { var res = new DependentAnnotationSecondaryMapperResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java index 7dc17b5695..71146df638 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperReconciler.java @@ -13,8 +13,10 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = @Dependent( - type = DependentAnnotationSecondaryMapperReconciler.ConfigMapDependentResource.class)) +@Workflow( + dependents = + @Dependent( + type = DependentAnnotationSecondaryMapperReconciler.ConfigMapDependentResource.class)) @ControllerConfiguration public class DependentAnnotationSecondaryMapperReconciler implements Reconciler, TestExecutionInfoProvider { @@ -33,27 +35,24 @@ public int getNumberOfExecutions() { return numberOfExecutions.get(); } - public static class ConfigMapDependentResource extends - KubernetesDependentResource + public static class ConfigMapDependentResource + extends KubernetesDependentResource implements Creator, - Updater, - Deleter { - - public ConfigMapDependentResource() { - super(ConfigMap.class); - } + Updater, + Deleter { @Override - protected ConfigMap desired(DependentAnnotationSecondaryMapperResource primary, + protected ConfigMap desired( + DependentAnnotationSecondaryMapperResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of("data", primary.getMetadata().getName())); return configMap; } } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperResource.java index f41f92d0ef..c9e8639573 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentannotationsecondarymapper/DependentAnnotationSecondaryMapperResource.java @@ -11,7 +11,5 @@ @Version("v1") @Kind("MaxIntervalTestCustomResource") @ShortNames("mit") -public class DependentAnnotationSecondaryMapperResource - extends CustomResource - implements Namespaced { -} +public class DependentAnnotationSecondaryMapperResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/CustomMappingConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/CustomMappingConfigMapDependentResource.java index ad8639f6cf..081cf31dbd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/CustomMappingConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/CustomMappingConfigMapDependentResource.java @@ -24,37 +24,35 @@ public class CustomMappingConfigMapDependentResource public static final String KEY = "key"; private static final SecondaryToPrimaryMapper mapper = - Mappers.fromAnnotation(CUSTOM_NAME_KEY, CUSTOM_NAMESPACE_KEY, CUSTOM_TYPE_KEY, + Mappers.fromAnnotation( + CUSTOM_NAME_KEY, + CUSTOM_NAMESPACE_KEY, + CUSTOM_TYPE_KEY, DependentCustomMappingCustomResource.class); - public CustomMappingConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(DependentCustomMappingCustomResource primary, + protected ConfigMap desired( + DependentCustomMappingCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of(KEY, primary.getSpec().getValue())) .build(); } @Override - protected void addSecondaryToPrimaryMapperAnnotations(ConfigMap desired, - DependentCustomMappingCustomResource primary) { - addSecondaryToPrimaryMapperAnnotations(desired, primary, CUSTOM_NAME_KEY, CUSTOM_NAMESPACE_KEY, - CUSTOM_TYPE_KEY); + protected void addSecondaryToPrimaryMapperAnnotations( + ConfigMap desired, DependentCustomMappingCustomResource primary) { + addSecondaryToPrimaryMapperAnnotations( + desired, primary, CUSTOM_NAME_KEY, CUSTOM_NAMESPACE_KEY, CUSTOM_TYPE_KEY); } - @Override public Set toPrimaryResourceIDs(ConfigMap resource) { return mapper.toPrimaryResourceIDs(resource); } - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java index 3d754fc395..42f365884f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingAnnotationIT.java @@ -24,7 +24,6 @@ class DependentCustomMappingAnnotationIT { .withReconciler(DependentCustomMappingReconciler.class) .build(); - @Test void testCustomMappingAnnotationForDependent() { var cr = extension.create(testResource()); @@ -36,25 +35,28 @@ void testCustomMappingAnnotationForDependent() { extension.delete(cr); - await().untilAsserted(() -> { - var resource = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(resource).isNull(); - }); + await() + .untilAsserted( + () -> { + var resource = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(resource).isNull(); + }); } private void assertConfigMapData(String val) { - await().untilAsserted(() -> { - var resource = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(resource).isNotNull(); - assertThat(resource.getMetadata().getAnnotations()) - .containsKey(CUSTOM_NAME_KEY) - .containsKey(CUSTOM_NAMESPACE_KEY); - assertThat(resource.getData()).containsEntry(CustomMappingConfigMapDependentResource.KEY, - val); - }); + await() + .untilAsserted( + () -> { + var resource = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(resource).isNotNull(); + assertThat(resource.getMetadata().getAnnotations()) + .containsKey(CUSTOM_NAME_KEY) + .containsKey(CUSTOM_NAMESPACE_KEY); + assertThat(resource.getData()) + .containsEntry(CustomMappingConfigMapDependentResource.KEY, val); + }); } - DependentCustomMappingCustomResource testResource() { var dr = new DependentCustomMappingCustomResource(); dr.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); @@ -63,6 +65,4 @@ DependentCustomMappingCustomResource testResource() { return dr; } - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingCustomResource.java index 124a8d1108..ed07777ff3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingCustomResource.java @@ -8,7 +8,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") public class DependentCustomMappingCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingReconciler.java index e8b5d581b1..764e98d8d5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentcustommappingannotation/DependentCustomMappingReconciler.java @@ -11,10 +11,9 @@ public class DependentCustomMappingReconciler @Override public UpdateControl reconcile( DependentCustomMappingCustomResource resource, - Context context) throws Exception { + Context context) + throws Exception { return UpdateControl.noUpdate(); } - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/ConfigMapDependentResource.java index 6bbe6a814e..30e0de5b7d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/ConfigMapDependentResource.java @@ -7,19 +7,17 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; -public class ConfigMapDependentResource extends - CRUDNoGCKubernetesDependentResource { +public class ConfigMapDependentResource + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, DependentDifferentNamespaceCustomResource> { public static final String KEY = "key"; public static final String NAMESPACE = "default"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(DependentDifferentNamespaceCustomResource primary, + protected ConfigMap desired( + DependentDifferentNamespaceCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceCustomResource.java index 020d147ba9..9545072809 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("ddn") public class DependentDifferentNamespaceCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java index b9abf05cad..c02bac5a5d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceIT.java @@ -27,41 +27,47 @@ class DependentDifferentNamespaceIT { void managesCRUDOperationsForDependentInDifferentNamespace() { var resource = extension.create(testResource()); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(KEY, INITIAL_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(KEY, INITIAL_VALUE); + }); resource.getSpec().setValue(CHANGED_VALUE); resource = extension.replace(resource); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm.getData()).containsEntry(KEY, CHANGED_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm.getData()).containsEntry(KEY, CHANGED_VALUE); + }); extension.delete(resource); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm).isNull(); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm).isNull(); + }); } private ConfigMap getDependentConfigMap() { - return extension.getKubernetesClient().configMaps() + return extension + .getKubernetesClient() + .configMaps() .inNamespace(ConfigMapDependentResource.NAMESPACE) - .withName(TEST_1).get(); + .withName(TEST_1) + .get(); } DependentDifferentNamespaceCustomResource testResource() { var res = new DependentDifferentNamespaceCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_1) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_1).build()); res.setSpec(new DependentDifferentNamespaceSpec()); res.getSpec().setValue(INITIAL_VALUE); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java index d4ae1c5e0a..3d2b71338c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentdifferentnamespace/DependentDifferentNamespaceReconciler.java @@ -6,13 +6,13 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class), -}) +@Workflow( + dependents = { + @Dependent(type = ConfigMapDependentResource.class), + }) @ControllerConfiguration public class DependentDifferentNamespaceReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -27,5 +27,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java index 328a4a0544..bc7b578d7f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterIT.java @@ -21,7 +21,8 @@ class DependentFilterIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(DependentFilterTestReconciler.class) + LocallyRunOperatorExtension.builder() + .withReconciler(DependentFilterTestReconciler.class) .build(); @Test @@ -29,29 +30,38 @@ void filtersUpdateOnConfigMap() { var resource = createResource(); operator.create(resource); - await().pollDelay(Duration.ofMillis(150)).untilAsserted(() -> { - assertThat(operator.getReconcilerOfType(DependentFilterTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(1); - }); + await() + .pollDelay(Duration.ofMillis(150)) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(DependentFilterTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + }); var configMap = operator.get(ConfigMap.class, RESOURCE_NAME); configMap.setData(Map.of(CM_VALUE_KEY, CONFIG_MAP_FILTER_VALUE)); operator.replace(configMap); - await().pollDelay(Duration.ofMillis(150)).untilAsserted(() -> { - assertThat(operator.getReconcilerOfType(DependentFilterTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(1); - }); + await() + .pollDelay(Duration.ofMillis(150)) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(DependentFilterTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + }); } DependentFilterTestCustomResource createResource() { DependentFilterTestCustomResource resource = new DependentFilterTestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); resource.setSpec(new DependentFilterTestResourceSpec()); resource.getSpec().setValue("value1"); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterTestCustomResource.java index 43822aafb8..bc4d92edbb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/DependentFilterTestCustomResource.java @@ -10,8 +10,7 @@ @Version("v1") @ShortNames("dft") public class DependentFilterTestCustomResource - extends CustomResource - implements Namespaced { + extends CustomResource implements Namespaced { public String getConfigMapName(int id) { return "configmap" + id; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/FilteredDependentConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/FilteredDependentConfigMap.java index 7cbb60c583..3b12673b4c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/FilteredDependentConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/FilteredDependentConfigMap.java @@ -15,18 +15,16 @@ public class FilteredDependentConfigMap extends CRUDKubernetesDependentResource { - public FilteredDependentConfigMap() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(DependentFilterTestCustomResource primary, + protected ConfigMap desired( + DependentFilterTestCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of(CM_VALUE_KEY, primary.getSpec().getValue())); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/UpdateFilter.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/UpdateFilter.java index 2dd4c8bf99..999b3f00d3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/UpdateFilter.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentfilter/UpdateFilter.java @@ -6,8 +6,7 @@ import static io.javaoperatorsdk.operator.dependent.dependentfilter.DependentFilterTestReconciler.CM_VALUE_KEY; import static io.javaoperatorsdk.operator.dependent.dependentfilter.DependentFilterTestReconciler.CONFIG_MAP_FILTER_VALUE; -public class UpdateFilter - implements OnUpdateFilter { +public class UpdateFilter implements OnUpdateFilter { @Override public boolean accept(ConfigMap resource, ConfigMap oldResource) { return !resource.getData().get(CM_VALUE_KEY).equals(CONFIG_MAP_FILTER_VALUE); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/ConfigMapDependentResource.java index 3f0ce9a073..87b827c527 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/ConfigMapDependentResource.java @@ -7,17 +7,15 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; -public class ConfigMapDependentResource extends - CRUDKubernetesDependentResource { +public class ConfigMapDependentResource + extends CRUDKubernetesDependentResource< + ConfigMap, DependentOperationEventFilterCustomResource> { public static final String KEY = "key1"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(DependentOperationEventFilterCustomResource primary, + protected ConfigMap desired( + DependentOperationEventFilterCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResource.java index 021c984ec3..67ab4c0937 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResource.java @@ -13,5 +13,4 @@ @ShortNames("oef") public class DependentOperationEventFilterCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java index 3149d1c6d0..d787b736d1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterCustomResourceTestReconciler.java @@ -7,14 +7,10 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class) -}) -@ControllerConfiguration( - informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) +@Workflow(dependents = {@Dependent(type = ConfigMapDependentResource.class)}) +@ControllerConfiguration(informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) public class DependentOperationEventFilterCustomResourceTestReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -29,5 +25,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java index 12413cd4b4..e9ddc6cd6e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentoperationeventfiltering/DependentOperationEventFilterIT.java @@ -27,30 +27,36 @@ class DependentOperationEventFilterIT { @Test void reconcileNotTriggeredWithDependentResourceCreateOrUpdate() { - var resource = - operator.create(createTestResource()); + var resource = operator.create(createTestResource()); - await().pollDelay(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(3)) + await() + .pollDelay(Duration.ofSeconds(1)) + .atMost(Duration.ofSeconds(3)) .until( - () -> ((DependentOperationEventFilterCustomResourceTestReconciler) operator - .getFirstReconciler()) - .getNumberOfExecutions() == 1); + () -> + ((DependentOperationEventFilterCustomResourceTestReconciler) + operator.getFirstReconciler()) + .getNumberOfExecutions() + == 1); assertThat(operator.get(ConfigMap.class, TEST).getData()) .containsEntry(ConfigMapDependentResource.KEY, SPEC_VAL_1); resource.getSpec().setValue(SPEC_VAL_2); operator.replace(resource); - await().pollDelay(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(3)) + await() + .pollDelay(Duration.ofSeconds(1)) + .atMost(Duration.ofSeconds(3)) .until( - () -> ((DependentOperationEventFilterCustomResourceTestReconciler) operator - .getFirstReconciler()) - .getNumberOfExecutions() == 2); + () -> + ((DependentOperationEventFilterCustomResourceTestReconciler) + operator.getFirstReconciler()) + .getNumberOfExecutions() + == 2); assertThat(operator.get(ConfigMap.class, TEST).getData()) .containsEntry(ConfigMapDependentResource.KEY, SPEC_VAL_2); } - private DependentOperationEventFilterCustomResource createTestResource() { DependentOperationEventFilterCustomResource cr = new DependentOperationEventFilterCustomResource(); @@ -60,5 +66,4 @@ private DependentOperationEventFilterCustomResource createTestResource() { cr.getSpec().setValue(SPEC_VAL_1); return cr; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/ConfigMapDependentResource.java index 9f80a90f0d..2a245a3721 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/ConfigMapDependentResource.java @@ -11,18 +11,16 @@ public class ConfigMapDependentResource extends CRUDKubernetesDependentResource { - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(DependentReInitializationCustomResource primary, + protected ConfigMap desired( + DependentReInitializationCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of("key", "val")) .build(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationCustomResource.java index a3c4e9f20b..59991ddda2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationCustomResource.java @@ -7,8 +7,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") -public class DependentReInitializationCustomResource - extends CustomResource - implements Namespaced { - -} +public class DependentReInitializationCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java index c215d82da2..270b89a6a5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationIT.java @@ -24,14 +24,11 @@ void dependentCanDeReInitialized() { startEndStopOperator(client, dependent); } - private static void startEndStopOperator(KubernetesClient client, - ConfigMapDependentResource dependent) { - Operator o1 = new Operator(o -> o - .withCloseClientOnStop(false) - .withKubernetesClient(client)); + private static void startEndStopOperator( + KubernetesClient client, ConfigMapDependentResource dependent) { + Operator o1 = new Operator(o -> o.withCloseClientOnStop(false).withKubernetesClient(client)); o1.register(new DependentReInitializationReconciler(dependent)); o1.start(); o1.stop(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationReconciler.java index 7f6072665f..8c435e5cc8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentreinitialization/DependentReInitializationReconciler.java @@ -18,7 +18,8 @@ public DependentReInitializationReconciler(ConfigMapDependentResource dependentR @Override public UpdateControl reconcile( DependentReInitializationCustomResource resource, - Context context) throws Exception { + Context context) + throws Exception { configMapDependentResource.reconcile(resource, context); return UpdateControl.noUpdate(); } @@ -26,9 +27,6 @@ public UpdateControl reconcile( @Override public List> prepareEventSources( EventSourceContext context) { - return EventSourceUtils.dependentEventSources(context, - configMapDependentResource); + return EventSourceUtils.dependentEventSources(context, configMapDependentResource); } - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java index 0530934dba..ae5cd25895 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefIT.java @@ -34,15 +34,16 @@ void dependentResourceCanReferenceEachOther() { .pollDelay(Duration.ofMillis(150)) .untilAsserted( () -> { - assertThat(operator - .getReconcilerOfType(DependentResourceCrossRefReconciler.class) - .isErrorHappened()).isFalse(); + assertThat( + operator + .getReconcilerOfType(DependentResourceCrossRefReconciler.class) + .isErrorHappened()) + .isFalse(); for (int i = 0; i < EXECUTION_NUMBER; i++) { assertThat(operator.get(ConfigMap.class, TEST_RESOURCE_NAME + i)).isNotNull(); assertThat(operator.get(Secret.class, TEST_RESOURCE_NAME + i)).isNotNull(); } }); - } DependentResourceCrossRefResource testResource(int n) { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefReconciler.java index 4464c02906..5d54ecdabe 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefReconciler.java @@ -14,11 +14,15 @@ import static io.javaoperatorsdk.operator.dependent.dependentresourcecrossref.DependentResourceCrossRefReconciler.SECRET_NAME; -@Workflow(dependents = { - @Dependent(name = SECRET_NAME, - type = DependentResourceCrossRefReconciler.SecretDependentResource.class), - @Dependent(type = DependentResourceCrossRefReconciler.ConfigMapDependentResource.class, - dependsOn = SECRET_NAME)}) +@Workflow( + dependents = { + @Dependent( + name = SECRET_NAME, + type = DependentResourceCrossRefReconciler.SecretDependentResource.class), + @Dependent( + type = DependentResourceCrossRefReconciler.ConfigMapDependentResource.class, + dependsOn = SECRET_NAME) + }) @ControllerConfiguration public class DependentResourceCrossRefReconciler implements Reconciler { @@ -42,7 +46,8 @@ public int getNumberOfExecutions() { @Override public ErrorStatusUpdateControl updateErrorStatus( DependentResourceCrossRefResource resource, - Context context, Exception e) { + Context context, + Exception e) { errorHappened = true; return ErrorStatusUpdateControl.noStatusUpdate(); } @@ -51,51 +56,44 @@ public boolean isErrorHappened() { return errorHappened; } - public static class SecretDependentResource extends - CRUDKubernetesDependentResource { - - public SecretDependentResource() { - super(Secret.class); - } + public static class SecretDependentResource + extends CRUDKubernetesDependentResource { @Override - protected Secret desired(DependentResourceCrossRefResource primary, + protected Secret desired( + DependentResourceCrossRefResource primary, Context context) { Secret secret = new Secret(); - secret.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + secret.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); secret.setData(Map.of("key", Base64.getEncoder().encodeToString("secretData".getBytes()))); return secret; } } - public static class ConfigMapDependentResource extends - CRUDKubernetesDependentResource { - - public ConfigMapDependentResource() { - super(ConfigMap.class); - } + public static class ConfigMapDependentResource + extends CRUDKubernetesDependentResource { @Override - protected ConfigMap desired(DependentResourceCrossRefResource primary, + protected ConfigMap desired( + DependentResourceCrossRefResource primary, Context context) { var secret = context.getSecondaryResource(Secret.class); if (secret.isEmpty()) { throw new IllegalStateException("Secret is empty"); } ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); - configMap - .setData(Map.of("secretKey", new ArrayList<>(secret.get().getData().keySet()).get(0))); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); + configMap.setData( + Map.of("secretKey", new ArrayList<>(secret.get().getData().keySet()).get(0))); return configMap; } } - - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefResource.java index fd89f7fa70..3e4abcc850 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentresourcecrossref/DependentResourceCrossRefResource.java @@ -7,7 +7,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") -public class DependentResourceCrossRefResource - extends CustomResource - implements Namespaced { -} +public class DependentResourceCrossRefResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSACustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSACustomResource.java index ed71a9db64..4c3d79917f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSACustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSACustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("dssa") -public class DependentSSACustomResource - extends CustomResource - implements Namespaced { -} +public class DependentSSACustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java index ec02dd7f5c..8ab686b1b8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMatchingIT.java @@ -27,12 +27,11 @@ public class DependentSSAMatchingIT { public static final String ADDITIONAL_KEY = "key2"; public static final String ADDITIONAL_VALUE = "Additional Value"; - @RegisterExtension LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() - .withReconciler(new DependentSSAReconciler(), - o -> o.withFieldManager(CUSTOM_FIELD_MANAGER_NAME)) + .withReconciler( + new DependentSSAReconciler(), o -> o.withFieldManager(CUSTOM_FIELD_MANAGER_NAME)) .build(); @Test @@ -40,56 +39,69 @@ void testMatchingAndUpdate() { SSAConfigMapDependent.NUMBER_OF_UPDATES.set(0); var resource = extension.create(testResource()); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, INITIAL_VALUE); - assertThat(cm.getMetadata().getManagedFields().stream() - .filter(fm -> fm.getManager().equals(CUSTOM_FIELD_MANAGER_NAME))).isNotEmpty(); - assertThat(SSAConfigMapDependent.NUMBER_OF_UPDATES.get()).isZero(); - }); - - ConfigMap cmPatch = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(resource.getMetadata().getNamespace()) - .build()) - .withData(Map.of(ADDITIONAL_KEY, ADDITIONAL_VALUE)) - .build(); - - extension.getKubernetesClient().configMaps().resource(cmPatch).patch(new PatchContext.Builder() - .withFieldManager(OTHER_FIELD_MANAGER) - .withPatchType(PatchType.SERVER_SIDE_APPLY) - .build()); - - await().pollDelay(Duration.ofMillis(300)).untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm.getData()).hasSize(2); - assertThat(SSAConfigMapDependent.NUMBER_OF_UPDATES.get()).isZero(); - assertThat(cm.getMetadata().getManagedFields()).hasSize(2); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, INITIAL_VALUE); + assertThat( + cm.getMetadata().getManagedFields().stream() + .filter(fm -> fm.getManager().equals(CUSTOM_FIELD_MANAGER_NAME))) + .isNotEmpty(); + assertThat(SSAConfigMapDependent.NUMBER_OF_UPDATES.get()).isZero(); + }); + + ConfigMap cmPatch = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(resource.getMetadata().getNamespace()) + .build()) + .withData(Map.of(ADDITIONAL_KEY, ADDITIONAL_VALUE)) + .build(); + + extension + .getKubernetesClient() + .configMaps() + .resource(cmPatch) + .patch( + new PatchContext.Builder() + .withFieldManager(OTHER_FIELD_MANAGER) + .withPatchType(PatchType.SERVER_SIDE_APPLY) + .build()); + + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm.getData()).hasSize(2); + assertThat(SSAConfigMapDependent.NUMBER_OF_UPDATES.get()).isZero(); + assertThat(cm.getMetadata().getManagedFields()).hasSize(2); + }); resource.getSpec().setValue(CHANGED_VALUE); extension.replace(resource); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm.getData()).hasSize(2); - assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, CHANGED_VALUE); - assertThat(cm.getData()).containsEntry(ADDITIONAL_KEY, ADDITIONAL_VALUE); - assertThat(cm.getMetadata().getManagedFields()).hasSize(2); - assertThat(SSAConfigMapDependent.NUMBER_OF_UPDATES.get()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm.getData()).hasSize(2); + assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, CHANGED_VALUE); + assertThat(cm.getData()).containsEntry(ADDITIONAL_KEY, ADDITIONAL_VALUE); + assertThat(cm.getMetadata().getManagedFields()).hasSize(2); + assertThat(SSAConfigMapDependent.NUMBER_OF_UPDATES.get()).isEqualTo(1); + }); } public DependentSSACustomResource testResource() { DependentSSACustomResource resource = new DependentSSACustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); resource.setSpec(new DependentSSASpec()); resource.getSpec().setValue(INITIAL_VALUE); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java index bd3aedb5f9..0d354febdf 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAMigrationIT.java @@ -31,20 +31,31 @@ class DependentSSAMigrationIT { void setup(TestInfo testInfo) { SSAConfigMapDependent.NUMBER_OF_UPDATES.set(0); LocallyRunOperatorExtension.applyCrd(DependentSSACustomResource.class, client); - testInfo.getTestMethod().ifPresent(method -> { - namespace = KubernetesResourceUtil.sanitizeName(method.getName()); - cleanup(); - client.namespaces().resource(new NamespaceBuilder().withMetadata(new ObjectMetaBuilder() - .withName(namespace) - .build()).build()).create(); - }); + testInfo + .getTestMethod() + .ifPresent( + method -> { + namespace = KubernetesResourceUtil.sanitizeName(method.getName()); + cleanup(); + client + .namespaces() + .resource( + new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(namespace).build()) + .build()) + .create(); + }); } @AfterEach void cleanup() { - client.namespaces().resource(new NamespaceBuilder().withMetadata(new ObjectMetaBuilder() - .withName(namespace) - .build()).build()).delete(); + client + .namespaces() + .resource( + new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(namespace).build()) + .build()) + .delete(); } @Test @@ -65,8 +76,7 @@ void usingDefaultFieldManagerDoesNotCreatesANewOneWithApplyOperation() { var legacyOperator = createOperator(client, true, null); DependentSSACustomResource testResource = reconcileWithLegacyOperator(legacyOperator); - var operator = createOperator(client, false, - FABRIC8_CLIENT_DEFAULT_FIELD_MANAGER); + var operator = createOperator(client, false, FABRIC8_CLIENT_DEFAULT_FIELD_MANAGER); reconcileWithNewApproach(testResource, operator); var cm = getDependentConfigMap(); @@ -75,22 +85,26 @@ void usingDefaultFieldManagerDoesNotCreatesANewOneWithApplyOperation() { assertThat(cm.getMetadata().getManagedFields()) // Jetty seems to be a bug in fabric8 client, it is only the default fieldManager if Jetty // is used as http client - .allMatch(fm -> fm.getManager().equals(FABRIC8_CLIENT_DEFAULT_FIELD_MANAGER) - || fm.getManager().equals("Jetty")); + .allMatch( + fm -> + fm.getManager().equals(FABRIC8_CLIENT_DEFAULT_FIELD_MANAGER) + || fm.getManager().equals("Jetty")); } - private void reconcileAgainWithLegacy(Operator legacyOperator, - DependentSSACustomResource testResource) { + private void reconcileAgainWithLegacy( + Operator legacyOperator, DependentSSACustomResource testResource) { legacyOperator.start(); testResource.getSpec().setValue(INITIAL_VALUE); testResource.getMetadata().setResourceVersion(null); client.resource(testResource).update(); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, INITIAL_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, INITIAL_VALUE); + }); legacyOperator.stop(); } @@ -99,20 +113,24 @@ private DependentSSACustomResource reconcileWithNewApproach( DependentSSACustomResource testResource, Operator operator) { operator.start(); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).hasSize(1); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).hasSize(1); + }); testResource.getSpec().setValue(CHANGED_VALUE); testResource.getMetadata().setResourceVersion(null); testResource = client.resource(testResource).update(); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, CHANGED_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm.getData()).containsEntry(SSAConfigMapDependent.DATA_KEY, CHANGED_VALUE); + }); operator.stop(); return testResource; } @@ -126,41 +144,41 @@ private DependentSSACustomResource reconcileWithLegacyOperator(Operator legacyOp var testResource = client.resource(testResource()).create(); - await().untilAsserted(() -> { - var cm = getDependentConfigMap(); - assertThat(cm).isNotNull(); - assertThat(cm.getMetadata().getManagedFields()).hasSize(1); - assertThat(cm.getData()).hasSize(1); - }); + await() + .untilAsserted( + () -> { + var cm = getDependentConfigMap(); + assertThat(cm).isNotNull(); + assertThat(cm.getMetadata().getManagedFields()).hasSize(1); + assertThat(cm.getData()).hasSize(1); + }); legacyOperator.stop(); return testResource; } - - private Operator createOperator(KubernetesClient client, boolean legacyDependentHandling, - String fieldManager) { + private Operator createOperator( + KubernetesClient client, boolean legacyDependentHandling, String fieldManager) { Operator operator = new Operator(o -> o.withKubernetesClient(client).withCloseClientOnStop(false)); var reconciler = new DependentSSAReconciler(!legacyDependentHandling); - operator.register(reconciler, o -> { - o.settingNamespace(namespace); - if (fieldManager != null) { - o.withFieldManager(fieldManager); - } - }); + operator.register( + reconciler, + o -> { + o.settingNamespace(namespace); + if (fieldManager != null) { + o.withFieldManager(fieldManager); + } + }); return operator; } public DependentSSACustomResource testResource() { DependentSSACustomResource resource = new DependentSSACustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withNamespace(namespace) - .withName(TEST_RESOURCE_NAME) - .build()); + resource.setMetadata( + new ObjectMetaBuilder().withNamespace(namespace).withName(TEST_RESOURCE_NAME).build()); resource.setSpec(new DependentSSASpec()); resource.getSpec().setValue(INITIAL_VALUE); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAReconciler.java index 8825f4007e..596f7e9991 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/DependentSSAReconciler.java @@ -28,9 +28,8 @@ public DependentSSAReconciler() { } public DependentSSAReconciler(boolean useSSA) { - ssaConfigMapDependent.configureWith(new KubernetesDependentResourceConfigBuilder() - .withUseSSA(useSSA) - .build()); + ssaConfigMapDependent.configureWith( + new KubernetesDependentResourceConfigBuilder().withUseSSA(useSSA).build()); this.useSSA = useSSA; } @@ -44,8 +43,7 @@ public SSAConfigMapDependent getSsaConfigMapDependent() { @Override public UpdateControl reconcile( - DependentSSACustomResource resource, - Context context) { + DependentSSACustomResource resource, Context context) { ssaConfigMapDependent.reconcile(resource, context); numberOfExecutions.addAndGet(1); @@ -59,7 +57,6 @@ public int getNumberOfExecutions() { @Override public List> prepareEventSources( EventSourceContext context) { - return EventSourceUtils.dependentEventSources(context, - ssaConfigMapDependent); + return EventSourceUtils.dependentEventSources(context, ssaConfigMapDependent); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/SSAConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/SSAConfigMapDependent.java index a912bfcd03..49d2c1de44 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/SSAConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/dependentssa/SSAConfigMapDependent.java @@ -9,31 +9,30 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; -public class SSAConfigMapDependent extends - CRUDKubernetesDependentResource { +public class SSAConfigMapDependent + extends CRUDKubernetesDependentResource { public static AtomicInteger NUMBER_OF_UPDATES = new AtomicInteger(0); public static final String DATA_KEY = "key1"; - public SSAConfigMapDependent() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(DependentSSACustomResource primary, - Context context) { + protected ConfigMap desired( + DependentSSACustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of(DATA_KEY, primary.getSpec().getValue())) .build(); } @Override - public ConfigMap update(ConfigMap actual, ConfigMap desired, + public ConfigMap update( + ConfigMap actual, + ConfigMap desired, DependentSSACustomResource primary, Context context) { NUMBER_OF_UPDATES.incrementAndGet(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateCustomResource.java index 353e86d2f9..21c3c0b4ea 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("ess") -public class ExternalStateCustomResource - extends CustomResource - implements Namespaced { -} +public class ExternalStateCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java index adfe1f31a1..87e588673d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentIT.java @@ -8,7 +8,8 @@ public class ExternalStateDependentIT extends ExternalStateTestBase { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(ExternalStateDependentReconciler.class) + LocallyRunOperatorExtension.builder() + .withReconciler(ExternalStateDependentReconciler.class) .build(); @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentReconciler.java index 9140f40587..5417851271 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateDependentReconciler.java @@ -14,16 +14,14 @@ @Workflow(dependents = @Dependent(type = ExternalWithStateDependentResource.class)) @ControllerConfiguration public class ExternalStateDependentReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { public static final String ID_KEY = "id"; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override public UpdateControl reconcile( - ExternalStateCustomResource resource, - Context context) { + ExternalStateCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); return UpdateControl.noUpdate(); @@ -36,11 +34,12 @@ public int getNumberOfExecutions() { @Override public List> prepareEventSources( EventSourceContext context) { - var configMapEventSource = new InformerEventSource<>( - InformerEventSourceConfiguration.from(ConfigMap.class, ExternalStateCustomResource.class) - .build(), - context); + var configMapEventSource = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, ExternalStateCustomResource.class) + .build(), + context); return List.of(configMapEventSource); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java index 066d777e80..bae36431b5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateIT.java @@ -23,8 +23,7 @@ class ExternalStateIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(ExternalStateReconciler.class) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(ExternalStateReconciler.class).build(); @Test public void reconcilesResourceWithPersistentState() { @@ -40,32 +39,34 @@ public void reconcilesResourceWithPersistentState() { } private void assertResourcesDeleted(ExternalStateCustomResource resource) { - await().untilAsserted(() -> { - var cm = operator.get(ConfigMap.class, resource.getMetadata().getName()); - var resources = externalService.listResources(); - assertThat(cm).isNull(); - assertThat(resources).isEmpty(); - }); + await() + .untilAsserted( + () -> { + var cm = operator.get(ConfigMap.class, resource.getMetadata().getName()); + var resources = externalService.listResources(); + assertThat(cm).isNull(); + assertThat(resources).isEmpty(); + }); } - private void assertResourcesCreated(ExternalStateCustomResource resource, - String initialTestData) { - await().untilAsserted(() -> { - var cm = operator.get(ConfigMap.class, resource.getMetadata().getName()); - var resources = externalService.listResources(); - assertThat(resources).hasSize(1); - var extRes = externalService.listResources().get(0); - assertThat(extRes.getData()).isEqualTo(initialTestData); - assertThat(cm).isNotNull(); - assertThat(cm.getData().get(ID_KEY)).isEqualTo(extRes.getId()); - }); + private void assertResourcesCreated( + ExternalStateCustomResource resource, String initialTestData) { + await() + .untilAsserted( + () -> { + var cm = operator.get(ConfigMap.class, resource.getMetadata().getName()); + var resources = externalService.listResources(); + assertThat(resources).hasSize(1); + var extRes = externalService.listResources().get(0); + assertThat(extRes.getData()).isEqualTo(initialTestData); + assertThat(cm).isNotNull(); + assertThat(cm.getData().get(ID_KEY)).isEqualTo(extRes.getId()); + }); } private ExternalStateCustomResource testResource() { var res = new ExternalStateCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new ExternalStateSpec()); res.getSpec().setData(INITIAL_TEST_DATA); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java index 5fb2932152..7b741102d7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateReconciler.java @@ -31,8 +31,9 @@ @ControllerConfiguration public class ExternalStateReconciler - implements Reconciler, Cleaner, - TestExecutionInfoProvider { + implements Reconciler, + Cleaner, + TestExecutionInfoProvider { public static final String ID_KEY = "id"; private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -40,7 +41,8 @@ public class ExternalStateReconciler private final ExternalIDGenServiceMock externalService = ExternalIDGenServiceMock.getInstance(); InformerEventSource configMapEventSource; - PerResourcePollingEventSource externalResourceEventSource; + PerResourcePollingEventSource + externalResourceEventSource; @Override public UpdateControl reconcile( @@ -48,39 +50,44 @@ public UpdateControl reconcile( numberOfExecutions.addAndGet(1); var externalResource = context.getSecondaryResource(ExternalResource.class); - externalResource.ifPresentOrElse(r -> { - if (!r.getData().equals(resource.getSpec().getData())) { - updateExternalResource(resource, r, context); - } - }, () -> { - if (externalResource.isEmpty()) { - createExternalResource(resource, context); - } - }); - + externalResource.ifPresentOrElse( + r -> { + if (!r.getData().equals(resource.getSpec().getData())) { + updateExternalResource(resource, r, context); + } + }, + () -> { + if (externalResource.isEmpty()) { + createExternalResource(resource, context); + } + }); return UpdateControl.noUpdate(); } - private void updateExternalResource(ExternalStateCustomResource resource, - ExternalResource externalResource, Context context) { + private void updateExternalResource( + ExternalStateCustomResource resource, + ExternalResource externalResource, + Context context) { var newResource = new ExternalResource(externalResource.getId(), resource.getSpec().getData()); externalService.update(newResource); - externalResourceEventSource.handleRecentResourceUpdate(ResourceID.fromResource(resource), - newResource, externalResource); + externalResourceEventSource.handleRecentResourceUpdate( + ResourceID.fromResource(resource), newResource, externalResource); } - private void createExternalResource(ExternalStateCustomResource resource, - Context context) { + private void createExternalResource( + ExternalStateCustomResource resource, Context context) { var createdResource = externalService.create(new ExternalResource(resource.getSpec().getData())); - var configMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()) - .withData(Map.of(ID_KEY, createdResource.getId())) - .build(); + var configMap = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()) + .withData(Map.of(ID_KEY, createdResource.getId())) + .build(); configMap.addOwnerReference(resource); context.getClient().configMaps().resource(configMap).create(); @@ -93,12 +100,16 @@ private void createExternalResource(ExternalStateCustomResource resource, } @Override - public DeleteControl cleanup(ExternalStateCustomResource resource, - Context context) { + public DeleteControl cleanup( + ExternalStateCustomResource resource, Context context) { var externalResource = context.getSecondaryResource(ExternalResource.class); externalResource.ifPresent(er -> externalService.delete(er.getId())); - context.getClient().configMaps().inNamespace(resource.getMetadata().getNamespace()) - .withName(resource.getMetadata().getName()).delete(); + context + .getClient() + .configMaps() + .inNamespace(resource.getMetadata().getNamespace()) + .withName(resource.getMetadata().getName()) + .delete(); return DeleteControl.defaultDelete(); } @@ -110,28 +121,33 @@ public int getNumberOfExecutions() { public List> prepareEventSources( EventSourceContext context) { - configMapEventSource = new InformerEventSource<>( - InformerEventSourceConfiguration.from(ConfigMap.class, ExternalStateCustomResource.class) - .build(), - context); + configMapEventSource = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, ExternalStateCustomResource.class) + .build(), + context); configMapEventSource.setEventSourcePriority(EventSourceStartPriority.RESOURCE_STATE_LOADER); - final PerResourcePollingEventSource.ResourceFetcher fetcher = - (ExternalStateCustomResource primaryResource) -> { - var configMap = - configMapEventSource.getSecondaryResource(primaryResource).orElse(null); - if (configMap == null) { - return Collections.emptySet(); - } - var id = configMap.getData().get(ID_KEY); - var externalResource = externalService.read(id); - return externalResource.map(Set::of).orElseGet(Collections::emptySet); - }; + final PerResourcePollingEventSource.ResourceFetcher< + ExternalResource, ExternalStateCustomResource> + fetcher = + (ExternalStateCustomResource primaryResource) -> { + var configMap = + configMapEventSource.getSecondaryResource(primaryResource).orElse(null); + if (configMap == null) { + return Collections.emptySet(); + } + var id = configMap.getData().get(ID_KEY); + var externalResource = externalService.read(id); + return externalResource.map(Set::of).orElseGet(Collections::emptySet); + }; externalResourceEventSource = - new PerResourcePollingEventSource<>(ExternalResource.class, context, + new PerResourcePollingEventSource<>( + ExternalResource.class, + context, new PerResourcePollingConfigurationBuilder<>(fetcher, Duration.ofMillis(300L)).build()); - return Arrays.asList(configMapEventSource, - externalResourceEventSource); + return Arrays.asList(configMapEventSource, externalResourceEventSource); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateTestBase.java index c58c7cf670..31aea2d6c7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateTestBase.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalStateTestBase.java @@ -34,32 +34,34 @@ public void reconcilesResourceWithPersistentState() { } private void assertResourcesDeleted(ExternalStateCustomResource resource) { - await().untilAsserted(() -> { - var cm = extension().get(ConfigMap.class, resource.getMetadata().getName()); - var resources = externalService.listResources(); - assertThat(cm).isNull(); - assertThat(resources).isEmpty(); - }); + await() + .untilAsserted( + () -> { + var cm = extension().get(ConfigMap.class, resource.getMetadata().getName()); + var resources = externalService.listResources(); + assertThat(cm).isNull(); + assertThat(resources).isEmpty(); + }); } - private void assertResourcesCreated(ExternalStateCustomResource resource, - String initialTestData) { - await().untilAsserted(() -> { - var cm = extension().get(ConfigMap.class, resource.getMetadata().getName()); - var resources = externalService.listResources(); - assertThat(resources).hasSize(1); - var extRes = externalService.listResources().get(0); - assertThat(extRes.getData()).isEqualTo(initialTestData); - assertThat(cm).isNotNull(); - assertThat(cm.getData().get(ID_KEY)).isEqualTo(extRes.getId()); - }); + private void assertResourcesCreated( + ExternalStateCustomResource resource, String initialTestData) { + await() + .untilAsserted( + () -> { + var cm = extension().get(ConfigMap.class, resource.getMetadata().getName()); + var resources = externalService.listResources(); + assertThat(resources).hasSize(1); + var extRes = externalService.listResources().get(0); + assertThat(extRes.getData()).isEqualTo(initialTestData); + assertThat(cm).isNotNull(); + assertThat(cm.getData().get(ID_KEY)).isEqualTo(extRes.getId()); + }); } private ExternalStateCustomResource testResource() { var res = new ExternalStateCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new ExternalStateSpec()); res.getSpec().setData(INITIAL_TEST_DATA); @@ -67,5 +69,4 @@ private ExternalStateCustomResource testResource() { } abstract LocallyRunOperatorExtension extension(); - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java index 47d6a25144..5a1fa6efb3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/ExternalWithStateDependentResource.java @@ -17,11 +17,11 @@ import io.javaoperatorsdk.operator.support.ExternalIDGenServiceMock; import io.javaoperatorsdk.operator.support.ExternalResource; -public class ExternalWithStateDependentResource extends - PerResourcePollingDependentResource - implements - DependentResourceWithExplicitState, - Updater { +public class ExternalWithStateDependentResource + extends PerResourcePollingDependentResource + implements DependentResourceWithExplicitState< + ExternalResource, ExternalStateCustomResource, ConfigMap>, + Updater { ExternalIDGenServiceMock externalService = ExternalIDGenServiceMock.getInstance(); @@ -31,18 +31,21 @@ public ExternalWithStateDependentResource() { @Override @SuppressWarnings("unchecked") - public Set fetchResources( - ExternalStateCustomResource primaryResource) { - return getResourceID(primaryResource).map(id -> { - var externalResource = externalService.read(id); - return externalResource.map(Set::of).orElseGet(Collections::emptySet); - }).orElseGet(Collections::emptySet); + public Set fetchResources(ExternalStateCustomResource primaryResource) { + return getResourceID(primaryResource) + .map( + id -> { + var externalResource = externalService.read(id); + return externalResource.map(Set::of).orElseGet(Collections::emptySet); + }) + .orElseGet(Collections::emptySet); } @Override protected Optional selectTargetSecondaryResource( Set secondaryResources, - ExternalStateCustomResource primary, Context context) { + ExternalStateCustomResource primary, + Context context) { var id = getResourceID(primary); return id.flatMap(k -> secondaryResources.stream().filter(e -> e.getId().equals(k)).findAny()); } @@ -54,8 +57,8 @@ private Optional getResourceID(ExternalStateCustomResource primaryResour } @Override - protected ExternalResource desired(ExternalStateCustomResource primary, - Context context) { + protected ExternalResource desired( + ExternalStateCustomResource primary, Context context) { return new ExternalResource(primary.getSpec().getData()); } @@ -65,42 +68,48 @@ public Class stateResourceClass() { } @Override - public ConfigMap stateResource(ExternalStateCustomResource primary, - ExternalResource resource) { - ConfigMap configMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) - .withData(Map.of(ExternalStateDependentReconciler.ID_KEY, resource.getId())) - .build(); + public ConfigMap stateResource(ExternalStateCustomResource primary, ExternalResource resource) { + ConfigMap configMap = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withData(Map.of(ExternalStateDependentReconciler.ID_KEY, resource.getId())) + .build(); configMap.addOwnerReference(primary); return configMap; } @Override - public ExternalResource create(ExternalResource desired, + public ExternalResource create( + ExternalResource desired, ExternalStateCustomResource primary, Context context) { return externalService.create(desired); } @Override - public ExternalResource update(ExternalResource actual, - ExternalResource desired, ExternalStateCustomResource primary, + public ExternalResource update( + ExternalResource actual, + ExternalResource desired, + ExternalStateCustomResource primary, Context context) { return externalService.update(new ExternalResource(actual.getId(), desired.getData())); } @Override - public Matcher.Result match(ExternalResource resource, + public Matcher.Result match( + ExternalResource resource, ExternalStateCustomResource primary, Context context) { return Matcher.Result.nonComputed(resource.getData().equals(primary.getSpec().getData())); } @Override - protected void handleDelete(ExternalStateCustomResource primary, + protected void handleDelete( + ExternalStateCustomResource primary, ExternalResource secondary, Context context) { externalService.delete(secondary.getId()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java index df3171fbec..ac3ddb3778 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/BulkDependentResourceExternalWithState.java @@ -17,12 +17,13 @@ import io.javaoperatorsdk.operator.support.ExternalIDGenServiceMock; import io.javaoperatorsdk.operator.support.ExternalResource; -public class BulkDependentResourceExternalWithState extends - PerResourcePollingDependentResource - implements - BulkDependentResource, - CRUDBulkDependentResource, - DependentResourceWithExplicitState { +public class BulkDependentResourceExternalWithState + extends PerResourcePollingDependentResource< + ExternalResource, ExternalStateBulkDependentCustomResource> + implements BulkDependentResource, + CRUDBulkDependentResource, + DependentResourceWithExplicitState< + ExternalResource, ExternalStateBulkDependentCustomResource, ConfigMap> { public static final String DELIMITER = "-"; ExternalIDGenServiceMock externalService = ExternalIDGenServiceMock.getInstance(); @@ -39,11 +40,12 @@ public Set fetchResources( getExternalStateEventSource().getSecondaryResources(primaryResource); Set res = new HashSet<>(); - configMaps.forEach(cm -> { - var id = cm.getData().get(ExternalStateDependentReconciler.ID_KEY); - var externalResource = externalService.read(id); - externalResource.ifPresent(res::add); - }); + configMaps.forEach( + cm -> { + var id = cm.getData().get(ExternalStateDependentReconciler.ID_KEY); + var externalResource = externalService.read(id); + externalResource.ifPresent(res::add); + }); return res; } @@ -53,42 +55,49 @@ public Class stateResourceClass() { } @Override - public ConfigMap stateResource(ExternalStateBulkDependentCustomResource primary, - ExternalResource resource) { - ConfigMap configMap = new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(configMapName(primary, resource)) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) - .withData(Map.of(ExternalStateDependentReconciler.ID_KEY, resource.getId())) - .build(); + public ConfigMap stateResource( + ExternalStateBulkDependentCustomResource primary, ExternalResource resource) { + ConfigMap configMap = + new ConfigMapBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(configMapName(primary, resource)) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withData(Map.of(ExternalStateDependentReconciler.ID_KEY, resource.getId())) + .build(); configMap.addOwnerReference(primary); return configMap; } @Override - public ExternalResource create(ExternalResource desired, + public ExternalResource create( + ExternalResource desired, ExternalStateBulkDependentCustomResource primary, Context context) { return externalService.create(desired); } @Override - public ExternalResource update(ExternalResource actual, - ExternalResource desired, ExternalStateBulkDependentCustomResource primary, + public ExternalResource update( + ExternalResource actual, + ExternalResource desired, + ExternalStateBulkDependentCustomResource primary, Context context) { return externalService.update(new ExternalResource(actual.getId(), desired.getData())); } @Override - protected void handleDelete(ExternalStateBulkDependentCustomResource primary, + protected void handleDelete( + ExternalStateBulkDependentCustomResource primary, ExternalResource secondary, Context context) { externalService.delete(secondary.getId()); } @Override - public Matcher.Result match(ExternalResource actualResource, + public Matcher.Result match( + ExternalResource actualResource, ExternalResource desired, ExternalStateBulkDependentCustomResource primary, Context context) { @@ -102,8 +111,8 @@ public Map desiredResources( int number = primary.getSpec().getNumber(); Map res = new HashMap<>(); for (int i = 0; i < number; i++) { - res.put(Integer.toString(i), - new ExternalResource(primary.getSpec().getData() + DELIMITER + i)); + res.put( + Integer.toString(i), new ExternalResource(primary.getSpec().getData() + DELIMITER + i)); } return res; } @@ -117,19 +126,22 @@ public Map getSecondaryResources( } @Override - public void handleDeleteTargetResource(ExternalStateBulkDependentCustomResource primary, - ExternalResource resource, String key, + public void handleDeleteTargetResource( + ExternalStateBulkDependentCustomResource primary, + ExternalResource resource, + String key, Context context) { externalService.delete(resource.getId()); } private String externalResourceIndex(ExternalResource externalResource) { - return externalResource.getData() + return externalResource + .getData() .substring(externalResource.getData().lastIndexOf(DELIMITER) + 1); } - private String configMapName(ExternalStateBulkDependentCustomResource primary, - ExternalResource resource) { + private String configMapName( + ExternalStateBulkDependentCustomResource primary, ExternalResource resource) { return primary.getMetadata().getName() + DELIMITER + externalResourceIndex(resource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentCustomResource.java index 4e8c07dcc7..7f75f20de9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("esb") public class ExternalStateBulkDependentCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java index 2e0f672c79..34401e8754 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkDependentReconciler.java @@ -14,8 +14,7 @@ @Workflow(dependents = @Dependent(type = BulkDependentResourceExternalWithState.class)) @ControllerConfiguration public class ExternalStateBulkDependentReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -35,12 +34,12 @@ public int getNumberOfExecutions() { @Override public List> prepareEventSources( EventSourceContext context) { - var configMapEventSource = new InformerEventSource<>( - InformerEventSourceConfiguration - .from(ConfigMap.class, ExternalStateBulkDependentCustomResource.class) - .build(), - context); + var configMapEventSource = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, ExternalStateBulkDependentCustomResource.class) + .build(), + context); return List.of(configMapEventSource); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java index 3677537b01..b2714fab47 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/externalstate/externalstatebulkdependent/ExternalStateBulkIT.java @@ -52,42 +52,62 @@ void reconcilesResourceWithPersistentState() { } private void assertResourcesDeleted(ExternalStateBulkDependentCustomResource resource) { - await().untilAsserted(() -> { - var configMaps = - operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) - .list().getItems().stream().filter( - cm -> cm.getMetadata().getName().startsWith(resource.getMetadata().getName())); - var resources = externalService.listResources(); - assertThat(configMaps).isEmpty(); - assertThat(resources).isEmpty(); - }); + await() + .untilAsserted( + () -> { + var configMaps = + operator + .getKubernetesClient() + .configMaps() + .inNamespace(operator.getNamespace()) + .list() + .getItems() + .stream() + .filter( + cm -> + cm.getMetadata() + .getName() + .startsWith(resource.getMetadata().getName())); + var resources = externalService.listResources(); + assertThat(configMaps).isEmpty(); + assertThat(resources).isEmpty(); + }); } - private void assertResources(ExternalStateBulkDependentCustomResource resource, - String initialTestData, int size) { - await().pollInterval(Duration.ofMillis(700)).untilAsserted(() -> { - var resources = externalService.listResources(); - assertThat(resources).hasSize(size); - assertThat(resources).allMatch(r -> r.getData().startsWith(initialTestData)); - - var configMaps = - operator.getKubernetesClient().configMaps().inNamespace(operator.getNamespace()) - .list().getItems().stream().filter( - cm -> cm.getMetadata().getName().startsWith(resource.getMetadata().getName())); - assertThat(configMaps).hasSize(size); - }); + private void assertResources( + ExternalStateBulkDependentCustomResource resource, String initialTestData, int size) { + await() + .pollInterval(Duration.ofMillis(700)) + .untilAsserted( + () -> { + var resources = externalService.listResources(); + assertThat(resources).hasSize(size); + assertThat(resources).allMatch(r -> r.getData().startsWith(initialTestData)); + + var configMaps = + operator + .getKubernetesClient() + .configMaps() + .inNamespace(operator.getNamespace()) + .list() + .getItems() + .stream() + .filter( + cm -> + cm.getMetadata() + .getName() + .startsWith(resource.getMetadata().getName())); + assertThat(configMaps).hasSize(size); + }); } private ExternalStateBulkDependentCustomResource testResource() { var res = new ExternalStateBulkDependentCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new ExternalStateBulkSpec()); res.getSpec().setNumber(INITIAL_BULK_SIZE); res.getSpec().setData(INITIAL_TEST_DATA); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/GenericKubernetesDependentTestBase.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/GenericKubernetesDependentTestBase.java index c26fcb42b4..061c0218f0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/GenericKubernetesDependentTestBase.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/GenericKubernetesDependentTestBase.java @@ -13,7 +13,8 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; -public abstract class GenericKubernetesDependentTestBase> { +public abstract class GenericKubernetesDependentTestBase< + R extends CustomResource> { public static final String INITIAL_DATA = "Initial data"; public static final String CHANGED_DATA = "Changed data"; @@ -23,30 +24,38 @@ public abstract class GenericKubernetesDependentTestBase { - var cm = extension().get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(ConfigMapGenericKubernetesDependent.KEY, INITIAL_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = extension().get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()) + .containsEntry(ConfigMapGenericKubernetesDependent.KEY, INITIAL_DATA); + }); resource.getSpec().setValue(CHANGED_DATA); resource = extension().replace(resource); - await().untilAsserted(() -> { - var cm = extension().get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm.getData()).containsEntry(ConfigMapGenericKubernetesDependent.KEY, CHANGED_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = extension().get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm.getData()) + .containsEntry(ConfigMapGenericKubernetesDependent.KEY, CHANGED_DATA); + }); extension().delete(resource); - await().timeout(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)).untilAsserted(() -> { - var cm = extension().get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNull(); - }); + await() + .timeout(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)) + .untilAsserted( + () -> { + var cm = extension().get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNull(); + }); } public abstract LocallyRunOperatorExtension extension(); public abstract R testResource(String name, String data); - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java index 54c91f66f1..25e0988ca0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/ConfigMapGenericKubernetesDependent.java @@ -14,12 +14,11 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; @KubernetesDependent -public class ConfigMapGenericKubernetesDependent extends - GenericKubernetesDependentResource - implements - Creator, - Updater, - GarbageCollected { +public class ConfigMapGenericKubernetesDependent + extends GenericKubernetesDependentResource + implements Creator, + Updater, + GarbageCollected { public static final String VERSION = "v1"; public static final String KIND = "ConfigMap"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedCustomResource.java index 24c1bbcb08..78e66ca74e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedCustomResource.java @@ -11,6 +11,4 @@ @Version("v1") @ShortNames("gkdm") public class GenericKubernetesDependentManagedCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java index 9bcccb9c46..93fc34fbf0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedIT.java @@ -24,12 +24,9 @@ public LocallyRunOperatorExtension extension() { @Override public GenericKubernetesDependentManagedCustomResource testResource(String name, String data) { var resource = new GenericKubernetesDependentManagedCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(name).build()); resource.setSpec(new GenericKubernetesDependentSpec()); resource.getSpec().setValue(INITIAL_DATA); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java index d122f8909e..60c709ad08 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentresourcemanaged/GenericKubernetesDependentManagedReconciler.java @@ -19,5 +19,4 @@ public UpdateControl reconcile( return UpdateControl.noUpdate(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java index 79324641f3..1f694a2fa4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/ConfigMapGenericKubernetesDependent.java @@ -12,12 +12,12 @@ import io.javaoperatorsdk.operator.processing.dependent.Updater; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesDependentResource; -public class ConfigMapGenericKubernetesDependent extends - GenericKubernetesDependentResource - implements - Creator, - Updater, - GarbageCollected { +public class ConfigMapGenericKubernetesDependent + extends GenericKubernetesDependentResource + implements Creator< + GenericKubernetesResource, GenericKubernetesDependentStandaloneCustomResource>, + Updater, + GarbageCollected { public static final String VERSION = "v1"; public static final String KIND = "ConfigMap"; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneCustomResource.java index eaf56831c5..10776fdce1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneCustomResource.java @@ -11,6 +11,4 @@ @Version("v1") @ShortNames("gkd") public class GenericKubernetesDependentStandaloneCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java index 07c264c6c2..9afdec5474 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneIT.java @@ -24,9 +24,7 @@ public LocallyRunOperatorExtension extension() { @Override public GenericKubernetesDependentStandaloneCustomResource testResource(String name, String data) { var resource = new GenericKubernetesDependentStandaloneCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(name).build()); resource.setSpec(new GenericKubernetesDependentSpec()); resource.getSpec().setValue(INITIAL_DATA); return resource; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java index 0e90d8e9d9..9e29965d39 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/generickubernetesresource/generickubernetesdependentstandalone/GenericKubernetesDependentStandaloneReconciler.java @@ -29,8 +29,9 @@ public UpdateControl reconci } @Override - public List> prepareEventSources( - EventSourceContext context) { + public List> + prepareEventSources( + EventSourceContext context) { return List.of(dependent.eventSource(context).orElseThrow()); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/ConfigMapDependentResource.java index 37e109e8de..348921cd93 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/ConfigMapDependentResource.java @@ -16,21 +16,18 @@ public class ConfigMapDependentResource public static final String DATA_KEY = "key"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(InformerRelatedBehaviorTestCustomResource primary, + protected ConfigMap desired( + InformerRelatedBehaviorTestCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withLabels(Map.of("app", "rbac-test")) - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withLabels(Map.of("app", "rbac-test")) + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of(DATA_KEY, primary.getMetadata().getName())) .build(); - } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorITS.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorITS.java index 21079c0504..8686d6f33b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorITS.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorITS.java @@ -35,16 +35,15 @@ * value (in case want to try with minikube use: "minikube start * --extra-config=apiserver.min-request-timeout=1") * - *

- * This is important when tests are affected by permission changes, since the watch permissions are - * just checked when established a watch request. So minimal request timeout is set to make sure + *

This is important when tests are affected by permission changes, since the watch permissions + * are just checked when established a watch request. So minimal request timeout is set to make sure * that with periodical watch reconnect the permission is tested again. - *

- *

- * The test ends with "ITS" (Special) since it needs to run separately from other ITs - *

+ * + *

The test ends with "ITS" (Special) since it needs to run separately from other ITs */ -@EnableKubeAPIServer(apiServerFlags = {"--min-request-timeout", "1"}, updateKubeConfigFile = true) +@EnableKubeAPIServer( + apiServerFlags = {"--min-request-timeout", "1"}, + updateKubeConfigFile = true) class InformerRelatedBehaviorITS { public static final String TEST_RESOURCE_NAME = "test1"; @@ -59,13 +58,16 @@ class InformerRelatedBehaviorITS { @BeforeEach void beforeEach(TestInfo testInfo) { - LocallyRunOperatorExtension.applyCrd(InformerRelatedBehaviorTestCustomResource.class, - adminClient); - testInfo.getTestMethod().ifPresent(method -> { - actualNamespace = KubernetesResourceUtil.sanitizeName(method.getName()); - additionalNamespace = actualNamespace + ADDITIONAL_NAMESPACE_SUFFIX; - adminClient.resource(namespace()).createOrReplace(); - }); + LocallyRunOperatorExtension.applyCrd( + InformerRelatedBehaviorTestCustomResource.class, adminClient); + testInfo + .getTestMethod() + .ifPresent( + method -> { + actualNamespace = KubernetesResourceUtil.sanitizeName(method.getName()); + additionalNamespace = actualNamespace + ADDITIONAL_NAMESPACE_SUFFIX; + adminClient.resource(namespace()).createOrReplace(); + }); // cleans up binding before test, not all test cases use cluster role removeClusterRoleBinding(); } @@ -103,7 +105,6 @@ void startsUpWhenNoPermissionToCustomResource() { assertThat(operator.getRuntimeInfo().allEventSourcesAreHealthy()).isTrue(); } - @Test void startsUpWhenNoPermissionToSecondaryResource() { adminClient.resource(testCustomResource()).createOrReplace(); @@ -134,27 +135,32 @@ void startsUpIfNoPermissionToOneOfTwoNamespaces() { private void assertInformerNotWatchingForAdditionalNamespace(Operator operator) { assertThat(operator.getRuntimeInfo().allEventSourcesAreHealthy()).isFalse(); var unhealthyEventSources = - operator.getRuntimeInfo().unhealthyInformerWrappingEventSourceHealthIndicator() + operator + .getRuntimeInfo() + .unhealthyInformerWrappingEventSourceHealthIndicator() .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); InformerHealthIndicator controllerHealthIndicator = - (InformerHealthIndicator) unhealthyEventSources - .get(ControllerEventSource.NAME) - .informerHealthIndicators().get(additionalNamespace); + (InformerHealthIndicator) + unhealthyEventSources + .get(ControllerEventSource.NAME) + .informerHealthIndicators() + .get(additionalNamespace); assertThat(controllerHealthIndicator).isNotNull(); assertThat(controllerHealthIndicator.getTargetNamespace()).isEqualTo(additionalNamespace); assertThat(controllerHealthIndicator.isWatching()).isFalse(); InformerHealthIndicator configMapHealthIndicator = - (InformerHealthIndicator) unhealthyEventSources - .get(InformerRelatedBehaviorTestReconciler.CONFIG_MAP_DEPENDENT_RESOURCE) - .informerHealthIndicators().get(additionalNamespace); + (InformerHealthIndicator) + unhealthyEventSources + .get(InformerRelatedBehaviorTestReconciler.CONFIG_MAP_DEPENDENT_RESOURCE) + .informerHealthIndicators() + .get(additionalNamespace); assertThat(configMapHealthIndicator).isNotNull(); assertThat(configMapHealthIndicator.getTargetNamespace()).isEqualTo(additionalNamespace); assertThat(configMapHealthIndicator.isWatching()).isFalse(); } - // this will be investigated separately under the issue below, it's not crucial functional wise, // it is rather "something working why it should", not other way around; but it's not a // showstopper @@ -175,7 +181,6 @@ void resilientForLoosingPermissionForCustomResource() { assertReconciled(); } - @Test void resilientForLoosingPermissionForSecondaryResource() { setFullResourcesAccess(); @@ -185,11 +190,18 @@ void resilientForLoosingPermissionForSecondaryResource() { waitForWatchReconnect(); adminClient.resource(testCustomResource()).createOrReplace(); - await().pollDelay(Duration.ofMillis(300)).untilAsserted(() -> { - var cm = - adminClient.configMaps().inNamespace(actualNamespace).withName(TEST_RESOURCE_NAME).get(); - assertThat(cm).isNull(); - }); + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> { + var cm = + adminClient + .configMaps() + .inNamespace(actualNamespace) + .withName(TEST_RESOURCE_NAME) + .get(); + assertThat(cm).isNull(); + }); setFullResourcesAccess(); assertReconciled(); @@ -226,54 +238,67 @@ private static void waitForWatchReconnect() { } private void assertNotReconciled() { - await().pollDelay(Duration.ofMillis(2000)).untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isEqualTo(0); - }); + await() + .pollDelay(Duration.ofMillis(2000)) + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(0); + }); } InformerRelatedBehaviorTestCustomResource testCustomResource() { InformerRelatedBehaviorTestCustomResource testCustomResource = new InformerRelatedBehaviorTestCustomResource(); - testCustomResource.setMetadata(new ObjectMetaBuilder() - .withNamespace(actualNamespace) - .withName(TEST_RESOURCE_NAME) - .build()); + testCustomResource.setMetadata( + new ObjectMetaBuilder() + .withNamespace(actualNamespace) + .withName(TEST_RESOURCE_NAME) + .build()); return testCustomResource; } private ConfigMap dependentConfigMap() { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(actualNamespace) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(actualNamespace) + .build()) .build(); } private void assertReconciled() { - await().untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(0); - var cm = - adminClient.configMaps().inNamespace(actualNamespace).withName(TEST_RESOURCE_NAME).get(); - assertThat(cm).isNotNull(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(0); + var cm = + adminClient + .configMaps() + .inNamespace(actualNamespace) + .withName(TEST_RESOURCE_NAME) + .get(); + assertThat(cm).isNotNull(); + }); } @SuppressWarnings("unchecked") private void assertRuntimeInfoNoCRPermission(Operator operator) { assertThat(operator.getRuntimeInfo().allEventSourcesAreHealthy()).isFalse(); var unhealthyEventSources = - operator.getRuntimeInfo().unhealthyEventSources() + operator + .getRuntimeInfo() + .unhealthyEventSources() .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); assertThat(unhealthyEventSources).isNotEmpty(); - assertThat(unhealthyEventSources.get(ControllerEventSource.NAME)) - .isNotNull(); - var informerHealthIndicators = operator.getRuntimeInfo() - .unhealthyInformerWrappingEventSourceHealthIndicator() - .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); + assertThat(unhealthyEventSources.get(ControllerEventSource.NAME)).isNotNull(); + var informerHealthIndicators = + operator + .getRuntimeInfo() + .unhealthyInformerWrappingEventSourceHealthIndicator() + .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); assertThat(informerHealthIndicators).isNotEmpty(); - assertThat(informerHealthIndicators.get(ControllerEventSource.NAME) - .informerHealthIndicators()) + assertThat(informerHealthIndicators.get(ControllerEventSource.NAME).informerHealthIndicators()) .hasSize(1); } @@ -281,26 +306,32 @@ private void assertRuntimeInfoNoCRPermission(Operator operator) { private void assertRuntimeInfoForSecondaryPermission(Operator operator) { assertThat(operator.getRuntimeInfo().allEventSourcesAreHealthy()).isFalse(); var unhealthyEventSources = - operator.getRuntimeInfo().unhealthyEventSources() + operator + .getRuntimeInfo() + .unhealthyEventSources() .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); assertThat(unhealthyEventSources).isNotEmpty(); assertThat(unhealthyEventSources.get(CONFIG_MAP_DEPENDENT_RESOURCE)).isNotNull(); - var informerHealthIndicators = operator.getRuntimeInfo() - .unhealthyInformerWrappingEventSourceHealthIndicator() - .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); + var informerHealthIndicators = + operator + .getRuntimeInfo() + .unhealthyInformerWrappingEventSourceHealthIndicator() + .get(INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER); assertThat(informerHealthIndicators).isNotEmpty(); assertThat( - informerHealthIndicators.get(CONFIG_MAP_DEPENDENT_RESOURCE).informerHealthIndicators()) + informerHealthIndicators.get(CONFIG_MAP_DEPENDENT_RESOURCE).informerHealthIndicators()) .hasSize(1); } KubernetesClient clientUsingServiceAccount() { - KubernetesClient client = new KubernetesClientBuilder() - .withConfig(new ConfigBuilder() - .withImpersonateUsername("rbac-test-user") - .withNamespace(actualNamespace) - .build()) - .build(); + KubernetesClient client = + new KubernetesClientBuilder() + .withConfig( + new ConfigBuilder() + .withImpersonateUsername("rbac-test-user") + .withNamespace(actualNamespace) + .build()) + .build(); return client; } @@ -308,26 +339,30 @@ Operator startOperator(boolean stopOnInformerErrorDuringStartup) { return startOperator(stopOnInformerErrorDuringStartup, true); } - Operator startOperator(boolean stopOnInformerErrorDuringStartup, boolean addStopHandler, - String... namespaces) { + Operator startOperator( + boolean stopOnInformerErrorDuringStartup, boolean addStopHandler, String... namespaces) { reconciler = new InformerRelatedBehaviorTestReconciler(); - Operator operator = new Operator( - co -> { - co.withKubernetesClient(clientUsingServiceAccount()); - co.withStopOnInformerErrorDuringStartup(stopOnInformerErrorDuringStartup); - co.withCacheSyncTimeout(Duration.ofMillis(3000)); - co.withReconciliationTerminationTimeout(Duration.ofSeconds(1)); - if (addStopHandler) { - co.withInformerStoppedHandler((informer, ex) -> replacementStopHandlerCalled = true); + Operator operator = + new Operator( + co -> { + co.withKubernetesClient(clientUsingServiceAccount()); + co.withStopOnInformerErrorDuringStartup(stopOnInformerErrorDuringStartup); + co.withCacheSyncTimeout(Duration.ofMillis(3000)); + co.withReconciliationTerminationTimeout(Duration.ofSeconds(1)); + if (addStopHandler) { + co.withInformerStoppedHandler( + (informer, ex) -> replacementStopHandlerCalled = true); + } + }); + operator.register( + reconciler, + o -> { + if (namespaces.length > 0) { + o.settingNamespaces(namespaces); } }); - operator.register(reconciler, o -> { - if (namespaces.length > 0) { - o.settingNamespaces(namespaces); - } - }); operator.start(); return operator; } @@ -348,24 +383,25 @@ private void setFullResourcesAccess() { } private void addRoleBindingsToTestNamespaces() { - var role = ReconcilerUtils - .loadYaml(Role.class, this.getClass(), "rback-test-only-main-ns-access.yaml"); + var role = + ReconcilerUtils.loadYaml( + Role.class, this.getClass(), "rback-test-only-main-ns-access.yaml"); adminClient.resource(role).inNamespace(actualNamespace).createOrReplace(); - var roleBinding = ReconcilerUtils - .loadYaml(RoleBinding.class, this.getClass(), - "rback-test-only-main-ns-access-binding.yaml"); + var roleBinding = + ReconcilerUtils.loadYaml( + RoleBinding.class, this.getClass(), "rback-test-only-main-ns-access-binding.yaml"); adminClient.resource(roleBinding).inNamespace(actualNamespace).createOrReplace(); } private void applyClusterRoleBinding() { - var clusterRoleBinding = ReconcilerUtils - .loadYaml(ClusterRoleBinding.class, this.getClass(), "rback-test-role-binding.yaml"); + var clusterRoleBinding = + ReconcilerUtils.loadYaml( + ClusterRoleBinding.class, this.getClass(), "rback-test-role-binding.yaml"); adminClient.resource(clusterRoleBinding).createOrReplace(); } private void applyClusterRole(String filename) { - var clusterRole = ReconcilerUtils - .loadYaml(ClusterRole.class, this.getClass(), filename); + var clusterRole = ReconcilerUtils.loadYaml(ClusterRole.class, this.getClass(), filename); adminClient.resource(clusterRole).createOrReplace(); } @@ -375,15 +411,14 @@ private Namespace namespace() { private Namespace namespace(String name) { Namespace n = new Namespace(); - n.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + n.setMetadata(new ObjectMetaBuilder().withName(name).build()); return n; } private void removeClusterRoleBinding() { - var clusterRoleBinding = ReconcilerUtils - .loadYaml(ClusterRoleBinding.class, this.getClass(), "rback-test-role-binding.yaml"); + var clusterRoleBinding = + ReconcilerUtils.loadYaml( + ClusterRoleBinding.class, this.getClass(), "rback-test-role-binding.yaml"); adminClient.resource(clusterRoleBinding).delete(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestCustomResource.java index 9269dc5d6e..8f4c603d81 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("rbt") -public class InformerRelatedBehaviorTestCustomResource - extends CustomResource - implements Namespaced { -} +public class InformerRelatedBehaviorTestCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java index 2fb7724f49..61c07fb88d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/informerrelatedbehavior/InformerRelatedBehaviorTestReconciler.java @@ -10,9 +10,11 @@ import io.javaoperatorsdk.operator.processing.event.ResourceID; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = @Dependent( - name = InformerRelatedBehaviorTestReconciler.CONFIG_MAP_DEPENDENT_RESOURCE, - type = ConfigMapDependentResource.class)) +@Workflow( + dependents = + @Dependent( + name = InformerRelatedBehaviorTestReconciler.CONFIG_MAP_DEPENDENT_RESOURCE, + type = ConfigMapDependentResource.class)) @ControllerConfiguration( name = InformerRelatedBehaviorTestReconciler.INFORMER_RELATED_BEHAVIOR_TEST_RECONCILER) public class InformerRelatedBehaviorTestReconciler @@ -39,5 +41,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestCustomResource.java index 33f9899689..dbe944c6fa 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestCustomResource.java @@ -10,7 +10,5 @@ @Version("v1") @ShortNames("dgc") public class DependentGarbageCollectionTestCustomResource - extends - CustomResource - implements Namespaced { -} + extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java index e786c1c2c1..880aaa6884 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/DependentGarbageCollectionTestReconciler.java @@ -50,7 +50,8 @@ public UpdateControl reconcile( @Override public ErrorStatusUpdateControl updateErrorStatus( DependentGarbageCollectionTestCustomResource resource, - Context context, Exception e) { + Context context, + Exception e) { // this can happen when a namespace is terminated in test if (e instanceof KubernetesClientException) { return ErrorStatusUpdateControl.noStatusUpdate(); @@ -63,24 +64,22 @@ public boolean isErrorOccurred() { return errorOccurred; } - private static class ConfigMapDependentResource extends - KubernetesDependentResource + private static class ConfigMapDependentResource + extends KubernetesDependentResource implements Creator, - Updater, - GarbageCollected { - - public ConfigMapDependentResource() { - super(ConfigMap.class); - } + Updater, + GarbageCollected { @Override - protected ConfigMap desired(DependentGarbageCollectionTestCustomResource primary, + protected ConfigMap desired( + DependentGarbageCollectionTestCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of("key", "data")); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java index afc89471a6..8e08c4e739 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/kubernetesdependentgarbagecollection/KubernetesDependentGarbageCollectionIT.java @@ -16,23 +16,24 @@ class KubernetesDependentGarbageCollectionIT { public static final String TEST_RESOURCE_NAME = "test1"; + @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() .withReconciler(new DependentGarbageCollectionTestReconciler()) .build(); - @Test void resourceSecondaryResourceIsGarbageCollected() { var resource = customResource(); - var createdResources = - operator.create(resource); + var createdResources = operator.create(resource); - await().untilAsserted(() -> { - ConfigMap configMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(configMap).isNotNull(); - }); + await() + .untilAsserted( + () -> { + ConfigMap configMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(configMap).isNotNull(); + }); ConfigMap configMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); assertThat(configMap.getMetadata().getOwnerReferences()).hasSize(1); @@ -41,42 +42,44 @@ void resourceSecondaryResourceIsGarbageCollected() { operator.delete(createdResources); - await().atMost(Duration.ofSeconds(IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS)) - .untilAsserted(() -> { - ConfigMap cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNull(); - }); + await() + .atMost(Duration.ofSeconds(IntegrationTestConstants.GARBAGE_COLLECTION_TIMEOUT_SECONDS)) + .untilAsserted( + () -> { + ConfigMap cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNull(); + }); } @Test void deletesSecondaryResource() { var resource = customResource(); - var createdResources = - operator.create(resource); + var createdResources = operator.create(resource); - await().untilAsserted(() -> { - ConfigMap configMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(configMap).isNotNull(); - }); + await() + .untilAsserted( + () -> { + ConfigMap configMap = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(configMap).isNotNull(); + }); createdResources.getSpec().setCreateConfigMap(false); operator.replace(createdResources); - await().untilAsserted(() -> { - ConfigMap cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNull(); - }); + await() + .untilAsserted( + () -> { + ConfigMap cm = operator.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNull(); + }); } DependentGarbageCollectionTestCustomResource customResource() { DependentGarbageCollectionTestCustomResource resource = new DependentGarbageCollectionTestCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); resource.setSpec(new DependentGarbageCollectionTestCustomResourceSpec()); resource.getSpec().setCreateConfigMap(true); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceConfigMap.java index d0bc50ddd8..7f895370f5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceConfigMap.java @@ -19,7 +19,8 @@ public MultipleDependentResourceConfigMap(String value) { } @Override - protected ConfigMap desired(MultipleDependentResourceCustomResource primary, + protected ConfigMap desired( + MultipleDependentResourceCustomResource primary, Context context) { return new ConfigMapBuilder() diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceCustomResource.java index 7b8fb15466..b9b052e2b8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("mdr") public class MultipleDependentResourceCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java index f8b9259b6a..a7dfefdaa8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceIT.java @@ -31,43 +31,48 @@ public class MultipleDependentResourceIT { void handlesCRUDOperations() { var res = extension.create(testResource()); - await().untilAsserted(() -> { - var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); - var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); - - assertThat(cm1).isNotNull(); - assertThat(cm2).isNotNull(); - assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); - assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + }); res.getSpec().setValue(CHANGED_VALUE); res = extension.replace(res); - await().untilAsserted(() -> { - var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); - var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + await() + .untilAsserted( + () -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); - assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); - assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); - }); + assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); extension.delete(res); - await().timeout(Duration.ofSeconds(120)).untilAsserted(() -> { - var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); - var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); + await() + .timeout(Duration.ofSeconds(120)) + .untilAsserted( + () -> { + var cm1 = extension.get(ConfigMap.class, getConfigMapName(FIRST_CONFIG_MAP_ID)); + var cm2 = extension.get(ConfigMap.class, getConfigMapName(SECOND_CONFIG_MAP_ID)); - assertThat(cm1).isNull(); - assertThat(cm2).isNull(); - }); + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); } MultipleDependentResourceCustomResource testResource() { var res = new MultipleDependentResourceCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .build()); + res.setMetadata(new ObjectMetaBuilder().withName("test1").build()); res.setSpec(new MultipleDependentResourceSpec()); res.getSpec().setValue(INITIAL_VALUE); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceReconciler.java index aeac786cda..f4088f36f1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresource/MultipleDependentResourceReconciler.java @@ -36,9 +36,11 @@ public UpdateControl reconcile( public List> prepareEventSources( EventSourceContext context) { InformerEventSource eventSource = - new InformerEventSource<>(InformerEventSourceConfiguration - .from(ConfigMap.class, MultipleDependentResourceCustomResource.class) - .build(), context); + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, MultipleDependentResourceCustomResource.class) + .build(), + context); firstDependentResourceConfigMap.setEventSource(eventSource); secondDependentResourceConfigMap.setEventSource(eventSource); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceConfigMap.java index defef91b75..2708e19657 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceConfigMap.java @@ -9,8 +9,8 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; public class MultipleDependentResourceConfigMap - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleDependentResourceCustomResourceNoDiscriminator> { public static final String DATA_KEY = "key"; private final int value; @@ -21,7 +21,8 @@ public MultipleDependentResourceConfigMap(int value) { } @Override - protected ConfigMap desired(MultipleDependentResourceCustomResourceNoDiscriminator primary, + protected ConfigMap desired( + MultipleDependentResourceCustomResourceNoDiscriminator primary, Context context) { Map data = new HashMap<>(); data.put(DATA_KEY, String.valueOf(value)); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceCustomResourceNoDiscriminator.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceCustomResourceNoDiscriminator.java index bd91aae234..b8af874b23 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceCustomResourceNoDiscriminator.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceCustomResourceNoDiscriminator.java @@ -10,8 +10,7 @@ @Version("v1") @ShortNames("mdwd") public class MultipleDependentResourceCustomResourceNoDiscriminator - extends CustomResource - implements Namespaced { + extends CustomResource implements Namespaced { public String getConfigMapName(int id) { return "configmap" + id; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithDiscriminatorReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithDiscriminatorReconciler.java index c96033d9bc..288ba2bb97 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithDiscriminatorReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithDiscriminatorReconciler.java @@ -13,7 +13,7 @@ @ControllerConfiguration public class MultipleDependentResourceWithDiscriminatorReconciler implements Reconciler, - TestExecutionInfoProvider { + TestExecutionInfoProvider { public static final int FIRST_CONFIG_MAP_ID = 1; public static final int SECOND_CONFIG_MAP_ID = 2; @@ -37,18 +37,22 @@ public UpdateControl rec return UpdateControl.noUpdate(); } - public int getNumberOfExecutions() { return numberOfExecutions.get(); } @Override - public List> prepareEventSources( - EventSourceContext context) { - InformerEventSource eventSource = - new InformerEventSource<>(InformerEventSourceConfiguration.from(ConfigMap.class, - MultipleDependentResourceCustomResourceNoDiscriminator.class) - .build(), context); + public List> + prepareEventSources( + EventSourceContext context) { + InformerEventSource + eventSource = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, + MultipleDependentResourceCustomResourceNoDiscriminator.class) + .build(), + context); firstDependentResourceConfigMap.setEventSource(eventSource); secondDependentResourceConfigMap.setEventSource(eventSource); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java index b653a8b606..8be51ebf1a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentresourcewithsametype/MultipleDependentResourceWithNoDiscriminatorIT.java @@ -16,6 +16,7 @@ class MultipleDependentResourceWithNoDiscriminatorIT { public static final String TEST_RESOURCE_NAME = "multipledependentresource-testresource"; + @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() @@ -32,20 +33,21 @@ void twoConfigMapsHaveBeenCreated() { var reconciler = operator.getReconcilerOfType(MultipleDependentResourceWithDiscriminatorReconciler.class); - await().pollDelay(Duration.ofMillis(300)) - .until(() -> reconciler.getNumberOfExecutions() <= 1); - - IntStream.of(MultipleDependentResourceWithDiscriminatorReconciler.FIRST_CONFIG_MAP_ID, - MultipleDependentResourceWithDiscriminatorReconciler.SECOND_CONFIG_MAP_ID) - .forEach(configMapId -> { - ConfigMap configMap = - operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId)); - assertThat(configMap).isNotNull(); - assertThat(configMap.getMetadata().getName()) - .isEqualTo(customResource.getConfigMapName(configMapId)); - assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY)) - .isEqualTo(String.valueOf(configMapId)); - }); + await().pollDelay(Duration.ofMillis(300)).until(() -> reconciler.getNumberOfExecutions() <= 1); + + IntStream.of( + MultipleDependentResourceWithDiscriminatorReconciler.FIRST_CONFIG_MAP_ID, + MultipleDependentResourceWithDiscriminatorReconciler.SECOND_CONFIG_MAP_ID) + .forEach( + configMapId -> { + ConfigMap configMap = + operator.get(ConfigMap.class, customResource.getConfigMapName(configMapId)); + assertThat(configMap).isNotNull(); + assertThat(configMap.getMetadata().getName()) + .isEqualTo(customResource.getConfigMapName(configMapId)); + assertThat(configMap.getData().get(MultipleDependentResourceConfigMap.DATA_KEY)) + .isEqualTo(String.valueOf(configMapId)); + }); } public MultipleDependentResourceCustomResourceNoDiscriminator createTestCustomResource() { @@ -58,5 +60,4 @@ public MultipleDependentResourceCustomResourceNoDiscriminator createTestCustomRe .build()); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java index 6f244f554c..5ba6d56be3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleDependentSameTypeMultiInformerIT.java @@ -41,33 +41,47 @@ void handlesCrudOperations() { assertConfigMapsDeleted(); } - private void assertConfigMapsPresent(String expectedData) { - await().untilAsserted(() -> { - var maps = operator.getKubernetesClient().configMaps() - .inNamespace(operator.getNamespace()).list().getItems().stream() - .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) - .collect(Collectors.toList()); - assertThat(maps).hasSize(2); - assertThat(maps).allMatch(cm -> cm.getData().get(DATA_KEY).equals(expectedData)); - }); + await() + .untilAsserted( + () -> { + var maps = + operator + .getKubernetesClient() + .configMaps() + .inNamespace(operator.getNamespace()) + .list() + .getItems() + .stream() + .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) + .collect(Collectors.toList()); + assertThat(maps).hasSize(2); + assertThat(maps).allMatch(cm -> cm.getData().get(DATA_KEY).equals(expectedData)); + }); } private void assertConfigMapsDeleted() { - await().atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)).untilAsserted(() -> { - var maps = operator.getKubernetesClient().configMaps() - .inNamespace(operator.getNamespace()).list().getItems().stream() - .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) - .collect(Collectors.toList()); - assertThat(maps).hasSize(0); - }); + await() + .atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)) + .untilAsserted( + () -> { + var maps = + operator + .getKubernetesClient() + .configMaps() + .inNamespace(operator.getNamespace()) + .list() + .getItems() + .stream() + .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) + .collect(Collectors.toList()); + assertThat(maps).hasSize(0); + }); } private MultipleManagedDependentResourceMultiInformerCustomResource testResource() { var res = new MultipleManagedDependentResourceMultiInformerCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new MultipleManagedDependentResourceMultiInformerSpec()); res.getSpec().setValue(DEFAULT_SPEC_VALUE); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap1.java index 47c65f1a95..855944ef98 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap1.java @@ -12,17 +12,14 @@ @KubernetesDependent public class MultipleManagedDependentResourceMultiInformerConfigMap1 - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleManagedDependentResourceMultiInformerCustomResource> { public static final String NAME_SUFFIX = "-1"; - public MultipleManagedDependentResourceMultiInformerConfigMap1() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleManagedDependentResourceMultiInformerCustomResource primary, + protected ConfigMap desired( + MultipleManagedDependentResourceMultiInformerCustomResource primary, Context context) { Map data = new HashMap<>(); data.put(MultipleManagedDependentResourceReconciler.DATA_KEY, primary.getSpec().getValue()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap2.java index 320768de24..7b28b322b7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerConfigMap2.java @@ -13,17 +13,14 @@ @KubernetesDependent public class MultipleManagedDependentResourceMultiInformerConfigMap2 - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleManagedDependentResourceMultiInformerCustomResource> { public static final String NAME_SUFFIX = "-2"; - public MultipleManagedDependentResourceMultiInformerConfigMap2() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleManagedDependentResourceMultiInformerCustomResource primary, + protected ConfigMap desired( + MultipleManagedDependentResourceMultiInformerCustomResource primary, Context context) { Map data = new HashMap<>(); data.put(DATA_KEY, primary.getSpec().getValue()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerCustomResource.java index 060a8f6b62..ad15168294 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerCustomResource.java @@ -11,6 +11,4 @@ @ShortNames("mmi") public class MultipleManagedDependentResourceMultiInformerCustomResource extends CustomResource - implements Namespaced { - -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java index f0fe034c97..59c53b8594 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledependentsametypemultiinformer/MultipleManagedDependentResourceMultiInformerReconciler.java @@ -6,16 +6,19 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = { - @Dependent(name = MultipleManagedDependentResourceMultiInformerReconciler.CONFIG_MAP_1_DR, - type = MultipleManagedDependentResourceMultiInformerConfigMap1.class), - @Dependent(name = MultipleManagedDependentResourceMultiInformerReconciler.CONFIG_MAP_2_DR, - type = MultipleManagedDependentResourceMultiInformerConfigMap2.class) -}) +@Workflow( + dependents = { + @Dependent( + name = MultipleManagedDependentResourceMultiInformerReconciler.CONFIG_MAP_1_DR, + type = MultipleManagedDependentResourceMultiInformerConfigMap1.class), + @Dependent( + name = MultipleManagedDependentResourceMultiInformerReconciler.CONFIG_MAP_2_DR, + type = MultipleManagedDependentResourceMultiInformerConfigMap2.class) + }) @ControllerConfiguration public class MultipleManagedDependentResourceMultiInformerReconciler implements Reconciler, - TestExecutionInfoProvider { + TestExecutionInfoProvider { public static final String DATA_KEY = "key"; public static final String CONFIG_MAP_1_DR = "ConfigMap1"; @@ -34,7 +37,6 @@ public UpdateControl { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleManagedDependentNoDiscriminatorCustomResource> { public static final String NAME_SUFFIX = "-1"; - public MultipleManagedDependentNoDiscriminatorConfigMap1() { - super(ConfigMap.class); - } - /* * Showcases optimization to avoid computing the whole desired state by providing the ResourceID * of the target resource. In this particular case this would not be necessary, since desired @@ -30,15 +26,17 @@ public MultipleManagedDependentNoDiscriminatorConfigMap1() { protected ResourceID targetSecondaryResourceID( MultipleManagedDependentNoDiscriminatorCustomResource primary, Context context) { - return new ResourceID(primary.getMetadata().getName() + NAME_SUFFIX, - primary.getMetadata().getNamespace()); + return new ResourceID( + primary.getMetadata().getName() + NAME_SUFFIX, primary.getMetadata().getNamespace()); } @Override - protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary, + protected ConfigMap desired( + MultipleManagedDependentNoDiscriminatorCustomResource primary, Context context) { Map data = new HashMap<>(); - data.put(MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY, + data.put( + MultipleManagedDependentSameTypeNoDiscriminatorReconciler.DATA_KEY, primary.getSpec().getValue()); return new ConfigMapBuilder() diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java index 373a70bcda..4455ef5d9b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorConfigMap2.java @@ -13,17 +13,14 @@ @KubernetesDependent public class MultipleManagedDependentNoDiscriminatorConfigMap2 - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleManagedDependentNoDiscriminatorCustomResource> { public static final String NAME_SUFFIX = "-2"; - public MultipleManagedDependentNoDiscriminatorConfigMap2() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleManagedDependentNoDiscriminatorCustomResource primary, + protected ConfigMap desired( + MultipleManagedDependentNoDiscriminatorCustomResource primary, Context context) { Map data = new HashMap<>(); data.put(DATA_KEY, primary.getSpec().getValue()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java index 7681cf7792..5af2ffec44 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorCustomResource.java @@ -11,6 +11,4 @@ @ShortNames("mnd") public class MultipleManagedDependentNoDiscriminatorCustomResource extends CustomResource - implements Namespaced { - -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java index cc6b79066a..1ed1da56f9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentNoDiscriminatorIT.java @@ -29,52 +29,74 @@ public class MultipleManagedDependentNoDiscriminatorIT { void handlesCRUDOperations() { var res = extension.create(testResource()); - await().untilAsserted(() -> { - var cm1 = extension.get(ConfigMap.class, - RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); - var cm2 = extension.get(ConfigMap.class, - RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); - - assertThat(cm1).isNotNull(); - assertThat(cm2).isNotNull(); - assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); - assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm1 = + extension.get( + ConfigMap.class, + RESOURCE_NAME + + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = + extension.get( + ConfigMap.class, + RESOURCE_NAME + + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_VALUE); + }); res.getSpec().setValue(CHANGED_VALUE); res = extension.replace(res); - await().untilAsserted(() -> { - var cm1 = extension.get(ConfigMap.class, - RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); - var cm2 = extension.get(ConfigMap.class, - RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); - - assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); - assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm1 = + extension.get( + ConfigMap.class, + RESOURCE_NAME + + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = + extension.get( + ConfigMap.class, + RESOURCE_NAME + + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + assertThat(cm2.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); extension.delete(res); - await().timeout(Duration.ofSeconds(60)).untilAsserted(() -> { - var cm1 = extension.get(ConfigMap.class, - RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); - var cm2 = extension.get(ConfigMap.class, - RESOURCE_NAME + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); - - assertThat(cm1).isNull(); - assertThat(cm2).isNull(); - }); + await() + .timeout(Duration.ofSeconds(60)) + .untilAsserted( + () -> { + var cm1 = + extension.get( + ConfigMap.class, + RESOURCE_NAME + + MultipleManagedDependentNoDiscriminatorConfigMap1.NAME_SUFFIX); + var cm2 = + extension.get( + ConfigMap.class, + RESOURCE_NAME + + MultipleManagedDependentNoDiscriminatorConfigMap2.NAME_SUFFIX); + + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); } MultipleManagedDependentNoDiscriminatorCustomResource testResource() { var res = new MultipleManagedDependentNoDiscriminatorCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); res.setSpec(new MultipleManagedDependentNoDiscriminatorSpec()); res.getSpec().setValue(INITIAL_VALUE); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java index 09c0b5cc23..488ab8a771 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipledrsametypenodiscriminator/MultipleManagedDependentSameTypeNoDiscriminatorReconciler.java @@ -13,16 +13,19 @@ import static io.javaoperatorsdk.operator.dependent.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; -@Workflow(dependents = { - @Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap1.class, - useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), - @Dependent(type = MultipleManagedDependentNoDiscriminatorConfigMap2.class, - useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) -}) +@Workflow( + dependents = { + @Dependent( + type = MultipleManagedDependentNoDiscriminatorConfigMap1.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), + @Dependent( + type = MultipleManagedDependentNoDiscriminatorConfigMap2.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) + }) @ControllerConfiguration public class MultipleManagedDependentSameTypeNoDiscriminatorReconciler implements Reconciler, - TestExecutionInfoProvider { + TestExecutionInfoProvider { public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; public static final String DATA_KEY = "key"; @@ -40,18 +43,18 @@ public UpdateControl reco return UpdateControl.noUpdate(); } - public int getNumberOfExecutions() { return numberOfExecutions.get(); } @Override - public List> prepareEventSources( - EventSourceContext context) { + public List> + prepareEventSources( + EventSourceContext context) { InformerEventSource ies = new InformerEventSource<>( - InformerEventSourceConfiguration.from(ConfigMap.class, - MultipleManagedDependentNoDiscriminatorCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, MultipleManagedDependentNoDiscriminatorCustomResource.class) .withName(CONFIG_MAP_EVENT_SOURCE) .build(), context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java index 4d69ef25e3..687bfcb5ac 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap1.java @@ -11,17 +11,14 @@ @KubernetesDependent public class MultipleManagedDependentResourceConfigMap1 - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleManagedDependentResourceCustomResource> { public static final String NAME_SUFFIX = "-1"; - public MultipleManagedDependentResourceConfigMap1() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleManagedDependentResourceCustomResource primary, + protected ConfigMap desired( + MultipleManagedDependentResourceCustomResource primary, Context context) { Map data = new HashMap<>(); data.put(MultipleManagedDependentResourceReconciler.DATA_KEY, primary.getSpec().getValue()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java index 11231470e7..1a1d6b51c0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceConfigMap2.java @@ -11,17 +11,14 @@ @KubernetesDependent public class MultipleManagedDependentResourceConfigMap2 - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + ConfigMap, MultipleManagedDependentResourceCustomResource> { public static final String NAME_SUFFIX = "-2"; - public MultipleManagedDependentResourceConfigMap2() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleManagedDependentResourceCustomResource primary, + protected ConfigMap desired( + MultipleManagedDependentResourceCustomResource primary, Context context) { Map data = new HashMap<>(); data.put(MultipleManagedDependentResourceReconciler.DATA_KEY, primary.getSpec().getValue()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java index 2c131978b6..9daf1af59d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceCustomResource.java @@ -10,7 +10,4 @@ @Version("v1") @ShortNames("mmd") public class MultipleManagedDependentResourceCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java index f25587b2ee..b2b90825b6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentResourceReconciler.java @@ -13,16 +13,19 @@ import static io.javaoperatorsdk.operator.dependent.multiplemanageddependentsametype.MultipleManagedDependentResourceReconciler.CONFIG_MAP_EVENT_SOURCE; -@Workflow(dependents = { - @Dependent(type = MultipleManagedDependentResourceConfigMap1.class, - useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), - @Dependent(type = MultipleManagedDependentResourceConfigMap2.class, - useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) -}) +@Workflow( + dependents = { + @Dependent( + type = MultipleManagedDependentResourceConfigMap1.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), + @Dependent( + type = MultipleManagedDependentResourceConfigMap2.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE) + }) @ControllerConfiguration public class MultipleManagedDependentResourceReconciler implements Reconciler, - TestExecutionInfoProvider { + TestExecutionInfoProvider { public static final String CONFIG_MAP_EVENT_SOURCE = "ConfigMapEventSource"; public static final String DATA_KEY = "key"; @@ -40,7 +43,6 @@ public UpdateControl reconcile( return UpdateControl.noUpdate(); } - public int getNumberOfExecutions() { return numberOfExecutions.get(); } @@ -50,8 +52,8 @@ public List> prep EventSourceContext context) { InformerEventSource ies = new InformerEventSource<>( - InformerEventSourceConfiguration - .from(ConfigMap.class, MultipleManagedDependentResourceCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, MultipleManagedDependentResourceCustomResource.class) .withName(CONFIG_MAP_EVENT_SOURCE) .build(), context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java index ed568e75e1..a835b5a255 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanageddependentsametype/MultipleManagedDependentSameTypeIT.java @@ -27,7 +27,6 @@ class MultipleManagedDependentSameTypeIT { .withReconciler(new MultipleManagedDependentResourceReconciler()) .build(); - @Test void handlesCrudOperations() { operator.create(testResource()); @@ -43,35 +42,49 @@ void handlesCrudOperations() { } private void assertConfigMapsPresent(String expectedData) { - await().untilAsserted(() -> { - var maps = operator.getKubernetesClient().configMaps() - .inNamespace(operator.getNamespace()).list().getItems().stream() - .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) - .collect(Collectors.toList()); - assertThat(maps).hasSize(2); - assertThat(maps).allMatch(cm -> cm.getData().get(DATA_KEY).equals(expectedData)); - }); + await() + .untilAsserted( + () -> { + var maps = + operator + .getKubernetesClient() + .configMaps() + .inNamespace(operator.getNamespace()) + .list() + .getItems() + .stream() + .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) + .collect(Collectors.toList()); + assertThat(maps).hasSize(2); + assertThat(maps).allMatch(cm -> cm.getData().get(DATA_KEY).equals(expectedData)); + }); } private void assertConfigMapsDeleted() { - await().atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)).untilAsserted(() -> { - var maps = operator.getKubernetesClient().configMaps() - .inNamespace(operator.getNamespace()).list().getItems().stream() - .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) - .collect(Collectors.toList()); - assertThat(maps).hasSize(0); - }); + await() + .atMost(Duration.ofSeconds(GARBAGE_COLLECTION_TIMEOUT_SECONDS)) + .untilAsserted( + () -> { + var maps = + operator + .getKubernetesClient() + .configMaps() + .inNamespace(operator.getNamespace()) + .list() + .getItems() + .stream() + .filter(cm -> cm.getMetadata().getName().startsWith(TEST_RESOURCE_NAME)) + .collect(Collectors.toList()); + assertThat(maps).hasSize(0); + }); } private MultipleManagedDependentResourceCustomResource testResource() { var res = new MultipleManagedDependentResourceCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new MultipleManagedDependentResourceSpec()); res.getSpec().setValue(DEFAULT_SPEC_VALUE); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java index 9cc3830be8..710297ae68 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/AbstractExternalDependentResource.java @@ -13,11 +13,12 @@ import io.javaoperatorsdk.operator.support.ExternalResource; import io.javaoperatorsdk.operator.support.ExternalServiceMock; -public abstract class AbstractExternalDependentResource extends - PollingDependentResource +public abstract class AbstractExternalDependentResource + extends PollingDependentResource< + ExternalResource, MultipleManagedExternalDependentResourceCustomResource> implements Creator, - Updater, - Deleter { + Updater, + Deleter { protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); @@ -31,14 +32,16 @@ public Map> fetchResources() { } @Override - public ExternalResource create(ExternalResource desired, + public ExternalResource create( + ExternalResource desired, MultipleManagedExternalDependentResourceCustomResource primary, Context context) { return externalServiceMock.create(desired); } @Override - public ExternalResource update(ExternalResource actual, + public ExternalResource update( + ExternalResource actual, ExternalResource desired, MultipleManagedExternalDependentResourceCustomResource primary, Context context) { @@ -46,7 +49,8 @@ public ExternalResource update(ExternalResource actual, } @Override - public Matcher.Result match(ExternalResource actualResource, + public Matcher.Result match( + ExternalResource actualResource, MultipleManagedExternalDependentResourceCustomResource primary, Context context) { var desired = desired(primary, context); @@ -54,15 +58,16 @@ public Matcher.Result match(ExternalResource actualResource, } @Override - public void delete(MultipleManagedExternalDependentResourceCustomResource primary, + public void delete( + MultipleManagedExternalDependentResourceCustomResource primary, Context context) { externalServiceMock.delete(toExternalResourceID(primary)); } - protected ExternalResource desired(MultipleManagedExternalDependentResourceCustomResource primary, + protected ExternalResource desired( + MultipleManagedExternalDependentResourceCustomResource primary, Context context) { - return new ExternalResource(toExternalResourceID(primary), - primary.getSpec().getValue()); + return new ExternalResource(toExternalResourceID(primary), primary.getSpec().getValue()); } protected String toExternalResourceID( @@ -71,5 +76,4 @@ protected String toExternalResourceID( } protected abstract String resourceIDSuffix(); - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java index df443880c9..a3b1693ab2 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceCustomResource.java @@ -11,7 +11,4 @@ @Version("v1") @ShortNames("mme") public class MultipleManagedExternalDependentResourceCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java index cd17f1706b..5834985e57 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentResourceReconciler.java @@ -25,16 +25,19 @@ import static io.javaoperatorsdk.operator.dependent.multiplemanagedexternaldependenttype.MultipleManagedExternalDependentResourceReconciler.EVENT_SOURCE_NAME; -@Workflow(dependents = { - @Dependent(type = ExternalDependentResource1.class, - useEventSourceWithName = EVENT_SOURCE_NAME), - @Dependent(type = ExternalDependentResource2.class, - useEventSourceWithName = EVENT_SOURCE_NAME) -}) +@Workflow( + dependents = { + @Dependent( + type = ExternalDependentResource1.class, + useEventSourceWithName = EVENT_SOURCE_NAME), + @Dependent( + type = ExternalDependentResource2.class, + useEventSourceWithName = EVENT_SOURCE_NAME) + }) @ControllerConfiguration() public class MultipleManagedExternalDependentResourceReconciler implements Reconciler, - TestExecutionInfoProvider { + TestExecutionInfoProvider { public static final String EVENT_SOURCE_NAME = "ConfigMapEventSource"; protected ExternalServiceMock externalServiceMock = ExternalServiceMock.getInstance(); @@ -56,26 +59,31 @@ public int getNumberOfExecutions() { } @Override - public List> prepareEventSources( - EventSourceContext context) { + public List> + prepareEventSources( + EventSourceContext context) { - final PollingEventSource.GenericResourceFetcher fetcher = () -> { - var lists = externalServiceMock.listResources(); - final Map> res = new HashMap<>(); - lists.forEach(er -> { - var resourceId = er.toResourceID(); - res.computeIfAbsent(resourceId, rid -> new HashSet<>()); - res.get(resourceId).add(er); - }); - return res; - }; + final PollingEventSource.GenericResourceFetcher fetcher = + () -> { + var lists = externalServiceMock.listResources(); + final Map> res = new HashMap<>(); + lists.forEach( + er -> { + var resourceId = er.toResourceID(); + res.computeIfAbsent(resourceId, rid -> new HashSet<>()); + res.get(resourceId).add(er); + }); + return res; + }; - PollingEventSource pollingEventSource = - new PollingEventSource<>(ExternalResource.class, - new PollingConfigurationBuilder<>(fetcher, Duration.ofMillis(1000L)) - .withName(EVENT_SOURCE_NAME) - .withCacheKeyMapper(ExternalResource::getId) - .build()); + PollingEventSource + pollingEventSource = + new PollingEventSource<>( + ExternalResource.class, + new PollingConfigurationBuilder<>(fetcher, Duration.ofMillis(1000L)) + .withName(EVENT_SOURCE_NAME) + .withCacheKeyMapper(ExternalResource::getId) + .build()); return List.of(pollingEventSource); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java index 024060202b..a8c1f889d0 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multiplemanagedexternaldependenttype/MultipleManagedExternalDependentSameTypeIT.java @@ -40,29 +40,30 @@ void handlesExternalCrudOperations() { } private void assertExternalResourceDeleted() { - await().untilAsserted(() -> { - var resources = externalServiceMock.listResources(); - assertThat(resources).hasSize(0); - }); + await() + .untilAsserted( + () -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(0); + }); } private void assertResourceCreatedWithData(String expectedData) { - await().untilAsserted(() -> { - var resources = externalServiceMock.listResources(); - assertThat(resources).hasSize(2); - assertThat(resources).allMatch(er -> er.getData().equals(expectedData)); - }); + await() + .untilAsserted( + () -> { + var resources = externalServiceMock.listResources(); + assertThat(resources).hasSize(2); + assertThat(resources).allMatch(er -> er.getData().equals(expectedData)); + }); } private MultipleManagedExternalDependentResourceCustomResource testResource() { var res = new MultipleManagedExternalDependentResourceCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new MultipleManagedDependentResourceSpec()); res.getSpec().setValue(DEFAULT_SPEC_VALUE); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java index d9c62d9a29..d5a704ca0c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultiOwnerDependentTriggeringIT.java @@ -26,53 +26,58 @@ class MultiOwnerDependentTriggeringIT { .withReconciler(MultipleOwnerDependentReconciler.class) .build(); - @Test void multiOwnerTriggeringAndManagement() { var res1 = extension.create(testResource("res1", VALUE_1)); var res2 = extension.create(testResource("res2", VALUE_2)); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, MultipleOwnerDependentConfigMap.RESOURCE_NAME); + await() + .untilAsserted( + () -> { + var cm = + extension.get(ConfigMap.class, MultipleOwnerDependentConfigMap.RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData()) - .containsEntry(VALUE_1, VALUE_1) - .containsEntry(VALUE_2, VALUE_2); - assertThat(cm.getMetadata().getOwnerReferences()).hasSize(2); - }); + assertThat(cm).isNotNull(); + assertThat(cm.getData()) + .containsEntry(VALUE_1, VALUE_1) + .containsEntry(VALUE_2, VALUE_2); + assertThat(cm.getMetadata().getOwnerReferences()).hasSize(2); + }); res1.getSpec().setValue(NEW_VALUE_1); extension.replace(res1); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, MultipleOwnerDependentConfigMap.RESOURCE_NAME); - assertThat(cm.getData()) - .containsEntry(NEW_VALUE_1, NEW_VALUE_1) - // note that it will still contain the old value too - .containsEntry(VALUE_1, VALUE_1); - assertThat(cm.getMetadata().getOwnerReferences()).hasSize(2); - }); + await() + .untilAsserted( + () -> { + var cm = + extension.get(ConfigMap.class, MultipleOwnerDependentConfigMap.RESOURCE_NAME); + assertThat(cm.getData()) + .containsEntry(NEW_VALUE_1, NEW_VALUE_1) + // note that it will still contain the old value too + .containsEntry(VALUE_1, VALUE_1); + assertThat(cm.getMetadata().getOwnerReferences()).hasSize(2); + }); res2.getSpec().setValue(NEW_VALUE_2); extension.replace(res2); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, MultipleOwnerDependentConfigMap.RESOURCE_NAME); - assertThat(cm.getData()).containsEntry(NEW_VALUE_2, NEW_VALUE_2); - assertThat(cm.getMetadata().getOwnerReferences()).hasSize(2); - }); + await() + .untilAsserted( + () -> { + var cm = + extension.get(ConfigMap.class, MultipleOwnerDependentConfigMap.RESOURCE_NAME); + assertThat(cm.getData()).containsEntry(NEW_VALUE_2, NEW_VALUE_2); + assertThat(cm.getMetadata().getOwnerReferences()).hasSize(2); + }); } MultipleOwnerDependentCustomResource testResource(String name, String value) { var res = new MultipleOwnerDependentCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(name).build()); res.setSpec(new MultipleOwnerDependentSpec()); res.getSpec().setValue(value); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentConfigMap.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentConfigMap.java index b906796708..28ddfcc907 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentConfigMap.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentConfigMap.java @@ -15,17 +15,13 @@ @KubernetesDependent(useSSA = BooleanWithUndefined.TRUE) public class MultipleOwnerDependentConfigMap - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource { public static final String RESOURCE_NAME = "test1"; - public MultipleOwnerDependentConfigMap() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleOwnerDependentCustomResource primary, + protected ConfigMap desired( + MultipleOwnerDependentCustomResource primary, Context context) { var cm = getSecondaryResource(primary, context); @@ -45,11 +41,12 @@ protected ConfigMap desired(MultipleOwnerDependentCustomResource primary, // need to change this since owner reference is present only for the creator primary resource. @Override - public Optional getSecondaryResource(MultipleOwnerDependentCustomResource primary, + public Optional getSecondaryResource( + MultipleOwnerDependentCustomResource primary, Context context) { InformerEventSource ies = - (InformerEventSource) context - .eventSourceRetriever().getEventSourceFor(ConfigMap.class); + (InformerEventSource) + context.eventSourceRetriever().getEventSourceFor(ConfigMap.class); return ies.get(new ResourceID(RESOURCE_NAME, primary.getMetadata().getNamespace())); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentCustomResource.java index d84e1bf5db..5f50d14d8e 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentCustomResource.java @@ -10,7 +10,4 @@ @Version("v1") @ShortNames("mod") public class MultipleOwnerDependentCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentReconciler.java index b4c01cb6b9..b0fd098743 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/multipleupdateondependent/MultipleOwnerDependentReconciler.java @@ -6,13 +6,10 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = { - @Dependent(type = MultipleOwnerDependentConfigMap.class) -}) +@Workflow(dependents = {@Dependent(type = MultipleOwnerDependentConfigMap.class)}) @ControllerConfiguration() public class MultipleOwnerDependentReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @@ -27,9 +24,7 @@ public UpdateControl reconcile( return UpdateControl.noUpdate(); } - public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/DeploymentDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/DeploymentDependent.java new file mode 100644 index 0000000000..5cfb66f67e --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/DeploymentDependent.java @@ -0,0 +1,95 @@ +package io.javaoperatorsdk.operator.dependent.prevblocklist; + +import java.util.Map; + +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.HasMetadata; +import io.fabric8.kubernetes.api.model.LabelSelectorBuilder; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.PodSpecBuilder; +import io.fabric8.kubernetes.api.model.PodTemplateSpecBuilder; +import io.fabric8.kubernetes.api.model.Quantity; +import io.fabric8.kubernetes.api.model.ResourceRequirementsBuilder; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.fabric8.kubernetes.api.model.apps.DeploymentBuilder; +import io.fabric8.kubernetes.api.model.apps.DeploymentSpecBuilder; +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.GenericKubernetesResourceMatcher; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; +import io.javaoperatorsdk.operator.processing.dependent.kubernetes.SSABasedGenericKubernetesResourceMatcher; + +@KubernetesDependent +public class DeploymentDependent + extends CRUDKubernetesDependentResource { + + public static final String RESOURCE_NAME = "test1"; + + public DeploymentDependent() { + super(Deployment.class); + } + + @Override + protected Deployment desired( + PrevAnnotationBlockCustomResource primary, + Context context) { + + return new DeploymentBuilder() + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) + .withSpec( + new DeploymentSpecBuilder() + .withReplicas(1) + .withSelector( + new LabelSelectorBuilder().withMatchLabels(Map.of("app", "nginx")).build()) + .withTemplate( + new PodTemplateSpecBuilder() + .withMetadata( + new ObjectMetaBuilder().withLabels(Map.of("app", "nginx")).build()) + .withSpec( + new PodSpecBuilder() + .withContainers( + new ContainerBuilder() + .withName("nginx") + .withImage("nginx:1.14.2") + .withResources( + new ResourceRequirementsBuilder() + .withLimits(Map.of("cpu", new Quantity("2000m"))) + .build()) + .build()) + .build()) + .build()) + .build()) + .build(); + } + + // for testing purposes replicating the matching logic but with the special matcher + @Override + public Result match( + Deployment actualResource, + Deployment desired, + PrevAnnotationBlockCustomResource primary, + Context context) { + final boolean matches; + addMetadata(true, actualResource, desired, primary, context); + if (useSSA(context)) { + matches = new SSAMatcherWithoutSanitization().matches(actualResource, desired, context); + } else { + matches = + GenericKubernetesResourceMatcher.match(desired, actualResource, false, false, context) + .matched(); + } + return Result.computed(matches, desired); + } + + // using this matcher, so we are able to reproduce issue with resource limits + static class SSAMatcherWithoutSanitization + extends SSABasedGenericKubernetesResourceMatcher { + + @Override + protected void sanitizeState(R actual, R desired, Map actualMap) {} + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockCustomResource.java new file mode 100644 index 0000000000..7aa3194672 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockCustomResource.java @@ -0,0 +1,13 @@ +package io.javaoperatorsdk.operator.dependent.prevblocklist; + +import io.fabric8.kubernetes.api.model.Namespaced; +import io.fabric8.kubernetes.client.CustomResource; +import io.fabric8.kubernetes.model.annotation.Group; +import io.fabric8.kubernetes.model.annotation.ShortNames; +import io.fabric8.kubernetes.model.annotation.Version; + +@Group("sample.javaoperatorsdk") +@Version("v1") +@ShortNames("pabc") +public class PrevAnnotationBlockCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconciler.java new file mode 100644 index 0000000000..7f3dab61fe --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconciler.java @@ -0,0 +1,34 @@ +package io.javaoperatorsdk.operator.dependent.prevblocklist; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.javaoperatorsdk.operator.api.reconciler.Context; +import io.javaoperatorsdk.operator.api.reconciler.ControllerConfiguration; +import io.javaoperatorsdk.operator.api.reconciler.Reconciler; +import io.javaoperatorsdk.operator.api.reconciler.UpdateControl; +import io.javaoperatorsdk.operator.api.reconciler.Workflow; +import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; +import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; + +@Workflow(dependents = {@Dependent(type = DeploymentDependent.class)}) +@ControllerConfiguration() +public class PrevAnnotationBlockReconciler + implements Reconciler, TestExecutionInfoProvider { + + private final AtomicInteger numberOfExecutions = new AtomicInteger(0); + + public PrevAnnotationBlockReconciler() {} + + @Override + public UpdateControl reconcile( + PrevAnnotationBlockCustomResource resource, + Context context) { + numberOfExecutions.getAndIncrement(); + + return UpdateControl.noUpdate(); + } + + public int getNumberOfExecutions() { + return numberOfExecutions.get(); + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java new file mode 100644 index 0000000000..137e2ba663 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockReconcilerIT.java @@ -0,0 +1,50 @@ +package io.javaoperatorsdk.operator.dependent.prevblocklist; + +import java.time.Duration; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.RegisterExtension; + +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.api.model.apps.Deployment; +import io.javaoperatorsdk.operator.junit.LocallyRunOperatorExtension; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.awaitility.Awaitility.await; + +class PrevAnnotationBlockReconcilerIT { + + public static final String TEST_1 = "test1"; + + @RegisterExtension + LocallyRunOperatorExtension extension = + LocallyRunOperatorExtension.builder() + // Removing resource from blocklist List would result in test failure + // .withConfigurationService( + // o -> o.previousAnnotationUsageBlocklist(Collections.emptyList())) + .withReconciler(PrevAnnotationBlockReconciler.class) + .build(); + + @Test + void doNotUsePrevAnnotationForDeploymentDependent() { + extension.create(testResource(TEST_1)); + + var reconciler = extension.getReconcilerOfType(PrevAnnotationBlockReconciler.class); + await() + .pollDelay(Duration.ofMillis(200)) + .untilAsserted( + () -> { + var deployment = extension.get(Deployment.class, TEST_1); + assertThat(deployment).isNotNull(); + assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(0).isLessThan(10); + }); + } + + PrevAnnotationBlockCustomResource testResource(String name) { + var res = new PrevAnnotationBlockCustomResource(); + res.setMetadata(new ObjectMetaBuilder().withName(name).build()); + res.setSpec(new PrevAnnotationBlockSpec()); + res.getSpec().setValue("value"); + return res; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockSpec.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockSpec.java new file mode 100644 index 0000000000..9d80e14bc1 --- /dev/null +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/prevblocklist/PrevAnnotationBlockSpec.java @@ -0,0 +1,15 @@ +package io.javaoperatorsdk.operator.dependent.prevblocklist; + +public class PrevAnnotationBlockSpec { + + private String value; + + public String getValue() { + return value; + } + + public PrevAnnotationBlockSpec setValue(String value) { + this.value = value; + return this; + } +} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerTestReconciler.java index bb97a32cf9..52094972da 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primaryindexer/DependentPrimaryIndexerTestReconciler.java @@ -22,12 +22,14 @@ import static io.javaoperatorsdk.operator.dependent.primaryindexer.DependentPrimaryIndexerTestReconciler.CONFIG_MAP_EVENT_SOURCE; -@Workflow(dependents = @Dependent(useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE, - type = DependentPrimaryIndexerTestReconciler.ReadOnlyConfigMapDependent.class)) +@Workflow( + dependents = + @Dependent( + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE, + type = DependentPrimaryIndexerTestReconciler.ReadOnlyConfigMapDependent.class)) @ControllerConfiguration public class DependentPrimaryIndexerTestReconciler extends AbstractPrimaryIndexerTestReconciler - implements - Reconciler { + implements Reconciler { public static final String CONFIG_MAP_EVENT_SOURCE = "configMapEventSource"; @@ -40,14 +42,16 @@ public List> prepareEventSource InformerEventSource es = new InformerEventSource<>( - InformerEventSourceConfiguration - .from(ConfigMap.class, PrimaryIndexerTestCustomResource.class) + InformerEventSourceConfiguration.from( + ConfigMap.class, PrimaryIndexerTestCustomResource.class) .withName(CONFIG_MAP_EVENT_SOURCE) - .withSecondaryToPrimaryMapper(resource -> cache - .byIndex(CONFIG_MAP_RELATION_INDEXER, resource.getMetadata().getName()) - .stream() - .map(ResourceID::fromResource) - .collect(Collectors.toSet())) + .withSecondaryToPrimaryMapper( + resource -> + cache + .byIndex(CONFIG_MAP_RELATION_INDEXER, resource.getMetadata().getName()) + .stream() + .map(ResourceID::fromResource) + .collect(Collectors.toSet())) .build(), context); @@ -57,18 +61,16 @@ public List> prepareEventSource public static class ReadOnlyConfigMapDependent extends KubernetesDependentResource { - public ReadOnlyConfigMapDependent() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(PrimaryIndexerTestCustomResource primary, + protected ConfigMap desired( + PrimaryIndexerTestCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(CONFIG_MAP_NAME) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(CONFIG_MAP_NAME) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .build(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapDependent.java index c9b85acc69..2b63bbf6b1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapDependent.java @@ -6,23 +6,21 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; -public class ConfigMapDependent extends - KubernetesDependentResource { +public class ConfigMapDependent + extends KubernetesDependentResource { public static final String TEST_CONFIG_MAP_NAME = "testconfigmap"; - public ConfigMapDependent() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(PrimaryToSecondaryDependentCustomResource primary, + protected ConfigMap desired( + PrimaryToSecondaryDependentCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(TEST_CONFIG_MAP_NAME) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(TEST_CONFIG_MAP_NAME) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .build(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapReconcilePrecondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapReconcilePrecondition.java index ab4ba62edf..024c275653 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapReconcilePrecondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/ConfigMapReconcilePrecondition.java @@ -15,10 +15,13 @@ public boolean isMet( DependentResource dependentResource, PrimaryToSecondaryDependentCustomResource primary, Context context) { - return dependentResource.getSecondaryResource(primary, context).map(cm -> { - var data = cm.getData().get(PrimaryToSecondaryDependentReconciler.DATA_KEY); - return data != null && !data.equals(DO_NOT_RECONCILE); - }) + return dependentResource + .getSecondaryResource(primary, context) + .map( + cm -> { + var data = cm.getData().get(PrimaryToSecondaryDependentReconciler.DATA_KEY); + return data != null && !data.equals(DO_NOT_RECONCILE); + }) .orElse(false); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentCustomResource.java index 8be9ecf5c6..c4f16b3e57 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("ptsd") public class PrimaryToSecondaryDependentCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java index 681c57e0d1..11a080a8bf 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentIT.java @@ -19,12 +19,9 @@ class PrimaryToSecondaryDependentIT { - public static final String TEST_CR_NAME = "test1"; public static final String TEST_DATA = "testData"; - public - - @RegisterExtension LocallyRunOperatorExtension operator = + public @RegisterExtension LocallyRunOperatorExtension operator = LocallyRunOperatorExtension.builder() .withReconciler(new PrimaryToSecondaryDependentReconciler()) .build(); @@ -35,28 +32,32 @@ void testPrimaryToSecondaryInDependentResources() { var cm = operator.create(configMap(DO_NOT_RECONCILE)); operator.create(testCustomResource()); - await().pollDelay(Duration.ofMillis(250)).untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isPositive(); - assertThat(operator.get(Secret.class, TEST_CR_NAME)).isNull(); - }); + await() + .pollDelay(Duration.ofMillis(250)) + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isPositive(); + assertThat(operator.get(Secret.class, TEST_CR_NAME)).isNull(); + }); cm.setData(Map.of(DATA_KEY, TEST_DATA)); var executions = reconciler.getNumberOfExecutions(); operator.replace(cm); - await().pollDelay(Duration.ofMillis(250)).untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(executions); - var secret = operator.get(Secret.class, TEST_CR_NAME); - assertThat(secret).isNotNull(); - assertThat(secret.getData().get(DATA_KEY)).isEqualTo(TEST_DATA); - }); + await() + .pollDelay(Duration.ofMillis(250)) + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isGreaterThan(executions); + var secret = operator.get(Secret.class, TEST_CR_NAME); + assertThat(secret).isNotNull(); + assertThat(secret.getData().get(DATA_KEY)).isEqualTo(TEST_DATA); + }); } PrimaryToSecondaryDependentCustomResource testCustomResource() { var res = new PrimaryToSecondaryDependentCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_CR_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_CR_NAME).build()); res.setSpec(new PrimaryToSecondaryDependentSpec()); res.getSpec().setConfigMapName(TEST_CONFIG_MAP_NAME); return res; @@ -64,11 +65,8 @@ PrimaryToSecondaryDependentCustomResource testCustomResource() { ConfigMap configMap(String data) { var cm = new ConfigMap(); - cm.setMetadata(new ObjectMetaBuilder() - .withName(TEST_CONFIG_MAP_NAME) - .build()); + cm.setMetadata(new ObjectMetaBuilder().withName(TEST_CONFIG_MAP_NAME).build()); cm.setData(Map.of(DATA_KEY, data)); return cm; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java index 76e2015be8..0ad691d4da 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/PrimaryToSecondaryDependentReconciler.java @@ -23,11 +23,15 @@ * Note that this is usually just used with read only resources. So it has limited usage, one reason * to use it is to have nice condition on that resource within a workflow. */ -@Workflow(dependents = {@Dependent(type = ConfigMapDependent.class, - name = CONFIG_MAP, - reconcilePrecondition = ConfigMapReconcilePrecondition.class, - useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), - @Dependent(type = SecretDependent.class, dependsOn = CONFIG_MAP)}) +@Workflow( + dependents = { + @Dependent( + type = ConfigMapDependent.class, + name = CONFIG_MAP, + reconcilePrecondition = ConfigMapReconcilePrecondition.class, + useEventSourceWithName = CONFIG_MAP_EVENT_SOURCE), + @Dependent(type = SecretDependent.class, dependsOn = CONFIG_MAP) + }) @ControllerConfiguration() public class PrimaryToSecondaryDependentReconciler implements Reconciler, TestExecutionInfoProvider { @@ -56,32 +60,53 @@ public int getNumberOfExecutions() { * do this setup elegantly within the bounds of the KubernetesDependentResource API. However, this * is quite a corner case; might be covered more out of the box in the future if there will be * demand for it. - **/ + */ @Override public List> prepareEventSources( EventSourceContext context) { // there is no owner reference in the config map, but we still want to trigger reconciliation if // the config map changes. So first we add an index which custom resource references the config // map. - context.getPrimaryCache().addIndexer(CONFIG_MAP_INDEX, (primary -> List - .of(indexKey(primary.getSpec().getConfigMapName(), primary.getMetadata().getNamespace())))); + context + .getPrimaryCache() + .addIndexer( + CONFIG_MAP_INDEX, + (primary -> + List.of( + indexKey( + primary.getSpec().getConfigMapName(), + primary.getMetadata().getNamespace())))); - var es = new InformerEventSource<>(InformerEventSourceConfiguration - .from(ConfigMap.class, PrimaryToSecondaryDependentCustomResource.class) - .withName(CONFIG_MAP_EVENT_SOURCE) - // if there is a many-to-many relationship (thus no direct owner reference) - // PrimaryToSecondaryMapper needs to be added - .withPrimaryToSecondaryMapper( - (PrimaryToSecondaryMapper) p -> Set - .of(new ResourceID(p.getSpec().getConfigMapName(), p.getMetadata().getNamespace()))) - // the index is used to trigger reconciliation of related custom resources if config map - // changes - .withSecondaryToPrimaryMapper(cm -> context.getPrimaryCache() - .byIndex(CONFIG_MAP_INDEX, indexKey(cm.getMetadata().getName(), - cm.getMetadata().getNamespace())) - .stream().map(ResourceID::fromResource).collect(Collectors.toSet())) - .build(), - context); + var es = + new InformerEventSource<>( + InformerEventSourceConfiguration.from( + ConfigMap.class, PrimaryToSecondaryDependentCustomResource.class) + .withName(CONFIG_MAP_EVENT_SOURCE) + // if there is a many-to-many relationship (thus no direct owner reference) + // PrimaryToSecondaryMapper needs to be added + .withPrimaryToSecondaryMapper( + (PrimaryToSecondaryMapper) + p -> + Set.of( + new ResourceID( + p.getSpec().getConfigMapName(), + p.getMetadata().getNamespace()))) + // the index is used to trigger reconciliation of related custom resources if config + // map + // changes + .withSecondaryToPrimaryMapper( + cm -> + context + .getPrimaryCache() + .byIndex( + CONFIG_MAP_INDEX, + indexKey( + cm.getMetadata().getName(), cm.getMetadata().getNamespace())) + .stream() + .map(ResourceID::fromResource) + .collect(Collectors.toSet())) + .build(), + context); return List.of(es); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/SecretDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/SecretDependent.java index aa9470676d..6371f453d7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/SecretDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/primarytosecondaydependent/SecretDependent.java @@ -13,20 +13,20 @@ public class SecretDependent extends CRUDKubernetesDependentResource { - public SecretDependent() { - super(Secret.class); - } - @Override - protected Secret desired(PrimaryToSecondaryDependentCustomResource primary, + protected Secret desired( + PrimaryToSecondaryDependentCustomResource primary, Context context) { Secret secret = new Secret(); - secret.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); - secret.setData(Map.of(DATA_KEY, context.getSecondaryResource(ConfigMap.class) - .orElseThrow().getData().get(DATA_KEY))); + secret.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); + secret.setData( + Map.of( + DATA_KEY, + context.getSecondaryResource(ConfigMap.class).orElseThrow().getData().get(DATA_KEY))); return secret; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ConfigMapReader.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ConfigMapReader.java index c904a1741e..eac66ecd0f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ConfigMapReader.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ConfigMapReader.java @@ -7,5 +7,4 @@ @Version("v1") @Group("josdk.io") -public class ConfigMapReader extends CustomResource implements Namespaced { -} +public class ConfigMapReader extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ReadOnlyDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ReadOnlyDependent.java index 63e43fef95..a6f0662948 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ReadOnlyDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/readonly/ReadOnlyDependent.java @@ -5,9 +5,4 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependentResource; @KubernetesDependent -public class ReadOnlyDependent extends KubernetesDependentResource { - - public ReadOnlyDependent() { - super(ConfigMap.class); - } -} +public class ReadOnlyDependent extends KubernetesDependentResource {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/ConfigMapDependentResource.java index b7e69febd1..358718b107 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/ConfigMapDependentResource.java @@ -16,21 +16,17 @@ public class ConfigMapDependentResource public static final String DATA_KEY = "key"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(RestartTestCustomResource primary, - Context context) { + protected ConfigMap desired( + RestartTestCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withLabels(Map.of("app", "restart-test")) - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withLabels(Map.of("app", "restart-test")) + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of(DATA_KEY, primary.getMetadata().getName())) .build(); - } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java index db51c81081..b8adb562dd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/OperatorRestartIT.java @@ -11,14 +11,14 @@ class OperatorRestartIT { - private final static Operator operator = new Operator(o -> o.withCloseClientOnStop(false)); - private final static RestartTestReconciler reconciler = new RestartTestReconciler(); + private static final Operator operator = new Operator(o -> o.withCloseClientOnStop(false)); + private static final RestartTestReconciler reconciler = new RestartTestReconciler(); private static int reconcileNumberBeforeStop = 0; @BeforeAll static void registerReconciler() { - LocallyRunOperatorExtension.applyCrd(RestartTestCustomResource.class, - operator.getKubernetesClient()); + LocallyRunOperatorExtension.applyCrd( + RestartTestCustomResource.class, operator.getKubernetesClient()); operator.register(reconciler); } @@ -43,15 +43,16 @@ void createResource() { @Test @Order(2) void reconcile() { - await().untilAsserted(() -> assertThat(reconciler.getNumberOfExecutions()) - .isGreaterThan(reconcileNumberBeforeStop)); + await() + .untilAsserted( + () -> + assertThat(reconciler.getNumberOfExecutions()) + .isGreaterThan(reconcileNumberBeforeStop)); } RestartTestCustomResource testCustomResource() { RestartTestCustomResource cr = new RestartTestCustomResource(); - cr.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .build()); + cr.setMetadata(new ObjectMetaBuilder().withName("test1").build()); return cr; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestCustomResource.java index 6f9c08251d..fd5c14360c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestCustomResource.java @@ -9,7 +9,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("rt") -public class RestartTestCustomResource - extends CustomResource - implements Namespaced { -} +public class RestartTestCustomResource extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestReconciler.java index 6f59c29dc8..1eeb8aa144 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/restart/RestartTestReconciler.java @@ -15,8 +15,7 @@ public class RestartTestReconciler @Override public UpdateControl reconcile( - RestartTestCustomResource resource, - Context context) { + RestartTestCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); return UpdateControl.noUpdate(); } @@ -24,5 +23,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceDependentResource.java index a88fa14463..56e34330e1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceDependentResource.java @@ -19,15 +19,15 @@ public class ServiceDependentResource public static AtomicInteger updated = new AtomicInteger(0); - public ServiceDependentResource() { - super(Service.class); - } - @Override - protected Service desired(ServiceStrictMatcherTestCustomResource primary, + protected Service desired( + ServiceStrictMatcherTestCustomResource primary, Context context) { - Service service = loadYaml(Service.class, ServiceStrictMatcherIT.class, - "/io/javaoperatorsdk/operator/service.yaml"); + Service service = + loadYaml( + Service.class, + ServiceStrictMatcherIT.class, + "/io/javaoperatorsdk/operator/service.yaml"); service.getMetadata().setName(primary.getMetadata().getName()); service.getMetadata().setNamespace(primary.getMetadata().getNamespace()); Map labels = new HashMap<>(); @@ -37,10 +37,16 @@ protected Service desired(ServiceStrictMatcherTestCustomResource primary, } @Override - public Matcher.Result match(Service actualResource, + public Matcher.Result match( + Service actualResource, ServiceStrictMatcherTestCustomResource primary, Context context) { - return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, false, + return GenericKubernetesResourceMatcher.match( + this, + actualResource, + primary, + context, + false, false, "/spec/ports", "/spec/clusterIP", @@ -53,7 +59,9 @@ public Matcher.Result match(Service actualResource, } @Override - public Service update(Service actual, Service desired, + public Service update( + Service actual, + Service desired, ServiceStrictMatcherTestCustomResource primary, Context context) { updated.addAndGet(1); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java index 7d1b306210..72edd4ae40 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherIT.java @@ -15,39 +15,46 @@ public class ServiceStrictMatcherIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new ServiceStrictMatcherTestReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new ServiceStrictMatcherTestReconciler()) .build(); - @Test void testTheMatchingDoesNoTTriggersFurtherUpdates() { var resource = operator.create(testResource()); - await().untilAsserted(() -> { - assertThat(operator.getReconcilerOfType(ServiceStrictMatcherTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(ServiceStrictMatcherTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(1); + }); // make an update to spec to reconcile again resource.getSpec().setValue(2); operator.replace(resource); - await().pollDelay(Duration.ofMillis(300)).untilAsserted(() -> { - assertThat(operator.getReconcilerOfType(ServiceStrictMatcherTestReconciler.class) - .getNumberOfExecutions()).isEqualTo(2); - assertThat(ServiceDependentResource.updated.get()).isZero(); - }); + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(ServiceStrictMatcherTestReconciler.class) + .getNumberOfExecutions()) + .isEqualTo(2); + assertThat(ServiceDependentResource.updated.get()).isZero(); + }); } - ServiceStrictMatcherTestCustomResource testResource() { var res = new ServiceStrictMatcherTestCustomResource(); res.setSpec(new ServiceStrictMatcherSpec()); res.getSpec().setValue(1); - res.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .build()); + res.setMetadata(new ObjectMetaBuilder().withName("test1").build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestCustomResource.java index 6e05628633..ac12749d32 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestCustomResource.java @@ -10,7 +10,4 @@ @Version("v1") @ShortNames("ssm") public class ServiceStrictMatcherTestCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java index 556f28113d..12f18b5319 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/servicestrictmatcher/ServiceStrictMatcherTestReconciler.java @@ -10,7 +10,6 @@ public class ServiceStrictMatcherTestReconciler implements Reconciler { - private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/ServiceAccountDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/ServiceAccountDependentResource.java index b5f8cd3225..1a598992b5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/ServiceAccountDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/ServiceAccountDependentResource.java @@ -9,24 +9,20 @@ import static io.javaoperatorsdk.operator.dependent.specialresourcesdependent.SpecialResourceSpec.INITIAL_VALUE; @KubernetesDependent -public class ServiceAccountDependentResource extends - CRUDKubernetesDependentResource { - - public ServiceAccountDependentResource() { - super(ServiceAccount.class); - } +public class ServiceAccountDependentResource + extends CRUDKubernetesDependentResource { @Override - protected ServiceAccount desired(SpecialResourceCustomResource primary, - Context context) { + protected ServiceAccount desired( + SpecialResourceCustomResource primary, Context context) { ServiceAccount res = new ServiceAccount(); - res.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); res.setAutomountServiceAccountToken(INITIAL_VALUE.equals(primary.getSpec().getValue())); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceCustomResource.java index fc7feeb805..84553fb61a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceCustomResource.java @@ -10,5 +10,4 @@ @Version("v1") @ShortNames("srd") public class SpecialResourceCustomResource extends CustomResource - implements Namespaced { -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceTestReconciler.java index 7d2720c5c2..bde90f7340 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourceTestReconciler.java @@ -7,21 +7,19 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = { - @Dependent(type = ServiceAccountDependentResource.class), -}) -@ControllerConfiguration( - informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) +@Workflow( + dependents = { + @Dependent(type = ServiceAccountDependentResource.class), + }) +@ControllerConfiguration(informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) public class SpecialResourceTestReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override public UpdateControl reconcile( - SpecialResourceCustomResource resource, - Context context) { + SpecialResourceCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); return UpdateControl.noUpdate(); } @@ -29,5 +27,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java index 89cd94e51d..3d62512bd7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/specialresourcesdependent/SpecialResourcesDependentIT.java @@ -22,38 +22,39 @@ public class SpecialResourcesDependentIT { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new SpecialResourceTestReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new SpecialResourceTestReconciler()) .build(); @Test void specialCRUDReconciler() { var resource = extension.create(testResource()); - await().untilAsserted(() -> { - var sa = extension.get(ServiceAccount.class, RESOURCE_NAME); - assertThat(sa).isNotNull(); - assertThat(sa.getAutomountServiceAccountToken()).isTrue(); - }); + await() + .untilAsserted( + () -> { + var sa = extension.get(ServiceAccount.class, RESOURCE_NAME); + assertThat(sa).isNotNull(); + assertThat(sa.getAutomountServiceAccountToken()).isTrue(); + }); resource.getSpec().setValue(CHANGED_VALUE); extension.replace(resource); - await().untilAsserted(() -> { - var sa = extension.get(ServiceAccount.class, RESOURCE_NAME); - assertThat(sa).isNotNull(); - assertThat(sa.getAutomountServiceAccountToken()).isFalse(); - }); - + await() + .untilAsserted( + () -> { + var sa = extension.get(ServiceAccount.class, RESOURCE_NAME); + assertThat(sa).isNotNull(); + assertThat(sa.getAutomountServiceAccountToken()).isFalse(); + }); } SpecialResourceCustomResource testResource() { SpecialResourceCustomResource res = new SpecialResourceCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); res.setSpec(new SpecialResourceSpec()); res.getSpec().setValue(INITIAL_VALUE); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherCustomResource.java index fe58579035..dffcea0e93 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherCustomResource.java @@ -9,8 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("slm") -public class SSALegacyMatcherCustomResource - extends CustomResource - implements Namespaced { - -} +public class SSALegacyMatcherCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherReconciler.java index 179496f1b9..29c97b1400 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSALegacyMatcherReconciler.java @@ -7,15 +7,13 @@ @Workflow(dependents = {@Dependent(type = ServiceDependentResource.class)}) @ControllerConfiguration -public class SSALegacyMatcherReconciler - implements Reconciler { +public class SSALegacyMatcherReconciler implements Reconciler { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); @Override public UpdateControl reconcile( - SSALegacyMatcherCustomResource resource, - Context context) { + SSALegacyMatcherCustomResource resource, Context context) { numberOfExecutions.addAndGet(1); return UpdateControl.noUpdate(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java index 090f60c7d5..63afd73d1a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/SSAWithLegacyMatcherIT.java @@ -16,34 +16,36 @@ public class SSAWithLegacyMatcherIT { @RegisterExtension LocallyRunOperatorExtension extension = - LocallyRunOperatorExtension.builder().withReconciler(new SSALegacyMatcherReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new SSALegacyMatcherReconciler()) .build(); @Test void matchesDependentWithLegacyMatcher() { var resource = extension.create(testResource()); - await().untilAsserted(() -> { - var service = extension.get(Service.class, TEST_RESOURCE_NAME); - assertThat(service).isNotNull(); - assertThat(ServiceDependentResource.createUpdateCount.get()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + var service = extension.get(Service.class, TEST_RESOURCE_NAME); + assertThat(service).isNotNull(); + assertThat(ServiceDependentResource.createUpdateCount.get()).isEqualTo(1); + }); resource.getSpec().setValue("other_value"); - await().untilAsserted(() -> { - assertThat(ServiceDependentResource.createUpdateCount.get()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + assertThat(ServiceDependentResource.createUpdateCount.get()).isEqualTo(1); + }); } SSALegacyMatcherCustomResource testResource() { SSALegacyMatcherCustomResource res = new SSALegacyMatcherCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new SSALegacyMatcherSpec()); res.getSpec().setValue("initial-value"); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/ServiceDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/ServiceDependentResource.java index 98036036cc..f4007b6151 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/ServiceDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/ssalegacymatcher/ServiceDependentResource.java @@ -18,16 +18,15 @@ public class ServiceDependentResource public static AtomicInteger createUpdateCount = new AtomicInteger(0); - public ServiceDependentResource() { - super(Service.class); - } - @Override - protected Service desired(SSALegacyMatcherCustomResource primary, - Context context) { + protected Service desired( + SSALegacyMatcherCustomResource primary, Context context) { - Service service = loadYaml(Service.class, SSAWithLegacyMatcherIT.class, - "/io/javaoperatorsdk/operator/service.yaml"); + Service service = + loadYaml( + Service.class, + SSAWithLegacyMatcherIT.class, + "/io/javaoperatorsdk/operator/service.yaml"); service.getMetadata().setName(primary.getMetadata().getName()); service.getMetadata().setNamespace(primary.getMetadata().getNamespace()); Map labels = new HashMap<>(); @@ -37,17 +36,22 @@ protected Service desired(SSALegacyMatcherCustomResource primary, } @Override - public Result match(Service actualResource, SSALegacyMatcherCustomResource primary, + public Result match( + Service actualResource, + SSALegacyMatcherCustomResource primary, Context context) { var desired = desired(primary, context); - return GenericKubernetesResourceMatcher.match(this, actualResource, primary, context, - false, false); + return GenericKubernetesResourceMatcher.match( + this, actualResource, primary, context, false, false); } // override just to check the exec count @Override - public Service update(Service actual, Service desired, SSALegacyMatcherCustomResource primary, + public Service update( + Service actual, + Service desired, + SSALegacyMatcherCustomResource primary, Context context) { createUpdateCount.addAndGet(1); return super.update(actual, desired, primary, context); @@ -55,7 +59,9 @@ public Service update(Service actual, Service desired, SSALegacyMatcherCustomRes // override just to check the exec count @Override - public Service create(Service desired, SSALegacyMatcherCustomResource primary, + public Service create( + Service desired, + SSALegacyMatcherCustomResource primary, Context context) { createUpdateCount.addAndGet(1); return super.create(desired, primary, context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java index 19853842e7..91fbd64a19 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentResourceIT.java @@ -22,7 +22,8 @@ public class StandaloneDependentResourceIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(new StandaloneDependentTestReconciler()) + LocallyRunOperatorExtension.builder() + .withReconciler(new StandaloneDependentTestReconciler()) .build(); @Test @@ -37,7 +38,7 @@ void dependentResourceManagesDeployment() { awaitForDeploymentReadyReplicas(1); assertThat( - ((StandaloneDependentTestReconciler) operator.getFirstReconciler()).isErrorOccurred()) + ((StandaloneDependentTestReconciler) operator.getFirstReconciler()).isErrorOccurred()) .isFalse(); } @@ -58,7 +59,7 @@ void executeUpdateForTestingCacheUpdateForGetResource() { awaitForDeploymentReadyReplicas(2); assertThat( - ((StandaloneDependentTestReconciler) operator.getFirstReconciler()).isErrorOccurred()) + ((StandaloneDependentTestReconciler) operator.getFirstReconciler()).isErrorOccurred()) .isFalse(); } @@ -101,5 +102,4 @@ public Version getVersion() { } }.getResourceCloner(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestCustomResource.java index 3f2d2c9c2e..dd966f5035 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestCustomResource.java @@ -10,7 +10,4 @@ @Version("v1") @ShortNames("sdt") public class StandaloneDependentTestCustomResource - extends - CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestReconciler.java index e10a20a93c..f5d9571711 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/standalonedependent/StandaloneDependentTestReconciler.java @@ -30,8 +30,7 @@ public StandaloneDependentTestReconciler() { @Override public List> prepareEventSources( EventSourceContext context) { - return EventSourceUtils.dependentEventSources(context, - deploymentDependent); + return EventSourceUtils.dependentEventSources(context, deploymentDependent); } @Override @@ -54,7 +53,8 @@ public UpdateControl reconcile( @Override public ErrorStatusUpdateControl updateErrorStatus( StandaloneDependentTestCustomResource resource, - Context context, Exception e) { + Context context, + Exception e) { // this can happen when a namespace is terminated in test if (e instanceof KubernetesClientException) { return ErrorStatusUpdateControl.noStatusUpdate(); @@ -67,18 +67,17 @@ public boolean isErrorOccurred() { return errorOccurred; } - private static class DeploymentDependentResource extends - CRUDKubernetesDependentResource { - - public DeploymentDependentResource() { - super(Deployment.class); - } + private static class DeploymentDependentResource + extends CRUDKubernetesDependentResource { @Override - protected Deployment desired(StandaloneDependentTestCustomResource primary, + protected Deployment desired( + StandaloneDependentTestCustomResource primary, Context context) { Deployment deployment = - ReconcilerUtils.loadYaml(Deployment.class, StandaloneDependentResourceIT.class, + ReconcilerUtils.loadYaml( + Deployment.class, + StandaloneDependentResourceIT.class, "/io/javaoperatorsdk/operator/nginx-deployment.yaml"); deployment.getMetadata().setName(primary.getMetadata().getName()); deployment.getSpec().setReplicas(primary.getSpec().getReplicaCount()); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerCustomResource.java index e500206207..ebfac08b09 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerCustomResource.java @@ -8,7 +8,4 @@ @Group("sample.javaoperatorsdk") @Version("v1") public class StatefulSetDesiredSanitizerCustomResource - extends CustomResource - implements Namespaced { - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java index 98ac4897d7..fb8e4a6880 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerDependentResource.java @@ -7,30 +7,29 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; public class StatefulSetDesiredSanitizerDependentResource - extends - CRUDKubernetesDependentResource { + extends CRUDKubernetesDependentResource< + StatefulSet, StatefulSetDesiredSanitizerCustomResource> { public static volatile Boolean nonMatchedAtLeastOnce; - public StatefulSetDesiredSanitizerDependentResource() { - super(StatefulSet.class); - } - @Override - protected StatefulSet desired(StatefulSetDesiredSanitizerCustomResource primary, + protected StatefulSet desired( + StatefulSetDesiredSanitizerCustomResource primary, Context context) { var template = - ReconcilerUtils.loadYaml(StatefulSet.class, getClass(), - "/io/javaoperatorsdk/operator/statefulset.yaml"); - template.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + ReconcilerUtils.loadYaml( + StatefulSet.class, getClass(), "/io/javaoperatorsdk/operator/statefulset.yaml"); + template.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); return template; } @Override - public Result match(StatefulSet actualResource, + public Result match( + StatefulSet actualResource, StatefulSetDesiredSanitizerCustomResource primary, Context context) { var res = super.match(actualResource, primary, context); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java index 49c08f7a8b..f54708a9ea 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerIT.java @@ -26,28 +26,30 @@ public class StatefulSetDesiredSanitizerIT { void testSSAMatcher() { var resource = extension.create(testResource()); - await().pollDelay(Duration.ofMillis(200)).untilAsserted(() -> { - var statefulSet = extension.get(StatefulSet.class, TEST_1); - assertThat(statefulSet).isNotNull(); - }); + await() + .pollDelay(Duration.ofMillis(200)) + .untilAsserted( + () -> { + var statefulSet = extension.get(StatefulSet.class, TEST_1); + assertThat(statefulSet).isNotNull(); + }); // make sure reconciliation happens at least once more resource.getSpec().setValue("changed value"); extension.replace(resource); - await().untilAsserted( - () -> assertThat(StatefulSetDesiredSanitizerDependentResource.nonMatchedAtLeastOnce) - .isFalse()); + await() + .untilAsserted( + () -> + assertThat(StatefulSetDesiredSanitizerDependentResource.nonMatchedAtLeastOnce) + .isFalse()); } StatefulSetDesiredSanitizerCustomResource testResource() { var res = new StatefulSetDesiredSanitizerCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_1) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_1).build()); res.setSpec(new StatefulSetDesiredSanitizerSpec()); res.getSpec().setValue("initial value"); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java index f84f71411e..284119ba61 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/dependent/statefulsetdesiredsanitizer/StatefulSetDesiredSanitizerReconciler.java @@ -11,7 +11,8 @@ public class StatefulSetDesiredSanitizerReconciler @Override public UpdateControl reconcile( StatefulSetDesiredSanitizerCustomResource resource, - Context context) throws Exception { + Context context) + throws Exception { return UpdateControl.noUpdate(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalIDGenServiceMock.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalIDGenServiceMock.java index f30b3fa959..df48fc282b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalIDGenServiceMock.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalIDGenServiceMock.java @@ -5,7 +5,7 @@ public class ExternalIDGenServiceMock { - private final static ExternalIDGenServiceMock serviceMock = new ExternalIDGenServiceMock(); + private static final ExternalIDGenServiceMock serviceMock = new ExternalIDGenServiceMock(); private final Map resourceMap = new ConcurrentHashMap<>(); @@ -38,5 +38,4 @@ public List listResources() { public static ExternalIDGenServiceMock getInstance() { return serviceMock; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java index dd3e5ab113..048b1642c8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/ExternalResource.java @@ -36,10 +36,8 @@ public String getData() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; ExternalResource that = (ExternalResource) o; return Objects.equals(id, that.id) && Objects.equals(data, that.data); } @@ -55,13 +53,16 @@ public ResourceID toResourceID() { } public static String toExternalResourceId(HasMetadata primary, int i) { - return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + - primary.getMetadata().getNamespace() + - EXTERNAL_RESOURCE_NAME_DELIMITER + i; + return primary.getMetadata().getName() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + i; } public static String toExternalResourceId(HasMetadata primary) { - return primary.getMetadata().getName() + EXTERNAL_RESOURCE_NAME_DELIMITER + - primary.getMetadata().getNamespace(); + return primary.getMetadata().getName() + + EXTERNAL_RESOURCE_NAME_DELIMITER + + primary.getMetadata().getNamespace(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java index 952ad75b19..3d40690f09 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/support/TestUtils.java @@ -37,9 +37,7 @@ public static TestCustomResource testCustomResource(String uid) { public static TestCustomResource testCustomResourceWithPrefix(String id) { TestCustomResource resource = new TestCustomResource(); resource.setMetadata( - new ObjectMetaBuilder() - .withName(TEST_CUSTOM_RESOURCE_PREFIX + id) - .build()); + new ObjectMetaBuilder().withName(TEST_CUSTOM_RESOURCE_PREFIX + id).build()); resource.setKind("CustomService"); resource.setSpec(new TestCustomResourceSpec()); resource.getSpec().setConfigMapName("test-config-map-" + id); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowCustomResource.java index cb48ea8c97..dafc1fc497 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowCustomResource.java @@ -10,5 +10,4 @@ @Version("v1") @ShortNames("cdc") public class ComplexWorkflowCustomResource - extends CustomResource implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java index cf7cdac004..4af8467c5c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowIT.java @@ -24,30 +24,38 @@ class ComplexWorkflowIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder() - .withReconciler(new ComplexWorkflowReconciler()) - .build(); + LocallyRunOperatorExtension.builder().withReconciler(new ComplexWorkflowReconciler()).build(); @Test void successfullyReconciles() { operator.create(testResource()); - await().atMost(Duration.ofSeconds(90)) - .untilAsserted(() -> { - var res = operator.get(ComplexWorkflowCustomResource.class, TEST_RESOURCE_NAME); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getStatus()) - .isEqualTo(ComplexWorkflowReconciler.RECONCILE_STATUS.READY); - }); + await() + .atMost(Duration.ofSeconds(90)) + .untilAsserted( + () -> { + var res = operator.get(ComplexWorkflowCustomResource.class, TEST_RESOURCE_NAME); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getStatus()) + .isEqualTo(ComplexWorkflowReconciler.RECONCILE_STATUS.READY); + }); - var firstStatefulSet = operator.get(StatefulSet.class, String.format("%s-%s", - FirstStatefulSet.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); - var secondStatefulSet = operator.get(StatefulSet.class, String.format("%s-%s", - SecondStatefulSet.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); - var firstService = operator.get(Service.class, String.format("%s-%s", - FirstService.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); - var secondService = operator.get(Service.class, String.format("%s-%s", - SecondService.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); + var firstStatefulSet = + operator.get( + StatefulSet.class, + String.format("%s-%s", FirstStatefulSet.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); + var secondStatefulSet = + operator.get( + StatefulSet.class, + String.format("%s-%s", SecondStatefulSet.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); + var firstService = + operator.get( + Service.class, + String.format("%s-%s", FirstService.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); + var secondService = + operator.get( + Service.class, + String.format("%s-%s", SecondService.DISCRIMINATOR_PREFIX, TEST_RESOURCE_NAME)); assertThat(firstService).isNotNull(); assertThat(secondService).isNotNull(); assertThat(firstStatefulSet).isNotNull(); @@ -58,13 +66,10 @@ void successfullyReconciles() { ComplexWorkflowCustomResource testResource() { var resource = new ComplexWorkflowCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); resource.setSpec(new ComplexWorkflowSpec()); resource.getSpec().setProjectId(TEST_RESOURCE_NAME); return resource; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowReconciler.java index d7e3fc56aa..952d4ec476 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowReconciler.java @@ -19,21 +19,29 @@ import static io.javaoperatorsdk.operator.workflow.complexdependent.ComplexWorkflowReconciler.SERVICE_EVENT_SOURCE_NAME; import static io.javaoperatorsdk.operator.workflow.complexdependent.ComplexWorkflowReconciler.STATEFUL_SET_EVENT_SOURCE_NAME; -@Workflow(dependents = { - @Dependent(name = "first-svc", type = FirstService.class, - useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), - @Dependent(name = "second-svc", type = SecondService.class, - useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), - @Dependent(name = "first", type = FirstStatefulSet.class, - useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, - dependsOn = {"first-svc"}, - readyPostcondition = StatefulSetReadyCondition.class), - @Dependent(name = "second", - type = SecondStatefulSet.class, - useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, - dependsOn = {"second-svc", "first"}, - readyPostcondition = StatefulSetReadyCondition.class), -}) +@Workflow( + dependents = { + @Dependent( + name = "first-svc", + type = FirstService.class, + useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), + @Dependent( + name = "second-svc", + type = SecondService.class, + useEventSourceWithName = SERVICE_EVENT_SOURCE_NAME), + @Dependent( + name = "first", + type = FirstStatefulSet.class, + useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, + dependsOn = {"first-svc"}, + readyPostcondition = StatefulSetReadyCondition.class), + @Dependent( + name = "second", + type = SecondStatefulSet.class, + useEventSourceWithName = STATEFUL_SET_EVENT_SOURCE_NAME, + dependsOn = {"second-svc", "first"}, + readyPostcondition = StatefulSetReadyCondition.class), + }) @ControllerConfiguration(name = "project-operator") public class ComplexWorkflowReconciler implements Reconciler { @@ -42,11 +50,14 @@ public class ComplexWorkflowReconciler implements Reconciler reconcile( - ComplexWorkflowCustomResource resource, - Context context) throws Exception { - var ready = context.managedWorkflowAndDependentResourceContext().getWorkflowReconcileResult() - .orElseThrow() - .allDependentResourcesReady(); + ComplexWorkflowCustomResource resource, Context context) + throws Exception { + var ready = + context + .managedWorkflowAndDependentResourceContext() + .getWorkflowReconcileResult() + .orElseThrow() + .allDependentResourcesReady(); var status = Objects.requireNonNullElseGet(resource.getStatus(), ComplexWorkflowStatus::new); status.setStatus(ready ? RECONCILE_STATUS.READY : RECONCILE_STATUS.NOT_READY); @@ -60,15 +71,15 @@ public List> prepareEventSources( EventSourceContext context) { InformerEventSource serviceEventSource = new InformerEventSource<>( - InformerEventSourceConfiguration - .from(Service.class, ComplexWorkflowCustomResource.class) + InformerEventSourceConfiguration.from( + Service.class, ComplexWorkflowCustomResource.class) .withName(SERVICE_EVENT_SOURCE_NAME) .build(), context); InformerEventSource statefulSetEventSource = new InformerEventSource<>( - InformerEventSourceConfiguration - .from(StatefulSet.class, ComplexWorkflowCustomResource.class) + InformerEventSourceConfiguration.from( + StatefulSet.class, ComplexWorkflowCustomResource.class) .withName(STATEFUL_SET_EVENT_SOURCE_NAME) .build(), context); @@ -76,6 +87,7 @@ public List> prepareEventSources( } public enum RECONCILE_STATUS { - READY, NOT_READY + READY, + NOT_READY } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowStatus.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowStatus.java index d9b498682a..226880073f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowStatus.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/ComplexWorkflowStatus.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.workflow.complexdependent; - public class ComplexWorkflowStatus { private ComplexWorkflowReconciler.RECONCILE_STATUS status; diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseService.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseService.java index 75a4d7ee03..3b57614dbc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseService.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseService.java @@ -15,10 +15,13 @@ public BaseService(String component) { } @Override - protected Service desired(ComplexWorkflowCustomResource primary, - Context context) { - var template = ReconcilerUtils.loadYaml(Service.class, getClass(), - "/io/javaoperatorsdk/operator/workflow/complexdependent/service.yaml"); + protected Service desired( + ComplexWorkflowCustomResource primary, Context context) { + var template = + ReconcilerUtils.loadYaml( + Service.class, + getClass(), + "/io/javaoperatorsdk/operator/workflow/complexdependent/service.yaml"); return new ServiceBuilder(template) .withMetadata(createMeta(primary).build()) diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseStatefulSet.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseStatefulSet.java index fe05db1c1b..3847ec4c87 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseStatefulSet.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/BaseStatefulSet.java @@ -14,10 +14,13 @@ public BaseStatefulSet(String component) { } @Override - protected StatefulSet desired(ComplexWorkflowCustomResource primary, - Context context) { - var template = ReconcilerUtils.loadYaml(StatefulSet.class, getClass(), - "/io/javaoperatorsdk/operator/workflow/complexdependent/statefulset.yaml"); + protected StatefulSet desired( + ComplexWorkflowCustomResource primary, Context context) { + var template = + ReconcilerUtils.loadYaml( + StatefulSet.class, + getClass(), + "/io/javaoperatorsdk/operator/workflow/complexdependent/statefulset.yaml"); var name = name(primary); var metadata = createMeta(primary).build(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstService.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstService.java index 36b1ec4845..57686c6f7d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstService.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstService.java @@ -9,5 +9,4 @@ public class FirstService extends BaseService { public FirstService() { super(DISCRIMINATOR_PREFIX); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstStatefulSet.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstStatefulSet.java index d9cd2933fd..4a065f7ca6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstStatefulSet.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/FirstStatefulSet.java @@ -10,5 +10,4 @@ public class FirstStatefulSet extends BaseStatefulSet { public FirstStatefulSet() { super(DISCRIMINATOR_PREFIX); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/SecondStatefulSet.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/SecondStatefulSet.java index 78ab7953eb..85508b9695 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/SecondStatefulSet.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/SecondStatefulSet.java @@ -10,5 +10,4 @@ public class SecondStatefulSet extends BaseStatefulSet { public SecondStatefulSet() { super(DISCRIMINATOR_PREFIX); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/StatefulSetReadyCondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/StatefulSetReadyCondition.java index 1116bd7f1d..59422941ac 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/StatefulSetReadyCondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/complexdependent/dependent/StatefulSetReadyCondition.java @@ -15,10 +15,13 @@ public boolean isMet( ComplexWorkflowCustomResource primary, Context context) { - return dependentResource.getSecondaryResource(primary, context).map(secondary -> { - var readyReplicas = secondary.getStatus().getReadyReplicas(); - return readyReplicas != null && readyReplicas > 0; - }) + return dependentResource + .getSecondaryResource(primary, context) + .map( + secondary -> { + var readyReplicas = secondary.getStatus().getReadyReplicas(); + return readyReplicas != null && readyReplicas > 0; + }) .orElse(false); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java index 85cb3aa6c5..1793cceefa 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationConditionIT.java @@ -25,43 +25,51 @@ public class CRDPresentActivationConditionIT { .withReconciler(new CRDPresentActivationReconciler()) .build(); - @Test void resourceCreatedOnlyIfCRDPresent() { // deleted so test can be repeated - extension.getKubernetesClient().resources(CustomResourceDefinition.class) - .withName(CRD_NAME).delete(); + extension + .getKubernetesClient() + .resources(CustomResourceDefinition.class) + .withName(CRD_NAME) + .delete(); var resource = extension.create(testResource()); - await().pollDelay(Duration.ofMillis(300)).untilAsserted(() -> { - var crd = extension.getKubernetesClient().resources(CustomResourceDefinition.class) - .withName(CRD_NAME).get(); - assertThat(crd).isNull(); - - var dr = extension.get(CRDPresentActivationDependentCustomResource.class, TEST_1); - assertThat(dr).isNull(); - }); - - LocallyRunOperatorExtension.applyCrd(CRDPresentActivationDependentCustomResource.class, - extension.getKubernetesClient()); + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> { + var crd = + extension + .getKubernetesClient() + .resources(CustomResourceDefinition.class) + .withName(CRD_NAME) + .get(); + assertThat(crd).isNull(); + + var dr = extension.get(CRDPresentActivationDependentCustomResource.class, TEST_1); + assertThat(dr).isNull(); + }); + + LocallyRunOperatorExtension.applyCrd( + CRDPresentActivationDependentCustomResource.class, extension.getKubernetesClient()); resource.getMetadata().setAnnotations(Map.of("sample", "value")); extension.replace(resource); - await().pollDelay(Duration.ofMillis(300)).untilAsserted(() -> { - var cm = extension.get(CRDPresentActivationDependentCustomResource.class, TEST_1); - assertThat(cm).isNull(); - }); - + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> { + var cm = extension.get(CRDPresentActivationDependentCustomResource.class, TEST_1); + assertThat(cm).isNull(); + }); } CRDPresentActivationCustomResource testResource() { var res = new CRDPresentActivationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_1) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_1).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationCustomResource.java index 5b8dc2f704..a010f42cca 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationCustomResource.java @@ -9,9 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("crdp") -public class CRDPresentActivationCustomResource - extends CustomResource - implements Namespaced { - - -} +public class CRDPresentActivationCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependent.java index fdfcda1700..11923e274b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependent.java @@ -5,22 +5,19 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; public class CRDPresentActivationDependent - extends - CRUDNoGCKubernetesDependentResource { - - public CRDPresentActivationDependent() { - super(CRDPresentActivationDependentCustomResource.class); - } + extends CRUDNoGCKubernetesDependentResource< + CRDPresentActivationDependentCustomResource, CRDPresentActivationCustomResource> { @Override protected CRDPresentActivationDependentCustomResource desired( CRDPresentActivationCustomResource primary, Context context) { var res = new CRDPresentActivationDependentCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); return res; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependentCustomResource.java index 18280ee6b3..4be93ab453 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationDependentCustomResource.java @@ -10,7 +10,4 @@ @Version("v1") @ShortNames("addp") public class CRDPresentActivationDependentCustomResource extends CustomResource - implements Namespaced { - - -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationReconciler.java index 9cc05eaddc..49aebd5e4f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/crdpresentactivation/CRDPresentActivationReconciler.java @@ -4,15 +4,17 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.dependent.workflow.CRDPresentActivationCondition; -@Workflow(dependents = { - @Dependent(type = CRDPresentActivationDependent.class, - activationCondition = CRDPresentActivationCondition.class), -}) +@Workflow( + dependents = { + @Dependent( + type = CRDPresentActivationDependent.class, + activationCondition = CRDPresentActivationCondition.class), + }) // to trigger reconciliation with metadata change @ControllerConfiguration(generationAwareEventProcessing = false) public class CRDPresentActivationReconciler implements Reconciler, - Cleaner { + Cleaner { @Override public UpdateControl reconcile( @@ -23,7 +25,8 @@ public UpdateControl reconcile( } @Override - public DeleteControl cleanup(CRDPresentActivationCustomResource resource, + public DeleteControl cleanup( + CRDPresentActivationCustomResource resource, Context context) { return DeleteControl.defaultDelete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/ConfigMapDependentResource.java index 0c1bdd5900..c9078848b4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/ConfigMapDependentResource.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.workflow.getnonactivesecondary; - import io.fabric8.kubernetes.api.model.ConfigMap; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; import io.javaoperatorsdk.operator.api.reconciler.Context; @@ -11,18 +10,16 @@ public class ConfigMapDependentResource public static final String DATA_KEY = "data"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(GetNonActiveSecondaryCustomResource primary, + protected ConfigMap desired( + GetNonActiveSecondaryCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); return configMap; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/GetNonActiveSecondaryCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/GetNonActiveSecondaryCustomResource.java index fddcd9fe75..be12dec053 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/GetNonActiveSecondaryCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/GetNonActiveSecondaryCustomResource.java @@ -9,9 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("gnas") -public class GetNonActiveSecondaryCustomResource - extends CustomResource - implements Namespaced { - - -} +public class GetNonActiveSecondaryCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/RouteDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/RouteDependentResource.java index c7d1b9a1b1..77ebef373a 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/RouteDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/RouteDependentResource.java @@ -8,19 +8,17 @@ public class RouteDependentResource extends CRUDKubernetesDependentResource { - public RouteDependentResource() { - super(Route.class); - } - @Override - protected Route desired(GetNonActiveSecondaryCustomResource primary, + protected Route desired( + GetNonActiveSecondaryCustomResource primary, Context context) { // basically does not matter since this should not be called Route route = new Route(); - route.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + route.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); return route; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java index c2c073df88..0de986ccf9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionIT.java @@ -23,18 +23,20 @@ public class WorkflowActivationConditionIT { void reconciledOnVanillaKubernetesDespiteRouteInWorkflow() { extension.create(testResource()); - await().untilAsserted(() -> { - assertThat(extension.getReconcilerOfType(WorkflowActivationConditionReconciler.class) - .getNumberOfReconciliationExecution()).isEqualTo(1); - }); + await() + .untilAsserted( + () -> { + assertThat( + extension + .getReconcilerOfType(WorkflowActivationConditionReconciler.class) + .getNumberOfReconciliationExecution()) + .isEqualTo(1); + }); } private GetNonActiveSecondaryCustomResource testResource() { var res = new GetNonActiveSecondaryCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionReconciler.java index 2d2d39274d..076919d5d1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/getnonactivesecondary/WorkflowActivationConditionReconciler.java @@ -10,11 +10,13 @@ import io.javaoperatorsdk.operator.api.reconciler.Workflow; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class), - @Dependent(type = RouteDependentResource.class, - activationCondition = FalseActivationCondition.class) -}) +@Workflow( + dependents = { + @Dependent(type = ConfigMapDependentResource.class), + @Dependent( + type = RouteDependentResource.class, + activationCondition = FalseActivationCondition.class) + }) @ControllerConfiguration public class WorkflowActivationConditionReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ConfigMapDependent.java index af70b6842a..adc633b877 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ConfigMapDependent.java @@ -7,15 +7,13 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; -public class ConfigMapDependent extends - CRUDNoGCKubernetesDependentResource { - - public ConfigMapDependent() { - super(ConfigMap.class); - } +public class ConfigMapDependent + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, ManagedDependentDefaultDeleteConditionCustomResource> { @Override - protected ConfigMap desired(ManagedDependentDefaultDeleteConditionCustomResource primary, + protected ConfigMap desired( + ManagedDependentDefaultDeleteConditionCustomResource primary, Context context) { return new ConfigMapBuilder() diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionCustomResource.java index 01b995c061..c421e75b54 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("mdcc") -public class ManagedDependentDefaultDeleteConditionCustomResource - extends CustomResource - implements Namespaced { -} +public class ManagedDependentDefaultDeleteConditionCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java index 530f9b8146..d992e9a1b4 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDefaultDeleteConditionReconciler.java @@ -7,11 +7,14 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.processing.dependent.workflow.KubernetesResourceDeletedCondition; -@Workflow(dependents = { - @Dependent(name = "ConfigMap", type = ConfigMapDependent.class), - @Dependent(type = SecretDependent.class, dependsOn = "ConfigMap", - deletePostcondition = KubernetesResourceDeletedCondition.class) -}) +@Workflow( + dependents = { + @Dependent(name = "ConfigMap", type = ConfigMapDependent.class), + @Dependent( + type = SecretDependent.class, + dependsOn = "ConfigMap", + deletePostcondition = KubernetesResourceDeletedCondition.class) + }) @ControllerConfiguration public class ManagedDependentDefaultDeleteConditionReconciler implements Reconciler { @@ -28,5 +31,4 @@ public UpdateControl recon return UpdateControl.noUpdate(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java index 4d688bf606..482d2e5091 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/ManagedDependentDeleteConditionIT.java @@ -23,23 +23,24 @@ public class ManagedDependentDeleteConditionIT { LocallyRunOperatorExtension extension = LocallyRunOperatorExtension.builder() .withConfigurationService(o -> o.withDefaultNonSSAResource(Set.of())) - .withReconciler(new ManagedDependentDefaultDeleteConditionReconciler()).build(); - + .withReconciler(new ManagedDependentDefaultDeleteConditionReconciler()) + .build(); @Test void resourceNotDeletedUntilDependentDeleted() { var resource = new ManagedDependentDefaultDeleteConditionCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + resource.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); resource = extension.create(resource); - await().timeout(Duration.ofSeconds(300)).untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, RESOURCE_NAME); - var sec = extension.get(Secret.class, RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(sec).isNotNull(); - }); + await() + .timeout(Duration.ofSeconds(300)) + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, RESOURCE_NAME); + var sec = extension.get(Secret.class, RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(sec).isNotNull(); + }); var secret = extension.get(Secret.class, RESOURCE_NAME); secret.getMetadata().getFinalizers().add(CUSTOM_FINALIZER); @@ -48,22 +49,26 @@ void resourceNotDeletedUntilDependentDeleted() { extension.delete(resource); // both resources are present until the finalizer removed - await().pollDelay(Duration.ofMillis(250)).untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, RESOURCE_NAME); - var sec = extension.get(Secret.class, RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(sec).isNotNull(); - }); + await() + .pollDelay(Duration.ofMillis(250)) + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, RESOURCE_NAME); + var sec = extension.get(Secret.class, RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(sec).isNotNull(); + }); secret.getMetadata().getFinalizers().clear(); extension.replace(secret); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, RESOURCE_NAME); - var sec = extension.get(Secret.class, RESOURCE_NAME); - assertThat(cm).isNull(); - assertThat(sec).isNull(); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, RESOURCE_NAME); + var sec = extension.get(Secret.class, RESOURCE_NAME); + assertThat(cm).isNull(); + assertThat(sec).isNull(); + }); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/SecretDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/SecretDependent.java index 6add6bf93b..a7d52511ea 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/SecretDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/manageddependentdeletecondition/SecretDependent.java @@ -10,15 +10,12 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; public class SecretDependent - extends - CRUDNoGCKubernetesDependentResource { - - public SecretDependent() { - super(Secret.class); - } + extends CRUDNoGCKubernetesDependentResource< + Secret, ManagedDependentDefaultDeleteConditionCustomResource> { @Override - protected Secret desired(ManagedDependentDefaultDeleteConditionCustomResource primary, + protected Secret desired( + ManagedDependentDefaultDeleteConditionCustomResource primary, Context context) { return new SecretBuilder() @@ -26,8 +23,10 @@ protected Secret desired(ManagedDependentDefaultDeleteConditionCustomResource pr .withName(primary.getMetadata().getName()) .withNamespace(primary.getMetadata().getNamespace()) .endMetadata() - .withData(Map.of("key", - new String(Base64.getEncoder().encode("val".getBytes(StandardCharsets.UTF_16))))) + .withData( + Map.of( + "key", + new String(Base64.getEncoder().encode("val".getBytes(StandardCharsets.UTF_16))))) .build(); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource1.java index a3198a3ca3..ed83b870ab 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource1.java @@ -11,24 +11,22 @@ @KubernetesDependent(informer = @Informer(name = "configMapInformer")) public class ConfigMapDependentResource1 - extends - CRUDNoGCKubernetesDependentResource { + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, MultipleDependentActivationCustomResource> { public static final String DATA_KEY = "data"; public static final String SUFFIX = "1"; - public ConfigMapDependentResource1() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleDependentActivationCustomResource primary, + protected ConfigMap desired( + MultipleDependentActivationCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName() + SUFFIX) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName() + SUFFIX) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue() + SUFFIX)); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource2.java index 5fcead39a2..73ccb55cdb 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/ConfigMapDependentResource2.java @@ -11,24 +11,22 @@ @KubernetesDependent(informer = @Informer(name = "configMapInformer")) public class ConfigMapDependentResource2 - extends - CRUDNoGCKubernetesDependentResource { + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, MultipleDependentActivationCustomResource> { public static final String DATA_KEY = "data"; public static final String SUFFIX = "2"; - public ConfigMapDependentResource2() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(MultipleDependentActivationCustomResource primary, + protected ConfigMap desired( + MultipleDependentActivationCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName() + SUFFIX) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName() + SUFFIX) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue() + SUFFIX)); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationCustomResource.java index 1e99c1c4b1..fc68a98e56 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationCustomResource.java @@ -10,8 +10,4 @@ @Version("v1") @ShortNames("mdar") public class MultipleDependentActivationCustomResource - extends CustomResource - implements Namespaced { - - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationReconciler.java index 14e3ed9811..9953120e76 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentActivationReconciler.java @@ -5,13 +5,16 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource1.class, - activationCondition = ActivationCondition.class), - @Dependent(type = ConfigMapDependentResource2.class, - activationCondition = ActivationCondition.class), - @Dependent(type = SecretDependentResource.class) -}) +@Workflow( + dependents = { + @Dependent( + type = ConfigMapDependentResource1.class, + activationCondition = ActivationCondition.class), + @Dependent( + type = ConfigMapDependentResource2.class, + activationCondition = ActivationCondition.class), + @Dependent(type = SecretDependentResource.class) + }) @ControllerConfiguration public class MultipleDependentActivationReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java index b82fc369f9..2360660adc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/MultipleDependentWithActivationIT.java @@ -27,49 +27,56 @@ public class MultipleDependentWithActivationIT { void bothDependentsWithActivationAreHandled() { var resource = extension.create(testResource()); - await().untilAsserted(() -> { - var cm1 = - extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource1.SUFFIX); - var cm2 = - extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource2.SUFFIX); - var secret = extension.get(Secret.class, TEST_RESOURCE_NAME); - assertThat(secret).isNotNull(); - assertThat(cm1).isNull(); - assertThat(cm2).isNull(); - }); + await() + .untilAsserted( + () -> { + var cm1 = + extension.get( + ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource1.SUFFIX); + var cm2 = + extension.get( + ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource2.SUFFIX); + var secret = extension.get(Secret.class, TEST_RESOURCE_NAME); + assertThat(secret).isNotNull(); + assertThat(cm1).isNull(); + assertThat(cm2).isNull(); + }); ActivationCondition.MET = true; resource.getSpec().setValue(CHANGED_VALUE); extension.replace(resource); - await().untilAsserted(() -> { - var cm1 = - extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource1.SUFFIX); - var cm2 = - extension.get(ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource2.SUFFIX); - var secret = extension.get(Secret.class, TEST_RESOURCE_NAME); - - assertThat(secret).isNotNull(); - assertThat(cm1).isNotNull(); - assertThat(cm2).isNotNull(); - assertThat(cm1.getData()).containsEntry(ConfigMapDependentResource1.DATA_KEY, - CHANGED_VALUE + ConfigMapDependentResource1.SUFFIX); - assertThat(cm2.getData()).containsEntry(ConfigMapDependentResource2.DATA_KEY, - CHANGED_VALUE + ConfigMapDependentResource2.SUFFIX); - }); - + await() + .untilAsserted( + () -> { + var cm1 = + extension.get( + ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource1.SUFFIX); + var cm2 = + extension.get( + ConfigMap.class, TEST_RESOURCE_NAME + ConfigMapDependentResource2.SUFFIX); + var secret = extension.get(Secret.class, TEST_RESOURCE_NAME); + + assertThat(secret).isNotNull(); + assertThat(cm1).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm1.getData()) + .containsEntry( + ConfigMapDependentResource1.DATA_KEY, + CHANGED_VALUE + ConfigMapDependentResource1.SUFFIX); + assertThat(cm2.getData()) + .containsEntry( + ConfigMapDependentResource2.DATA_KEY, + CHANGED_VALUE + ConfigMapDependentResource2.SUFFIX); + }); } MultipleDependentActivationCustomResource testResource() { var res = new MultipleDependentActivationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new MultipleDependentActivationSpec()); res.getSpec().setValue(INITIAL_VALUE); return res; } - - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/SecretDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/SecretDependentResource.java index 6340d07b58..330f0e3c0f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/SecretDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/multipledependentwithactivation/SecretDependentResource.java @@ -11,21 +11,20 @@ public class SecretDependentResource extends CRUDKubernetesDependentResource { - public SecretDependentResource() { - super(Secret.class); - } - @Override - protected Secret desired(MultipleDependentActivationCustomResource primary, + protected Secret desired( + MultipleDependentActivationCustomResource primary, Context context) { // basically does not matter since this should not be called Secret secret = new Secret(); - secret.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); - secret.setData(Map.of("data", - Base64.getEncoder().encodeToString(primary.getSpec().getValue().getBytes()))); + secret.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); + secret.setData( + Map.of( + "data", Base64.getEncoder().encodeToString(primary.getSpec().getValue().getBytes()))); return secret; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource1.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource1.java index 0478fe4248..eec904d2c7 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource1.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource1.java @@ -12,22 +12,20 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; @KubernetesDependent(informer = @Informer(labelSelector = "dependent = cm1")) -public class ConfigMapDependentResource1 extends - CRUDKubernetesDependentResource { - - public ConfigMapDependentResource1() { - super(ConfigMap.class); - } +public class ConfigMapDependentResource1 + extends CRUDKubernetesDependentResource { @Override - public ReconcileResult reconcile(OrderedManagedDependentCustomResource primary, + public ReconcileResult reconcile( + OrderedManagedDependentCustomResource primary, Context context) { OrderedManagedDependentTestReconciler.dependentExecution.add(this.getClass()); return super.reconcile(primary, context); } @Override - protected ConfigMap desired(OrderedManagedDependentCustomResource primary, + protected ConfigMap desired( + OrderedManagedDependentCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); @@ -42,5 +40,4 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, configMap.setData(data); return configMap; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource2.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource2.java index efbd6ec450..8e4a1467ec 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource2.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/ConfigMapDependentResource2.java @@ -12,22 +12,20 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; @KubernetesDependent(informer = @Informer(labelSelector = "dependent = cm2")) -public class ConfigMapDependentResource2 extends - CRUDKubernetesDependentResource { - - public ConfigMapDependentResource2() { - super(ConfigMap.class); - } +public class ConfigMapDependentResource2 + extends CRUDKubernetesDependentResource { @Override - public ReconcileResult reconcile(OrderedManagedDependentCustomResource primary, + public ReconcileResult reconcile( + OrderedManagedDependentCustomResource primary, Context context) { OrderedManagedDependentTestReconciler.dependentExecution.add(this.getClass()); return super.reconcile(primary, context); } @Override - protected ConfigMap desired(OrderedManagedDependentCustomResource primary, + protected ConfigMap desired( + OrderedManagedDependentCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); @@ -42,5 +40,4 @@ protected ConfigMap desired(OrderedManagedDependentCustomResource primary, configMap.setData(data); return configMap; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentCustomResource.java index b73f92565d..ec3622dd86 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentCustomResource.java @@ -11,7 +11,5 @@ @Version("v1") @Kind("OrderedManagedDependentCustomResource") @ShortNames("omd") -public class OrderedManagedDependentCustomResource - extends CustomResource - implements Namespaced { -} +public class OrderedManagedDependentCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java index 73ab6e65f6..ff25911a8c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentIT.java @@ -23,9 +23,14 @@ class OrderedManagedDependentIT { void managedDependentsAreReconciledInOrder() { operator.create(createTestResource()); - await().pollDelay(Duration.ofSeconds(1)).atMost(Duration.ofSeconds(5)) - .until(() -> ((OrderedManagedDependentTestReconciler) operator.getFirstReconciler()) - .getNumberOfExecutions() == 1); + await() + .pollDelay(Duration.ofSeconds(1)) + .atMost(Duration.ofSeconds(5)) + .until( + () -> + ((OrderedManagedDependentTestReconciler) operator.getFirstReconciler()) + .getNumberOfExecutions() + == 1); assertThat(OrderedManagedDependentTestReconciler.dependentExecution.get(0)) .isEqualTo(ConfigMapDependentResource1.class); @@ -33,12 +38,10 @@ void managedDependentsAreReconciledInOrder() { .isEqualTo(ConfigMapDependentResource2.class); } - private OrderedManagedDependentCustomResource createTestResource() { OrderedManagedDependentCustomResource cr = new OrderedManagedDependentCustomResource(); cr.setMetadata(new ObjectMeta()); cr.getMetadata().setName("test"); return cr; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentTestReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentTestReconciler.java index 4336d940b0..77c3d830c1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentTestReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/orderedmanageddependent/OrderedManagedDependentTestReconciler.java @@ -10,15 +10,14 @@ import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; import io.javaoperatorsdk.operator.support.TestExecutionInfoProvider; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource1.class, name = "cm1"), - @Dependent(type = ConfigMapDependentResource2.class, dependsOn = "cm1") -}) -@ControllerConfiguration( - informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) +@Workflow( + dependents = { + @Dependent(type = ConfigMapDependentResource1.class, name = "cm1"), + @Dependent(type = ConfigMapDependentResource2.class, dependsOn = "cm1") + }) +@ControllerConfiguration(informer = @Informer(namespaces = Constants.WATCH_CURRENT_NAMESPACE)) public class OrderedManagedDependentTestReconciler - implements Reconciler, - TestExecutionInfoProvider { + implements Reconciler, TestExecutionInfoProvider { private final AtomicInteger numberOfExecutions = new AtomicInteger(0); public static final List> dependentExecution = @@ -35,5 +34,4 @@ public UpdateControl reconcile( public int getNumberOfExecutions() { return numberOfExecutions.get(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/ConfigMapDependentResource.java index a909a3a706..cb2357bf8b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/ConfigMapDependentResource.java @@ -8,23 +8,21 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; public class ConfigMapDependentResource - extends - CRUDNoGCKubernetesDependentResource { + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, WorkflowActivationCleanupCustomResource> { public static final String DATA_KEY = "data"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(WorkflowActivationCleanupCustomResource primary, + protected ConfigMap desired( + WorkflowActivationCleanupCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue())); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupCustomResource.java index d98c9cd166..195ac565fd 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupCustomResource.java @@ -10,8 +10,4 @@ @Version("v1") @ShortNames("wacc") public class WorkflowActivationCleanupCustomResource - extends CustomResource - implements Namespaced { - - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java index f41d15ae27..80bbf22d40 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupIT.java @@ -23,24 +23,26 @@ public class WorkflowActivationCleanupIT { @BeforeEach void beforeEach(TestInfo testInfo) { - LocallyRunOperatorExtension.applyCrd(WorkflowActivationCleanupCustomResource.class, - client); + LocallyRunOperatorExtension.applyCrd(WorkflowActivationCleanupCustomResource.class, client); - testInfo.getTestMethod() + testInfo + .getTestMethod() .ifPresent(method -> testNamespace = KubernetesResourceUtil.sanitizeName(method.getName())); client.namespaces().resource(testNamespace(testNamespace)).create(); operator = new Operator(o -> o.withCloseClientOnStop(false)); - operator.register(new WorkflowActivationCleanupReconciler(), - o -> o.settingNamespaces(testNamespace)); + operator.register( + new WorkflowActivationCleanupReconciler(), o -> o.settingNamespaces(testNamespace)); } @AfterEach void stopOperator() { client.namespaces().withName(testNamespace).delete(); - await().untilAsserted(() -> { - var ns = client.namespaces().withName(testNamespace).get(); - assertThat(ns).isNull(); - }); + await() + .untilAsserted( + () -> { + var ns = client.namespaces().withName(testNamespace).get(); + assertThat(ns).isNull(); + }); operator.stop(); } @@ -50,28 +52,31 @@ void testCleanupOnMarkedResourceOnOperatorStartup() { client.resource(resource).delete(); operator.start(); - await().untilAsserted(() -> { - var res = client.resource(resource).get(); - assertThat(res).isNull(); - }); + await() + .untilAsserted( + () -> { + var res = client.resource(resource).get(); + assertThat(res).isNull(); + }); } private WorkflowActivationCleanupCustomResource testResourceWithFinalizer() { var resource = new WorkflowActivationCleanupCustomResource(); - resource.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .withFinalizers("workflowactivationcleanupcustomresources.sample.javaoperatorsdk/finalizer") - .withNamespace(testNamespace) - .build()); + resource.setMetadata( + new ObjectMetaBuilder() + .withName("test1") + .withFinalizers( + "workflowactivationcleanupcustomresources.sample.javaoperatorsdk/finalizer") + .withNamespace(testNamespace) + .build()); resource.setSpec(new WorkflowActivationCleanupSpec()); resource.getSpec().setValue("val1"); return resource; } private Namespace testNamespace(String name) { - return new NamespaceBuilder().withMetadata(new ObjectMetaBuilder() - .withName(name) - .build()).build(); + return new NamespaceBuilder() + .withMetadata(new ObjectMetaBuilder().withName(name).build()) + .build(); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java index 64a7a28a5c..ea158c9e08 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcleanup/WorkflowActivationCleanupReconciler.java @@ -3,14 +3,16 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class, - activationCondition = TestActivcationCondition.class), -}) +@Workflow( + dependents = { + @Dependent( + type = ConfigMapDependentResource.class, + activationCondition = TestActivcationCondition.class), + }) @ControllerConfiguration public class WorkflowActivationCleanupReconciler implements Reconciler, - Cleaner { + Cleaner { @Override public UpdateControl reconcile( @@ -21,7 +23,8 @@ public UpdateControl reconcile( } @Override - public DeleteControl cleanup(WorkflowActivationCleanupCustomResource resource, + public DeleteControl cleanup( + WorkflowActivationCleanupCustomResource resource, Context context) { return DeleteControl.defaultDelete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/ConfigMapDependentResource.java index a5b255b10f..5f2e92ed55 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/ConfigMapDependentResource.java @@ -12,18 +12,16 @@ public class ConfigMapDependentResource public static final String DATA_KEY = "data"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(WorkflowActivationConditionCustomResource primary, + protected ConfigMap desired( + WorkflowActivationConditionCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue())); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/RouteDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/RouteDependentResource.java index 92fe86d5db..7d2d091c94 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/RouteDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/RouteDependentResource.java @@ -8,19 +8,17 @@ public class RouteDependentResource extends CRUDKubernetesDependentResource { - public RouteDependentResource() { - super(Route.class); - } - @Override - protected Route desired(WorkflowActivationConditionCustomResource primary, + protected Route desired( + WorkflowActivationConditionCustomResource primary, Context context) { // basically does not matter since this should not be called Route route = new Route(); - route.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + route.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); return route; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionCustomResource.java index d04b3c08e0..9c6347f032 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionCustomResource.java @@ -10,8 +10,4 @@ @Version("v1") @ShortNames("wac") public class WorkflowActivationConditionCustomResource - extends CustomResource - implements Namespaced { - - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java index 355855e6c6..a5b5b23fe3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionIT.java @@ -27,21 +27,20 @@ public class WorkflowActivationConditionIT { void reconciledOnVanillaKubernetesDespiteRouteInWorkflow() { extension.create(testResource()); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(DATA_KEY, TEST_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE_NAME); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(DATA_KEY, TEST_DATA); + }); } private WorkflowActivationConditionCustomResource testResource() { var res = new WorkflowActivationConditionCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).build()); res.setSpec(new WorkflowActivationConditionSpec()); res.getSpec().setValue(TEST_DATA); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionReconciler.java index 2ac931bcdb..b8bcb210c5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowactivationcondition/WorkflowActivationConditionReconciler.java @@ -3,11 +3,13 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class), - @Dependent(type = RouteDependentResource.class, - activationCondition = IsOpenShiftCondition.class) -}) +@Workflow( + dependents = { + @Dependent(type = ConfigMapDependentResource.class), + @Dependent( + type = RouteDependentResource.class, + activationCondition = IsOpenShiftCondition.class) + }) @ControllerConfiguration public class WorkflowActivationConditionReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapDependentResource.java index 9bee03d474..fac6ecae88 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapDependentResource.java @@ -18,42 +18,40 @@ public class ConfigMapDependentResource extends KubernetesDependentResource implements Creator, - Updater, - Deleter { + Updater, + Deleter { public static final String READY_TO_DELETE_ANNOTATION = "ready-to-delete"; private static final Logger log = LoggerFactory.getLogger(ConfigMapDependentResource.class); - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(WorkflowAllFeatureCustomResource primary, - Context context) { + protected ConfigMap desired( + WorkflowAllFeatureCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of("key", "data")); return configMap; } @Override - public void delete(WorkflowAllFeatureCustomResource primary, - Context context) { + public void delete( + WorkflowAllFeatureCustomResource primary, Context context) { Optional optionalConfigMap = context.getSecondaryResource(ConfigMap.class); if (optionalConfigMap.isEmpty()) { log.debug("Config Map not found for primary: {}", ResourceID.fromResource(primary)); return; } - optionalConfigMap.ifPresent((configMap -> { - if (configMap.getMetadata().getAnnotations() != null - && configMap.getMetadata().getAnnotations().get(READY_TO_DELETE_ANNOTATION) != null) { - context.getClient().resource(configMap).delete(); - } - })); + optionalConfigMap.ifPresent( + (configMap -> { + if (configMap.getMetadata().getAnnotations() != null + && configMap.getMetadata().getAnnotations().get(READY_TO_DELETE_ANNOTATION) != null) { + context.getClient().resource(configMap).delete(); + } + })); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapReconcileCondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapReconcileCondition.java index 024e110edb..0854908dd8 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapReconcileCondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/ConfigMapReconcileCondition.java @@ -15,7 +15,8 @@ public class ConfigMapReconcileCondition @Override public Result detailedIsMet( DependentResource dependentResource, - WorkflowAllFeatureCustomResource primary, Context context) { + WorkflowAllFeatureCustomResource primary, + Context context) { final var createConfigMap = primary.getSpec().isCreateConfigMap(); return Result.withResult(createConfigMap, createConfigMap ? CREATE_SET : CREATE_NOT_SET); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentDependentResource.java index 5abcf4c28c..92956e05f6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentDependentResource.java @@ -5,18 +5,16 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; -public class DeploymentDependentResource extends - CRUDNoGCKubernetesDependentResource { - - public DeploymentDependentResource() { - super(Deployment.class); - } +public class DeploymentDependentResource + extends CRUDNoGCKubernetesDependentResource { @Override - protected Deployment desired(WorkflowAllFeatureCustomResource primary, - Context context) { + protected Deployment desired( + WorkflowAllFeatureCustomResource primary, Context context) { Deployment deployment = - ReconcilerUtils.loadYaml(Deployment.class, WorkflowAllFeatureIT.class, + ReconcilerUtils.loadYaml( + Deployment.class, + WorkflowAllFeatureIT.class, "/io/javaoperatorsdk/operator/nginx-deployment.yaml"); deployment.getMetadata().setName(primary.getMetadata().getName()); deployment.getSpec().setReplicas(2); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentReadyCondition.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentReadyCondition.java index 5e023a3d9d..40ad17e680 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentReadyCondition.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/DeploymentReadyCondition.java @@ -12,11 +12,14 @@ public boolean isMet( DependentResource dependentResource, WorkflowAllFeatureCustomResource primary, Context context) { - return dependentResource.getSecondaryResource(primary, context) - .map(deployment -> { - var readyReplicas = deployment.getStatus().getReadyReplicas(); - return readyReplicas != null && deployment.getSpec().getReplicas().equals(readyReplicas); - }) + return dependentResource + .getSecondaryResource(primary, context) + .map( + deployment -> { + var readyReplicas = deployment.getStatus().getReadyReplicas(); + return readyReplicas != null + && deployment.getSpec().getReplicas().equals(readyReplicas); + }) .orElse(false); } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureCustomResource.java index cc3987710b..4b3a75b10b 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureCustomResource.java @@ -11,8 +11,4 @@ @ShortNames("waf") public class WorkflowAllFeatureCustomResource extends CustomResource - implements Namespaced { - - - -} + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java index 20e5ea5ae3..93551dcf43 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureIT.java @@ -22,88 +22,107 @@ public class WorkflowAllFeatureIT { @RegisterExtension LocallyRunOperatorExtension operator = - LocallyRunOperatorExtension.builder().withReconciler(WorkflowAllFeatureReconciler.class) + LocallyRunOperatorExtension.builder() + .withReconciler(WorkflowAllFeatureReconciler.class) .build(); @Test void configMapNotReconciledUntilDeploymentReady() { operator.create(customResource(true)); - await().untilAsserted( - () -> { - assertThat(operator - .getReconcilerOfType(WorkflowAllFeatureReconciler.class) - .getNumberOfReconciliationExecution()) - .isPositive(); - assertThat(operator.get(Deployment.class, RESOURCE_NAME)).isNotNull(); - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNull(); - assertThat(getPrimaryStatus().getMsgFromCondition()) - .isEqualTo(ConfigMapReconcileCondition.NOT_RECONCILED_YET); - }); - - await().atMost(ONE_MINUTE).untilAsserted(() -> { - assertThat(operator - .getReconcilerOfType(WorkflowAllFeatureReconciler.class) - .getNumberOfReconciliationExecution()) - .isGreaterThan(1); - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); - final var primaryStatus = getPrimaryStatus(); - assertThat(primaryStatus.getReady()).isTrue(); - assertThat(primaryStatus.getMsgFromCondition()) - .isEqualTo(ConfigMapReconcileCondition.CREATE_SET); - }); + await() + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(WorkflowAllFeatureReconciler.class) + .getNumberOfReconciliationExecution()) + .isPositive(); + assertThat(operator.get(Deployment.class, RESOURCE_NAME)).isNotNull(); + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + assertThat(getPrimaryStatus().getMsgFromCondition()) + .isEqualTo(ConfigMapReconcileCondition.NOT_RECONCILED_YET); + }); + + await() + .atMost(ONE_MINUTE) + .untilAsserted( + () -> { + assertThat( + operator + .getReconcilerOfType(WorkflowAllFeatureReconciler.class) + .getNumberOfReconciliationExecution()) + .isGreaterThan(1); + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + final var primaryStatus = getPrimaryStatus(); + assertThat(primaryStatus.getReady()).isTrue(); + assertThat(primaryStatus.getMsgFromCondition()) + .isEqualTo(ConfigMapReconcileCondition.CREATE_SET); + }); markConfigMapForDelete(); } private WorkflowAllFeatureStatus getPrimaryStatus() { - return operator.get(WorkflowAllFeatureCustomResource.class, RESOURCE_NAME) - .getStatus(); + return operator.get(WorkflowAllFeatureCustomResource.class, RESOURCE_NAME).getStatus(); } - @Test void configMapNotReconciledIfReconcileConditionNotMet() { var resource = operator.create(customResource(false)); - await().atMost(ONE_MINUTE).untilAsserted(() -> { - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNull(); - assertThat(getPrimaryStatus().getReady()).isTrue(); - }); + await() + .atMost(ONE_MINUTE) + .untilAsserted( + () -> { + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + assertThat(getPrimaryStatus().getReady()).isTrue(); + }); resource.getSpec().setCreateConfigMap(true); operator.replace(resource); - await().untilAsserted(() -> { - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); - assertThat(getPrimaryStatus().getReady()).isTrue(); - }); + await() + .untilAsserted( + () -> { + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + assertThat(getPrimaryStatus().getReady()).isTrue(); + }); } - @Test void configMapNotDeletedUntilNotMarked() { var resource = operator.create(customResource(true)); - await().atMost(ONE_MINUTE).untilAsserted(() -> { - assertThat(getPrimaryStatus()) - .isNotNull(); - assertThat(getPrimaryStatus().getReady()).isTrue(); - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); - }); + await() + .atMost(ONE_MINUTE) + .untilAsserted( + () -> { + assertThat(getPrimaryStatus()).isNotNull(); + assertThat(getPrimaryStatus().getReady()).isTrue(); + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + }); operator.delete(resource); - await().pollDelay(Duration.ofMillis(300)).untilAsserted(() -> { - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); - assertThat(operator.get(WorkflowAllFeatureCustomResource.class, RESOURCE_NAME)).isNotNull(); - }); + await() + .pollDelay(Duration.ofMillis(300)) + .untilAsserted( + () -> { + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + assertThat(operator.get(WorkflowAllFeatureCustomResource.class, RESOURCE_NAME)) + .isNotNull(); + }); markConfigMapForDelete(); - await().atMost(ONE_MINUTE).untilAsserted(() -> { - assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNull(); - assertThat(operator.get(WorkflowAllFeatureCustomResource.class, RESOURCE_NAME)).isNull(); - }); + await() + .atMost(ONE_MINUTE) + .untilAsserted( + () -> { + assertThat(operator.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + assertThat(operator.get(WorkflowAllFeatureCustomResource.class, RESOURCE_NAME)) + .isNull(); + }); } private void markConfigMapForDelete() { @@ -117,12 +136,9 @@ private void markConfigMapForDelete() { private WorkflowAllFeatureCustomResource customResource(boolean createConfigMap) { var res = new WorkflowAllFeatureCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); res.setSpec(new WorkflowAllFeatureSpec()); res.getSpec().setCreateConfigMap(createConfigMap); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureReconciler.java index fb8b0b4a3d..9e9a365e04 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowallfeature/WorkflowAllFeatureReconciler.java @@ -15,18 +15,22 @@ import static io.javaoperatorsdk.operator.workflow.workflowallfeature.WorkflowAllFeatureReconciler.DEPLOYMENT_NAME; -@Workflow(dependents = { - @Dependent(name = DEPLOYMENT_NAME, type = DeploymentDependentResource.class, - readyPostcondition = DeploymentReadyCondition.class), - @Dependent(type = ConfigMapDependentResource.class, - reconcilePrecondition = ConfigMapReconcileCondition.class, - deletePostcondition = ConfigMapDeletePostCondition.class, - dependsOn = DEPLOYMENT_NAME) -}) +@Workflow( + dependents = { + @Dependent( + name = DEPLOYMENT_NAME, + type = DeploymentDependentResource.class, + readyPostcondition = DeploymentReadyCondition.class), + @Dependent( + type = ConfigMapDependentResource.class, + reconcilePrecondition = ConfigMapReconcileCondition.class, + deletePostcondition = ConfigMapDeletePostCondition.class, + dependsOn = DEPLOYMENT_NAME) + }) @ControllerConfiguration public class WorkflowAllFeatureReconciler implements Reconciler, - Cleaner { + Cleaner { public static final String DEPLOYMENT_NAME = "deployment"; @@ -41,13 +45,18 @@ public UpdateControl reconcile( if (resource.getStatus() == null) { resource.setStatus(new WorkflowAllFeatureStatus()); } - final var reconcileResult = context.managedWorkflowAndDependentResourceContext() - .getWorkflowReconcileResult(); - final var msgFromCondition = reconcileResult.orElseThrow().getDependentConditionResult( - DependentResource.defaultNameFor(ConfigMapDependentResource.class), - Condition.Type.RECONCILE, String.class) - .orElse(ConfigMapReconcileCondition.NOT_RECONCILED_YET); - resource.getStatus() + final var reconcileResult = + context.managedWorkflowAndDependentResourceContext().getWorkflowReconcileResult(); + final var msgFromCondition = + reconcileResult + .orElseThrow() + .getDependentConditionResult( + DependentResource.defaultNameFor(ConfigMapDependentResource.class), + Condition.Type.RECONCILE, + String.class) + .orElse(ConfigMapReconcileCondition.NOT_RECONCILED_YET); + resource + .getStatus() .withReady(reconcileResult.orElseThrow().allDependentResourcesReady()) .withMsgFromCondition(msgFromCondition); return UpdateControl.patchStatus(resource); @@ -62,7 +71,8 @@ public int getNumberOfCleanupExecution() { } @Override - public DeleteControl cleanup(WorkflowAllFeatureCustomResource resource, + public DeleteControl cleanup( + WorkflowAllFeatureCustomResource resource, Context context) { numberOfCleanupExecution.addAndGet(1); return DeleteControl.defaultDelete(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/ConfigMapDependent.java index 228ed39564..17190c5a92 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/ConfigMapDependent.java @@ -8,21 +8,19 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; -public class ConfigMapDependent extends - CRUDNoGCKubernetesDependentResource { - - public ConfigMapDependent() { - super(ConfigMap.class); - } +public class ConfigMapDependent + extends CRUDNoGCKubernetesDependentResource { @Override - protected ConfigMap desired(WorkflowExplicitCleanupCustomResource primary, + protected ConfigMap desired( + WorkflowExplicitCleanupCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of("key", "val")) .build(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java index 14ae6011a9..8622421db9 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("wec") -public class WorkflowExplicitCleanupCustomResource - extends CustomResource - implements Namespaced { -} +public class WorkflowExplicitCleanupCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java index e6a5b7a3cc..b6d4969fd3 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupIT.java @@ -24,25 +24,26 @@ public class WorkflowExplicitCleanupIT { void workflowInvokedExplicitly() { var res = extension.create(testResource()); - await().untilAsserted(() -> { - assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); - }); + await() + .untilAsserted( + () -> { + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + }); extension.delete(res); // The ConfigMap is not garbage collected, this tests that even if the cleaner is not // implemented the workflow cleanup still called even if there is explicit invocation - await().untilAsserted(() -> { - assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); - }); + await() + .untilAsserted( + () -> { + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + }); } WorkflowExplicitCleanupCustomResource testResource() { var res = new WorkflowExplicitCleanupCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java index 4361b5ac47..1dac660839 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitcleanup/WorkflowExplicitCleanupReconciler.java @@ -3,12 +3,11 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(explicitInvocation = true, - dependents = @Dependent(type = ConfigMapDependent.class)) +@Workflow(explicitInvocation = true, dependents = @Dependent(type = ConfigMapDependent.class)) @ControllerConfiguration public class WorkflowExplicitCleanupReconciler implements Reconciler, - Cleaner { + Cleaner { @Override public UpdateControl reconcile( @@ -21,7 +20,8 @@ public UpdateControl reconcile( } @Override - public DeleteControl cleanup(WorkflowExplicitCleanupCustomResource resource, + public DeleteControl cleanup( + WorkflowExplicitCleanupCustomResource resource, Context context) { context.managedWorkflowAndDependentResourceContext().cleanupManageWorkflow(); diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/ConfigMapDependent.java index 1632de5bbd..a2638b48b5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/ConfigMapDependent.java @@ -8,21 +8,20 @@ import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; -public class ConfigMapDependent extends - CRUDNoGCKubernetesDependentResource { - - public ConfigMapDependent() { - super(ConfigMap.class); - } +public class ConfigMapDependent + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, WorkflowExplicitInvocationCustomResource> { @Override - protected ConfigMap desired(WorkflowExplicitInvocationCustomResource primary, + protected ConfigMap desired( + WorkflowExplicitInvocationCustomResource primary, Context context) { return new ConfigMapBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()) + .withMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()) .withData(Map.of("key", primary.getSpec().getValue())) .build(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java index c64964b02b..e625fa21d5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationCustomResource.java @@ -10,6 +10,4 @@ @Version("v1") @ShortNames("wei") public class WorkflowExplicitInvocationCustomResource - extends CustomResource - implements Namespaced { -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java index ba77057acc..eaa799300f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationIT.java @@ -27,10 +27,12 @@ void workflowInvokedExplicitly() { var res = extension.create(testResource()); var reconciler = extension.getReconcilerOfType(WorkflowExplicitInvocationReconciler.class); - await().untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); - assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(1); + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + }); reconciler.setInvokeWorkflow(true); @@ -38,28 +40,30 @@ void workflowInvokedExplicitly() { res.getSpec().setValue("changed value"); res = extension.replace(res); - await().untilAsserted(() -> { - assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2); - assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.getNumberOfExecutions()).isEqualTo(2); + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNotNull(); + }); extension.delete(res); // The ConfigMap is not garbage collected, this tests that even if the cleaner is not // implemented the workflow cleanup still called even if there is explicit invocation - await().timeout(Duration.ofSeconds(30)).untilAsserted(() -> { - assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); - }); + await() + .timeout(Duration.ofSeconds(30)) + .untilAsserted( + () -> { + assertThat(extension.get(ConfigMap.class, RESOURCE_NAME)).isNull(); + }); } WorkflowExplicitInvocationCustomResource testResource() { var res = new WorkflowExplicitInvocationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(RESOURCE_NAME) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(RESOURCE_NAME).build()); res.setSpec(new WorkflowExplicitInvocationSpec()); res.getSpec().setValue("initial value"); return res; } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java index 4ac64b4cf8..99249326f5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowexplicitinvocation/WorkflowExplicitInvocationReconciler.java @@ -5,8 +5,7 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(explicitInvocation = true, - dependents = @Dependent(type = ConfigMapDependent.class)) +@Workflow(explicitInvocation = true, dependents = @Dependent(type = ConfigMapDependent.class)) @ControllerConfiguration public class WorkflowExplicitInvocationReconciler implements Reconciler { @@ -25,7 +24,6 @@ public UpdateControl reconcile( context.managedWorkflowAndDependentResourceContext().reconcileManagedWorkflow(); } - return UpdateControl.noUpdate(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/ConfigMapDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/ConfigMapDependentResource.java index 82d8d9c236..3366a61a1f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/ConfigMapDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/ConfigMapDependentResource.java @@ -8,23 +8,21 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; public class ConfigMapDependentResource - extends - CRUDNoGCKubernetesDependentResource { + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, WorkflowMultipleActivationCustomResource> { public static final String DATA_KEY = "data"; - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override - protected ConfigMap desired(WorkflowMultipleActivationCustomResource primary, + protected ConfigMap desired( + WorkflowMultipleActivationCustomResource primary, Context context) { ConfigMap configMap = new ConfigMap(); - configMap.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); + configMap.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); configMap.setData(Map.of(DATA_KEY, primary.getSpec().getValue())); return configMap; } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/SecretDependentResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/SecretDependentResource.java index 872a5b5770..cd1fbfcedc 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/SecretDependentResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/SecretDependentResource.java @@ -11,21 +11,20 @@ public class SecretDependentResource extends CRUDKubernetesDependentResource { - public SecretDependentResource() { - super(Secret.class); - } - @Override - protected Secret desired(WorkflowMultipleActivationCustomResource primary, + protected Secret desired( + WorkflowMultipleActivationCustomResource primary, Context context) { // basically does not matter since this should not be called Secret secret = new Secret(); - secret.setMetadata(new ObjectMetaBuilder() - .withName(primary.getMetadata().getName()) - .withNamespace(primary.getMetadata().getNamespace()) - .build()); - secret.setData(Map.of("data", - Base64.getEncoder().encodeToString(primary.getSpec().getValue().getBytes()))); + secret.setMetadata( + new ObjectMetaBuilder() + .withName(primary.getMetadata().getName()) + .withNamespace(primary.getMetadata().getNamespace()) + .build()); + secret.setData( + Map.of( + "data", Base64.getEncoder().encodeToString(primary.getSpec().getValue().getBytes()))); return secret; } } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationCustomResource.java index faf442b849..4951108ff5 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationCustomResource.java @@ -10,8 +10,4 @@ @Version("v1") @ShortNames("mwac") public class WorkflowMultipleActivationCustomResource - extends CustomResource - implements Namespaced { - - -} + extends CustomResource implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java index e793e9cb19..0d5f3b60f1 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationIT.java @@ -33,77 +33,97 @@ void deactivatingAndReactivatingDependent() { ActivationCondition.MET = true; var cr1 = extension.create(testResource()); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - var secret = extension.get(Secret.class, TEST_RESOURCE1); - assertThat(cm).isNotNull(); - assertThat(secret).isNotNull(); - assertThat(cm.getData()).containsEntry(DATA_KEY, INITIAL_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + var secret = extension.get(Secret.class, TEST_RESOURCE1); + assertThat(cm).isNotNull(); + assertThat(secret).isNotNull(); + assertThat(cm.getData()).containsEntry(DATA_KEY, INITIAL_DATA); + }); extension.delete(cr1); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - assertThat(cm).isNull(); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + assertThat(cm).isNull(); + }); ActivationCondition.MET = false; cr1 = extension.create(testResource()); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - var secret = extension.get(Secret.class, TEST_RESOURCE1); - assertThat(cm).isNull(); - assertThat(secret).isNotNull(); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + var secret = extension.get(Secret.class, TEST_RESOURCE1); + assertThat(cm).isNull(); + assertThat(secret).isNotNull(); + }); ActivationCondition.MET = true; cr1.getSpec().setValue(CHANGED_VALUE); extension.replace(cr1); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - assertThat(cm).isNotNull(); - assertThat(cm.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + assertThat(cm).isNotNull(); + assertThat(cm.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); ActivationCondition.MET = false; cr1.getSpec().setValue(INITIAL_DATA); extension.replace(cr1); - await().pollDelay(Duration.ofMillis(POLL_DELAY)).untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - assertThat(cm).isNotNull(); - // data not changed - assertThat(cm.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); - }); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + assertThat(cm).isNotNull(); + // data not changed + assertThat(cm.getData()).containsEntry(DATA_KEY, CHANGED_VALUE); + }); var numOfReconciliation = - extension.getReconcilerOfType(WorkflowMultipleActivationReconciler.class) + extension + .getReconcilerOfType(WorkflowMultipleActivationReconciler.class) .getNumberOfReconciliationExecution(); var actualCM = extension.get(ConfigMap.class, TEST_RESOURCE1); actualCM.getData().put("data2", "additionaldata"); extension.replace(actualCM); - await().pollDelay(Duration.ofMillis(POLL_DELAY)).untilAsserted(() -> { - // change in config map does not induce reconciliation if inactive (thus informer is not - // present) - assertThat(extension.getReconcilerOfType(WorkflowMultipleActivationReconciler.class) - .getNumberOfReconciliationExecution()).isEqualTo(numOfReconciliation); - }); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> { + // change in config map does not induce reconciliation if inactive (thus informer is + // not + // present) + assertThat( + extension + .getReconcilerOfType(WorkflowMultipleActivationReconciler.class) + .getNumberOfReconciliationExecution()) + .isEqualTo(numOfReconciliation); + }); extension.delete(cr1); - await().pollDelay(Duration.ofMillis(POLL_DELAY)).untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - assertThat(cm).isNotNull(); - }); + await() + .pollDelay(Duration.ofMillis(POLL_DELAY)) + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + assertThat(cm).isNotNull(); + }); } WorkflowMultipleActivationCustomResource testResource(String name) { var res = new WorkflowMultipleActivationCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(name) - .build()); + res.setMetadata(new ObjectMetaBuilder().withName(name).build()); res.setSpec(new WorkflowMultipleActivationSpec()); res.getSpec().setValue(INITIAL_DATA); return res; @@ -123,14 +143,15 @@ void simpleConcurrencyTest() { extension.create(testResource()); extension.create(testResource2()); - await().untilAsserted(() -> { - var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); - var cm2 = extension.get(ConfigMap.class, TEST_RESOURCE2); - assertThat(cm).isNotNull(); - assertThat(cm2).isNotNull(); - assertThat(cm.getData()).containsEntry(DATA_KEY, INITIAL_DATA); - assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_DATA); - }); + await() + .untilAsserted( + () -> { + var cm = extension.get(ConfigMap.class, TEST_RESOURCE1); + var cm2 = extension.get(ConfigMap.class, TEST_RESOURCE2); + assertThat(cm).isNotNull(); + assertThat(cm2).isNotNull(); + assertThat(cm.getData()).containsEntry(DATA_KEY, INITIAL_DATA); + assertThat(cm2.getData()).containsEntry(DATA_KEY, INITIAL_DATA); + }); } - } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java index 6018fb7112..460638024f 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowmultipleactivation/WorkflowMultipleActivationReconciler.java @@ -5,11 +5,13 @@ import io.javaoperatorsdk.operator.api.reconciler.*; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class, - activationCondition = ActivationCondition.class), - @Dependent(type = SecretDependentResource.class) -}) +@Workflow( + dependents = { + @Dependent( + type = ConfigMapDependentResource.class, + activationCondition = ActivationCondition.class), + @Dependent(type = SecretDependentResource.class) + }) @ControllerConfiguration public class WorkflowMultipleActivationReconciler implements Reconciler { diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/ConfigMapDependent.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/ConfigMapDependent.java index fcd817c4f7..6dbac41f8c 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/ConfigMapDependent.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/ConfigMapDependent.java @@ -1,17 +1,13 @@ package io.javaoperatorsdk.operator.workflow.workflowsilentexceptionhandling; - import io.fabric8.kubernetes.api.model.ConfigMap; import io.javaoperatorsdk.operator.api.reconciler.Context; import io.javaoperatorsdk.operator.api.reconciler.dependent.ReconcileResult; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDNoGCKubernetesDependentResource; -public class ConfigMapDependent extends - CRUDNoGCKubernetesDependentResource { - - public ConfigMapDependent() { - super(ConfigMap.class); - } +public class ConfigMapDependent + extends CRUDNoGCKubernetesDependentResource< + ConfigMap, HandleWorkflowExceptionsInReconcilerCustomResource> { @Override public ReconcileResult reconcile( @@ -21,7 +17,8 @@ public ReconcileResult reconcile( } @Override - public void delete(HandleWorkflowExceptionsInReconcilerCustomResource primary, + public void delete( + HandleWorkflowExceptionsInReconcilerCustomResource primary, Context context) { throw new RuntimeException("Exception thrown on purpose"); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerCustomResource.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerCustomResource.java index db5e94e40b..e05f73cc6d 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerCustomResource.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerCustomResource.java @@ -9,7 +9,5 @@ @Group("sample.javaoperatorsdk") @Version("v1") @ShortNames("hweir") -public class HandleWorkflowExceptionsInReconcilerCustomResource - extends CustomResource - implements Namespaced { -} +public class HandleWorkflowExceptionsInReconcilerCustomResource extends CustomResource + implements Namespaced {} diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerReconciler.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerReconciler.java index 304c0d73ec..a8cbb11049 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerReconciler.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/HandleWorkflowExceptionsInReconcilerReconciler.java @@ -9,12 +9,13 @@ import io.javaoperatorsdk.operator.api.reconciler.Workflow; import io.javaoperatorsdk.operator.api.reconciler.dependent.Dependent; -@Workflow(handleExceptionsInReconciler = true, +@Workflow( + handleExceptionsInReconciler = true, dependents = @Dependent(type = ConfigMapDependent.class)) @ControllerConfiguration public class HandleWorkflowExceptionsInReconcilerReconciler implements Reconciler, - Cleaner { + Cleaner { private volatile boolean errorsFoundInReconcilerResult = false; private volatile boolean errorsFoundInCleanupResult = false; @@ -24,19 +25,27 @@ public UpdateControl reconci HandleWorkflowExceptionsInReconcilerCustomResource resource, Context context) { - errorsFoundInReconcilerResult = context.managedWorkflowAndDependentResourceContext() - .getWorkflowReconcileResult().orElseThrow().erroredDependentsExist(); - + errorsFoundInReconcilerResult = + context + .managedWorkflowAndDependentResourceContext() + .getWorkflowReconcileResult() + .orElseThrow() + .erroredDependentsExist(); return UpdateControl.noUpdate(); } @Override - public DeleteControl cleanup(HandleWorkflowExceptionsInReconcilerCustomResource resource, + public DeleteControl cleanup( + HandleWorkflowExceptionsInReconcilerCustomResource resource, Context context) { - errorsFoundInCleanupResult = context.managedWorkflowAndDependentResourceContext() - .getWorkflowCleanupResult().orElseThrow().erroredDependentsExist(); + errorsFoundInCleanupResult = + context + .managedWorkflowAndDependentResourceContext() + .getWorkflowCleanupResult() + .orElseThrow() + .erroredDependentsExist(); return DeleteControl.defaultDelete(); } diff --git a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java index b23fee2d20..2f5b7246c6 100644 --- a/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java +++ b/operator-framework/src/test/java/io/javaoperatorsdk/operator/workflow/workflowsilentexceptionhandling/WorkflowSilentExceptionHandlingIT.java @@ -23,23 +23,24 @@ void handleExceptionsInReconciler() { var reconciler = extension.getReconcilerOfType(HandleWorkflowExceptionsInReconcilerReconciler.class); - await().untilAsserted(() -> { - assertThat(reconciler.isErrorsFoundInReconcilerResult()).isTrue(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.isErrorsFoundInReconcilerResult()).isTrue(); + }); extension.delete(testResource()); - await().untilAsserted(() -> { - assertThat(reconciler.isErrorsFoundInCleanupResult()).isTrue(); - }); + await() + .untilAsserted( + () -> { + assertThat(reconciler.isErrorsFoundInCleanupResult()).isTrue(); + }); } HandleWorkflowExceptionsInReconcilerCustomResource testResource() { var res = new HandleWorkflowExceptionsInReconcilerCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName("test1") - .build()); + res.setMetadata(new ObjectMetaBuilder().withName("test1").build()); return res; } - } diff --git a/operator-framework/src/test/resources/io/javaoperatorsdk/operator/statefulset.yaml b/operator-framework/src/test/resources/io/javaoperatorsdk/operator/statefulset.yaml index f40fbeb607..bb8a2df04b 100644 --- a/operator-framework/src/test/resources/io/javaoperatorsdk/operator/statefulset.yaml +++ b/operator-framework/src/test/resources/io/javaoperatorsdk/operator/statefulset.yaml @@ -21,6 +21,13 @@ spec: ports: - containerPort: 80 name: web + env: + - name: APP1_HOST_NAME + value: "" + - name: APP2_HOST_NAME + value: "localhost" + - name: APP3_HOST_NAME + value: " " volumeMounts: - name: www mountPath: /usr/share/nginx/html diff --git a/pom.xml b/pom.xml index a4dbc128ff..fb32bb1aaa 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT pom Operator SDK for Java Java SDK for implementing Kubernetes operators @@ -60,26 +60,26 @@ https://sonarcloud.io jdk - 5.12.0 - 7.1.0 + 5.12.2 + 7.3.1 2.0.12 2.24.3 - 5.15.2 + 5.18.0 3.17.0 0.21.0 1.13.0 3.27.3 4.3.0 2.7.3 - 1.14.4 + 1.15.0 3.2.0 0.9.14 - 2.18.0 + 2.19.0 4.15 2.11 3.14.0 - 3.5.2 + 3.5.3 3.11.2 3.3.1 3.3.1 @@ -90,8 +90,8 @@ 3.0.0 3.1.4 9.0.1 - 3.4.4 - 2.44.3 + 3.4.5 + 2.44.4 @@ -327,53 +327,42 @@ WatchPermissionAwareTest + + com.diffplug.spotless + spotless-maven-plugin + + + + pom.xml + ./**/pom.xml + + + false + + + + + true + + + java,javax,org,io,com,,\# + + + + + + + + apply + + compile + + + + - - spotless - - - contributing - - - - - - com.diffplug.spotless - spotless-maven-plugin - - - - pom.xml - ./**/pom.xml - - - false - - - - - contributing/eclipse-google-style.xml - - - contributing/eclipse.importorder - - - - - - - - apply - - compile - - - - - - integration-tests diff --git a/sample-operators/controller-namespace-deletion/pom.xml b/sample-operators/controller-namespace-deletion/pom.xml index 9608b44db1..75ef1ee5eb 100644 --- a/sample-operators/controller-namespace-deletion/pom.xml +++ b/sample-operators/controller-namespace-deletion/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT sample-controller-namespace-deletion diff --git a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionCustomResource.java b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionCustomResource.java index ae0f1034ee..59bb0eb8b9 100644 --- a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionCustomResource.java +++ b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionCustomResource.java @@ -9,6 +9,4 @@ @Version("v1") public class ControllerNamespaceDeletionCustomResource extends CustomResource - implements Namespaced { - -} + implements Namespaced {} diff --git a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionOperator.java b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionOperator.java index 5364852467..70ee3e60a4 100644 --- a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionOperator.java +++ b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionOperator.java @@ -18,14 +18,18 @@ public class ControllerNamespaceDeletionOperator { public static void main(String[] args) { - Runtime.getRuntime().addShutdownHook(new Thread(() -> { - log.info("Shutting down..."); - boolean allResourcesDeleted = waitUntilResourcesDeleted(); - log.info("All resources within timeout: {}", allResourcesDeleted); - })); + Runtime.getRuntime() + .addShutdownHook( + new Thread( + () -> { + log.info("Shutting down..."); + boolean allResourcesDeleted = waitUntilResourcesDeleted(); + log.info("All resources within timeout: {}", allResourcesDeleted); + })); Operator operator = new Operator(); - operator.register(new ControllerNamespaceDeletionReconciler(), + operator.register( + new ControllerNamespaceDeletionReconciler(), ControllerConfigurationOverrider::watchingOnlyCurrentNamespace); operator.start(); } @@ -35,9 +39,11 @@ private static boolean waitUntilResourcesDeleted() { var startTime = LocalTime.now(); while (startTime.until(LocalTime.now(), SECONDS) < 20) { var items = - client.resources(ControllerNamespaceDeletionCustomResource.class) + client + .resources(ControllerNamespaceDeletionCustomResource.class) .inNamespace(client.getConfiguration().getNamespace()) - .list().getItems(); + .list() + .getItems(); log.info("Custom resource in namespace: {}", items); if (items.isEmpty()) { return true; diff --git a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionReconciler.java b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionReconciler.java index 7261f269b4..688f3d8b3d 100644 --- a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionReconciler.java +++ b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionReconciler.java @@ -14,7 +14,7 @@ public class ControllerNamespaceDeletionReconciler implements Reconciler, - Cleaner { + Cleaner { private static final Logger log = LoggerFactory.getLogger(ControllerNamespaceDeletionReconciler.class); @@ -25,7 +25,9 @@ public class ControllerNamespaceDeletionReconciler public UpdateControl reconcile( ControllerNamespaceDeletionCustomResource resource, Context context) { - log.info("Reconciling: {} in namespace: {}", resource.getMetadata().getName(), + log.info( + "Reconciling: {} in namespace: {}", + resource.getMetadata().getName(), resource.getMetadata().getNamespace()); var response = createResponseResource(resource); @@ -37,16 +39,18 @@ public UpdateControl reconcile( private ControllerNamespaceDeletionCustomResource createResponseResource( ControllerNamespaceDeletionCustomResource resource) { var res = new ControllerNamespaceDeletionCustomResource(); - res.setMetadata(new ObjectMetaBuilder() - .withName(resource.getMetadata().getName()) - .withNamespace(resource.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(resource.getMetadata().getName()) + .withNamespace(resource.getMetadata().getNamespace()) + .build()); res.setStatus(new ControllerNamespaceDeletionStatus()); return res; } @Override - public DeleteControl cleanup(ControllerNamespaceDeletionCustomResource resource, + public DeleteControl cleanup( + ControllerNamespaceDeletionCustomResource resource, Context context) { log.info("Cleaning up resource"); try { diff --git a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionSpec.java b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionSpec.java index dc5092e7e5..0107449eb8 100644 --- a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionSpec.java +++ b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionSpec.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.sample; - public class ControllerNamespaceDeletionSpec { private String value; diff --git a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionStatus.java b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionStatus.java index 732fa7d626..36db2f33c4 100644 --- a/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionStatus.java +++ b/sample-operators/controller-namespace-deletion/src/main/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionStatus.java @@ -1,6 +1,5 @@ package io.javaoperatorsdk.operator.sample; - public class ControllerNamespaceDeletionStatus { private String value; diff --git a/sample-operators/controller-namespace-deletion/src/test/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionE2E.java b/sample-operators/controller-namespace-deletion/src/test/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionE2E.java index 36c7f132ab..e3900a040d 100644 --- a/sample-operators/controller-namespace-deletion/src/test/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionE2E.java +++ b/sample-operators/controller-namespace-deletion/src/test/java/io/javaoperatorsdk/operator/sample/ControllerNamespaceDeletionE2E.java @@ -26,7 +26,6 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; - class ControllerNamespaceDeletionE2E { private static final Logger log = LoggerFactory.getLogger(ControllerNamespaceDeletionE2E.class); @@ -46,28 +45,44 @@ void customResourceCleanedUpOnNamespaceDeletion() { deployController(); client.resource(testResource()).serverSideApply(); - await().untilAsserted(() -> { - var res = client.resources(ControllerNamespaceDeletionCustomResource.class) - .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get(); - assertThat(res.getStatus()).isNotNull(); - assertThat(res.getStatus().getValue()).isEqualTo(INITIAL_VALUE); - }); + await() + .untilAsserted( + () -> { + var res = + client + .resources(ControllerNamespaceDeletionCustomResource.class) + .inNamespace(namespace) + .withName(TEST_RESOURCE_NAME) + .get(); + assertThat(res.getStatus()).isNotNull(); + assertThat(res.getStatus().getValue()).isEqualTo(INITIAL_VALUE); + }); client.namespaces().withName(namespace).delete(); - await().timeout(Duration.ofSeconds(20)).untilAsserted(() -> { - var ns = client.resources(ControllerNamespaceDeletionCustomResource.class) - .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get(); - assertThat(ns).isNull(); - }); + await() + .timeout(Duration.ofSeconds(20)) + .untilAsserted( + () -> { + var ns = + client + .resources(ControllerNamespaceDeletionCustomResource.class) + .inNamespace(namespace) + .withName(TEST_RESOURCE_NAME) + .get(); + assertThat(ns).isNull(); + }); log.info("Removing finalizers from role and role bing and service account"); removeRoleAndRoleBindingFinalizers(); - await().timeout(Duration.ofSeconds(20)).untilAsserted(() -> { - var ns = client.namespaces().withName(namespace).get(); - assertThat(ns).isNull(); - }); + await() + .timeout(Duration.ofSeconds(20)) + .untilAsserted( + () -> { + var ns = client.namespaces().withName(namespace).get(); + assertThat(ns).isNull(); + }); } private void removeRoleAndRoleBindingFinalizers() { @@ -87,42 +102,42 @@ private void removeRoleAndRoleBindingFinalizers() { ControllerNamespaceDeletionCustomResource testResource() { var cr = new ControllerNamespaceDeletionCustomResource(); - cr.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(namespace) - .build()); + cr.setMetadata( + new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).withNamespace(namespace).build()); cr.setSpec(new ControllerNamespaceDeletionSpec()); cr.getSpec().setValue(INITIAL_VALUE); return cr; } - @BeforeEach void setup() { namespace = "controller-namespace-" + UUID.randomUUID(); - client = new KubernetesClientBuilder().withConfig(new ConfigBuilder() - .withNamespace(namespace) - .build()).build(); + client = + new KubernetesClientBuilder() + .withConfig(new ConfigBuilder().withNamespace(namespace).build()) + .build(); applyCRD(); - client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(namespace) - .endMetadata().build()).create(); + client + .namespaces() + .resource( + new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()) + .create(); } void deployController() { try { List resources = client.load(new FileInputStream("k8s/operator.yaml")).items(); - resources.forEach(hm -> { - hm.getMetadata().setNamespace(namespace); - if (hm.getKind().equalsIgnoreCase("rolebinding")) { - var crb = (RoleBinding) hm; - for (var subject : crb.getSubjects()) { - subject.setNamespace(namespace); - } - } - }); - client.resourceList(resources) - .inNamespace(namespace) - .createOrReplace(); + resources.forEach( + hm -> { + hm.getMetadata().setNamespace(namespace); + if (hm.getKind().equalsIgnoreCase("rolebinding")) { + var crb = (RoleBinding) hm; + for (var subject : crb.getSubjects()) { + subject.setNamespace(namespace); + } + } + }); + client.resourceList(resources).inNamespace(namespace).createOrReplace(); } catch (FileNotFoundException e) { throw new RuntimeException(e); diff --git a/sample-operators/leader-election/pom.xml b/sample-operators/leader-election/pom.xml index 74aab104f1..5611726540 100644 --- a/sample-operators/leader-election/pom.xml +++ b/sample-operators/leader-election/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT sample-leader-election diff --git a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java index 1e54ddd915..5f8d0aa911 100644 --- a/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java +++ b/sample-operators/leader-election/src/main/java/io/javaoperatorsdk/operator/sample/LeaderElectionTestReconciler.java @@ -11,8 +11,7 @@ import io.javaoperatorsdk.operator.sample.v1.LeaderElectionStatus; @ControllerConfiguration() -public class LeaderElectionTestReconciler - implements Reconciler { +public class LeaderElectionTestReconciler implements Reconciler { private final String reconcilerName; @@ -22,8 +21,7 @@ public LeaderElectionTestReconciler(String reconcilerName) { @Override public UpdateControl reconcile( - LeaderElection resource, - Context context) { + LeaderElection resource, Context context) { if (resource.getStatus() == null) { resource.setStatus(new LeaderElectionStatus()); @@ -36,5 +34,4 @@ public UpdateControl reconcile( // update status is with optimistic locking return UpdateControl.patchStatus(resource).rescheduleAfter(Duration.ofSeconds(1)); } - } diff --git a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java index dd6f4bc244..60c4fb9dc1 100644 --- a/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java +++ b/sample-operators/leader-election/src/test/java/io/javaoperatorsdk/operator/sample/LeaderElectionE2E.java @@ -59,44 +59,70 @@ void otherInstancesTakesOverWhenSteppingDown(String yamlFilePrefix) { deployOperatorsInOrder(yamlFilePrefix); log.info("Awaiting custom resource reconciliations"); - await().pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) + await() + .pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) .atMost(Duration.ofSeconds(MAX_WAIT_SECONDS)) - .untilAsserted(() -> { - var actualStatus = client.resources(LeaderElection.class) - .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get().getStatus(); - - assertThat(actualStatus).isNotNull(); - assertThat(actualStatus.getReconciledBy()) - .hasSizeGreaterThan(MINIMAL_EXPECTED_RECONCILIATION); - }); + .untilAsserted( + () -> { + var actualStatus = + client + .resources(LeaderElection.class) + .inNamespace(namespace) + .withName(TEST_RESOURCE_NAME) + .get() + .getStatus(); + + assertThat(actualStatus).isNotNull(); + assertThat(actualStatus.getReconciledBy()) + .hasSizeGreaterThan(MINIMAL_EXPECTED_RECONCILIATION); + }); client.pods().inNamespace(namespace).withName(OPERATOR_1_POD_NAME).delete(); - var actualListSize = client.resources(LeaderElection.class) - .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get().getStatus().getReconciledBy() - .size(); + var actualListSize = + client + .resources(LeaderElection.class) + .inNamespace(namespace) + .withName(TEST_RESOURCE_NAME) + .get() + .getStatus() + .getReconciledBy() + .size(); - await().pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) + await() + .pollDelay(Duration.ofSeconds(MINIMAL_SECONDS_FOR_RENEWAL)) .atMost(Duration.ofSeconds(240)) - .untilAsserted(() -> { - var actualStatus = client.resources(LeaderElection.class) - .inNamespace(namespace).withName(TEST_RESOURCE_NAME).get().getStatus(); - - assertThat(actualStatus).isNotNull(); - assertThat(actualStatus.getReconciledBy()) - .hasSizeGreaterThan(actualListSize + MINIMAL_EXPECTED_RECONCILIATION); - }); + .untilAsserted( + () -> { + var actualStatus = + client + .resources(LeaderElection.class) + .inNamespace(namespace) + .withName(TEST_RESOURCE_NAME) + .get() + .getStatus(); + + assertThat(actualStatus).isNotNull(); + assertThat(actualStatus.getReconciledBy()) + .hasSizeGreaterThan(actualListSize + MINIMAL_EXPECTED_RECONCILIATION); + }); assertReconciliations( - client.resources(LeaderElection.class).inNamespace(namespace) - .withName(TEST_RESOURCE_NAME).get().getStatus().getReconciledBy()); + client + .resources(LeaderElection.class) + .inNamespace(namespace) + .withName(TEST_RESOURCE_NAME) + .get() + .getStatus() + .getReconciledBy()); } private void assertReconciliations(List reconciledBy) { log.info("Reconciled by content: {}", reconciledBy); - OptionalInt firstO2StatusIndex = IntStream.range(0, reconciledBy.size()) - .filter(i -> reconciledBy.get(i).equals(OPERATOR_2_POD_NAME)) - .findFirst(); + OptionalInt firstO2StatusIndex = + IntStream.range(0, reconciledBy.size()) + .filter(i -> reconciledBy.get(i).equals(OPERATOR_2_POD_NAME)) + .findFirst(); assertThat(firstO2StatusIndex).isPresent(); assertThat(reconciledBy.subList(0, firstO2StatusIndex.getAsInt() - 1)) .allMatch(s -> s.equals(OPERATOR_1_POD_NAME)); @@ -106,28 +132,33 @@ private void assertReconciliations(List reconciledBy) { private void applyCustomResource() { var res = new LeaderElection(); - res.setMetadata(new ObjectMetaBuilder() - .withName(TEST_RESOURCE_NAME) - .withNamespace(namespace) - .build()); + res.setMetadata( + new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).withNamespace(namespace).build()); client.resource(res).create(); } @BeforeEach void setup() { namespace = "leader-election-it-" + UUID.randomUUID(); - client = new KubernetesClientBuilder().withConfig(new ConfigBuilder() - .withNamespace(namespace) - .build()).build(); + client = + new KubernetesClientBuilder() + .withConfig(new ConfigBuilder().withNamespace(namespace).build()) + .build(); applyCRD(); - client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(namespace) - .endMetadata().build()).create(); + client + .namespaces() + .resource( + new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()) + .create(); } @AfterEach void tearDown() { - client.namespaces().resource(new NamespaceBuilder().withNewMetadata().withName(namespace) - .endMetadata().build()).delete(); + client + .namespaces() + .resource( + new NamespaceBuilder().withNewMetadata().withName(namespace).endMetadata().build()) + .delete(); await() .atMost(Duration.ofSeconds(60)) .untilAsserted(() -> assertThat(client.namespaces().withName(namespace).get()).isNull()); @@ -136,24 +167,29 @@ void tearDown() { private void deployOperatorsInOrder(String yamlFilePrefix) { log.info("Installing 1st instance"); applyResources("k8s/" + yamlFilePrefix + "operator.yaml"); - await().atMost(Duration.ofSeconds(POD_STARTUP_TIMEOUT)).untilAsserted(() -> { - var pod = client.pods().inNamespace(namespace).withName(OPERATOR_1_POD_NAME).get(); - assertThat(pod.getStatus().getContainerStatuses()).isNotEmpty(); - assertThat(pod.getStatus().getContainerStatuses().get(0).getReady()).isTrue(); - }); + await() + .atMost(Duration.ofSeconds(POD_STARTUP_TIMEOUT)) + .untilAsserted( + () -> { + var pod = client.pods().inNamespace(namespace).withName(OPERATOR_1_POD_NAME).get(); + assertThat(pod.getStatus().getContainerStatuses()).isNotEmpty(); + assertThat(pod.getStatus().getContainerStatuses().get(0).getReady()).isTrue(); + }); log.info("Installing 2nd instance"); applyResources("k8s/" + yamlFilePrefix + "operator-instance-2.yaml"); - await().atMost(Duration.ofSeconds(POD_STARTUP_TIMEOUT)).untilAsserted(() -> { - var pod = client.pods().inNamespace(namespace).withName(OPERATOR_2_POD_NAME).get(); - assertThat(pod.getStatus().getContainerStatuses()).isNotEmpty(); - assertThat(pod.getStatus().getContainerStatuses().get(0).getReady()).isTrue(); - }); + await() + .atMost(Duration.ofSeconds(POD_STARTUP_TIMEOUT)) + .untilAsserted( + () -> { + var pod = client.pods().inNamespace(namespace).withName(OPERATOR_2_POD_NAME).get(); + assertThat(pod.getStatus().getContainerStatuses()).isNotEmpty(); + assertThat(pod.getStatus().getContainerStatuses().get(0).getReady()).isTrue(); + }); } void applyCRD() { - String path = - "./src/main/resources/kubernetes/leaderelections.sample.javaoperatorsdk-v1.yml"; + String path = "./src/main/resources/kubernetes/leaderelections.sample.javaoperatorsdk-v1.yml"; try (InputStream is = new FileInputStream(path)) { final var crd = client.load(is); crd.createOrReplace(); @@ -167,18 +203,17 @@ void applyCRD() { void applyResources(String path) { try { List resources = client.load(new FileInputStream(path)).items(); - resources.forEach(hm -> { - hm.getMetadata().setNamespace(namespace); - if (hm.getKind().toLowerCase(Locale.ROOT).equals("clusterrolebinding")) { - var crb = (ClusterRoleBinding) hm; - for (var subject : crb.getSubjects()) { - subject.setNamespace(namespace); - } - } - }); - client.resourceList(resources) - .inNamespace(namespace) - .createOrReplace(); + resources.forEach( + hm -> { + hm.getMetadata().setNamespace(namespace); + if (hm.getKind().toLowerCase(Locale.ROOT).equals("clusterrolebinding")) { + var crb = (ClusterRoleBinding) hm; + for (var subject : crb.getSubjects()) { + subject.setNamespace(namespace); + } + } + }); + client.resourceList(resources).inNamespace(namespace).createOrReplace(); } catch (FileNotFoundException e) { throw new RuntimeException(e); diff --git a/sample-operators/mysql-schema/pom.xml b/sample-operators/mysql-schema/pom.xml index d0e5beab97..263a832d10 100644 --- a/sample-operators/mysql-schema/pom.xml +++ b/sample-operators/mysql-schema/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT sample-mysql-schema-operator @@ -42,7 +42,7 @@ mysql mysql-connector-java - 8.0.30 + 8.0.33 org.apache.logging.log4j diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLDbConfig.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLDbConfig.java index 0f63cc846a..6f409720fe 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLDbConfig.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLDbConfig.java @@ -17,11 +17,14 @@ public MySQLDbConfig(String host, String port, String user, String password) { } public static MySQLDbConfig loadFromEnvironmentVars() { - if (ObjectUtils.anyNull(System.getenv("MYSQL_HOST"), - System.getenv("MYSQL_USER"), System.getenv("MYSQL_PASSWORD"))) { + if (ObjectUtils.anyNull( + System.getenv("MYSQL_HOST"), + System.getenv("MYSQL_USER"), + System.getenv("MYSQL_PASSWORD"))) { throw new IllegalStateException("Mysql server parameters not defined"); } - return new MySQLDbConfig(System.getenv("MYSQL_HOST"), + return new MySQLDbConfig( + System.getenv("MYSQL_HOST"), System.getenv("MYSQL_PORT"), System.getenv("MYSQL_USER"), System.getenv("MYSQL_PASSWORD")); diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchema.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchema.java index 80eb25f8c7..adc6335c43 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchema.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchema.java @@ -7,5 +7,4 @@ @Group("mysql.sample.javaoperatorsdk") @Version("v1") -public class MySQLSchema extends CustomResource implements Namespaced { -} +public class MySQLSchema extends CustomResource implements Namespaced {} diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java index ce3595f0c3..c155f0ac6b 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperator.java @@ -23,17 +23,22 @@ public class MySQLSchemaOperator { public static void main(String[] args) throws IOException { log.info("MySQL Schema Operator starting"); - Operator operator = new Operator(overrider -> overrider - .withMetrics(MicrometerMetrics.withoutPerResourceMetrics(new LoggingMeterRegistry()))); + Operator operator = + new Operator( + overrider -> + overrider.withMetrics( + MicrometerMetrics.withoutPerResourceMetrics(new LoggingMeterRegistry()))); MySQLSchemaReconciler schemaReconciler = new MySQLSchemaReconciler(); // override the default configuration - operator.register(schemaReconciler, - configOverrider -> configOverrider.replacingNamedDependentResourceConfig( - SchemaDependentResource.NAME, - new ResourcePollerConfig(Duration.ofMillis(300), - MySQLDbConfig.loadFromEnvironmentVars()))); + operator.register( + schemaReconciler, + configOverrider -> + configOverrider.replacingNamedDependentResourceConfig( + SchemaDependentResource.NAME, + new ResourcePollerConfig( + Duration.ofMillis(300), MySQLDbConfig.loadFromEnvironmentVars()))); operator.start(); new FtBasic(new TkFork(new FkRegex("/health", "ALL GOOD!")), 8080).start(Exit.NEVER); diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java index 7e229ca4bd..38e94f4d8f 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/MySQLSchemaReconciler.java @@ -15,36 +15,44 @@ import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_USERNAME; import static java.lang.String.format; -@Workflow(dependents = { - @Dependent(type = SecretDependentResource.class, name = SecretDependentResource.NAME), - @Dependent(type = SchemaDependentResource.class, name = SchemaDependentResource.NAME, - dependsOn = SecretDependentResource.NAME) -}) +@Workflow( + dependents = { + @Dependent(type = SecretDependentResource.class, name = SecretDependentResource.NAME), + @Dependent( + type = SchemaDependentResource.class, + name = SchemaDependentResource.NAME, + dependsOn = SecretDependentResource.NAME) + }) @ControllerConfiguration -public class MySQLSchemaReconciler - implements Reconciler { +public class MySQLSchemaReconciler implements Reconciler { static final Logger log = LoggerFactory.getLogger(MySQLSchemaReconciler.class); - @Override public UpdateControl reconcile(MySQLSchema schema, Context context) { // we only need to update the status if we just built the schema, i.e. when it's present in the // context Secret secret = context.getSecondaryResource(Secret.class).orElseThrow(); - return context.getSecondaryResource(Schema.class, SchemaDependentResource.NAME).map(s -> { - var statusUpdateResource = createForStatusUpdate(schema, s, secret.getMetadata().getName(), - decode(secret.getData().get(MYSQL_SECRET_USERNAME))); - log.info("Schema {} created - updating CR status", s.getName()); - return UpdateControl.patchStatus(statusUpdateResource); - }).orElseGet(UpdateControl::noUpdate); + return context + .getSecondaryResource(Schema.class, SchemaDependentResource.NAME) + .map( + s -> { + var statusUpdateResource = + createForStatusUpdate( + schema, + s, + secret.getMetadata().getName(), + decode(secret.getData().get(MYSQL_SECRET_USERNAME))); + log.info("Schema {} created - updating CR status", s.getName()); + return UpdateControl.patchStatus(statusUpdateResource); + }) + .orElseGet(UpdateControl::noUpdate); } @Override - public ErrorStatusUpdateControl updateErrorStatus(MySQLSchema schema, - Context context, - Exception e) { + public ErrorStatusUpdateControl updateErrorStatus( + MySQLSchema schema, Context context, Exception e) { SchemaStatus status = new SchemaStatus(); status.setUrl(null); status.setUserName(null); @@ -54,20 +62,16 @@ public ErrorStatusUpdateControl updateErrorStatus(MySQLSchema schem return ErrorStatusUpdateControl.patchStatus(schema); } - - private MySQLSchema createForStatusUpdate(MySQLSchema mySQLSchema, Schema schema, - String secretName, - String userName) { + private MySQLSchema createForStatusUpdate( + MySQLSchema mySQLSchema, Schema schema, String secretName, String userName) { MySQLSchema res = new MySQLSchema(); - res.setMetadata(new ObjectMetaBuilder() - .withName(mySQLSchema.getMetadata().getName()) - .withNamespace(mySQLSchema.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(mySQLSchema.getMetadata().getName()) + .withNamespace(mySQLSchema.getMetadata().getNamespace()) + .build()); SchemaStatus status = new SchemaStatus(); - status.setUrl( - format( - "jdbc:mysql://%1$s/%2$s", - System.getenv("MYSQL_HOST"), schema.getName())); + status.setUrl(format("jdbc:mysql://%1$s/%2$s", System.getenv("MYSQL_HOST"), schema.getName())); status.setUserName(userName); status.setSecretName(secretName); status.setStatus("CREATED"); diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/ResourcePollerConfig.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/ResourcePollerConfig.java index 44de818f88..feaf326a8d 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/ResourcePollerConfig.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/ResourcePollerConfig.java @@ -9,7 +9,6 @@ public class ResourcePollerConfig { private final Duration pollPeriod; private final MySQLDbConfig mySQLDbConfig; - public ResourcePollerConfig(Duration pollPeriod, MySQLDbConfig mySQLDbConfig) { this.pollPeriod = pollPeriod; this.mySQLDbConfig = mySQLDbConfig; diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java index 60875e6ae3..ec2b03325c 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SchemaDependentResource.java @@ -32,15 +32,21 @@ import static io.javaoperatorsdk.operator.sample.dependent.SecretDependentResource.MYSQL_SECRET_USERNAME; import static java.lang.String.format; -@SchemaConfig(pollPeriod = 400, host = "127.0.0.1", +@SchemaConfig( + pollPeriod = 400, + host = "127.0.0.1", port = SchemaDependentResource.LOCAL_PORT, - user = "root", password = "password") // NOSONAR: password is only used locally, example only -@Configured(by = SchemaConfig.class, with = ResourcePollerConfig.class, + user = "root", + password = "password") // NOSONAR: password is only used locally, example only +@Configured( + by = SchemaConfig.class, + with = ResourcePollerConfig.class, converter = ResourcePollerConfigConverter.class) public class SchemaDependentResource extends PerResourcePollingDependentResource implements ConfiguredDependentResource, - Creator, Deleter { + Creator, + Deleter { public static final String NAME = "schema"; public static final int LOCAL_PORT = 3307; @@ -48,10 +54,6 @@ public class SchemaDependentResource private MySQLDbConfig dbConfig; - public SchemaDependentResource() { - super(Schema.class); - } - @Override public Optional configuration() { return Optional.of(new ResourcePollerConfig(getPollingPeriod(), dbConfig)); @@ -78,9 +80,7 @@ public Schema create(Schema target, MySQLSchema mySQLSchema, Context context) { try (Connection connection = getConnection()) { var userName = primary.getStatus() != null ? primary.getStatus().getUserName() : null; - SchemaService.deleteSchemaAndRelatedUser(connection, primary.getMetadata().getName(), - userName); + SchemaService.deleteSchemaAndRelatedUser( + connection, primary.getMetadata().getName(), userName); } catch (SQLException e) { throw new RuntimeException("Error while trying to delete Schema", e); } @@ -112,8 +111,10 @@ public static String decode(String value) { @Override public Set fetchResources(MySQLSchema primaryResource) { try (Connection connection = getConnection()) { - var schema = SchemaService.getSchema(connection, primaryResource.getMetadata().getName()) - .map(Set::of).orElseGet(Collections::emptySet); + var schema = + SchemaService.getSchema(connection, primaryResource.getMetadata().getName()) + .map(Set::of) + .orElseGet(Collections::emptySet); log.debug("Fetched schema: {}", schema); return schema; } catch (SQLException e) { @@ -121,19 +122,25 @@ public Set fetchResources(MySQLSchema primaryResource) { } } - static class ResourcePollerConfigConverter implements - ConfigurationConverter { + static class ResourcePollerConfigConverter + implements ConfigurationConverter { @Override - public ResourcePollerConfig configFrom(SchemaConfig configAnnotation, + public ResourcePollerConfig configFrom( + SchemaConfig configAnnotation, DependentResourceSpec spec, ControllerConfiguration parentConfiguration) { if (configAnnotation != null) { - return new ResourcePollerConfig(Duration.ofMillis(configAnnotation.pollPeriod()), - new MySQLDbConfig(configAnnotation.host(), String.valueOf(configAnnotation.port()), - configAnnotation.user(), configAnnotation.password())); + return new ResourcePollerConfig( + Duration.ofMillis(configAnnotation.pollPeriod()), + new MySQLDbConfig( + configAnnotation.host(), + String.valueOf(configAnnotation.port()), + configAnnotation.user(), + configAnnotation.password())); } - return new ResourcePollerConfig(Duration.ofMillis(SchemaConfig.DEFAULT_POLL_PERIOD), + return new ResourcePollerConfig( + Duration.ofMillis(SchemaConfig.DEFAULT_POLL_PERIOD), MySQLDbConfig.loadFromEnvironmentVars()); } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SecretDependentResource.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SecretDependentResource.java index e6cf2a45e7..cff28feadd 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SecretDependentResource.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/dependent/SecretDependentResource.java @@ -27,18 +27,15 @@ public class SecretDependentResource extends KubernetesDependentResource context) { - final var password = RandomStringUtils - .randomAlphanumeric(16); // NOSONAR: we don't need cryptographically-strong randomness here + final var password = + RandomStringUtils.randomAlphanumeric( + 16); // NOSONAR: we don't need cryptographically-strong randomness here final var name = schema.getMetadata().getName(); final var secretName = getSecretName(name); final var userName = String.format(USERNAME_FORMAT, name); @@ -63,12 +60,12 @@ public Result match(Secret actual, MySQLSchema primary, Context toPrimaryResourceIDs(Secret resource) { String name = resource.getMetadata().getName(); - return Set.of(new ResourceID(name.substring(0, name.length() - SECRET_SUFFIX.length()), - resource.getMetadata().getNamespace())); + return Set.of( + new ResourceID( + name.substring(0, name.length() - SECRET_SUFFIX.length()), + resource.getMetadata().getNamespace())); } - } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java index 87fb88e9a4..08fe3295b5 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/Schema.java @@ -23,10 +23,8 @@ public String getCharacterSet() { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; Schema schema = (Schema) o; return Objects.equals(name, schema.name); } @@ -38,9 +36,6 @@ public int hashCode() { @Override public String toString() { - return "Schema{" + - "name='" + name + '\'' + - ", characterSet='" + characterSet + '\'' + - '}'; + return "Schema{" + "name='" + name + '\'' + ", characterSet='" + characterSet + '\'' + '}'; } } diff --git a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/SchemaService.java b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/SchemaService.java index 84504a5fec..3690219902 100644 --- a/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/SchemaService.java +++ b/sample-operators/mysql-schema/src/main/java/io/javaoperatorsdk/operator/sample/schema/SchemaService.java @@ -12,7 +12,6 @@ public class SchemaService { - private static final Logger log = LoggerFactory.getLogger(SchemaService.class); private final MySQLDbConfig mySQLDbConfig; @@ -29,16 +28,12 @@ public Optional getSchema(String name) { } } - public static Schema createSchemaAndRelatedUser(Connection connection, String schemaName, - String encoding, - String userName, - String password) { + public static Schema createSchemaAndRelatedUser( + Connection connection, String schemaName, String encoding, String userName, String password) { try { try (Statement statement = connection.createStatement()) { statement.execute( - format( - "CREATE SCHEMA `%1$s` DEFAULT CHARACTER SET %2$s", - schemaName, encoding)); + format("CREATE SCHEMA `%1$s` DEFAULT CHARACTER SET %2$s", schemaName, encoding)); } if (!userExists(connection, userName)) { try (Statement statement = connection.createStatement()) { @@ -46,8 +41,7 @@ public static Schema createSchemaAndRelatedUser(Connection connection, String sc } } try (Statement statement = connection.createStatement()) { - statement.execute( - format("GRANT ALL ON `%1$s`.* TO '%2$s'", schemaName, userName)); + statement.execute(format("GRANT ALL ON `%1$s`.* TO '%2$s'", schemaName, userName)); } return new Schema(schemaName, encoding); @@ -56,8 +50,8 @@ public static Schema createSchemaAndRelatedUser(Connection connection, String sc } } - public static void deleteSchemaAndRelatedUser(Connection connection, String schemaName, - String userName) { + public static void deleteSchemaAndRelatedUser( + Connection connection, String schemaName, String userName) { try { if (schemaExists(connection, schemaName)) { try (Statement statement = connection.createStatement()) { @@ -80,8 +74,7 @@ public static void deleteSchemaAndRelatedUser(Connection connection, String sche private static boolean userExists(Connection connection, String username) { try (PreparedStatement ps = - connection.prepareStatement( - "SELECT 1 FROM mysql.user WHERE user = ?")) { + connection.prepareStatement("SELECT 1 FROM mysql.user WHERE user = ?")) { ps.setString(1, username); try (ResultSet resultSet = ps.executeQuery()) { return resultSet.next(); @@ -106,8 +99,10 @@ public static Optional getSchema(Connection connection, String schemaNam if (!exists) { return Optional.empty(); } else { - return Optional.of(new Schema(resultSet.getString("SCHEMA_NAME"), - resultSet.getString("DEFAULT_CHARACTER_SET_NAME"))); + return Optional.of( + new Schema( + resultSet.getString("SCHEMA_NAME"), + resultSet.getString("DEFAULT_CHARACTER_SET_NAME"))); } } } catch (SQLException e) { @@ -121,11 +116,10 @@ private Connection getConnection() { format("jdbc:mysql://%1$s:%2$s", mySQLDbConfig.getHost(), mySQLDbConfig.getPort()); log.debug("Connecting to '{}' with user '{}'", connectionString, mySQLDbConfig.getUser()); - return DriverManager.getConnection(connectionString, mySQLDbConfig.getUser(), - mySQLDbConfig.getPassword()); + return DriverManager.getConnection( + connectionString, mySQLDbConfig.getUser(), mySQLDbConfig.getPassword()); } catch (SQLException e) { throw new IllegalStateException(e); } } - } diff --git a/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java b/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java index 346ebcb9ef..92339d0e2c 100644 --- a/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java +++ b/sample-operators/mysql-schema/src/test/java/io/javaoperatorsdk/operator/sample/MySQLSchemaOperatorE2E.java @@ -36,7 +36,7 @@ class MySQLSchemaOperatorE2E { static final String MY_SQL_NS = "mysql"; - private final static List infrastructure = new ArrayList<>(); + private static final List infrastructure = new ArrayList<>(); public static final String TEST_RESOURCE_NAME = "mydb1"; static { @@ -61,7 +61,7 @@ boolean isLocal() { isLocal() ? LocallyRunOperatorExtension.builder() .withReconciler(new MySQLSchemaReconciler()) // configuration for schema comes from - // SchemaDependentResource annotation + // SchemaDependentResource annotation .withInfrastructure(infrastructure) .withPortForward(MY_SQL_NS, "app", "mysql", 3306, SchemaDependentResource.LOCAL_PORT) .build() @@ -77,7 +77,9 @@ void test() { MySQLSchema testSchema = new MySQLSchema(); testSchema.setMetadata( - new ObjectMetaBuilder().withName(TEST_RESOURCE_NAME).withNamespace(operator.getNamespace()) + new ObjectMetaBuilder() + .withName(TEST_RESOURCE_NAME) + .withNamespace(operator.getNamespace()) .build()); testSchema.setSpec(new SchemaSpec()); testSchema.getSpec().setEncoding("utf8"); @@ -103,8 +105,11 @@ void test() { assertThat(updatedSchema.getStatus().getUserName(), is(notNullValue())); }); - client.resources(MySQLSchema.class).inNamespace(operator.getNamespace()) - .withName(testSchema.getMetadata().getName()).delete(); + client + .resources(MySQLSchema.class) + .inNamespace(operator.getNamespace()) + .withName(testSchema.getMetadata().getName()) + .delete(); await() .atMost(2, MINUTES) diff --git a/sample-operators/pom.xml b/sample-operators/pom.xml index 450a6bc153..d89c0677d6 100644 --- a/sample-operators/pom.xml +++ b/sample-operators/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk java-operator-sdk - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT sample-operators diff --git a/sample-operators/tomcat-operator/pom.xml b/sample-operators/tomcat-operator/pom.xml index 314e0ef96c..0bc6f86eda 100644 --- a/sample-operators/tomcat-operator/pom.xml +++ b/sample-operators/tomcat-operator/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT sample-tomcat-operator diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/DeploymentDependentResource.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/DeploymentDependentResource.java index d6eaad24bb..c7f25e996c 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/DeploymentDependentResource.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/DeploymentDependentResource.java @@ -9,15 +9,11 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -@KubernetesDependent(informer = @Informer( - labelSelector = "app.kubernetes.io/managed-by=tomcat-operator")) +@KubernetesDependent( + informer = @Informer(labelSelector = "app.kubernetes.io/managed-by=tomcat-operator")) public class DeploymentDependentResource extends CRUDKubernetesDependentResource { - public DeploymentDependentResource() { - super(Deployment.class); - } - private static String tomcatImage(Tomcat tomcat) { return "tomcat:" + tomcat.getSpec().getVersion(); } @@ -28,28 +24,35 @@ protected Deployment desired(Tomcat tomcat, Context context) { ReconcilerUtils.loadYaml(Deployment.class, getClass(), "deployment.yaml"); final ObjectMeta tomcatMetadata = tomcat.getMetadata(); final String tomcatName = tomcatMetadata.getName(); - deployment = new DeploymentBuilder(deployment) - .editMetadata() - .withName(tomcatName) - .withNamespace(tomcatMetadata.getNamespace()) - .addToLabels("app", tomcatName) - .addToLabels("app.kubernetes.io/part-of", tomcatName) - .addToLabels("app.kubernetes.io/managed-by", "tomcat-operator") - .endMetadata() - .editSpec() - .editSelector().addToMatchLabels("app", tomcatName).endSelector() - .withReplicas(tomcat.getSpec().getReplicas()) - // set tomcat version - .editTemplate() - // make sure label selector matches label (which has to be matched by service selector - // too) - .editMetadata().addToLabels("app", tomcatName).endMetadata() - .editSpec() - .editFirstContainer().withImage(tomcatImage(tomcat)).endContainer() - .endSpec() - .endTemplate() - .endSpec() - .build(); + deployment = + new DeploymentBuilder(deployment) + .editMetadata() + .withName(tomcatName) + .withNamespace(tomcatMetadata.getNamespace()) + .addToLabels("app", tomcatName) + .addToLabels("app.kubernetes.io/part-of", tomcatName) + .addToLabels("app.kubernetes.io/managed-by", "tomcat-operator") + .endMetadata() + .editSpec() + .editSelector() + .addToMatchLabels("app", tomcatName) + .endSelector() + .withReplicas(tomcat.getSpec().getReplicas()) + // set tomcat version + .editTemplate() + // make sure label selector matches label (which has to be matched by service selector + // too) + .editMetadata() + .addToLabels("app", tomcatName) + .endMetadata() + .editSpec() + .editFirstContainer() + .withImage(tomcatImage(tomcat)) + .endContainer() + .endSpec() + .endTemplate() + .endSpec() + .build(); return deployment; } } diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/ServiceDependentResource.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/ServiceDependentResource.java index 8a93b48804..bb0359458e 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/ServiceDependentResource.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/ServiceDependentResource.java @@ -9,14 +9,10 @@ import io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource; import io.javaoperatorsdk.operator.processing.dependent.kubernetes.KubernetesDependent; -@KubernetesDependent(informer = @Informer( - labelSelector = "app.kubernetes.io/managed-by=tomcat-operator")) +@KubernetesDependent( + informer = @Informer(labelSelector = "app.kubernetes.io/managed-by=tomcat-operator")) public class ServiceDependentResource extends CRUDKubernetesDependentResource { - public ServiceDependentResource() { - super(Service.class); - } - @Override protected Service desired(Tomcat tomcat, Context context) { final ObjectMeta tomcatMetadata = tomcat.getMetadata(); @@ -31,5 +27,4 @@ protected Service desired(Tomcat tomcat, Context context) { .endSpec() .build(); } - } diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java index f89a5f22e0..5cef5ea25c 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/TomcatReconciler.java @@ -15,10 +15,11 @@ * Runs a specified number of Tomcat app server Pods. It uses a Deployment to create the Pods. Also * creates a Service over which the Pods can be accessed. */ -@Workflow(dependents = { - @Dependent(type = DeploymentDependentResource.class), - @Dependent(type = ServiceDependentResource.class) -}) +@Workflow( + dependents = { + @Dependent(type = DeploymentDependentResource.class), + @Dependent(type = ServiceDependentResource.class) + }) @ControllerConfiguration public class TomcatReconciler implements Reconciler { @@ -26,23 +27,28 @@ public class TomcatReconciler implements Reconciler { @Override public UpdateControl reconcile(Tomcat tomcat, Context context) { - return context.getSecondaryResource(Deployment.class).map(deployment -> { - Tomcat updatedTomcat = createTomcatForStatusUpdate(tomcat, deployment); - log.info( - "Updating status of Tomcat {} in namespace {} to {} ready replicas", - tomcat.getMetadata().getName(), - tomcat.getMetadata().getNamespace(), - tomcat.getStatus() == null ? 0 : tomcat.getStatus().getReadyReplicas()); - return UpdateControl.patchStatus(updatedTomcat); - }).orElseGet(UpdateControl::noUpdate); + return context + .getSecondaryResource(Deployment.class) + .map( + deployment -> { + Tomcat updatedTomcat = createTomcatForStatusUpdate(tomcat, deployment); + log.info( + "Updating status of Tomcat {} in namespace {} to {} ready replicas", + tomcat.getMetadata().getName(), + tomcat.getMetadata().getNamespace(), + tomcat.getStatus() == null ? 0 : tomcat.getStatus().getReadyReplicas()); + return UpdateControl.patchStatus(updatedTomcat); + }) + .orElseGet(UpdateControl::noUpdate); } private Tomcat createTomcatForStatusUpdate(Tomcat tomcat, Deployment deployment) { Tomcat res = new Tomcat(); - res.setMetadata(new ObjectMetaBuilder() - .withName(tomcat.getMetadata().getName()) - .withNamespace(tomcat.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(tomcat.getMetadata().getName()) + .withNamespace(tomcat.getMetadata().getNamespace()) + .build()); DeploymentStatus deploymentStatus = Objects.requireNonNullElse(deployment.getStatus(), new DeploymentStatus()); int readyReplicas = Objects.requireNonNullElse(deploymentStatus.getReadyReplicas(), 0); diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java index d61f8791f7..2d5ce3f925 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/Webapp.java @@ -8,9 +8,7 @@ import io.fabric8.kubernetes.model.annotation.Group; import io.fabric8.kubernetes.model.annotation.Version; -/** - * Represents a web application deployed in a Tomcat deployment - */ +/** Represents a web application deployed in a Tomcat deployment */ @Group("tomcatoperator.io") @Version("v1") public class Webapp extends CustomResource implements Namespaced { diff --git a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java index 82d0152b3c..0a26aece2e 100644 --- a/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java +++ b/sample-operators/tomcat-operator/src/main/java/io/javaoperatorsdk/operator/sample/WebappReconciler.java @@ -33,8 +33,7 @@ import io.javaoperatorsdk.operator.processing.event.source.informer.InformerEventSource; @ControllerConfiguration -public class WebappReconciler - implements Reconciler, Cleaner { +public class WebappReconciler implements Reconciler, Cleaner { private static final Logger log = LoggerFactory.getLogger(WebappReconciler.class); @@ -53,17 +52,21 @@ public List> prepareEventSources(EventSourceContext webappsMatchingTomcatName = - (Tomcat t) -> context.getPrimaryCache() - .list(webApp -> webApp.getSpec().getTomcat().equals(t.getMetadata().getName())) - .map(ResourceID::fromResource) - .collect(Collectors.toSet()); + (Tomcat t) -> + context + .getPrimaryCache() + .list(webApp -> webApp.getSpec().getTomcat().equals(t.getMetadata().getName())) + .map(ResourceID::fromResource) + .collect(Collectors.toSet()); InformerEventSourceConfiguration configuration = InformerEventSourceConfiguration.from(Tomcat.class, Webapp.class) .withSecondaryToPrimaryMapper(webappsMatchingTomcatName) .withPrimaryToSecondaryMapper( - (Webapp primary) -> Set.of(new ResourceID(primary.getSpec().getTomcat(), - primary.getMetadata().getNamespace()))) + (Webapp primary) -> + Set.of( + new ResourceID( + primary.getSpec().getTomcat(), primary.getMetadata().getNamespace()))) .build(); return List.of(new InformerEventSource<>(configuration, context)); } @@ -79,29 +82,49 @@ public UpdateControl reconcile(Webapp webapp, Context context) { return UpdateControl.noUpdate(); } - Tomcat tomcat = context.getSecondaryResource(Tomcat.class) - .orElseThrow( - () -> new IllegalStateException("Cannot find Tomcat " + webapp.getSpec().getTomcat() - + " for Webapp " + webapp.getMetadata().getName() + " in namespace " - + webapp.getMetadata().getNamespace())); + Tomcat tomcat = + context + .getSecondaryResource(Tomcat.class) + .orElseThrow( + () -> + new IllegalStateException( + "Cannot find Tomcat " + + webapp.getSpec().getTomcat() + + " for Webapp " + + webapp.getMetadata().getName() + + " in namespace " + + webapp.getMetadata().getNamespace())); if (tomcat.getStatus() != null && Objects.equals(tomcat.getSpec().getReplicas(), tomcat.getStatus().getReadyReplicas())) { log.info( "Tomcat is ready and webapps not yet deployed. Commencing deployment of {} in Tomcat {}", - webapp.getMetadata().getName(), tomcat.getMetadata().getName()); - String[] command = new String[] {"wget", "-O", - "/data/" + webapp.getSpec().getContextPath() + ".war", webapp.getSpec().getUrl()}; + webapp.getMetadata().getName(), + tomcat.getMetadata().getName()); + String[] command = + new String[] { + "wget", + "-O", + "/data/" + webapp.getSpec().getContextPath() + ".war", + webapp.getSpec().getUrl() + }; if (log.isInfoEnabled()) { - command = new String[] {"time", "wget", "-O", - "/data/" + webapp.getSpec().getContextPath() + ".war", webapp.getSpec().getUrl()}; + command = + new String[] { + "time", + "wget", + "-O", + "/data/" + webapp.getSpec().getContextPath() + ".war", + webapp.getSpec().getUrl() + }; } String[] commandStatusInAllPods = executeCommandInAllPods(kubernetesClient, webapp, command); return UpdateControl.patchStatus(createWebAppForStatusUpdate(webapp, commandStatusInAllPods)); } else { - log.info("WebappController invoked but Tomcat not ready yet ({}/{})", + log.info( + "WebappController invoked but Tomcat not ready yet ({}/{})", tomcat.getStatus() != null ? tomcat.getStatus().getReadyReplicas() : 0, tomcat.getSpec().getReplicas()); return UpdateControl.noUpdate(); @@ -110,10 +133,11 @@ public UpdateControl reconcile(Webapp webapp, Context context) { private Webapp createWebAppForStatusUpdate(Webapp actual, String[] commandStatusInAllPods) { var webapp = new Webapp(); - webapp.setMetadata(new ObjectMetaBuilder() - .withName(actual.getMetadata().getName()) - .withNamespace(actual.getMetadata().getNamespace()) - .build()); + webapp.setMetadata( + new ObjectMetaBuilder() + .withName(actual.getMetadata().getName()) + .withNamespace(actual.getMetadata().getNamespace()) + .build()); webapp.setStatus(new WebappStatus()); webapp.getStatus().setDeployedArtifact(actual.getSpec().getUrl()); webapp.getStatus().setDeploymentStatus(commandStatusInAllPods); @@ -166,8 +190,7 @@ private String[] executeCommandInAllPods( } catch (ExecutionException e) { status[i] = pod.getMetadata().getName() + ": ExecutionException - " + e.getMessage(); } catch (InterruptedException e) { - status[i] = - pod.getMetadata().getName() + ": InterruptedException - " + e.getMessage(); + status[i] = pod.getMetadata().getName() + ": InterruptedException - " + e.getMessage(); } catch (TimeoutException e) { status[i] = pod.getMetadata().getName() + ": TimeoutException - " + e.getMessage(); } @@ -178,7 +201,8 @@ private String[] executeCommandInAllPods( private ExecWatch execCmd(Pod pod, CompletableFuture data, String... command) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); - return kubernetesClient.pods() + return kubernetesClient + .pods() .inNamespace(pod.getMetadata().getNamespace()) .withName(pod.getMetadata().getName()) .inContainer("war-downloader") @@ -216,5 +240,4 @@ public void onClose(int code, String reason) { data.complete(baos.toString()); } } - } diff --git a/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java b/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java index 3095e7db8c..a38c705898 100644 --- a/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java +++ b/sample-operators/tomcat-operator/src/test/java/io/javaoperatorsdk/operator/sample/TomcatOperatorE2E.java @@ -26,13 +26,13 @@ class TomcatOperatorE2E { - final static Logger log = LoggerFactory.getLogger(TomcatOperatorE2E.class); + static final Logger log = LoggerFactory.getLogger(TomcatOperatorE2E.class); - final static KubernetesClient client = new DefaultKubernetesClient(); + static final KubernetesClient client = new DefaultKubernetesClient(); public TomcatOperatorE2E() throws FileNotFoundException {} - final static int tomcatReplicas = 2; + static final int tomcatReplicas = 2; boolean isLocal() { String deployment = System.getProperty("test.deployment"); @@ -42,23 +42,25 @@ boolean isLocal() { } @RegisterExtension - AbstractOperatorExtension operator = isLocal() ? LocallyRunOperatorExtension.builder() - .waitForNamespaceDeletion(false) - .withReconciler(new TomcatReconciler()) - .withReconciler(new WebappReconciler(client)) - .build() - : ClusterDeployedOperatorExtension.builder() - .waitForNamespaceDeletion(false) - .withOperatorDeployment( - client.load(new FileInputStream("k8s/operator.yaml")).items()) - .build(); + AbstractOperatorExtension operator = + isLocal() + ? LocallyRunOperatorExtension.builder() + .waitForNamespaceDeletion(false) + .withReconciler(new TomcatReconciler()) + .withReconciler(new WebappReconciler(client)) + .build() + : ClusterDeployedOperatorExtension.builder() + .waitForNamespaceDeletion(false) + .withOperatorDeployment(client.load(new FileInputStream("k8s/operator.yaml")).items()) + .build(); Tomcat getTomcat() { Tomcat tomcat = new Tomcat(); - tomcat.setMetadata(new ObjectMetaBuilder() - .withName("test-tomcat1") - .withNamespace(operator.getNamespace()) - .build()); + tomcat.setMetadata( + new ObjectMetaBuilder() + .withName("test-tomcat1") + .withNamespace(operator.getNamespace()) + .build()); tomcat.setSpec(new TomcatSpec()); tomcat.getSpec().setReplicas(tomcatReplicas); tomcat.getSpec().setVersion(9); @@ -67,10 +69,11 @@ Tomcat getTomcat() { Webapp getWebapp() { Webapp webapp1 = new Webapp(); - webapp1.setMetadata(new ObjectMetaBuilder() - .withName("test-webapp1") - .withNamespace(operator.getNamespace()) - .build()); + webapp1.setMetadata( + new ObjectMetaBuilder() + .withName("test-webapp1") + .withNamespace(operator.getNamespace()) + .build()); webapp1.setSpec(new WebappSpec()); webapp1.getSpec().setContextPath("webapp1"); webapp1.getSpec().setTomcat(getTomcat().getMetadata().getName()); @@ -91,37 +94,46 @@ void test() { webappClient.inNamespace(operator.getNamespace()).resource(webapp1).create(); log.info("Waiting 5 minutes for Tomcat and Webapp CR statuses to be updated"); - await().atMost(5, MINUTES).untilAsserted(() -> { - Tomcat updatedTomcat = - tomcatClient.inNamespace(operator.getNamespace()).withName(tomcat.getMetadata().getName()) - .get(); - Webapp updatedWebapp = - webappClient.inNamespace(operator.getNamespace()) - .withName(webapp1.getMetadata().getName()).get(); - assertThat(updatedTomcat.getStatus(), is(notNullValue())); - assertThat(updatedTomcat.getStatus().getReadyReplicas(), equalTo(tomcatReplicas)); - assertThat(updatedWebapp.getStatus(), is(notNullValue())); - assertThat(updatedWebapp.getStatus().getDeployedArtifact(), is(notNullValue())); - }); + await() + .atMost(5, MINUTES) + .untilAsserted( + () -> { + Tomcat updatedTomcat = + tomcatClient + .inNamespace(operator.getNamespace()) + .withName(tomcat.getMetadata().getName()) + .get(); + Webapp updatedWebapp = + webappClient + .inNamespace(operator.getNamespace()) + .withName(webapp1.getMetadata().getName()) + .get(); + assertThat(updatedTomcat.getStatus(), is(notNullValue())); + assertThat(updatedTomcat.getStatus().getReadyReplicas(), equalTo(tomcatReplicas)); + assertThat(updatedWebapp.getStatus(), is(notNullValue())); + assertThat(updatedWebapp.getStatus().getDeployedArtifact(), is(notNullValue())); + }); String url = "https://" + tomcat.getMetadata().getName() + "/" + webapp1.getSpec().getContextPath() + "/"; var inClusterCurl = new InClusterCurl(client, operator.getNamespace()); log.info("Starting curl Pod and waiting 5 minutes for GET of {} to return 200", url); - await("wait-for-webapp").atMost(6, MINUTES).untilAsserted(() -> { - try { - var curlOutput = inClusterCurl.checkUrl(url); - assertThat(curlOutput, equalTo("200")); - } catch (KubernetesClientException ex) { - throw new AssertionError(ex); - } - }); + await("wait-for-webapp") + .atMost(6, MINUTES) + .untilAsserted( + () -> { + try { + var curlOutput = inClusterCurl.checkUrl(url); + assertThat(curlOutput, equalTo("200")); + } catch (KubernetesClientException ex) { + throw new AssertionError(ex); + } + }); log.info("Deleting test Tomcat object: {}", tomcat); tomcatClient.inNamespace(operator.getNamespace()).resource(tomcat).delete(); log.info("Deleting test Webapp object: {}", webapp1); webappClient.inNamespace(operator.getNamespace()).resource(webapp1).delete(); } - } diff --git a/sample-operators/webpage/pom.xml b/sample-operators/webpage/pom.xml index 2266303adc..d2bfacc70e 100644 --- a/sample-operators/webpage/pom.xml +++ b/sample-operators/webpage/pom.xml @@ -5,7 +5,7 @@ io.javaoperatorsdk sample-operators - 5.0.4-SNAPSHOT + 5.1.1-SNAPSHOT sample-webpage-operator diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/Utils.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/Utils.java index 72d04b42ed..6dc83aff08 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/Utils.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/Utils.java @@ -14,10 +14,11 @@ private Utils() {} public static WebPage createWebPageForStatusUpdate(WebPage webPage, String configMapName) { WebPage res = new WebPage(); - res.setMetadata(new ObjectMetaBuilder() - .withName(webPage.getMetadata().getName()) - .withNamespace(webPage.getMetadata().getNamespace()) - .build()); + res.setMetadata( + new ObjectMetaBuilder() + .withName(webPage.getMetadata().getName()) + .withNamespace(webPage.getMetadata().getNamespace()) + .build()); res.setStatus(createStatus(configMapName)); return res; } @@ -72,8 +73,16 @@ public static Ingress makeDesiredIngress(WebPage webPage) { Ingress ingress = loadYaml(Ingress.class, Utils.class, "ingress.yaml"); ingress.getMetadata().setName(webPage.getMetadata().getName()); ingress.getMetadata().setNamespace(webPage.getMetadata().getNamespace()); - ingress.getSpec().getRules().get(0).getHttp().getPaths().get(0) - .getBackend().getService().setName(serviceName(webPage)); + ingress + .getSpec() + .getRules() + .get(0) + .getHttp() + .getPaths() + .get(0) + .getBackend() + .getService() + .setName(serviceName(webPage)); return ingress; } } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java index 6adbeb03b5..e6f0730ddb 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageDependentsWorkflowReconciler.java @@ -21,15 +21,13 @@ import static io.javaoperatorsdk.operator.sample.Utils.*; -/** - * Shows how to implement reconciler using standalone dependent resources. - */ +/** Shows how to implement reconciler using standalone dependent resources. */ @ControllerConfiguration( - informer = @Informer( - labelSelector = WebPageDependentsWorkflowReconciler.DEPENDENT_RESOURCE_LABEL_SELECTOR)) + informer = + @Informer( + labelSelector = WebPageDependentsWorkflowReconciler.DEPENDENT_RESOURCE_LABEL_SELECTOR)) @SuppressWarnings("unused") -public class WebPageDependentsWorkflowReconciler - implements Reconciler { +public class WebPageDependentsWorkflowReconciler implements Reconciler { public static final String DEPENDENT_RESOURCE_LABEL_SELECTOR = "!low-level"; @@ -42,20 +40,20 @@ public class WebPageDependentsWorkflowReconciler public WebPageDependentsWorkflowReconciler(KubernetesClient kubernetesClient) { initDependentResources(kubernetesClient); - workflow = new WorkflowBuilder() - .addDependentResource(configMapDR) - .addDependentResource(deploymentDR) - .addDependentResource(serviceDR) - .addDependentResourceAndConfigure(ingressDR) - .withReconcilePrecondition(new ExposedIngressCondition()) - .build(); + workflow = + new WorkflowBuilder() + .addDependentResource(configMapDR) + .addDependentResource(deploymentDR) + .addDependentResource(serviceDR) + .addDependentResourceAndConfigure(ingressDR) + .withReconcilePrecondition(new ExposedIngressCondition()) + .build(); } @Override public List> prepareEventSources(EventSourceContext context) { - return EventSourceUtils.dependentEventSources(context, configMapDR, - deploymentDR, serviceDR, - ingressDR); + return EventSourceUtils.dependentEventSources( + context, configMapDR, deploymentDR, serviceDR, ingressDR); } @Override @@ -65,10 +63,10 @@ public UpdateControl reconcile(WebPage webPage, Context contex workflow.reconcile(webPage, context); - return UpdateControl - .patchStatus( - createWebPageForStatusUpdate(webPage, context.getSecondaryResource(ConfigMap.class) - .orElseThrow().getMetadata().getName())); + return UpdateControl.patchStatus( + createWebPageForStatusUpdate( + webPage, + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName())); } @Override @@ -85,11 +83,14 @@ private void initDependentResources(KubernetesClient client) { this.ingressDR = new IngressDependentResource(); Arrays.asList(configMapDR, deploymentDR, serviceDR, ingressDR) - .forEach(dr -> dr.configureWith(new KubernetesDependentResourceConfigBuilder() - .withKubernetesDependentInformerConfig(InformerConfiguration.builder(dr.resourceType()) - .withLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR) - .build()) - .build())); + .forEach( + dr -> + dr.configureWith( + new KubernetesDependentResourceConfigBuilder() + .withKubernetesDependentInformerConfig( + InformerConfiguration.builder(dr.resourceType()) + .withLabelSelector(DEPENDENT_RESOURCE_LABEL_SELECTOR) + .build()) + .build())); } - } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java index e59f7fe0fc..558914c861 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageManagedDependentsReconciler.java @@ -8,24 +8,23 @@ import static io.javaoperatorsdk.operator.sample.Utils.*; -/** - * Shows how to implement a reconciler with managed dependent resources. - */ -@Workflow(dependents = { - @Dependent(type = ConfigMapDependentResource.class), - @Dependent(type = DeploymentDependentResource.class), - @Dependent(type = ServiceDependentResource.class), - @Dependent(type = IngressDependentResource.class, - reconcilePrecondition = ExposedIngressCondition.class) -}) -public class WebPageManagedDependentsReconciler - implements Reconciler, Cleaner { +/** Shows how to implement a reconciler with managed dependent resources. */ +@Workflow( + dependents = { + @Dependent(type = ConfigMapDependentResource.class), + @Dependent(type = DeploymentDependentResource.class), + @Dependent(type = ServiceDependentResource.class), + @Dependent( + type = IngressDependentResource.class, + reconcilePrecondition = ExposedIngressCondition.class) + }) +public class WebPageManagedDependentsReconciler implements Reconciler, Cleaner { public static final String SELECTOR = "managed"; @Override - public ErrorStatusUpdateControl updateErrorStatus(WebPage resource, - Context context, Exception e) { + public ErrorStatusUpdateControl updateErrorStatus( + WebPage resource, Context context, Exception e) { return handleError(resource, e); } @@ -34,8 +33,8 @@ public UpdateControl reconcile(WebPage webPage, Context contex throws Exception { simulateErrorIfRequested(webPage); - final var name = context.getSecondaryResource(ConfigMap.class).orElseThrow() - .getMetadata().getName(); + final var name = + context.getSecondaryResource(ConfigMap.class).orElseThrow().getMetadata().getName(); return UpdateControl.patchStatus(createWebPageForStatusUpdate(webPage, name)); } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java index ff80cc5901..1885e2d3b3 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageOperator.java @@ -18,7 +18,6 @@ public class WebPageOperator { public static final String WEBPAGE_MANAGED_DEPENDENT_RESOURCE_ENV_VALUE = "managed"; private static final Logger log = LoggerFactory.getLogger(WebPageOperator.class); - /** * Based on env variables a different flavor of Reconciler is used, showcasing how the same logic * can be implemented using the low level and higher level APIs. @@ -30,8 +29,7 @@ public static void main(String[] args) throws IOException { String reconcilerEnvVar = System.getenv(WEBPAGE_RECONCILER_ENV); if (WEBPAGE_CLASSIC_RECONCILER_ENV_VALUE.equals(reconcilerEnvVar)) { operator.register(new WebPageReconciler()); - } else if (WEBPAGE_MANAGED_DEPENDENT_RESOURCE_ENV_VALUE - .equals(reconcilerEnvVar)) { + } else if (WEBPAGE_MANAGED_DEPENDENT_RESOURCE_ENV_VALUE.equals(reconcilerEnvVar)) { operator.register(new WebPageManagedDependentsReconciler()); } else { operator.register(new WebPageStandaloneDependentsReconciler()); diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java index a687929b22..bdeef954a2 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageReconciler.java @@ -27,16 +27,13 @@ /** Shows how to implement reconciler using the low level api directly. */ @RateLimited(maxReconciliations = 2, within = 3) @ControllerConfiguration -public class WebPageReconciler - implements Reconciler { +public class WebPageReconciler implements Reconciler { public static final String INDEX_HTML = "index.html"; private static final Logger log = LoggerFactory.getLogger(WebPageReconciler.class); - public WebPageReconciler() { - - } + public WebPageReconciler() {} @Override public List> prepareEventSources(EventSourceContext context) { @@ -64,8 +61,8 @@ public List> prepareEventSources(EventSourceContext reconcile(WebPage webPage, Context contex String configMapName = configMapName(webPage); String deploymentName = deploymentName(webPage); - ConfigMap desiredHtmlConfigMap = makeDesiredHtmlConfigMap(ns, configMapName, webPage); Deployment desiredDeployment = makeDesiredDeployment(webPage, deploymentName, ns, configMapName); @@ -94,7 +90,11 @@ public UpdateControl reconcile(WebPage webPage, Context contex "Creating or updating ConfigMap {} in {}", desiredHtmlConfigMap.getMetadata().getName(), ns); - context.getClient().configMaps().inNamespace(ns).resource(desiredHtmlConfigMap) + context + .getClient() + .configMaps() + .inNamespace(ns) + .resource(desiredHtmlConfigMap) .serverSideApply(); } @@ -104,7 +104,12 @@ public UpdateControl reconcile(WebPage webPage, Context contex "Creating or updating Deployment {} in {}", desiredDeployment.getMetadata().getName(), ns); - context.getClient().apps().deployments().inNamespace(ns).resource(desiredDeployment) + context + .getClient() + .apps() + .deployments() + .inNamespace(ns) + .resource(desiredDeployment) .serverSideApply(); } @@ -114,8 +119,7 @@ public UpdateControl reconcile(WebPage webPage, Context contex "Creating or updating Deployment {} in {}", desiredDeployment.getMetadata().getName(), ns); - context.getClient().services().inNamespace(ns).resource(desiredService) - .serverSideApply(); + context.getClient().services().inNamespace(ns).resource(desiredService).serverSideApply(); } var existingIngress = context.getSecondaryResource(Ingress.class); @@ -124,16 +128,15 @@ public UpdateControl reconcile(WebPage webPage, Context contex if (existingIngress.isEmpty() || !match(desiredIngress, existingIngress.get())) { context.getClient().resource(desiredIngress).inNamespace(ns).serverSideApply(); } - } else - existingIngress.ifPresent( - ingress -> context.getClient().resource(ingress).delete()); + } else existingIngress.ifPresent(ingress -> context.getClient().resource(ingress).delete()); // not that this is not necessary, eventually mounted config map would be updated, just this way // is much faster; what is handy for demo purposes. // https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/#mounted-configmaps-are-updated-automatically - if (previousConfigMap != null && !StringUtils.equals( - previousConfigMap.getData().get(INDEX_HTML), - desiredHtmlConfigMap.getData().get(INDEX_HTML))) { + if (previousConfigMap != null + && !StringUtils.equals( + previousConfigMap.getData().get(INDEX_HTML), + desiredHtmlConfigMap.getData().get(INDEX_HTML))) { log.info("Restarting pods because HTML has changed in {}", ns); context.getClient().pods().inNamespace(ns).withLabel("app", deploymentName(webPage)).delete(); } @@ -144,11 +147,27 @@ public UpdateControl reconcile(WebPage webPage, Context contex private boolean match(Ingress desiredIngress, Ingress existingIngress) { String desiredServiceName = - desiredIngress.getSpec().getRules().get(0).getHttp().getPaths().get(0) - .getBackend().getService().getName(); + desiredIngress + .getSpec() + .getRules() + .get(0) + .getHttp() + .getPaths() + .get(0) + .getBackend() + .getService() + .getName(); String existingServiceName = - existingIngress.getSpec().getRules().get(0).getHttp().getPaths().get(0) - .getBackend().getService().getName(); + existingIngress + .getSpec() + .getRules() + .get(0) + .getHttp() + .getPaths() + .get(0) + .getBackend() + .getService() + .getName(); return Objects.equals(desiredServiceName, existingServiceName); } @@ -156,8 +175,14 @@ private boolean match(Deployment desiredDeployment, Deployment deployment) { if (deployment == null) { return false; } else { - return desiredDeployment.getSpec().getReplicas().equals(deployment.getSpec().getReplicas()) && - desiredDeployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage() + return desiredDeployment.getSpec().getReplicas().equals(deployment.getSpec().getReplicas()) + && desiredDeployment + .getSpec() + .getTemplate() + .getSpec() + .getContainers() + .get(0) + .getImage() .equals( deployment.getSpec().getTemplate().getSpec().getContainers().get(0).getImage()); } @@ -190,8 +215,8 @@ private Service makeDesiredService(WebPage webPage, String ns, Deployment desire return desiredService; } - private Deployment makeDesiredDeployment(WebPage webPage, String deploymentName, String ns, - String configMapName) { + private Deployment makeDesiredDeployment( + WebPage webPage, String deploymentName, String ns, String configMapName) { Deployment desiredDeployment = ReconcilerUtils.loadYaml(Deployment.class, getClass(), "deployment.yaml"); desiredDeployment.getMetadata().setName(deploymentName); diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java index b413e9ba53..5ba464ca97 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/WebPageStandaloneDependentsReconciler.java @@ -26,12 +26,9 @@ import static io.javaoperatorsdk.operator.sample.Utils.*; import static io.javaoperatorsdk.operator.sample.WebPageManagedDependentsReconciler.SELECTOR; -/** - * Shows how to implement reconciler using standalone dependent resources and workflows. - */ +/** Shows how to implement reconciler using standalone dependent resources and workflows. */ @ControllerConfiguration -public class WebPageStandaloneDependentsReconciler - implements Reconciler { +public class WebPageStandaloneDependentsReconciler implements Reconciler { private final Workflow workflow; @@ -92,14 +89,17 @@ private Workflow createDependentResourcesAndWorkflow() { var serviceDR = new ServiceDependentResource(); var ingressDR = new IngressDependentResource(); - // configure them with our label selector Arrays.asList(configMapDR, deploymentDR, serviceDR, ingressDR) - .forEach(dr -> dr.configureWith(new KubernetesDependentResourceConfigBuilder() - .withKubernetesDependentInformerConfig(InformerConfiguration.builder(dr.resourceType()) - .withLabelSelector(SELECTOR + "=true") - .build()) - .build())); + .forEach( + dr -> + dr.configureWith( + new KubernetesDependentResourceConfigBuilder() + .withKubernetesDependentInformerConfig( + InformerConfiguration.builder(dr.resourceType()) + .withLabelSelector(SELECTOR + "=true") + .build()) + .build())); // connect the dependent resources into a workflow, configuring them as we go // Note the method call order is significant and configuration applies to the dependent being diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPage.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPage.java index c468fa212a..08a6efbd29 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPage.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPage.java @@ -7,14 +7,10 @@ @Group("sample.javaoperatorsdk") @Version("v1") -public class WebPage extends CustomResource - implements Namespaced { +public class WebPage extends CustomResource implements Namespaced { @Override public String toString() { - return "WebPage{" + - "spec=" + spec + - ", status=" + status + - '}'; + return "WebPage{" + "spec=" + spec + ", status=" + status + '}'; } } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageSpec.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageSpec.java index 56fd7dda40..12d2ca8d4b 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageSpec.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageSpec.java @@ -24,8 +24,6 @@ public WebPageSpec setExposed(Boolean exposed) { @Override public String toString() { - return "WebPageSpec{" + - "html='" + html + '\'' + - '}'; + return "WebPageSpec{" + "html='" + html + '\'' + '}'; } } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageStatus.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageStatus.java index 36409ac7f9..43ed108082 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageStatus.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/customresource/WebPageStatus.java @@ -35,10 +35,16 @@ public WebPageStatus setErrorMessage(String errorMessage) { @Override public String toString() { - return "WebPageStatus{" + - "htmlConfigMap='" + htmlConfigMap + '\'' + - ", areWeGood='" + areWeGood + '\'' + - ", errorMessage='" + errorMessage + '\'' + - '}'; + return "WebPageStatus{" + + "htmlConfigMap='" + + htmlConfigMap + + '\'' + + ", areWeGood='" + + areWeGood + + '\'' + + ", errorMessage='" + + errorMessage + + '\'' + + '}'; } } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ConfigMapDependentResource.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ConfigMapDependentResource.java index 816db3688e..0cf8faad7c 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ConfigMapDependentResource.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ConfigMapDependentResource.java @@ -20,10 +20,6 @@ public class ConfigMapDependentResource extends CRUDKubernetesDependentResource { - public ConfigMapDependentResource() { - super(ConfigMap.class); - } - @Override protected ConfigMap desired(WebPage webPage, Context context) { Map data = new HashMap<>(); diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/DeploymentDependentResource.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/DeploymentDependentResource.java index b427e42d33..4deef0f1c0 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/DeploymentDependentResource.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/DeploymentDependentResource.java @@ -22,10 +22,6 @@ public class DeploymentDependentResource extends CRUDKubernetesDependentResource { - public DeploymentDependentResource() { - super(Deployment.class); - } - @Override protected Deployment desired(WebPage webPage, Context context) { Map labels = new HashMap<>(); @@ -37,20 +33,14 @@ protected Deployment desired(WebPage webPage, Context context) { deployment.getMetadata().setLabels(labels); deployment.getSpec().getSelector().getMatchLabels().put("app", deploymentName); - deployment - .getSpec() - .getTemplate() - .getMetadata() - .getLabels() - .put("app", deploymentName); + deployment.getSpec().getTemplate().getMetadata().getLabels().put("app", deploymentName); deployment .getSpec() .getTemplate() .getSpec() .getVolumes() .get(0) - .setConfigMap( - new ConfigMapVolumeSourceBuilder().withName(configMapName(webPage)).build()); + .setConfigMap(new ConfigMapVolumeSourceBuilder().withName(configMapName(webPage)).build()); return deployment; } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ExposedIngressCondition.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ExposedIngressCondition.java index 80691de96e..d07adc59d6 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ExposedIngressCondition.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ExposedIngressCondition.java @@ -9,8 +9,10 @@ public class ExposedIngressCondition implements Condition { @Override - public boolean isMet(DependentResource dependentResource, - WebPage primary, Context context) { + public boolean isMet( + DependentResource dependentResource, + WebPage primary, + Context context) { return primary.getSpec().getExposed(); } } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/IngressDependentResource.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/IngressDependentResource.java index e680d10fcf..3f3e64e8ed 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/IngressDependentResource.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/IngressDependentResource.java @@ -15,13 +15,8 @@ informer = @Informer(labelSelector = WebPageManagedDependentsReconciler.SELECTOR)) public class IngressDependentResource extends CRUDKubernetesDependentResource { - public IngressDependentResource() { - super(Ingress.class); - } - @Override protected Ingress desired(WebPage webPage, Context context) { return makeDesiredIngress(webPage); } - } diff --git a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ServiceDependentResource.java b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ServiceDependentResource.java index ff9e7a1a52..01e8953fa9 100644 --- a/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ServiceDependentResource.java +++ b/sample-operators/webpage/src/main/java/io/javaoperatorsdk/operator/sample/dependentresource/ServiceDependentResource.java @@ -17,12 +17,10 @@ // this annotation only activates when using managed dependents and is not otherwise needed @KubernetesDependent(informer = @Informer(labelSelector = SELECTOR)) -public class ServiceDependentResource extends - io.javaoperatorsdk.operator.processing.dependent.kubernetes.CRUDKubernetesDependentResource { - - public ServiceDependentResource() { - super(Service.class); - } +public class ServiceDependentResource + extends io.javaoperatorsdk.operator.processing.dependent.kubernetes + .CRUDKubernetesDependentResource< + Service, WebPage> { @Override protected Service desired(WebPage webPage, Context context) { diff --git a/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorAbstractTest.java b/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorAbstractTest.java index 6c9a5512bb..c20a7aef8b 100644 --- a/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorAbstractTest.java +++ b/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorAbstractTest.java @@ -71,38 +71,50 @@ void testAddingWebPage() { // update part: changing title operator().replace(createWebPage(TITLE2)); - await().atMost(Duration.ofSeconds(LONG_WAIT_SECONDS)) + await() + .atMost(Duration.ofSeconds(LONG_WAIT_SECONDS)) .pollInterval(POLL_INTERVAL) - .untilAsserted(() -> { - String page = operator().get(ConfigMap.class, Utils.configMapName(webPage)).getData() - .get(INDEX_HTML); - // not using portforward here since there were issues with GitHub actions - // String page = httpGetForWebPage(webPage); - assertThat(page).isNotNull().contains(TITLE2); - }); + .untilAsserted( + () -> { + String page = + operator() + .get(ConfigMap.class, Utils.configMapName(webPage)) + .getData() + .get(INDEX_HTML); + // not using portforward here since there were issues with GitHub actions + // String page = httpGetForWebPage(webPage); + assertThat(page).isNotNull().contains(TITLE2); + }); // delete part: deleting webpage operator().delete(createWebPage(TITLE2)); - await().atMost(Duration.ofSeconds(WAIT_SECONDS)) + await() + .atMost(Duration.ofSeconds(WAIT_SECONDS)) .pollInterval(POLL_INTERVAL) - .untilAsserted(() -> { - Deployment deployment = operator().get(Deployment.class, deploymentName(webPage)); - assertThat(deployment).isNull(); - }); + .untilAsserted( + () -> { + Deployment deployment = operator().get(Deployment.class, deploymentName(webPage)); + assertThat(deployment).isNull(); + }); } String httpGetForWebPage(WebPage webPage) { LocalPortForward portForward = null; try { portForward = - client.services().inNamespace(webPage.getMetadata().getNamespace()) - .withName(serviceName(webPage)).portForward(80); + client + .services() + .inNamespace(webPage.getMetadata().getNamespace()) + .withName(serviceName(webPage)) + .portForward(80); HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(10)).build(); HttpRequest request = - HttpRequest.newBuilder().GET() - .uri(new URI("http://localhost:" + portForward.getLocalPort())).build(); + HttpRequest.newBuilder() + .GET() + .uri(new URI("http://localhost:" + portForward.getLocalPort())) + .build(); return httpClient.send(request, HttpResponse.BodyHandlers.ofString()).body(); } catch (URISyntaxException | IOException | InterruptedException e) { return null; @@ -128,7 +140,9 @@ WebPage createWebPage(String title) { .setHtml( "\n" + " \n" - + " " + title + "\n" + + " " + + title + + "\n" + " \n" + " \n" + " Hello World! \n" @@ -139,5 +153,4 @@ WebPage createWebPage(String title) { } abstract AbstractOperatorExtension operator(); - } diff --git a/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorE2E.java b/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorE2E.java index 45fc4cd5ba..89bbceef57 100644 --- a/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorE2E.java +++ b/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorE2E.java @@ -31,22 +31,30 @@ public WebPageOperatorE2E() throws FileNotFoundException {} .build() : ClusterDeployedOperatorExtension.builder() .waitForNamespaceDeletion(false) - .withOperatorDeployment(client.load(new FileInputStream("k8s/operator.yaml")).items(), + .withOperatorDeployment( + client.load(new FileInputStream("k8s/operator.yaml")).items(), resources -> { - Deployment deployment = (Deployment) resources.stream() - .filter(r -> r instanceof Deployment).findFirst().orElseThrow(); + Deployment deployment = + (Deployment) + resources.stream() + .filter(r -> r instanceof Deployment) + .findFirst() + .orElseThrow(); Container container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); if (container.getEnv() == null) { container.setEnv(new ArrayList<>()); } - container.getEnv().add( - new EnvVar(WEBPAGE_RECONCILER_ENV, WEBPAGE_CLASSIC_RECONCILER_ENV_VALUE, - null)); + container + .getEnv() + .add( + new EnvVar( + WEBPAGE_RECONCILER_ENV, + WEBPAGE_CLASSIC_RECONCILER_ENV_VALUE, + null)); }) .build(); - @Override AbstractOperatorExtension operator() { return operator; diff --git a/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorManagedDependentResourcesE2E.java b/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorManagedDependentResourcesE2E.java index 92b0ce0a41..95217237c9 100644 --- a/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorManagedDependentResourcesE2E.java +++ b/sample-operators/webpage/src/test/java/io/javaoperatorsdk/operator/sample/WebPageOperatorManagedDependentResourcesE2E.java @@ -29,18 +29,27 @@ public WebPageOperatorManagedDependentResourcesE2E() throws FileNotFoundExceptio .build() : ClusterDeployedOperatorExtension.builder() .waitForNamespaceDeletion(false) - .withOperatorDeployment(client.load(new FileInputStream("k8s/operator.yaml")).items(), + .withOperatorDeployment( + client.load(new FileInputStream("k8s/operator.yaml")).items(), resources -> { - Deployment deployment = (Deployment) resources.stream() - .filter(r -> r instanceof Deployment).findFirst().orElseThrow(); + Deployment deployment = + (Deployment) + resources.stream() + .filter(r -> r instanceof Deployment) + .findFirst() + .orElseThrow(); Container container = deployment.getSpec().getTemplate().getSpec().getContainers().get(0); if (container.getEnv() == null) { container.setEnv(new ArrayList<>()); } - container.getEnv().add( - new EnvVar(WEBPAGE_RECONCILER_ENV, - WEBPAGE_MANAGED_DEPENDENT_RESOURCE_ENV_VALUE, null)); + container + .getEnv() + .add( + new EnvVar( + WEBPAGE_RECONCILER_ENV, + WEBPAGE_MANAGED_DEPENDENT_RESOURCE_ENV_VALUE, + null)); }) .build();