diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index cd7af26..1b15853 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -9,6 +9,11 @@ on: jobs: build: runs-on: ubuntu-latest + env: + TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE: /var/run/docker.sock + DOCKER_HOST: unix:///var/run/docker.sock + TESTCONTAINERS_RYUK_DISABLED: "true" + TESTCONTAINERS_CHECKS_DISABLE: "true" steps: - uses: actions/checkout@v3 @@ -19,5 +24,21 @@ jobs: with: java-version: '17' distribution: 'adopt' + - name: Debug Docker (Testcontainers) + run: | + echo "=== whoami ===" + whoami + echo "=== groups ===" + groups + echo "=== id ===" + id + echo "=== DOCKER_HOST ===" + echo "${DOCKER_HOST:-}" + echo "=== Docker socket ===" + ls -la /var/run/docker.sock 2>&1 || true + echo "=== docker info ===" + docker info 2>&1 || true + echo "=== docker run hello-world ===" + docker run --rm hello-world 2>&1 || true - name: Build with Maven run: mvn --batch-mode package diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 84c58c7..720f8c9 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -12,6 +12,11 @@ jobs: outputs: version: ${{ steps.release-outputs.outputs.version }} service: ${{ steps.release-outputs.outputs.service }} + env: + TESTCONTAINERS_DOCKER_SOCKET_OVERRIDE: /var/run/docker.sock + DOCKER_HOST: unix:///var/run/docker.sock + TESTCONTAINERS_RYUK_DISABLED: "true" + TESTCONTAINERS_CHECKS_DISABLE: "true" steps: - name: Login to Docker Hub @@ -69,6 +74,22 @@ jobs: git tag ${{ steps.bump.outputs.new_base_version }} git push origin HEAD:${GITHUB_REF##*/} git push origin ${{ steps.bump.outputs.new_base_version }} + - name: Debug Docker (Testcontainers) + run: | + echo "=== whoami ===" + whoami + echo "=== groups ===" + groups + echo "=== id ===" + id + echo "=== DOCKER_HOST ===" + echo "${DOCKER_HOST:-}" + echo "=== Docker socket ===" + ls -la /var/run/docker.sock 2>&1 || true + echo "=== docker info ===" + docker info 2>&1 || true + echo "=== docker run hello-world ===" + docker run --rm hello-world 2>&1 || true - name: Build package run: mvn --batch-mode clean package - name: Build and push image diff --git a/Dockerfile b/Dockerfile index ff074c1..b5fc73a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM eclipse-temurin:17-jre-jammy +FROM eclipse-temurin:21-jre MAINTAINER protege.stanford.edu EXPOSE 7777 diff --git a/pom.xml b/pom.xml index 026a9da..7a6ab93 100644 --- a/pom.xml +++ b/pom.xml @@ -70,7 +70,7 @@ org.testcontainers minio - 1.19.7 + 1.21.3 test @@ -97,7 +97,7 @@ org.testcontainers testcontainers - 1.18.1 + 1.21.4 test @@ -120,12 +120,13 @@ org.testcontainers junit-jupiter + 1.21.4 test org.testcontainers rabbitmq - 1.19.7 + 1.21.4 test diff --git a/src/main/java/edu/stanford/protege/webprotege/gateway/FormsController.java b/src/main/java/edu/stanford/protege/webprotege/gateway/FormsController.java index aa2ff51..31090e8 100644 --- a/src/main/java/edu/stanford/protege/webprotege/gateway/FormsController.java +++ b/src/main/java/edu/stanford/protege/webprotege/gateway/FormsController.java @@ -39,7 +39,12 @@ public FormsController(RpcClient rpcClient, ObjectMapper objectMapper) { @GetMapping("/data/projects/{projectId}/forms") public ResponseEntity> getForms(@PathVariable(PROJECT_ID) ProjectId projectId, @AuthenticationPrincipal Jwt jwt) { - return rpcClient.call(jwt, GET_FORM_DESCRIPTORS, Map.of(PROJECT_ID, projectId)); + try { + CorrelationMDCUtil.setCorrelationId(UUID.randomUUID().toString()); + return rpcClient.call(jwt, GET_FORM_DESCRIPTORS, Map.of(PROJECT_ID, projectId)); + } finally { + CorrelationMDCUtil.clearCorrelationId(); + } } @PostMapping("/data/projects/{projectId}/forms") @@ -51,6 +56,7 @@ public ResponseEntity> setForms(@PathVariable(PROJECT_ID) Pr // formDescriptors // formSelectors try { + CorrelationMDCUtil.setCorrelationId(UUID.randomUUID().toString()); var tree = objectMapper.readValue(forms, new TypeReference>() {}); var params = new LinkedHashMap<>(tree); params.put("changeRequestId", ChangeRequestId.generate()); @@ -59,6 +65,8 @@ public ResponseEntity> setForms(@PathVariable(PROJECT_ID) Pr } catch (JsonProcessingException e) { throw new ResponseStatusException(HttpStatus.BAD_REQUEST, e.getMessage()); + } finally { + CorrelationMDCUtil.clearCorrelationId(); } } } diff --git a/src/main/java/edu/stanford/protege/webprotege/gateway/RpcRequestProcessor.java b/src/main/java/edu/stanford/protege/webprotege/gateway/RpcRequestProcessor.java index 65ed466..2bb7c60 100644 --- a/src/main/java/edu/stanford/protege/webprotege/gateway/RpcRequestProcessor.java +++ b/src/main/java/edu/stanford/protege/webprotege/gateway/RpcRequestProcessor.java @@ -10,6 +10,9 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.amqp.AmqpTimeoutException; +import org.springframework.amqp.core.AmqpMessageReturnedException; +import org.springframework.amqp.core.AmqpReplyTimeoutException; import org.springframework.beans.factory.annotation.Value; import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatusCode; @@ -19,6 +22,8 @@ import java.util.Collections; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.TimeoutException; /** * Matthew Horridge @@ -76,11 +81,22 @@ private CompletableFuture sendMessage(RpcRequest request, String ac payload, userId); return reply .exceptionally(e -> { + if(e instanceof CompletionException completionException) { + if(completionException.getCause() instanceof AmqpReplyTimeoutException timeoutException) { + logger.error("Timeout while waiting for reply to message on channel {}: {}", request.methodName(), timeoutException.getMessage(), e); + throw new ResponseStatusException(HttpStatus.GATEWAY_TIMEOUT, "Timed out while waiting for reply to message on channel " + request.methodName(), timeoutException); + } + else if(completionException.getCause() instanceof AmqpMessageReturnedException messageReturnedException) { + logger.error("Message returned: {}", messageReturnedException.getMessage(), e); + throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "Message to channel " + request.methodName() + " was returned"); + } + } if (e instanceof ProjectUnderMaintenanceException) { throw (ProjectUnderMaintenanceException) e; } logger.error("Error during send and receive: {}. Returning failed future with ResponseStatusException HTTP 500 Internal Server Error", e.getMessage(), e); throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage(), e); + }) .thenCompose(msg -> { try { diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 1527696..2e54c96 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -44,6 +44,6 @@ spring.security.oauth2: authorization-grant-type: authorization_code scope: openid provider.keycloak: - issuer-uri: http://webprotege-local.edu/keycloak-admin/realms/webprotege + issuer-uri: http://webprotege-local.edu/keycloak/realms/webprotege user-name-attribute: preferred_username - resourceserver.jwt.issuer-uri: http://webprotege-local.edu/keycloak-admin/realms/webprotege \ No newline at end of file + resourceserver.jwt.issuer-uri: http://webprotege-local.edu/keycloak/realms/webprotege \ No newline at end of file