From 89b80887f82d89be763bf218210e47cb44929f31 Mon Sep 17 00:00:00 2001 From: rickchoijd Date: Fri, 24 Jan 2025 00:09:26 +0000 Subject: [PATCH] [ESWE-1181] Add DB (PostgreSQL) - Added local DB (PostgreSQL) - Added Flyway - Enabled JPA --- Dockerfile | 8 +++- README.md | 4 +- build.gradle.kts | 5 +++ docker-compose.yml | 12 ++++++ .../values.yaml | 5 +++ .../integration/IntegrationTestBase.kt | 11 +++++ .../testcontainers/PostgresContainer.kt | 42 +++++++++++++++++++ .../resources/application-test.yml | 9 ++++ src/main/resources/application-dev.yml | 9 ++++ src/main/resources/application.yml | 24 +++++++++++ 10 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/testcontainers/PostgresContainer.kt diff --git a/Dockerfile b/Dockerfile index 3659c76..a84bae6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ FROM --platform=$BUILDPLATFORM eclipse-temurin:21-jdk-jammy AS builder ARG BUILD_NUMBER -ENV BUILD_NUMBER ${BUILD_NUMBER:-1_0_0} +ENV BUILD_NUMBER=${BUILD_NUMBER:-1_0_0} WORKDIR /app ADD . . @@ -11,7 +11,7 @@ FROM eclipse-temurin:21-jre-jammy LABEL maintainer="HMPPS Digital Studio " ARG BUILD_NUMBER -ENV BUILD_NUMBER ${BUILD_NUMBER:-1_0_0} +ENV BUILD_NUMBER=${BUILD_NUMBER:-1_0_0} RUN apt-get update && \ apt-get -y upgrade && \ @@ -23,6 +23,10 @@ RUN ln -snf "/usr/share/zoneinfo/$TZ" /etc/localtime && echo "$TZ" > /etc/timezo RUN addgroup --gid 2000 --system appgroup && \ adduser --uid 2000 --system appuser --gid 2000 +# Install AWS RDS Root cert into Java truststore +RUN mkdir /home/appuser/.postgresql +ADD --chown=appuser:appgroup https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem /home/appuser/.postgresql/root.crt + WORKDIR /app COPY --from=builder --chown=appuser:appgroup /app/build/libs/hmpps-jobs-board-integration-api*.jar /app/app.jar COPY --from=builder --chown=appuser:appgroup /app/build/libs/applicationinsights-agent*.jar /app/agent.jar diff --git a/README.md b/README.md index 87c06bf..26bd578 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ The integration service has a `/health` endpoint which indicates the service is docker compose pull && docker compose up -d ``` -will build the application and run it and HMPPS Auth within a local docker instance. +will build the application and run it and HMPPS Auth, with a `PostgreSQL` database and `localstack` within local docker. ### Running the application in Intellij @@ -44,7 +44,7 @@ will build the application and run it and HMPPS Auth within a local docker insta docker compose pull && docker compose up --scale hmpps-jobs-board-integration-api=0 -d ``` -will just start a docker instance of HMPPS Auth and `localstack`. The application should then be started with a `dev` or `local` active profile +will just start a docker instance of HMPPS Auth, `PostgreSQL` database, and `localstack`. The application should then be started with a `dev` or `local` active profile in Intellij. * supply required env var, e.g. * `spring.profiles.active`=`dev` diff --git a/build.gradle.kts b/build.gradle.kts index 8e19d4b..67e5705 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,9 +13,13 @@ ext["logback.version"] = "1.5.15" dependencies { implementation("uk.gov.justice.service.hmpps:hmpps-kotlin-spring-boot-starter:1.1.1") implementation("uk.gov.justice.service.hmpps:hmpps-sqs-spring-boot-starter:5.2.2") + implementation("org.springframework.boot:spring-boot-starter-data-jpa") implementation("org.springframework.boot:spring-boot-starter-webflux") implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.7.0") + runtimeOnly("org.flywaydb:flyway-database-postgresql") + runtimeOnly("org.postgresql:postgresql") + testImplementation("org.jetbrains.kotlin:kotlin-test-junit5") } @@ -45,6 +49,7 @@ testing { implementation("org.apache.commons:commons-compress:1.27.1") } implementation("org.testcontainers:localstack") + implementation("org.testcontainers:postgresql") implementation("org.jetbrains.kotlin:kotlin-test-junit5") } diff --git a/docker-compose.yml b/docker-compose.yml index 3f92bc8..d9e2368 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -34,6 +34,18 @@ services: - SPRING_PROFILES_ACTIVE=dev - APPLICATION_AUTHENTICATION_UI_ALLOWLIST=0.0.0.0/0 + job-board-intg-db: + image: postgres:16.3 + container_name: integration-db + networks: + - hmpps + ports: + - "5432:5432" + environment: + - POSTGRES_PASSWORD=job-board-intg + - POSTGRES_USER=job-board-intg + - POSTGRES_DB=job-board-intg + localstack: image: localstack/localstack:4 container_name: integration-localstack diff --git a/helm_deploy/hmpps-jobs-board-integration-api/values.yaml b/helm_deploy/hmpps-jobs-board-integration-api/values.yaml index d54b9f5..bf64177 100644 --- a/helm_deploy/hmpps-jobs-board-integration-api/values.yaml +++ b/helm_deploy/hmpps-jobs-board-integration-api/values.yaml @@ -36,6 +36,11 @@ generic-service: MN_JOBBOARD_API_TOKEN: "MN_JOBBOARD_API_TOKEN" application-insights: APPLICATIONINSIGHTS_CONNECTION_STRING: "APPLICATIONINSIGHTS_CONNECTION_STRING" + rds-postgresql-instance-output: + DATABASE_USERNAME: "database_username" + DATABASE_PASSWORD: "database_password" + DATABASE_NAME: "database_name" + DATABASE_ENDPOINT: "rds_instance_endpoint" allowlist: groups: diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt index 4027c20..eb2379c 100644 --- a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/IntegrationTestBase.kt @@ -15,6 +15,7 @@ import org.springframework.test.context.bean.override.mockito.MockitoSpyBean import org.springframework.test.web.reactive.server.WebTestClient import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.testcontainers.LocalStackContainer import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.testcontainers.LocalStackContainer.setLocalStackProperties +import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.testcontainers.PostgresContainer import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.ExampleApiExtension import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.ExampleApiExtension.Companion.exampleApi import uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.wiremock.HmppsAuthApiExtension @@ -54,10 +55,20 @@ abstract class IntegrationTestBase { companion object { private val localStackContainer by lazy { LocalStackContainer.instance } + private val postgresContainer = PostgresContainer.flywayContainer @JvmStatic @DynamicPropertySource fun configureTestContainers(registry: DynamicPropertyRegistry) { + postgresContainer?.run { + registry.add("spring.datasource.url", postgresContainer::getJdbcUrl) + registry.add("spring.datasource.username", postgresContainer::getUsername) + registry.add("spring.datasource.password", postgresContainer::getPassword) + registry.add("spring.flyway.url", postgresContainer::getJdbcUrl) + registry.add("spring.flyway.user", postgresContainer::getUsername) + registry.add("spring.flyway.password", postgresContainer::getPassword) + } + localStackContainer?.also { setLocalStackProperties(it, registry) } } } diff --git a/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/testcontainers/PostgresContainer.kt b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/testcontainers/PostgresContainer.kt new file mode 100644 index 0000000..a2ab3c7 --- /dev/null +++ b/src/integrationTest/kotlin/uk/gov/justice/digital/hmpps/jobsboardintegrationapi/integration/testcontainers/PostgresContainer.kt @@ -0,0 +1,42 @@ +package uk.gov.justice.digital.hmpps.jobsboardintegrationapi.integration.testcontainers + +import org.slf4j.LoggerFactory +import org.testcontainers.containers.PostgreSQLContainer +import org.testcontainers.containers.wait.strategy.Wait +import java.io.IOException +import java.net.ServerSocket + +object PostgresContainer { + val flywayContainer: PostgreSQLContainer? by lazy { startPostgresqlContainer() } + val repositoryContainer: PostgreSQLContainer? by lazy { startPostgresqlContainer() } + + private fun startPostgresqlContainer(): PostgreSQLContainer? { + if (isPostgresRunning()) { + log.warn("Using existing PostgreSQL database") + return null + } + + log.info("Creating a TestContainers PostgreSQL database") + + return PostgreSQLContainer("postgres:16.3").apply { + withEnv("HOSTNAME_EXTERNAL", "localhost") + withDatabaseName("job-board-intg") + withUsername("job-board-intg") + withPassword("job-board-intg") + setWaitStrategy(Wait.forListeningPort()) + withReuse(true) + start() + } + } + + private fun isPostgresRunning(): Boolean = + try { + val serverSocket = ServerSocket(5432) + serverSocket.localPort == 0 + } catch (error: IOException) { + log.warn("A PostgreSQL database is running") + true + } + + private val log = LoggerFactory.getLogger(this::class.java) +} diff --git a/src/integrationTest/resources/application-test.yml b/src/integrationTest/resources/application-test.yml index fcd9099..a3ccbb9 100644 --- a/src/integrationTest/resources/application-test.yml +++ b/src/integrationTest/resources/application-test.yml @@ -5,6 +5,15 @@ management.endpoint: health.cache.time-to-live: 0 info.cache.time-to-live: 0 +spring: + jpa: + show-sql: true + datasource: + url: 'jdbc:postgresql://${DATABASE_ENDPOINT}/${DATABASE_NAME}?sslmode=disable&autosave=conservative' + flyway: + baseline-on-migrate: true + clean-disabled: false + hmpps-auth: url: "http://localhost:8090/auth" diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index 20dab86..4a20ca3 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -16,3 +16,12 @@ api: integration: enabled: false + +spring: + datasource: + url: 'jdbc:postgresql://localhost:5432/job-board-intg' + username: 'job-board-intg' + password: 'job-board-intg' + flyway: + user: 'job-board-intg' + password: 'job-board-intg' diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 77cf165..86ce70a 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -46,6 +46,30 @@ spring: local: - "dev" - "localstack" + + jpa: + open-in-view: false + show-sql: false + generate-ddl: false + hibernate: + ddl-auto: none + + datasource: + url: 'jdbc:postgresql://${DATABASE_ENDPOINT}/${DATABASE_NAME}?sslmode=verify-full' + username: ${DATABASE_USERNAME} + password: ${DATABASE_PASSWORD} + hikari: + pool-name: Hmpps-job-board-integration + connectionTimeout: 1000 + validationTimeout: 500 + + flyway: + baselineOnMigrate: true + validateMigrationNaming: true + url: ${spring.datasource.url} + user: ${DATABASE_USERNAME} + password: ${DATABASE_PASSWORD} + server: port: 8080 servlet: