diff --git a/.github/workflows/NativeTests.yaml b/.github/workflows/NativeTests.yaml new file mode 100644 index 0000000000..0fbcc96def --- /dev/null +++ b/.github/workflows/NativeTests.yaml @@ -0,0 +1,116 @@ +name: NativeTests + +on: + push: + branches: + - main + pull_request: + types: [opened, synchronize, reopened] + paths-ignore: + - 'spring-cloud-generator/**' + - 'spring-cloud-previews/**' + workflow_dispatch: + + +jobs: + NativeTests: + if: | + github.actor != 'dependabot[bot]' && (( + github.event_name == 'pull_request' && github.repository == github.event.pull_request.head.repo.full_name + ) || (github.event_name != 'pull_request')) + runs-on: ubuntu-20.04 + strategy: + fail-fast: false + steps: + - name: Get current date + id: date + run: echo "date=$(date +'%Y-%m-%d' --utc)" >> $GITHUB_OUTPUT + - uses: actions/checkout@v3 + - uses: actions/setup-java@v3 + with: + java-version: 17 + distribution: 'temurin' + - name: Set Up Authentication + uses: google-github-actions/auth@v1 + with: + credentials_json: ${{ secrets.SPRING_CLOUD_GCP_CI_NATIVE_SA_KEY }} + - name: Setup gcloud + uses: google-github-actions/setup-gcloud@v1 + with: + project_id: spring-cloud-gcp-ci-native + - name: Maven go offline + id: mvn-offline + if: steps.mvn-cache.outputs.cache-hit != 'true' + run: ./mvnw compile dependency:go-offline + - name: Mvn install # Need this when the directory/pom structure changes + id: install1 + continue-on-error: true + run: | + ./mvnw \ + --batch-mode \ + --threads 1.5C \ + --define maven.test.skip=true \ + --define maven.javadoc.skip=true \ + install + - name: Retry install on failure + id: install2 + if: steps.install1.outcome == 'failure' + run: | + ./mvnw \ + --batch-mode \ + --threads 1.5C \ + --define maven.test.skip=true \ + --define maven.javadoc.skip=true \ + install + - name: Wait our turn for running native tests + uses: softprops/turnstyle@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + continue-after-seconds: 3600 # 60 min + same-branch-only: false + - uses: graalvm/setup-graalvm@v1 + with: + version: '22.3.0' + java-version: '17' + components: 'native-image' + github-token: ${{ secrets.GITHUB_TOKEN }} + native-image-job-reports: 'true' + - name: Native Tests in Samples + id: intTest1 + continue-on-error: true + run: | + ../mvnw \ + --batch-mode \ + --activate-profiles native-sample-config,nativeTest \ + --define notAllModules=true \ + --define maven.javadoc.skip=true \ + test + working-directory: ./spring-cloud-gcp-samples + - name: Retry on Failure + id: intTest2 + if: steps.intTest1.outcome == 'failure' + run: | + ../mvnw \ + --batch-mode \ + --activate-profiles native-sample-config,nativeTest \ + --define notAllModules=true \ + --define maven.javadoc.skip=true \ + test + working-directory: ./spring-cloud-gcp-samples + - name: Aggregate Report + run: | + ../mvnw \ + --batch-mode \ + --define aggregate=true \ + surefire-report:report-only + working-directory: ./spring-cloud-gcp-samples + - name: Archive logs + if: always() + continue-on-error: true + uses: actions/upload-artifact@v3 + with: + name: Native Test Logs + path: | + **/target/failsafe-reports/* + **/target/site diff --git a/spring-cloud-gcp-logging/src/main/java/com/google/cloud/spring/logging/aot/LoggingRuntimeHints.java b/spring-cloud-gcp-logging/src/main/java/com/google/cloud/spring/logging/aot/LoggingRuntimeHints.java new file mode 100644 index 0000000000..a35f2b8893 --- /dev/null +++ b/spring-cloud-gcp-logging/src/main/java/com/google/cloud/spring/logging/aot/LoggingRuntimeHints.java @@ -0,0 +1,43 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spring.logging.aot; + +import com.google.cloud.spring.logging.LoggingAppender; +import com.google.cloud.spring.logging.StackdriverJsonLayout; +import com.google.cloud.spring.logging.TraceIdLoggingEnhancer; +import java.util.Arrays; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHints; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; + +public class LoggingRuntimeHints implements RuntimeHintsRegistrar { + @Override + public void registerHints(RuntimeHints hints, ClassLoader classLoader) { + hints + .reflection() + .registerTypes( + Arrays.asList( + TypeReference.of(LoggingAppender.class), + TypeReference.of(TraceIdLoggingEnhancer.class), + TypeReference.of(StackdriverJsonLayout.class)), + hint -> + hint.withMembers( + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_METHODS)); + } +} diff --git a/spring-cloud-gcp-logging/src/main/resources/META-INF/spring/aot.factories b/spring-cloud-gcp-logging/src/main/resources/META-INF/spring/aot.factories new file mode 100644 index 0000000000..80e12af31a --- /dev/null +++ b/spring-cloud-gcp-logging/src/main/resources/META-INF/spring/aot.factories @@ -0,0 +1,2 @@ +org.springframework.aot.hint.RuntimeHintsRegistrar=\ + com.google.cloud.spring.logging.aot.LoggingRuntimeHints \ No newline at end of file diff --git a/spring-cloud-gcp-logging/src/test/java/com/google/cloud/spring/logging/aot/LoggingRuntimeHintsTest.java b/spring-cloud-gcp-logging/src/test/java/com/google/cloud/spring/logging/aot/LoggingRuntimeHintsTest.java new file mode 100644 index 0000000000..bb85a6528c --- /dev/null +++ b/spring-cloud-gcp-logging/src/test/java/com/google/cloud/spring/logging/aot/LoggingRuntimeHintsTest.java @@ -0,0 +1,40 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spring.logging.aot; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.aot.hint.predicate.RuntimeHintsPredicates.reflection; + +import com.google.cloud.spring.logging.LoggingAppender; +import com.google.cloud.spring.logging.StackdriverJsonLayout; +import com.google.cloud.spring.logging.TraceIdLoggingEnhancer; +import org.junit.jupiter.api.Test; +import org.springframework.aot.hint.RuntimeHints; + +class LoggingRuntimeHintsTest { + + @Test + void shouldRegisterHints() { + RuntimeHints hints = new RuntimeHints(); + new LoggingRuntimeHints().registerHints(hints, getClass().getClassLoader()); + + assertThat(hints) + .matches(reflection().onType(LoggingAppender.class)) + .matches(reflection().onType(TraceIdLoggingEnhancer.class)) + .matches(reflection().onType(StackdriverJsonLayout.class)); + } +} diff --git a/spring-cloud-gcp-samples/pom.xml b/spring-cloud-gcp-samples/pom.xml index 780f25de70..120ca1e5b2 100644 --- a/spring-cloud-gcp-samples/pom.xml +++ b/spring-cloud-gcp-samples/pom.xml @@ -42,44 +42,55 @@ true - - spring-cloud-gcp-config-sample - spring-cloud-gcp-trace-sample - spring-cloud-gcp-logging-sample - spring-cloud-gcp-sql-mysql-sample - spring-cloud-gcp-integration-pubsub-sample - spring-cloud-gcp-storage-resource-sample - spring-cloud-gcp-data-spanner-repository-sample - spring-cloud-gcp-data-spanner-template-sample - spring-cloud-gcp-data-datastore-sample - spring-cloud-gcp-data-datastore-basic-sample - spring-cloud-gcp-pubsub-sample - spring-cloud-gcp-data-jpa-sample - spring-cloud-gcp-vision-api-sample - spring-cloud-gcp-integration-storage-sample - spring-cloud-gcp-pubsub-bus-config-sample - spring-cloud-gcp-pubsub-stream-dead-letter-sample - spring-cloud-gcp-pubsub-stream-functional-sample - spring-cloud-gcp-pubsub-reactive-sample - spring-cloud-gcp-integration-pubsub-json-sample - spring-cloud-gcp-security-iap-sample - spring-cloud-gcp-sql-postgres-sample - spring-cloud-gcp-sql-postgres-r2dbc-sample - spring-cloud-gcp-vision-ocr-demo - spring-cloud-gcp-firestore-sample - spring-cloud-gcp-data-multi-sample - spring-cloud-gcp-data-firestore-sample - spring-cloud-gcp-bigquery-sample - spring-cloud-gcp-security-firebase-sample - spring-cloud-gcp-secretmanager-sample - spring-cloud-gcp-kotlin-samples - spring-cloud-gcp-metrics-sample - spring-cloud-gcp-kms-sample - + + default + + + !notAllModules + + + + spring-cloud-gcp-config-sample + spring-cloud-gcp-trace-sample + spring-cloud-gcp-logging-sample + spring-cloud-gcp-sql-mysql-sample + spring-cloud-gcp-integration-pubsub-sample + spring-cloud-gcp-storage-resource-sample + spring-cloud-gcp-data-spanner-repository-sample + spring-cloud-gcp-data-spanner-template-sample + spring-cloud-gcp-data-datastore-sample + spring-cloud-gcp-data-datastore-basic-sample + spring-cloud-gcp-pubsub-sample + spring-cloud-gcp-data-jpa-sample + spring-cloud-gcp-vision-api-sample + spring-cloud-gcp-integration-storage-sample + spring-cloud-gcp-pubsub-bus-config-sample + spring-cloud-gcp-pubsub-stream-dead-letter-sample + spring-cloud-gcp-pubsub-stream-functional-sample + spring-cloud-gcp-pubsub-reactive-sample + spring-cloud-gcp-integration-pubsub-json-sample + spring-cloud-gcp-security-iap-sample + spring-cloud-gcp-sql-postgres-sample + spring-cloud-gcp-sql-postgres-r2dbc-sample + spring-cloud-gcp-vision-ocr-demo + spring-cloud-gcp-firestore-sample + spring-cloud-gcp-data-multi-sample + spring-cloud-gcp-data-firestore-sample + spring-cloud-gcp-bigquery-sample + spring-cloud-gcp-security-firebase-sample + spring-cloud-gcp-secretmanager-sample + spring-cloud-gcp-kotlin-samples + spring-cloud-gcp-metrics-sample + spring-cloud-gcp-kms-sample + + native-sample-config + + spring-cloud-gcp-logging-sample + @@ -168,7 +179,6 @@ org.graalvm.buildtools native-maven-plugin - 0.9.23 true diff --git a/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/LoggingSampleApplicationIntegrationTests.java b/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/LoggingSampleApplicationIntegrationTests.java index 59a7d3b08e..6f88f365dc 100644 --- a/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/LoggingSampleApplicationIntegrationTests.java +++ b/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/LoggingSampleApplicationIntegrationTests.java @@ -40,6 +40,7 @@ import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.test.web.client.TestRestTemplate; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.context.annotation.ImportRuntimeHints; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; @@ -52,6 +53,7 @@ @SpringBootTest( webEnvironment = WebEnvironment.RANDOM_PORT, classes = {Application.class}) +@ImportRuntimeHints(TestRuntimeHints.class) class LoggingSampleApplicationIntegrationTests { private static final String LOG_FILTER_FORMAT = diff --git a/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/TestRuntimeHints.java b/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/TestRuntimeHints.java new file mode 100644 index 0000000000..5669dda7dc --- /dev/null +++ b/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample/src/test/java/com.example/TestRuntimeHints.java @@ -0,0 +1,44 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.example; + +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.core.ConsoleAppender; +import java.util.Arrays; +import org.springframework.aot.hint.MemberCategory; +import org.springframework.aot.hint.RuntimeHintsRegistrar; +import org.springframework.aot.hint.TypeReference; + +/** Runtime Hints for GraalVM Native Images * */ +public class TestRuntimeHints implements RuntimeHintsRegistrar { + @Override + public void registerHints( + org.springframework.aot.hint.RuntimeHints hints, ClassLoader classLoader) { + hints + .reflection() + .registerTypes( + Arrays.asList( + TypeReference.of(ConsoleAppender.class), + TypeReference.of(PatternLayoutEncoder.class)), + hint -> + hint.withMembers( + MemberCategory.INVOKE_DECLARED_CONSTRUCTORS, + MemberCategory.INVOKE_PUBLIC_METHODS)); + hints.resources().registerPattern("logback-test.xml"); + hints.resources().registerPattern("com/google/cloud/spring/logging/logback-appender.xml"); + } +}