diff --git a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStore.kt b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStore.kt index 596ffacbb..8aefc6e02 100644 --- a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStore.kt +++ b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStore.kt @@ -11,4 +11,5 @@ interface APIDataStore { fun getReleaseInfo(): ReleaseInfo fun loadDataFromDb(forceUpdate: Boolean): AdoptRepos fun getUpdateInfo(): UpdatedInfo + suspend fun isConnectedToDb(): Boolean } diff --git a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStoreImpl.kt b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStoreImpl.kt index 2fcbe6335..04769e743 100644 --- a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStoreImpl.kt +++ b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/APIDataStoreImpl.kt @@ -192,6 +192,10 @@ open class APIDataStoreImpl : APIDataStore { return updatedAt } + override suspend fun isConnectedToDb(): Boolean { + return dataStore.isConnected() + } + // open for override fun getAdoptRepos(): AdoptRepos { return binaryRepos diff --git a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/ApiPersistence.kt b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/ApiPersistence.kt index 3b00bac10..568190f92 100644 --- a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/ApiPersistence.kt +++ b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/ApiPersistence.kt @@ -32,4 +32,5 @@ interface ApiPersistence { suspend fun hasReleaseNotesForGithubId(gitHubId: GitHubId): Boolean suspend fun putReleaseNote(releaseNotes: ReleaseNotes) suspend fun getReleaseNotes(vendor: Vendor, releaseName: String): ReleaseNotes? + suspend fun isConnected(): Boolean } diff --git a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt index db5db6dab..7788026de 100644 --- a/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt +++ b/adoptium-api-v3-persistence/src/main/kotlin/net/adoptium/api/v3/dataSources/persitence/mongo/MongoApiPersistence.kt @@ -39,6 +39,7 @@ open class MongoApiPersistence @Inject constructor(mongoClient: MongoClient) : M private val releaseInfoCollection: MongoCollection = createCollection(mongoClient.getDatabase(), RELEASE_INFO_DB) private val updateTimeCollection: MongoCollection = createCollection(mongoClient.getDatabase(), UPDATE_TIME_DB) private val githubReleaseNotesCollection: MongoCollection = createCollection(mongoClient.getDatabase(), GH_RELEASE_NOTES) + private val client: MongoClient = mongoClient companion object { @JvmStatic @@ -218,5 +219,9 @@ open class MongoApiPersistence @Inject constructor(mongoClient: MongoClient) : M .firstOrNull() } + override suspend fun isConnected(): Boolean { + return client.getDatabase().runCommand(Document("serverStatus", 1)).getDouble("uptime") >= 0 + } + private fun matchGithubId(gitHubId: GitHubId) = Document("gitHubId.id", gitHubId.id) } diff --git a/adoptium-frontend-parent/adoptium-api-v3-frontend/src/test/kotlin/net/adoptium/api/ApiDataStoreStub.kt b/adoptium-frontend-parent/adoptium-api-v3-frontend/src/test/kotlin/net/adoptium/api/ApiDataStoreStub.kt index 040005870..9b04c53e7 100644 --- a/adoptium-frontend-parent/adoptium-api-v3-frontend/src/test/kotlin/net/adoptium/api/ApiDataStoreStub.kt +++ b/adoptium-frontend-parent/adoptium-api-v3-frontend/src/test/kotlin/net/adoptium/api/ApiDataStoreStub.kt @@ -66,4 +66,8 @@ open class ApiDataStoreStub : APIDataStore { 123 ) } + + override suspend fun isConnectedToDb(): Boolean { + return true + } } diff --git a/adoptium-updater-parent/adoptium-api-v3-updater/src/main/kotlin/net/adoptium/api/v3/V3Updater.kt b/adoptium-updater-parent/adoptium-api-v3-updater/src/main/kotlin/net/adoptium/api/v3/V3Updater.kt index 1a33f7b89..3f00b610c 100644 --- a/adoptium-updater-parent/adoptium-api-v3-updater/src/main/kotlin/net/adoptium/api/v3/V3Updater.kt +++ b/adoptium-updater-parent/adoptium-api-v3-updater/src/main/kotlin/net/adoptium/api/v3/V3Updater.kt @@ -1,6 +1,8 @@ package net.adoptium.api.v3 +import com.mongodb.MongoException import io.quarkus.arc.profile.UnlessBuildProfile +import io.quarkus.runtime.Quarkus import io.quarkus.runtime.Startup import jakarta.enterprise.context.ApplicationScoped import jakarta.inject.Inject @@ -12,8 +14,8 @@ import kotlinx.coroutines.sync.withLock import net.adoptium.api.v3.config.APIConfig import net.adoptium.api.v3.dataSources.APIDataStore import net.adoptium.api.v3.dataSources.ReleaseVersionResolver -import net.adoptium.api.v3.dataSources.UpdaterJsonMapper import net.adoptium.api.v3.dataSources.UpdatableVersionSupplier +import net.adoptium.api.v3.dataSources.UpdaterJsonMapper import net.adoptium.api.v3.dataSources.models.AdoptRepos import net.adoptium.api.v3.dataSources.persitence.ApiPersistence import net.adoptium.api.v3.models.Release @@ -29,6 +31,7 @@ import java.util.concurrent.Executors import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import kotlin.concurrent.timerTask +import kotlin.system.exitProcess @UnlessBuildProfile("test") @ApplicationScoped @@ -205,8 +208,9 @@ class V3Updater @Inject constructor( } fun run(instantFullUpdate: Boolean) { - val executor = Executors.newScheduledThreadPool(2) + assertConnectedToDb() + val executor = Executors.newScheduledThreadPool(2) val delay = if (instantFullUpdate) 0L else 1L @@ -214,6 +218,11 @@ class V3Updater @Inject constructor( apiDataStore.loadDataFromDb(true) } catch (e: java.lang.Exception) { LOGGER.error("Failed to load db", e) + if (e is MongoException) { + LOGGER.error("Failed to connect to db, exiting") + Quarkus.asyncExit(2) + Quarkus.waitForExit() + } AdoptRepos(emptyList()) } @@ -236,6 +245,23 @@ class V3Updater @Inject constructor( ) } + private fun assertConnectedToDb() { + val connected = try { + runBlocking { + return@runBlocking apiDataStore.isConnectedToDb() + } + } catch (e: java.lang.Exception) { + LOGGER.error("Failed to load db", e) + false + } + + if (!connected) { + LOGGER.error("Failed to connect to db, exiting process") + Quarkus.asyncExit(2) + Quarkus.waitForExit() + } + } + private fun fullUpdate(currentRepo: AdoptRepos, releasesOnly: Boolean): AdoptRepos? { // Must catch errors or may kill the scheduler try { diff --git a/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/V3UpdaterTest.kt b/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/V3UpdaterTest.kt index 43caf4dd5..bbe450f8c 100644 --- a/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/V3UpdaterTest.kt +++ b/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/V3UpdaterTest.kt @@ -1,10 +1,17 @@ package net.adoptium.api -import net.adoptium.api.v3.ReleaseIncludeFilter +import io.mockk.coEvery +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.quarkus.runtime.ApplicationLifecycleManager +import io.quarkus.runtime.Quarkus import kotlinx.coroutines.runBlocking import net.adoptium.api.v3.ReleaseFilterType +import net.adoptium.api.v3.ReleaseIncludeFilter import net.adoptium.api.v3.TimeSource import net.adoptium.api.v3.V3Updater +import net.adoptium.api.v3.dataSources.APIDataStore import net.adoptium.api.v3.dataSources.models.AdoptRepos import net.adoptium.api.v3.models.ReleaseType import net.adoptium.api.v3.models.Vendor @@ -13,6 +20,7 @@ import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.condition.EnabledIfSystemProperty import org.slf4j.LoggerFactory +import java.util.concurrent.atomic.AtomicBoolean class V3UpdaterTest { @@ -21,6 +29,34 @@ class V3UpdaterTest { private val LOGGER = LoggerFactory.getLogger(this::class.java) } + @Test + fun `exit is called when db not present`() { + runBlocking { + val apiDataStore: APIDataStore = mockk() + coEvery { apiDataStore.isConnectedToDb() } returns false + + mockkStatic(Quarkus::class) + val called = AtomicBoolean(false) + every { Quarkus.asyncExit(any()) } answers { + called.set(true) + ApplicationLifecycleManager.exit(2) + } + + val updater = V3Updater( + mockk(), + apiDataStore, + mockk(), + mockk(), + mockk(), + mockk(), + mockk() + ) + + updater.run(true) + assertTrue(called.get()) + } + } + @Test fun `checksum works`() { runBlocking { diff --git a/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/testDoubles/InMemoryApiPersistence.kt b/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/testDoubles/InMemoryApiPersistence.kt index 7fde7d07f..9493cb9fc 100644 --- a/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/testDoubles/InMemoryApiPersistence.kt +++ b/adoptium-updater-parent/adoptium-api-v3-updater/src/test/kotlin/net/adoptium/api/testDoubles/InMemoryApiPersistence.kt @@ -116,4 +116,8 @@ open class InMemoryApiPersistence @Inject constructor(var repos: AdoptRepos) : A .filter { it.vendor == vendor } .firstOrNull { it.release_name == releaseName } } + + override suspend fun isConnected(): Boolean { + return true + } }