diff --git a/.github/workflows/hrm-ci.yml b/.github/workflows/hrm-ci.yml index ab56530bf..c9e81b9ae 100644 --- a/.github/workflows/hrm-ci.yml +++ b/.github/workflows/hrm-ci.yml @@ -1,4 +1,4 @@ -# Copyright (C) 2021-2023 Technology Matters + # Copyright (C) 2021-2023 Technology Matters # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published # by the Free Software Foundation, either version 3 of the License, or @@ -149,6 +149,9 @@ jobs: - name: Build run: npm run build + - name: Build + run: npm run build + - name: Build Service Docker Image uses: docker/build-push-action@v6 with: @@ -157,20 +160,6 @@ jobs: push: false tags: hrm-service - - name: Setup database tables - uses: m4nu56/postgresql-action@1.0 - with: - # Version of PostgreSQL to use - postgresql version: 16.3 - # POSTGRES_DB - name for the default database that is created - postgresql db: hrmdb - # POSTGRES_USER - create the specified user with superuser power - postgresql user: rdsadmin - # POSTGRES_PASSWORD - superuser password - postgresql password: postgres - # POSTGRES_INIT_SCRIPTS - directory containing DB init scripts - postgresql init scripts: test-support/db-init - - name: Run Service Tests run: npm run test:service:ci diff --git a/hrm-domain/hrm-core/profile/profileDataAccess.ts b/hrm-domain/hrm-core/profile/profileDataAccess.ts index 545ea2a0a..d7ad9b5f1 100644 --- a/hrm-domain/hrm-core/profile/profileDataAccess.ts +++ b/hrm-domain/hrm-core/profile/profileDataAccess.ts @@ -51,7 +51,7 @@ import { listProfilesSql, } from './sql/profile-list-sql'; import { getPaginationElements } from '../search'; -import { TOUCH_PROFILE_SQL, updateProfileByIdSql } from './sql/profile-update.sql'; +import { TOUCH_PROFILE_SQL, updateProfileByIdSql } from './sql/profileUpdate.sql'; import { ensureRejection, newErr, diff --git a/hrm-domain/hrm-core/profile/sql/profile-update.sql.ts b/hrm-domain/hrm-core/profile/sql/profileUpdate.sql.ts similarity index 100% rename from hrm-domain/hrm-core/profile/sql/profile-update.sql.ts rename to hrm-domain/hrm-core/profile/sql/profileUpdate.sql.ts diff --git a/hrm-domain/hrm-service/db-migrate.js b/hrm-domain/hrm-service/db-migrate.js index 7327e1cbd..4ca3e59ea 100644 --- a/hrm-domain/hrm-service/db-migrate.js +++ b/hrm-domain/hrm-service/db-migrate.js @@ -82,10 +82,18 @@ async function migrate() { console.log('Migration complete.', JSON.stringify(ret)); break; } catch (err) { - console.log('Migration failed. Retrying...', err); - // eslint-disable-next-line @typescript-eslint/no-loop-func - await new Promise(resolve => setTimeout(resolve, 250)); - lastErr = err; + if ( + ['SequelizeConnectionRefusedError', 'SequelizeConnectionError'].includes(err.name) + ) { + console.debug( + "Migration failed to connect to DB, assuming it's not ready yet & retrying...", + ); + // eslint-disable-next-line @typescript-eslint/no-loop-func + await new Promise(resolve => setTimeout(resolve, 250)); + lastErr = err; + } else { + throw err; + } } } if (ret) { diff --git a/hrm-domain/hrm-service/docker-database/Dockerfile b/hrm-domain/hrm-service/docker-database/Dockerfile new file mode 100644 index 000000000..43a0308ec --- /dev/null +++ b/hrm-domain/hrm-service/docker-database/Dockerfile @@ -0,0 +1,23 @@ +# Copyright (C) 2021-2023 Technology Matters +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published +# by the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see https://www.gnu.org/licenses/. + +FROM library/postgres:16.8 + +RUN apt-get update \ + && apt-get install -y --no-install-recommends postgresql-16-pgaudit \ + && rm -rf /var/lib/apt/lists/* + +EXPOSE 5432 + +CMD ["postgres", "-c", "shared_preload_libraries=pgaudit"] \ No newline at end of file diff --git a/hrm-domain/hrm-service/docker-database/docker-compose-persistent.yml b/hrm-domain/hrm-service/docker-database/docker-compose-persistent.yml index 0b57e4edb..ed6a76739 100644 --- a/hrm-domain/hrm-service/docker-database/docker-compose-persistent.yml +++ b/hrm-domain/hrm-service/docker-database/docker-compose-persistent.yml @@ -14,7 +14,7 @@ services: db: - image: library/postgres:16.8 + build: . environment: POSTGRES_USER: rdsadmin @@ -25,7 +25,7 @@ services: - ./hrmdb.sql:/docker-entrypoint-initdb.d/hrmdb.sql - hrm_postgres_volume:/var/lib/postgresql - command: ['postgres', '-c', 'log_statement=all'] + command: ['postgres', '-c', 'log_statement=all', '-c', 'shared_preload_libraries=pgaudit'] ports: - '5432:5432' diff --git a/hrm-domain/hrm-service/docker-database/docker-compose.yml b/hrm-domain/hrm-service/docker-database/docker-compose.yml index ed6c5c31d..a7900262c 100644 --- a/hrm-domain/hrm-service/docker-database/docker-compose.yml +++ b/hrm-domain/hrm-service/docker-database/docker-compose.yml @@ -14,7 +14,7 @@ services: db: - image: library/postgres:16.8 + build: . environment: POSTGRES_USER: rdsadmin @@ -24,7 +24,7 @@ services: volumes: - ./hrmdb.sql:/docker-entrypoint-initdb.d/hrmdb.sql - command: ['postgres', '-c', 'log_statement=all'] + command: ['postgres', '-c', 'log_statement=all', '-c', 'shared_preload_libraries=pgaudit'] ports: - '5432:5432' diff --git a/hrm-domain/hrm-service/docker-database/hrmdb.sql b/hrm-domain/hrm-service/docker-database/hrmdb.sql index 8050cddb5..346f26c68 100644 --- a/hrm-domain/hrm-service/docker-database/hrmdb.sql +++ b/hrm-domain/hrm-service/docker-database/hrmdb.sql @@ -39,7 +39,7 @@ SET default_with_oids = false; -- CREATE ROLE hrm; -ALTER ROLE hrm WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity'; +ALTER ROLE hrm WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity'; CREATE ROLE rds_ad; ALTER ROLE rds_ad WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; CREATE ROLE rds_iam; @@ -49,14 +49,14 @@ ALTER ROLE rds_password WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN CREATE ROLE rds_replication; ALTER ROLE rds_replication WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; CREATE ROLE rds_superuser; -ALTER ROLE rds_superuser WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; +ALTER ROLE rds_superuser WITH SUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; ALTER ROLE rdsadmin WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN REPLICATION BYPASSRLS VALID UNTIL 'infinity'; CREATE ROLE rdsrepladmin; ALTER ROLE rdsrepladmin WITH NOSUPERUSER NOINHERIT NOCREATEROLE NOCREATEDB NOLOGIN REPLICATION NOBYPASSRLS; CREATE ROLE read_only_user; ALTER ROLE read_only_user WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB LOGIN NOREPLICATION NOBYPASSRLS; CREATE ROLE resources; -ALTER ROLE resources WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity'; +ALTER ROLE resources WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity'; -- -- User Configurations -- diff --git a/hrm-domain/hrm-service/migrations/20260105104900-pg_audit_plugin_enable.js b/hrm-domain/hrm-service/migrations/20260105104900-pg_audit_plugin_enable.js new file mode 100644 index 000000000..0d07fae9f --- /dev/null +++ b/hrm-domain/hrm-service/migrations/20260105104900-pg_audit_plugin_enable.js @@ -0,0 +1,32 @@ +/** + * Copyright (C) 2021-2023 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ +'use strict'; + +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + up: async queryInterface => { + await queryInterface.sequelize.query(` + CREATE EXTENSION IF NOT EXISTS pgaudit; + `); + console.log('"label" column set to type TEXT'); + }, + down: async queryInterface => { + await queryInterface.sequelize.query(` + DROP EXTENSION pgaudit; + `); + console.log('"label" column set to type TEXT'); + }, +}; diff --git a/hrm-domain/hrm-service/package.json b/hrm-domain/hrm-service/package.json index d6b5e728b..6a09793b1 100644 --- a/hrm-domain/hrm-service/package.json +++ b/hrm-domain/hrm-service/package.json @@ -19,8 +19,8 @@ "test": "run-s test:service", "test:run:watch": "cross-env AWS_REGION=us-east-1 CI=true TWILIO_ACCOUNT_SID=ACxxx TWILIO_AUTH_TOKEN=xxxxxx RDS_PASSWORD=postgres jest --maxWorkers=1 --forceExit --watch", "test:service": "cross-env POSTGRES_PORT=5433 run-s -c docker:compose:test:up test:service:ci:migrate test:service:run docker:compose:test:down", - "test:service:run": "cross-env AWS_REGION=us-east-1 SSM_ENDPOINT=http://mock-ssm/ SNS_ENDPOINT=http://mock-sns/ jest --verbose --maxWorkers=1 --forceExit service-tests", - "test:service:ci": "run-s test:service:ci:migrate test:service:ci:run", + "test:service:run": "cross-env AWS_REGION=us-east-1 SSM_ENDPOINT=http://mock-ssm/ SNS_ENDPOINT=http://mock-sns/ jest --verbose --maxWorkers=1 --forceExit --coverage service-tests", + "test:service:ci": "run-s test:service", "test:service:ci:migrate": "cross-env CI=true RDS_PASSWORD=postgres node ./db-migrate", "test:service:ci:run": "cross-env AWS_REGION=us-east-1 SSM_ENDPOINT=http://mock-ssm/ SNS_ENDPOINT=http://mock-sns/ CI=true TWILIO_ACCOUNT_SID=ACxxx TWILIO_AUTH_TOKEN=xxxxxx RDS_PASSWORD=postgres jest --verbose --maxWorkers=1 --forceExit --coverage service-tests", "test:coverage": "run-s docker:compose:test:up test:service:ci:migrate test:coverage:run docker:compose:test:down", diff --git a/hrm-domain/integration-tests/docker-compose-integration-tests.yml b/hrm-domain/integration-tests/docker-compose-integration-tests.yml index 21534732a..7457e8e30 100644 --- a/hrm-domain/integration-tests/docker-compose-integration-tests.yml +++ b/hrm-domain/integration-tests/docker-compose-integration-tests.yml @@ -16,7 +16,7 @@ version: '3' services: db: - image: library/postgres:16.8 + build: ../hrm-service/docker-database environment: POSTGRES_USER: rdsadmin @@ -26,7 +26,7 @@ services: volumes: - ./db-init/dump.sql:/docker-entrypoint-initdb.d/dump.sql - command: ['postgres', '-c', 'log_statement=all'] + command: ['postgres', '-c', 'log_statement=all', '-c', 'shared_preload_libraries=pgaudit'] ports: - '5433:5432' diff --git a/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/package.json b/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/package.json index 8e428b4fb..1d7ba97c7 100644 --- a/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/package.json +++ b/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/package.json @@ -26,7 +26,7 @@ "docker:build": "docker build -t uscr-beacon-poller --build-arg lambda_name=uscr-beacon-poller --build-arg lambda_dir=hrm-domain/lambdas/custom-integrations -f ../../../../lambdas/Dockerfile ../../../../", "test:service:run": "jest --verbose --maxWorkers=1 --forceExit tests/service", "test:unit": "jest --verbose tests/unit", - "test:service": "cd ./tests/service && docker compose up -d && cd ../../../../../hrm-service && cross-env POSTGRES_PORT=5433 npm run test:service:ci:migrate && cd ../lambdas/custom-integrations/uscr-beacon-poller && cross-env HRM_DATABASE_PORT=5433 npm run test:service:run && cd ./tests/service && docker compose down", - "test:service:ci": "cd ./tests/service && docker compose -f docker-compose-ci.yml up -d && cd ../../../../../hrm-service && npm run test:service:ci:migrate && cd ../lambdas/custom-integrations/uscr-beacon-poller && npm run test:service:run; cd ./tests/service && docker compose -f docker-compose-ci.yml down" + "test:service": "cd ./tests/service && docker compose up -d && cd ../../../../../hrm-service && cross-env POSTGRES_PORT=5434 npm run test:service:ci:migrate && cd ../lambdas/custom-integrations/uscr-beacon-poller && cross-env HRM_DATABASE_PORT=5434 npm run test:service:run && cd ./tests/service && docker compose down", + "test:service:ci": "run-s test:service" } } diff --git a/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/tests/service/docker-compose.yml b/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/tests/service/docker-compose.yml index cdd19ee0e..3edcc4643 100644 --- a/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/tests/service/docker-compose.yml +++ b/hrm-domain/lambdas/custom-integrations/uscr-beacon-poller/tests/service/docker-compose.yml @@ -16,7 +16,7 @@ version: '3' services: db: - image: library/postgres:16.3 + build: ../../../../../hrm-service/docker-database environment: POSTGRES_USER: rdsadmin @@ -26,10 +26,10 @@ services: volumes: - ../../../../../../test-support/db-init/dump.sql:/docker-entrypoint-initdb.d/dump.sql - command: ['postgres', '-c', 'log_statement=all'] + command: ['postgres', '-c', 'log_statement=all', '-c', 'shared_preload_libraries=pgaudit'] ports: - - '127.0.0.1:5433:5432' + - '127.0.0.1:5434:5432' service: image: hrm-service ports: diff --git a/resources-domain/resources-service/create-db.js b/resources-domain/resources-service/create-db.js index dedb924b0..b8309aeab 100644 --- a/resources-domain/resources-service/create-db.js +++ b/resources-domain/resources-service/create-db.js @@ -64,10 +64,17 @@ async function create() { lastErr = undefined; break; } catch (err) { - console.log('Creation failed. Retrying...', err); - // eslint-disable-next-line @typescript-eslint/no-loop-func - await new Promise(resolve => setTimeout(resolve, 250)); - lastErr = err; + // Catch and ECONNRESET, ECONNREFUSED or messages with 'connection' / 'connect' in. Cast a broad net! + if (err.message?.toLowerCase()?.includes('conn')) { + console.debug( + "Creation failed connecting to DB, assuming it's not ready yet & retrying...", + ); + // eslint-disable-next-line @typescript-eslint/no-loop-func + await new Promise(resolve => setTimeout(resolve, 250)); + lastErr = err; + } else { + throw err; + } } } diff --git a/resources-domain/resources-service/db-migrate.js b/resources-domain/resources-service/db-migrate.js index 97279e8b5..529097086 100644 --- a/resources-domain/resources-service/db-migrate.js +++ b/resources-domain/resources-service/db-migrate.js @@ -79,13 +79,21 @@ async function migrate() { try { // eslint-disable-next-line no-await-in-loop ret = await umzug.up(); - console.log('Migration complete.', JSON.stringify(ret)); + console.info('Migration complete.', JSON.stringify(ret)); break; } catch (err) { - console.log('Migration failed. Retrying...', err); - // eslint-disable-next-line @typescript-eslint/no-loop-func - await new Promise(resolve => setTimeout(resolve, 250)); - lastErr = err; + if ( + ['SequelizeConnectionRefusedError', 'SequelizeConnectionError'].includes(err.name) + ) { + console.debug( + "Migration failed to connect to DB, assuming it's not ready yet & retrying...", + ); + // eslint-disable-next-line @typescript-eslint/no-loop-func + await new Promise(resolve => setTimeout(resolve, 250)); + lastErr = err; + } else { + throw err; + } } } if (ret) { diff --git a/resources-domain/resources-service/package.json b/resources-domain/resources-service/package.json index 3a6de5f94..7d4c3c84f 100644 --- a/resources-domain/resources-service/package.json +++ b/resources-domain/resources-service/package.json @@ -30,7 +30,7 @@ "test:run:watch": "cross-env AWS_REGION=us-east-1 CI=true TWILIO_ACCOUNT_SID=ACxxx TWILIO_AUTH_TOKEN=xxxxxx RDS_PASSWORD=postgres RESOURCES_PASSWORD=resources-password jest --maxWorkers=1 --forceExit --watch", "test:unit": "jest tests/unit", "test:service": "cross-env POSTGRES_PORT=5433 RDS_USERNAME=hrm RDS_PASSWORD=postgres RESOURCES_PASSWORD=resources-password run-s -c docker:compose:test:up db:create:schema test:service:ci:migrate test:service:ci:run docker:compose:test:down", - "test:service:ci": "RDS_USERNAME=rdsadmin RDS_PASSWORD=postgres RESOURCES_PASSWORD=resources-password run-s db:create:schema test:service:ci:migrate test:service:ci:run", + "test:service:ci": "run-s test:service", "test:service:ci:migrate": "node ./db-migrate", "test:service:ci:run": "cross-env AWS_REGION=us-east-1 CI=true TWILIO_ACCOUNT_SID=ACxxx TWILIO_AUTH_TOKEN=xxxxxx SSM_ENDPOINT=http://mock-ssm/ jest --verbose --maxWorkers=1 --forceExit --coverage tests/service", "test:coverage": "run-s docker:compose:test:up test:service:migrate test:coverage:run docker:compose:test:down", diff --git a/test-support/db-init/dump.sql b/test-support/db-init/dump.sql index 9cbceef15..bbfa48222 100644 --- a/test-support/db-init/dump.sql +++ b/test-support/db-init/dump.sql @@ -58,7 +58,7 @@ BEGIN END $do$; -ALTER ROLE hrm WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity'; +ALTER ROLE hrm WITH SUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS VALID UNTIL 'infinity'; CREATE ROLE rds_ad; ALTER ROLE rds_ad WITH NOSUPERUSER INHERIT NOCREATEROLE NOCREATEDB NOLOGIN NOREPLICATION NOBYPASSRLS; CREATE ROLE rds_iam; diff --git a/test-support/docker-compose-service-test.yml b/test-support/docker-compose-service-test.yml index 7d58b2aca..7bc0295a7 100644 --- a/test-support/docker-compose-service-test.yml +++ b/test-support/docker-compose-service-test.yml @@ -14,7 +14,7 @@ services: db: - image: library/postgres:16.8 + build: ../hrm-domain/hrm-service/docker-database environment: POSTGRES_USER: rdsadmin @@ -24,7 +24,7 @@ services: volumes: - ./db-init/dump.sql:/docker-entrypoint-initdb.d/dump.sql - command: ['postgres', '-c', 'log_statement=all'] + command: ['postgres', '-c', 'log_statement=all', '-c', 'shared_preload_libraries=pgaudit'] ports: - '5433:5432'