diff --git a/.circleci/config.yml b/.circleci/config.yml index 9723ba22..c35f70d4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -14,10 +14,29 @@ parameters: jobs: validate: executor: - name: hmpps/java - tag: "19.0" + name: hmpps/localstack + jdk_tag: "19.0" + localstack_tag: "2.0" + localstack_type: "localstack" + services: "opensearch" steps: + - run: + command: | + while true; do + sleep 5 + ps auxwwf + echo "======" + done + background: true - checkout + - hmpps/install_aws_cli + - hmpps/wait_till_ready + - run: + name: Set up opensearch domain + command: src/test/resources/localstack/setup-opensearch.sh + - hmpps/wait_till_ready: + port: 4566 + host: os01.eu-west-2.opensearch.localhost.localstack.cloud - restore_cache: keys: - gradle-{{ checksum "build.gradle.kts" }} diff --git a/docker-compose-test.yml b/docker-compose-test.yml index 8e3acb21..5a668020 100644 --- a/docker-compose-test.yml +++ b/docker-compose-test.yml @@ -1,19 +1,17 @@ version: "3" services: - opensearch: - container_name: opensearch - image: opensearchproject/opensearch:2.5.0 + localstack: + image: localstack/localstack:2.0 networks: - hmpps - environment: - - node.name=opensearch - - cluster.name=prisoner-search-cluster - - discovery.type=single-node - - bootstrap.memory_lock=true - - plugins.security.disabled=true - - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" + container_name: localstack-psi ports: - - "9200:9200" + - "4566:4566" + environment: + - SERVICES=opensearch + volumes: + - "$PWD/src/test/resources/localstack/setup-opensearch.sh:/etc/localstack/init/ready.d/init-aws.sh" + - "${LOCALSTACK_VOLUME_DIR:-./volume}:/var/lib/localstack" networks: hmpps: diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/health/IndexInfo.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/health/IndexInfo.kt new file mode 100644 index 00000000..a5666870 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/health/IndexInfo.kt @@ -0,0 +1,17 @@ +package uk.gov.justice.digital.hmpps.prisonersearchindexer.health + +import org.springframework.boot.actuate.info.Info +import org.springframework.boot.actuate.info.InfoContributor +import org.springframework.stereotype.Component +import uk.gov.justice.digital.hmpps.prisonersearchindexer.services.IndexStatusService + +@Component +class IndexInfo( + private val indexStatusService: IndexStatusService, +) : InfoContributor { + + override fun contribute(builder: Info.Builder) { + val indexStatus = indexStatusService.getCurrentIndex() + builder.withDetail("index-status", indexStatus) + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/model/IndexStatus.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/model/IndexStatus.kt new file mode 100644 index 00000000..5b4ea36e --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/model/IndexStatus.kt @@ -0,0 +1,36 @@ +package uk.gov.justice.digital.hmpps.prisonersearchindexer.model + +import org.springframework.data.annotation.Id +import org.springframework.data.elasticsearch.annotations.DateFormat +import org.springframework.data.elasticsearch.annotations.Document +import org.springframework.data.elasticsearch.annotations.Field +import org.springframework.data.elasticsearch.annotations.FieldType +import java.time.LocalDateTime + +@Document(indexName = "offender-index-status") +class IndexStatus( + @Id + @Field(type = FieldType.Keyword) + var id: String = "STATUS", + + @Field(type = FieldType.Keyword) + var currentIndex: SyncIndex, + + @Field(type = FieldType.Date, format = [DateFormat.date_hour_minute_second]) + var startIndexTime: LocalDateTime?, + + @Field(type = FieldType.Date, format = [DateFormat.date_hour_minute_second]) + var endIndexTime: LocalDateTime?, + + @Field(type = FieldType.Boolean) + var inProgress: Boolean, + + @Field(type = FieldType.Boolean) + var inError: Boolean = false, + +) { + + fun toggleIndex() { + currentIndex = if (currentIndex == SyncIndex.INDEX_A) SyncIndex.INDEX_B else SyncIndex.INDEX_A + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/model/SyncIndex.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/model/SyncIndex.kt new file mode 100644 index 00000000..a6cbc6ad --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/model/SyncIndex.kt @@ -0,0 +1,10 @@ +package uk.gov.justice.digital.hmpps.prisonersearchindexer.model + +enum class SyncIndex(val indexName: String) { + + INDEX_A("prisoner-search-a"), INDEX_B("prisoner-search-b"); + + fun otherIndex(): SyncIndex { + return if (this == INDEX_A) INDEX_B else INDEX_A + } +} diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/repository/IndexStatusRepository.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/repository/IndexStatusRepository.kt new file mode 100644 index 00000000..39c6325c --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/repository/IndexStatusRepository.kt @@ -0,0 +1,8 @@ +package uk.gov.justice.digital.hmpps.prisonersearchindexer.repository + +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository +import org.springframework.stereotype.Repository +import uk.gov.justice.digital.hmpps.prisonersearchindexer.model.IndexStatus + +@Repository +interface IndexStatusRepository : ElasticsearchRepository diff --git a/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/services/IndexStatusService.kt b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/services/IndexStatusService.kt new file mode 100644 index 00000000..3b3141f2 --- /dev/null +++ b/src/main/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/services/IndexStatusService.kt @@ -0,0 +1,16 @@ +package uk.gov.justice.digital.hmpps.prisonersearchindexer.services + +import org.springframework.data.repository.findByIdOrNull +import org.springframework.stereotype.Service +import uk.gov.justice.digital.hmpps.prisonersearchindexer.model.IndexStatus +import uk.gov.justice.digital.hmpps.prisonersearchindexer.model.SyncIndex +import uk.gov.justice.digital.hmpps.prisonersearchindexer.repository.IndexStatusRepository + +@Service +class IndexStatusService( + private val indexStatusRepository: IndexStatusRepository, +) { + + fun getCurrentIndex(): IndexStatus = indexStatusRepository.findByIdOrNull("STATUS") + ?: indexStatusRepository.save(IndexStatus("STATUS", SyncIndex.INDEX_A, null, null, false)) +} diff --git a/src/main/resources/application-dev.yml b/src/main/resources/application-dev.yml index e863887a..3aa291f3 100644 --- a/src/main/resources/application-dev.yml +++ b/src/main/resources/application-dev.yml @@ -35,4 +35,4 @@ spring: public-key-location: classpath:local-public-key.pub opensearch: - uris: http://localhost:9200 + uris: http://os01.eu-west-2.opensearch.localhost.localstack.cloud:4566 diff --git a/src/test/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/integration/health/InfoTest.kt b/src/test/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/integration/health/InfoTest.kt index be541aa1..2ac19dd2 100644 --- a/src/test/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/integration/health/InfoTest.kt +++ b/src/test/kotlin/uk/gov/justice/digital/hmpps/prisonersearchindexer/integration/health/InfoTest.kt @@ -10,8 +10,7 @@ class InfoTest : IntegrationTestBase() { @Test fun `Info page is accessible`() { - webTestClient.get() - .uri("/info") + webTestClient.get().uri("/info") .exchange() .expectStatus() .isOk @@ -28,4 +27,15 @@ class InfoTest : IntegrationTestBase() { assertThat(it).startsWith(LocalDateTime.now().format(DateTimeFormatter.ISO_DATE)) } } + + @Test + fun `Info page reports index status`() { + webTestClient.get().uri("/info") + .exchange() + .expectStatus() + .isOk + .expectBody() + .jsonPath("index-status.id").isEqualTo("STATUS") + .jsonPath("index-status.inProgress").isEqualTo("false") + } } diff --git a/src/test/resources/application-test.yml b/src/test/resources/application-test.yml index ae1d0326..8b324603 100644 --- a/src/test/resources/application-test.yml +++ b/src/test/resources/application-test.yml @@ -27,3 +27,6 @@ incentives: client: id: incentives-api secret: clientsecret + +opensearch: + uris: http://os01.eu-west-2.opensearch.localhost.localstack.cloud:4566 diff --git a/src/test/resources/localstack/setup-opensearch.sh b/src/test/resources/localstack/setup-opensearch.sh new file mode 100755 index 00000000..df0db6fa --- /dev/null +++ b/src/test/resources/localstack/setup-opensearch.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -e +export TERM=ansi +export AWS_ACCESS_KEY_ID=foobar +export AWS_SECRET_ACCESS_KEY=foobar +export AWS_DEFAULT_REGION=eu-west-2 + +aws --endpoint-url=http://localhost:4566 opensearch create-domain --domain-name os01 + +echo "OpenSearch configured. Please wait until 'cluster on http://127.0.0.1:xxxxx is ready"