diff --git a/src/lib/server-impl.test.ts b/src/lib/server-impl.test.ts index 7a1bd69d68..66d522ff61 100644 --- a/src/lib/server-impl.test.ts +++ b/src/lib/server-impl.test.ts @@ -1,6 +1,6 @@ import express from 'express'; import { createTestConfig } from '../test/config/test-config'; -import { start, create } from './server-impl'; +import { create, start } from './server-impl'; import FakeEventStore from '../test/fixtures/fake-event-store'; jest.mock( @@ -20,6 +20,9 @@ const settingStore = { get: () => { Promise.resolve('secret'); }, + postgresVersion: () => { + Promise.resolve('16.2'); + }, }; jest.mock('./metrics', () => ({ diff --git a/src/lib/server-impl.ts b/src/lib/server-impl.ts index e95b8cd4d8..1ed4ba6937 100644 --- a/src/lib/server-impl.ts +++ b/src/lib/server-impl.ts @@ -35,6 +35,7 @@ import * as eventType from './types/events'; import { Db } from './db/db'; import { defaultLockKey, defaultTimeout, withDbLock } from './util/db-lock'; import { scheduleServices } from './features/scheduler/schedule-services'; +import { compareAndLogPostgresVersion } from './util/postgres-version-checker'; async function createApp( config: IUnleashConfig, @@ -45,6 +46,7 @@ async function createApp( const serverVersion = config.enterpriseVersion ?? version; const db = createDb(config); const stores = createStores(config, db); + await compareAndLogPostgresVersion(config, stores.settingStore); const services = createServices(stores, config, db); if (!config.disableScheduler) { await scheduleServices(services, config); diff --git a/src/lib/util/postgres-version-checker.test.ts b/src/lib/util/postgres-version-checker.test.ts new file mode 100644 index 0000000000..797af963fb --- /dev/null +++ b/src/lib/util/postgres-version-checker.test.ts @@ -0,0 +1,95 @@ +import type { ISettingStore, IUnleashConfig } from '../types'; +import { createTestConfig } from '../../test/config/test-config'; +import { compareAndLogPostgresVersion } from './postgres-version-checker'; +import FakeSettingStore from '../../test/fixtures/fake-setting-store'; + +let config: IUnleashConfig; +let settingStore: ISettingStore; +let infoMessages: string[]; +let errorMessages: string[]; + +const fakeSettingStore = (postgresVersion: string): ISettingStore => { + const temp = new FakeSettingStore(); + jest.spyOn(temp, 'postgresVersion').mockResolvedValue(postgresVersion); + return temp; +}; + +beforeEach(() => { + infoMessages = []; + errorMessages = []; + config = createTestConfig({ + getLogger: () => { + return { + info: (message: string) => { + infoMessages.push(message); + }, + error: (message: string) => { + errorMessages.push(message); + }, + warn: (message: string) => {}, + debug: (message: string) => {}, + fatal(message: any, ...args) {}, + }; + }, + }); +}); +describe('postgres-version-checker', () => { + describe('Postgres version below 13.0 will yield error messages', () => { + test('12.1.7', async () => { + settingStore = fakeSettingStore('12.1.7'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(1); + expect(infoMessages).toHaveLength(0); + }); + test('12.1', async () => { + settingStore = fakeSettingStore('12.1'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(1); + expect(infoMessages).toHaveLength(0); + }); + test('11.1', async () => { + settingStore = fakeSettingStore('11.1'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(1); + expect(infoMessages).toHaveLength(0); + }); + test('10.1', async () => { + settingStore = fakeSettingStore('10.1'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(1); + expect(infoMessages).toHaveLength(0); + }); + test('9.6', async () => { + settingStore = fakeSettingStore('9.6'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(1); + expect(infoMessages).toHaveLength(0); + }); + }); + describe('Postgres version at 13.0 or higher will yield an info message', () => { + test('13.9.2', async () => { + settingStore = fakeSettingStore('13.9.2'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(0); + expect(infoMessages).toHaveLength(1); + }); + test('14.9', async () => { + settingStore = fakeSettingStore('14.9'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(0); + expect(infoMessages).toHaveLength(1); + }); + test('15.9', async () => { + settingStore = fakeSettingStore('15.9'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(0); + expect(infoMessages).toHaveLength(1); + }); + test('16.2', async () => { + settingStore = fakeSettingStore('16.2'); + await compareAndLogPostgresVersion(config, settingStore); + expect(errorMessages).toHaveLength(0); + expect(infoMessages).toHaveLength(1); + }); + }); +}); diff --git a/src/lib/util/postgres-version-checker.ts b/src/lib/util/postgres-version-checker.ts new file mode 100644 index 0000000000..26af8652a8 --- /dev/null +++ b/src/lib/util/postgres-version-checker.ts @@ -0,0 +1,20 @@ +import type { ISettingStore, IUnleashConfig } from '../types'; +import semver, { lt, type SemVer } from 'semver'; + +const MIN_SUPPORTED_POSTGRES_VERSION: SemVer = semver.parse('13.0.0')!; + +export async function compareAndLogPostgresVersion( + config: IUnleashConfig, + settingStore: ISettingStore, +): Promise { + const logger = config.getLogger('server-impl/postgresVersionWarner'); + const postgresVersion = await settingStore.postgresVersion(); + const pgSemVer = semver.coerce(postgresVersion); // Postgres usually reports Major.Minor, semver needs a patch version included in string + if (pgSemVer !== null && lt(pgSemVer, MIN_SUPPORTED_POSTGRES_VERSION)) { + logger.error( + `You are running an unsupported version of PostgreSQL: ${postgresVersion}. You'll have to upgrade to Postgres 13 or newer to continue getting our support.`, + ); + } else { + logger.info(`Running PostgreSQL version ${postgresVersion}.`); + } +}