diff --git a/.github/workflows/continuous.yaml b/.github/workflows/continuous.yaml index 95b8c925..e37e721c 100644 --- a/.github/workflows/continuous.yaml +++ b/.github/workflows/continuous.yaml @@ -65,7 +65,7 @@ jobs: - name: Install Java uses: actions/setup-java@v4 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Validate Gradle wrapper diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 2c902e3c..71db8e72 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -19,7 +19,7 @@ jobs: - name: Install Java uses: actions/setup-java@v4 with: - java-version: '17' + java-version: '21' distribution: 'temurin' - name: Validate Gradle wrapper diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index be754ef1..00000000 --- a/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM ghcr.io/graalvm/native-image:ol9-java17-22.3.3 AS builder - -# Install tar and gzip to extract the Maven binaries -RUN microdnf install --nodocs -y \ - findutils \ - && microdnf clean all \ - && rm -rf /var/cache/yum - -WORKDIR /build - -# Copy the source code into the image for building -COPY . /build - -# Build -RUN ./gradlew nativeCompile - -# The deployment Image -FROM docker.io/oraclelinux:9-slim - -EXPOSE 8080 -EXPOSE 8081 - -WORKDIR /workspace - -# Copy the native executable into the container -COPY --from=builder /build/build/native/nativeCompile . -ENTRYPOINT ["/workspace/morp"] \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index c5596003..c9bad28c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -63,7 +63,7 @@ dependencies { } java { - sourceCompatibility = JavaVersion.VERSION_17 + sourceCompatibility = JavaVersion.VERSION_21 } sonarqube { @@ -115,7 +115,7 @@ testing { targets { all { testTask.configure { - mustRunAfter(tasks.named("dockerBuild")) + mustRunAfter(tasks.bootBuildImage) testLogging.exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL testLogging.showStandardStreams = true } @@ -125,17 +125,6 @@ testing { } } -tasks.create("dockerBuild") { - mustRunAfter(tasks.test) - executable("docker") - args(listOf("build", "-t", "${registry}/${project.name}:${project.version}", "-t", "${registry}/${project.name}:latest", ".")) -} - -tasks.create("dockerBuildPush") { - executable("docker") - args(listOf("buildx", "build", "--platform", "linux/amd64,linux/arm64", "-t", "${registry}/${project.name}:${project.version}", "--push", ".")) -} - tasks.withType { mustRunAfter(tasks.test) imageName.value("${registry}/${project.name}:${project.version}") @@ -157,7 +146,7 @@ tasks.withType { tasks.check { dependsOn(tasks.test) - dependsOn(tasks.named("dockerBuild")) + dependsOn(tasks.bootBuildImage) dependsOn(testing.suites.named("integrationTest")) dependsOn(tasks.jacocoTestReport) } diff --git a/src/integrationTest/java/io/jaconi/morp/ProxyIT.java b/src/integrationTest/java/io/jaconi/morp/ProxyIT.java index b15d9836..325a4c34 100644 --- a/src/integrationTest/java/io/jaconi/morp/ProxyIT.java +++ b/src/integrationTest/java/io/jaconi/morp/ProxyIT.java @@ -44,7 +44,7 @@ void testKeycloak() { var step1 = containerSetup.getWebTestClient().get() .uri("/upstream/tenant1/test") .accept(MediaType.TEXT_HTML) - .header("host", "morp:8081") + .header("host", "morp:8080") .exchange() .expectStatus().is3xxRedirection() .expectHeader().location("/oauth2/authorization/tenant1") @@ -114,7 +114,7 @@ void testKeycloak() { var step5 = containerSetup.getWebTestClient().get() .uri(step4.getResponseHeaders().getLocation()) .cookie(SESSION_COOKIE, session) - .header("host", "morp:8081") + .header("host", "morp:8080") .exchange() .expectStatus().is3xxRedirection() .expectHeader().location("/upstream/tenant1/test") @@ -129,7 +129,7 @@ void testKeycloak() { var step6 = containerSetup.getWebTestClient().get() .uri(step5.getResponseHeaders().getLocation().getPath()) .accept(MediaType.TEXT_HTML) - .header("host", "morp:8081") + .header("host", "morp:8080") .cookie(SESSION_COOKIE, session) .header("x-tenant-id", "tenant1") .exchange() diff --git a/src/integrationTest/java/io/jaconi/morp/SeleniumIT.java b/src/integrationTest/java/io/jaconi/morp/SeleniumIT.java index f74090b2..a724bb40 100644 --- a/src/integrationTest/java/io/jaconi/morp/SeleniumIT.java +++ b/src/integrationTest/java/io/jaconi/morp/SeleniumIT.java @@ -47,9 +47,8 @@ static class Config { // we start Chrome for each test to ensure a clean state (i.e. cookies etc) - // screen recording not working on ARM Mac due to missing ARM image of vnc-recorder @Container - public final BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(ArmUtil.select("seleniarm/standalone-chromium:107.0", "selenium/standalone-chrome:107.0")) + public final BrowserWebDriverContainer chrome = new BrowserWebDriverContainer<>(ArmUtil.select("seleniarm/standalone-chromium:latest", "selenium/standalone-chrome:latest")) .withNetwork(containerSetup.getNetwork()) .withNetworkAliases("chrome") .withCapabilities(new ChromeOptions()) @@ -83,8 +82,8 @@ void tearDown() { @ParameterizedTest @CsvSource({ - "tenant1, morp:8081, /upstream/tenant1, /test", - "tenant1, tenant1-morp:8081, /upstream, /test" + "tenant1, morp:8080, /upstream/tenant1, /test", + "tenant1, tenant1-morp:8080, /upstream, /test" }) void testWithKeycloak(String tenant, String host, String prefix, String path) throws MalformedURLException { @@ -105,7 +104,7 @@ void testWithKeycloak(String tenant, String host, String prefix, String path) th // assert that we ended up in the right place URL url = new URL(driver.getCurrentUrl()); assertThat(url.getHost()).isEqualTo(StringUtils.substringBefore(host, ":")); - assertThat(url.getPort()).isEqualTo(8081); + assertThat(url.getPort()).isEqualTo(8080); assertThat(url.getPath()).isEqualTo(prefix + path); assertThat(driver.findElement(By.id("test")).getText()).isEqualTo("Hello from mockserver"); @@ -128,8 +127,8 @@ void testWithKeycloak(String tenant, String host, String prefix, String path) th @ParameterizedTest @CsvSource({ - "tenant2, morp:8081, /upstream/tenant2, /test", - "tenant2, tenant2-morp:8081, /upstream, /test" + "tenant2, morp:8080, /upstream/tenant2, /test", + "tenant2, tenant2-morp:8080, /upstream, /test" }) void testWithOkta(String tenant, String host, String prefix, String path) { diff --git a/src/integrationTest/java/io/jaconi/morp/TestContainerSetup.java b/src/integrationTest/java/io/jaconi/morp/TestContainerSetup.java index b1a1015f..a31dcae0 100644 --- a/src/integrationTest/java/io/jaconi/morp/TestContainerSetup.java +++ b/src/integrationTest/java/io/jaconi/morp/TestContainerSetup.java @@ -12,6 +12,7 @@ import org.testcontainers.containers.Network; import org.testcontainers.containers.output.Slf4jLogConsumer; import org.testcontainers.containers.wait.strategy.HttpWaitStrategy; +import org.testcontainers.lifecycle.Startables; import org.testcontainers.utility.DockerImageName; import org.testcontainers.utility.DockerLoggerFactory; import reactor.netty.http.client.HttpClient; @@ -54,29 +55,28 @@ public TestContainerSetup() { // setup mockserver (as protected upstream) var tag = "mockserver-%s".formatted(MockServerClient.class.getPackage().getImplementationVersion()); - this.mockserver = new MockServerContainer(DockerImageName.parse("mockserver/mockserver").withTag(tag)) + DockerImageName mockServerImage = DockerImageName.parse("mockserver/mockserver").withTag(tag); + this.mockserver = new MockServerContainer(mockServerImage) .withNetwork(network) - .withNetworkAliases("upstream"); + .withNetworkAliases("upstream") + .withLogConsumer(new Slf4jLogConsumer(DockerLoggerFactory.getLogger(mockServerImage.asCanonicalNameString()))); // setup morp as auth proxy for upstream - this.morp = new GenericContainer<>(DockerImageName.parse("ghcr.io/jaconi-io/morp:latest")) + DockerImageName morpImage = DockerImageName.parse("ghcr.io/jaconi-io/morp:latest"); + this.morp = new GenericContainer<>(morpImage) .withNetwork(network) .withNetworkAliases("morp", "tenant1-morp", "tenant2-morp") - .withExposedPorts(8081, 8082) + .withExposedPorts(8080, 8081) .withEnv("SPRING_PROFILES_ACTIVE", "test") .withFileSystemBind( "./src/integrationTest/resources/morp/application.yaml", "/workspace/config/application.yaml", BindMode.READ_ONLY) .waitingFor(new HttpWaitStrategy() - .forPort(8082) + .forPort(8081) .forPath("/actuator/health/readiness") - .withStartupTimeout(Duration.ofMinutes(5))); - - // start the containers - keycloak.start(); - mockserver.withLogConsumer(new Slf4jLogConsumer(DockerLoggerFactory.getLogger(mockserver.getDockerImageName()))) - .start(); + .withStartupTimeout(Duration.ofMinutes(5))) + .withLogConsumer(new Slf4jLogConsumer(DockerLoggerFactory.getLogger(morpImage.asCanonicalNameString()))); // for local development convenience, bind mount the git-ignored 'secret.properties' (if it exists) if (Files.exists(Path.of("./secret.properties"))) { @@ -92,8 +92,8 @@ public TestContainerSetup() { .filter(e -> e.getKey().startsWith("MORP_")) .forEach(e -> morp.withEnv(e.getKey(), e.getValue())); - morp.withLogConsumer(new Slf4jLogConsumer(DockerLoggerFactory.getLogger(morp.getDockerImageName()))) - .start(); + // start the containers + Startables.deepStart(keycloak, mockserver, morp).join(); // create client to control mockserver (running as container) mockServerClient = new MockServerClient(mockserver.getHost(), mockserver.getServerPort()); @@ -103,11 +103,11 @@ public TestContainerSetup() { .wiretap(true) // hex dump wiretap .compress(true); webTestClient = WebTestClient.bindToServer(new ReactorClientHttpConnector(httpClient)) - .baseUrl("http://localhost:" + morp.getMappedPort(8081)) + .baseUrl("http://localhost:" + morp.getMappedPort(8080)) .build(); managementTestClient = WebTestClient.bindToServer(new ReactorClientHttpConnector(httpClient)) - .baseUrl("http://localhost:" + morp.getMappedPort(8082)) + .baseUrl("http://localhost:" + morp.getMappedPort(8081)) .build(); } diff --git a/src/integrationTest/resources/morp/application.yaml b/src/integrationTest/resources/morp/application.yaml index 2d29c7fc..c2cecd9e 100644 --- a/src/integrationTest/resources/morp/application.yaml +++ b/src/integrationTest/resources/morp/application.yaml @@ -1,10 +1,3 @@ -# keycloak is running on 8080 so we need different ports for Morp here -server: - port: 8081 -management: - server: - port: 8082 - # Routing config spring: cloud: diff --git a/src/test/java/io/jaconi/morp/MorpApplicationTests.java b/src/test/java/io/jaconi/morp/MorpApplicationTests.java index 073cd5ab..c4156088 100644 --- a/src/test/java/io/jaconi/morp/MorpApplicationTests.java +++ b/src/test/java/io/jaconi/morp/MorpApplicationTests.java @@ -14,7 +14,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DisabledInAotMode class MorpApplicationTests { diff --git a/src/test/java/io/jaconi/morp/oauth/MorpReactiveOAuth2UserServiceTest.java b/src/test/java/io/jaconi/morp/oauth/MorpReactiveOAuth2UserServiceTest.java index f74c5977..9891400b 100644 --- a/src/test/java/io/jaconi/morp/oauth/MorpReactiveOAuth2UserServiceTest.java +++ b/src/test/java/io/jaconi/morp/oauth/MorpReactiveOAuth2UserServiceTest.java @@ -31,7 +31,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DisabledInAotMode class MorpReactiveOAuth2UserServiceTest { diff --git a/src/test/java/io/jaconi/morp/oauth/MorpReactiveOidcUserServiceTest.java b/src/test/java/io/jaconi/morp/oauth/MorpReactiveOidcUserServiceTest.java index cf1c9cdd..b80ec5b6 100644 --- a/src/test/java/io/jaconi/morp/oauth/MorpReactiveOidcUserServiceTest.java +++ b/src/test/java/io/jaconi/morp/oauth/MorpReactiveOidcUserServiceTest.java @@ -27,7 +27,7 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DisabledInAotMode class MorpReactiveOidcUserServiceTest { diff --git a/src/test/java/io/jaconi/morp/oauth/RegistrationResolverTest.java b/src/test/java/io/jaconi/morp/oauth/RegistrationResolverTest.java index 6bbcd572..fcbe8ee0 100644 --- a/src/test/java/io/jaconi/morp/oauth/RegistrationResolverTest.java +++ b/src/test/java/io/jaconi/morp/oauth/RegistrationResolverTest.java @@ -16,7 +16,7 @@ import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.when; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DisabledInAotMode class RegistrationResolverTest { diff --git a/src/test/java/io/jaconi/morp/oauth/TenantAwareClientRegistrationRepositoryTest.java b/src/test/java/io/jaconi/morp/oauth/TenantAwareClientRegistrationRepositoryTest.java index 75adc01c..ab434d46 100644 --- a/src/test/java/io/jaconi/morp/oauth/TenantAwareClientRegistrationRepositoryTest.java +++ b/src/test/java/io/jaconi/morp/oauth/TenantAwareClientRegistrationRepositoryTest.java @@ -17,7 +17,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; -@SpringBootTest +@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT) @DisabledInAotMode class TenantAwareClientRegistrationRepositoryTest {