From 55c5243fa4efa39d67aed81341042a7163bd7568 Mon Sep 17 00:00:00 2001 From: Edouard Demotes Date: Mon, 18 Oct 2021 17:19:31 -0400 Subject: [PATCH] feat(terraform): add backups for Postgres service (#292) * feat(terraform): add backup container with default to EFS storage * feat(terraform): ability to prevent EFS destroy * feat(terraform): ability to prevent volume destroy * feat(terraform): prevent backup destroy * refactor(terraform): improve app config override * fix(terraform): set default db to api * feat(terraform): ability to backup volumes * feat(terraform): ability to backup mount point * fix(terraform): remove unused envs from backups * fix(terraform): connect backup and postgres through private dns --- apps/terraform/src/configs/apps.config.ts | 17 +++-- .../src/configs/environments.config.ts | 21 ++++--- .../src/interfaces/environment.interface.ts | 28 +++++++-- apps/terraform/src/stacks/main.stack.ts | 21 +++---- libs/terraform/component/volume/README.md | 1 + .../volume/src/lib/volume.component.ts | 16 +++++ .../src/lib/interfaces/container.interface.ts | 9 ++- .../src/lib/interfaces/services.interface.ts | 9 +++ .../lib/services/backend-service.component.ts | 9 +++ .../ecs/src/lib/services/service.component.ts | 62 ++++++++++++++----- libs/terraform/service/postgres/README.md | 49 +++++++++++++++ .../postgres/src/lib/backup.container.ts | 24 +++++++ .../src/lib/interfaces/backup.interface.ts | 33 ++++++++++ .../postgres/src/lib/interfaces/index.ts | 1 + .../src/lib/interfaces/postgres.interface.ts | 4 ++ .../postgres/src/lib/postgres.component.ts | 48 +++++++++++++- 16 files changed, 295 insertions(+), 57 deletions(-) create mode 100644 libs/terraform/service/postgres/src/lib/backup.container.ts create mode 100644 libs/terraform/service/postgres/src/lib/interfaces/backup.interface.ts diff --git a/apps/terraform/src/configs/apps.config.ts b/apps/terraform/src/configs/apps.config.ts index 5af93ef9c..2cc4551b8 100644 --- a/apps/terraform/src/configs/apps.config.ts +++ b/apps/terraform/src/configs/apps.config.ts @@ -1,7 +1,4 @@ -import { ApiComponentPublicConfig } from '@tractr/terraform-service-api'; -import { PostgresComponentPublicConfig } from '@tractr/terraform-service-postgres'; -import { PwaComponentPublicConfig } from '@tractr/terraform-service-pwa'; -import { ReverseProxyComponentPublicConfig } from '@tractr/terraform-service-reverse-proxy'; +import { Environment } from '../interfaces'; /** * @example @@ -20,10 +17,10 @@ import { ReverseProxyComponentPublicConfig } from '@tractr/terraform-service-rev * }, * } */ -export const ApiConfig: ApiComponentPublicConfig = {}; -export const PwaConfig: PwaComponentPublicConfig = {}; - -export const PostgresConfig: PostgresComponentPublicConfig = {}; - -export const ReverseProxyConfig: ReverseProxyComponentPublicConfig = {}; +export const AppConfig: Required = { + api: {}, + pwa: {}, + postgres: {}, + reverseProxy: {}, +}; diff --git a/apps/terraform/src/configs/environments.config.ts b/apps/terraform/src/configs/environments.config.ts index 8610b61a0..640ca3365 100644 --- a/apps/terraform/src/configs/environments.config.ts +++ b/apps/terraform/src/configs/environments.config.ts @@ -5,19 +5,24 @@ export const Environments: Environment[] = [ name: 'Production', resourceId: 'prod', subDomain: 'www', - pwaConfig: { containerConfig: { imageTag: 'production' } }, - apiConfig: { - containerConfig: { imageTag: 'production' }, - desiredCount: 2, - cpu: '512', - memory: '1024', + config: { + pwa: { containerConfig: { imageTag: 'production' } }, + api: { + containerConfig: { imageTag: 'production' }, + desiredCount: 2, + cpu: '512', + memory: '1024', + }, + postgres: { enableBackups: true }, }, }, { name: 'Staging', resourceId: 'staging', subDomain: 'staging', - pwaConfig: { containerConfig: { imageTag: 'latest' } }, - apiConfig: { containerConfig: { imageTag: 'latest' } }, + config: { + pwa: { containerConfig: { imageTag: 'latest' } }, + api: { containerConfig: { imageTag: 'latest' } }, + }, }, ]; diff --git a/apps/terraform/src/interfaces/environment.interface.ts b/apps/terraform/src/interfaces/environment.interface.ts index 149eee40d..91af5b9ec 100644 --- a/apps/terraform/src/interfaces/environment.interface.ts +++ b/apps/terraform/src/interfaces/environment.interface.ts @@ -1,5 +1,7 @@ import { ApiComponentPublicConfig } from '@tractr/terraform-service-api'; +import { PostgresComponentPublicConfig } from '@tractr/terraform-service-postgres'; import { PwaComponentPublicConfig } from '@tractr/terraform-service-pwa'; +import { ReverseProxyComponentPublicConfig } from '@tractr/terraform-service-reverse-proxy'; export interface Environment { /** @@ -14,12 +16,26 @@ export interface Environment { * Subdomain that will host this environment */ subDomain: string; + /** - * PWA config override - */ - pwaConfig: PwaComponentPublicConfig; - /** - * API config override + * Configs that override the main configs */ - apiConfig: ApiComponentPublicConfig; + config: { + /** + * PWA config override + */ + pwa?: PwaComponentPublicConfig; + /** + * API config override + */ + api?: ApiComponentPublicConfig; + /** + * Postgres config override + */ + postgres?: PostgresComponentPublicConfig; + /** + * Reverse proxy config override + */ + reverseProxy?: ReverseProxyComponentPublicConfig; + }; } diff --git a/apps/terraform/src/stacks/main.stack.ts b/apps/terraform/src/stacks/main.stack.ts index 160b704aa..22daa4066 100644 --- a/apps/terraform/src/stacks/main.stack.ts +++ b/apps/terraform/src/stacks/main.stack.ts @@ -1,13 +1,7 @@ import { Construct } from 'constructs'; import * as deepmerge from 'deepmerge'; -import { - ApiConfig, - Environments, - PostgresConfig, - PwaConfig, - ReverseProxyConfig, -} from '../configs'; +import { AppConfig, Environments } from '../configs'; import { TerraformEnvironmentVariables } from '../dtos'; import { AwsStack, AwsStackConfig } from '@tractr/terraform-aws-stack'; @@ -59,12 +53,15 @@ export class MainStack extends AwsStack { // Create a pool for each environment for (const environment of Environments) { + // Merge app configs and environment configs + const mergedConfig = deepmerge(AppConfig, environment.config); + // Add the pool group that will host our container const poolGroup = new PoolGroup(this, environment.resourceId, { registryGroup: this.registryGroup, networkGroup: this.networkGroup, zoneGroup: this.zoneGroup, - reverseProxyConfig: ReverseProxyConfig, + reverseProxyConfig: mergedConfig.reverseProxy, subDomain: environment.subDomain, ownerPictureConfig: { s3PublicRead: true, @@ -73,15 +70,13 @@ export class MainStack extends AwsStack { }); // Add a pwa as a http service - const mergedPwaConfig = deepmerge(PwaConfig, environment.pwaConfig); - poolGroup.addHttpService(PwaComponent, 'pwa', mergedPwaConfig); + poolGroup.addHttpService(PwaComponent, 'pwa', mergedConfig.pwa); // Add a api as a http service - const mergedApiConfig = deepmerge(ApiConfig, environment.apiConfig); const api = poolGroup.addHttpService( ApiComponent, 'api', - mergedApiConfig, + mergedConfig.api, ); // Add a postgres as a backend service @@ -89,7 +84,7 @@ export class MainStack extends AwsStack { PostgresComponent, 'postgres', [api], - PostgresConfig, + mergedConfig.postgres, ); // Store group diff --git a/libs/terraform/component/volume/README.md b/libs/terraform/component/volume/README.md index 18e233f61..7df5952c3 100644 --- a/libs/terraform/component/volume/README.md +++ b/libs/terraform/component/volume/README.md @@ -10,6 +10,7 @@ const volume = new VolumeComponent(this, 'vol', { vpcId: 'xxxxxxxx', subnetsIds: ['aaaaa', 'bbbbb'], clientsSecurityGroupsIds: ['ssssss'], + preventDestroy: false, }); ``` diff --git a/libs/terraform/component/volume/src/lib/volume.component.ts b/libs/terraform/component/volume/src/lib/volume.component.ts index 1bcc09331..9b9482595 100644 --- a/libs/terraform/component/volume/src/lib/volume.component.ts +++ b/libs/terraform/component/volume/src/lib/volume.component.ts @@ -1,4 +1,5 @@ import { + EfsBackupPolicy, EfsFileSystem, EfsMountTarget, SecurityGroup, @@ -23,6 +24,8 @@ export interface VolumeComponentConfig extends ConstructOptions { | 'AFTER_30_DAYS' | 'AFTER_60_DAYS' | 'AFTER_90_DAYS'; + preventDestroy?: boolean; + enableBackups?: boolean; } export class VolumeComponent< @@ -32,12 +35,17 @@ export class VolumeComponent< protected readonly efsFileSystem: EfsFileSystem; + protected readonly efsBackupPolicy: EfsBackupPolicy | undefined; + protected readonly efsMountTargets: EfsMountTarget[]; constructor(scope: AwsProviderConstruct, id: string, config: T) { super(scope, id, config); this.securityGroup = this.createSecurityGroup(); this.efsFileSystem = this.createEfsFileSystem(); + if (this.config.enableBackups) { + this.efsBackupPolicy = this.createEfsBackupPolicy(); + } this.efsMountTargets = this.createEfsMountTargets(); } @@ -65,10 +73,18 @@ export class VolumeComponent< lifecyclePolicy: [ { transitionToIa: this.config.transitionToIa || 'AFTER_90_DAYS' }, ], + lifecycle: { preventDestroy: !!this.config.preventDestroy }, tags: this.getResourceNameAsTag('fs'), }); } + protected createEfsBackupPolicy() { + return new EfsBackupPolicy(this, 'bck', { + fileSystemId: this.getFileSystemIdAsToken(), + backupPolicy: [{ status: 'ENABLED' }], + }); + } + protected createEfsMountTargets() { return this.config.subnetsIds.map((subnetId, index) => this.createEfsMountTarget(subnetId, index), diff --git a/libs/terraform/service/ecs/src/lib/interfaces/container.interface.ts b/libs/terraform/service/ecs/src/lib/interfaces/container.interface.ts index f9866bb47..fda83b71a 100644 --- a/libs/terraform/service/ecs/src/lib/interfaces/container.interface.ts +++ b/libs/terraform/service/ecs/src/lib/interfaces/container.interface.ts @@ -23,7 +23,7 @@ export interface ContainerDefinition { hostPort?: number; protocol?: 'tcp' | 'udp'; }[]; - mountPoints?: MountPoint[]; + mountPoints?: MountPointDefinition[]; dockerLabels?: Record; } @@ -42,11 +42,16 @@ export interface ImageDefinition { imageUri: string; } -export interface MountPoint { +export interface MountPointDefinition { sourceVolume: string; containerPath: string; } +export interface MountPoint extends MountPointDefinition { + preventDestroy?: boolean; + enableBackups?: boolean; +} + export type EnvironmentCb = ( service: ServiceComponent, config: C, diff --git a/libs/terraform/service/ecs/src/lib/interfaces/services.interface.ts b/libs/terraform/service/ecs/src/lib/interfaces/services.interface.ts index 719c21db0..c82767051 100644 --- a/libs/terraform/service/ecs/src/lib/interfaces/services.interface.ts +++ b/libs/terraform/service/ecs/src/lib/interfaces/services.interface.ts @@ -1,5 +1,7 @@ import { ConstructOptions } from 'constructs'; +import { Container } from '../containers'; + import { DockerApplications } from '@tractr/terraform-group-registry'; // Check cpu/memory pairs: https://docs.aws.amazon.com/AmazonECS/latest/developerguide/task-cpu-memory-error.html @@ -17,6 +19,13 @@ export type MemoryValue = | '16384' | '30720'; +export type VolumesConfig = { + preventDestroy: boolean; + enableBackups: boolean; + containers: Container[]; +}; +export type VolumesConfigs = Record; + export interface ServiceComponentInternalConfig extends ConstructOptions { vpcId: string; subnetsIds: string[]; diff --git a/libs/terraform/service/ecs/src/lib/services/backend-service.component.ts b/libs/terraform/service/ecs/src/lib/services/backend-service.component.ts index 8424aca86..ab7d9bb04 100644 --- a/libs/terraform/service/ecs/src/lib/services/backend-service.component.ts +++ b/libs/terraform/service/ecs/src/lib/services/backend-service.component.ts @@ -30,9 +30,18 @@ export abstract class BackendServiceComponent< fromPort: port, toPort: port, securityGroups: this.config.clientsSecurityGroupsIds, + selfAttribute: this.shouldAccessItself(), })), }; } + /** + * Whether to add `self` attribute on security group ingress rules in order to allow this service to call itself. + * For exemple, a service with two different containers that need to call each other. + */ + protected shouldAccessItself(): boolean { + return false; + } + protected abstract getIngressPorts(): number[]; } diff --git a/libs/terraform/service/ecs/src/lib/services/service.component.ts b/libs/terraform/service/ecs/src/lib/services/service.component.ts index 0c72e8b23..710db61db 100644 --- a/libs/terraform/service/ecs/src/lib/services/service.component.ts +++ b/libs/terraform/service/ecs/src/lib/services/service.component.ts @@ -17,6 +17,8 @@ import { Container } from '../containers'; import { ServiceComponentConfig, ServiceComponentDefaultConfig, + VolumesConfig, + VolumesConfigs, } from '../interfaces'; import { @@ -53,7 +55,7 @@ export abstract class ServiceComponent< protected readonly containers: Container[]; - protected readonly volumesNames: string[]; + protected readonly volumesConfigs: VolumesConfigs; protected readonly volumeComponentsMap: | Record @@ -68,7 +70,7 @@ export abstract class ServiceComponent< this.config = deepmerge(this.getDefaultConfig(), config) as C & D; this.containers = this.getContainers(); - this.volumesNames = this.getVolumesNames(); + this.volumesConfigs = this.getVolumesConfigs(); this.securityGroup = this.createSecurityGroup(); if (this.shouldCreateVolumeComponent()) { this.volumeComponentsMap = this.createVolumeComponentsMap(); @@ -193,39 +195,65 @@ export abstract class ServiceComponent< }; } - protected getVolumesNames(): string[] { - const volumes = new Set(); - this.containers.forEach((container) => { - container.getMountPoints().forEach((mountPoint) => { - volumes.add(mountPoint.sourceVolume); - }); - }); - return [...volumes]; + /** + * Aggregate and group all mounts points across every container and merge boolean properties + * @protected + */ + protected getVolumesConfigs(): VolumesConfigs { + const volumes: VolumesConfigs = {}; + for (const container of this.containers) { + const mountPoints = container.getMountPoints(); + for (const mountPoint of mountPoints) { + const name = mountPoint.sourceVolume; + const previous: VolumesConfig = volumes[name] + ? volumes[name] + : { + containers: [], + preventDestroy: false, + enableBackups: false, + }; + + volumes[name] = { + containers: [...previous.containers, container], + preventDestroy: mountPoint.preventDestroy || previous.preventDestroy, + enableBackups: mountPoint.enableBackups || previous.enableBackups, + }; + } + } + return volumes; } protected shouldCreateVolumeComponent() { - return this.volumesNames.length > 0; + return Object.keys(this.volumesConfigs).length > 0; } protected createVolumeComponentsMap() { - return this.volumesNames.reduce( - (map, name) => ({ + return Object.entries(this.volumesConfigs).reduce( + (map, [name, config]) => ({ ...map, - [name]: this.createVolumeComponent(name), + [name]: this.createVolumeComponent(name, config), }), {} as Record, ); } - protected createVolumeComponent(name: string) { - return new VolumeComponent(this, name, this.getVolumeComponentConfig()); + protected createVolumeComponent(name: string, config: VolumesConfig) { + return new VolumeComponent( + this, + name, + this.getVolumeComponentConfig(config), + ); } - protected getVolumeComponentConfig(): VolumeComponentConfig { + protected getVolumeComponentConfig( + config: VolumesConfig, + ): VolumeComponentConfig { return { vpcId: this.config.vpcId, subnetsIds: this.config.subnetsIds, clientsSecurityGroupsIds: [this.getSecurityGroupIdAsToken()], + preventDestroy: config.preventDestroy, + enableBackups: config.enableBackups, }; } diff --git a/libs/terraform/service/postgres/README.md b/libs/terraform/service/postgres/README.md index edeb2a05f..8c18886cd 100644 --- a/libs/terraform/service/postgres/README.md +++ b/libs/terraform/service/postgres/README.md @@ -20,3 +20,52 @@ const postgres = new PostgresComponent(this, 'postgres', { }); ``` +## Backups + +This service also provide a backup system based on image `tractr/postgres-backup` which is based on `blacklabelops/volumerize`. +This last one is able to store backups on many providers. +By default, this service stores the backups on a persistent container Volume, i.e. on AWS EFS using the VolumeComponent. +This EFS is also backup by AWS by using the `Automatic backup` option. + +You may want to store backups on another providers. +See the [Volumerize documentation](https://github.com/blacklabelops/volumerize) for more examples. +This is an exemple that stores backups on AWS S3: + +```typescript +const postgres = new PostgresComponent(this, 'postgres', { + containerConfig: { + imageTag: '13-alpine', + environments: { + POSTGRES_DB: 'api', + POSTGRES_USER: Secret(), + POSTGRES_PASSWORD: Secret(), + }, + }, + enableBackups: true, + backupsConfig: { + imageTag: 'v1.7', + environments: { + VOLUMERIZE_SOURCE: '/source', + VOLUMERIZE_TARGET: 's3://s3.eu-central-1.amazonaws.com/duplicitytest', + VOLUMERIZE_JOBBER_TIME: '0 0 */4 * * *', + VOLUMERIZE_FULL_IF_OLDER_THAN: '3D', + JOB_NAME2: 'RemoveOldBackups', + JOB_COMMAND2: '/etc/volumerize/remove-older-than 1M --force', + JOB_TIME2: '0 0 2 * * *', + JOB_NAME3: 'CleanupBackups', + JOB_COMMAND3: '/etc/volumerize/cleanup --force', + JOB_TIME3: '0 0 3 * * *', + AWS_ACCESS_KEY_ID: 'QQWDQIWIDO1QO', + AWS_SECRET_ACCESS_KEY: 'ewlfkwkejflkjwlkej3fjw381', + POSTGRES_USER: Secret(), + POSTGRES_PASSWORD: Secret(), + POSTGRES_HOST: (service) => service.getServiceDomainName('postgres'), + POSTGRES_PORT: '5432', + POSTGRES_DB: 'api', + }, + }, + desiredCount: 1, + cpu: '256', + memory: '512', +}); +``` diff --git a/libs/terraform/service/postgres/src/lib/backup.container.ts b/libs/terraform/service/postgres/src/lib/backup.container.ts new file mode 100644 index 000000000..d9497b77c --- /dev/null +++ b/libs/terraform/service/postgres/src/lib/backup.container.ts @@ -0,0 +1,24 @@ +import { BackupContainerConfig } from './interfaces'; + +import { Container, MountPoint } from '@tractr/terraform-service-ecs'; + +export class BackupContainer extends Container { + protected getAppName(): string { + return 'tractr/postgres-backup'; + } + + getMountPoints(): MountPoint[] { + return [ + { + sourceVolume: 'cache', + containerPath: '/volumerize-cache/', + }, + { + sourceVolume: 'backups', + containerPath: '/backups/', + preventDestroy: true, + enableBackups: true, + }, + ]; + } +} diff --git a/libs/terraform/service/postgres/src/lib/interfaces/backup.interface.ts b/libs/terraform/service/postgres/src/lib/interfaces/backup.interface.ts new file mode 100644 index 000000000..5a84fefcc --- /dev/null +++ b/libs/terraform/service/postgres/src/lib/interfaces/backup.interface.ts @@ -0,0 +1,33 @@ +import { + ContainerInternalConfig, + ContainerPublicConfig, + EnvironmentOrSecretValue, +} from '@tractr/terraform-service-ecs'; + +export interface BackupContainerPublicConfig extends ContainerPublicConfig { + environments: ContainerPublicConfig['environments'] & { + VOLUMERIZE_SOURCE: EnvironmentOrSecretValue; + VOLUMERIZE_TARGET: EnvironmentOrSecretValue; + VOLUMERIZE_JOBBER_TIME: EnvironmentOrSecretValue; + VOLUMERIZE_FULL_IF_OLDER_THAN: EnvironmentOrSecretValue; + JOB_NAME2?: EnvironmentOrSecretValue; + JOB_COMMAND2?: EnvironmentOrSecretValue; + JOB_TIME2?: EnvironmentOrSecretValue; + JOB_NOTIFY_ERR2?: EnvironmentOrSecretValue; + JOB_NOTIFY_FAIL2?: EnvironmentOrSecretValue; + JOB_NAME3?: EnvironmentOrSecretValue; + JOB_COMMAND3?: EnvironmentOrSecretValue; + JOB_TIME3?: EnvironmentOrSecretValue; + JOB_NOTIFY_ERR3?: EnvironmentOrSecretValue; + JOB_NOTIFY_FAIL3?: EnvironmentOrSecretValue; + AWS_ACCESS_KEY_ID?: EnvironmentOrSecretValue; + AWS_SECRET_ACCESS_KEY?: EnvironmentOrSecretValue; + POSTGRES_USER: EnvironmentOrSecretValue; + POSTGRES_PASSWORD: EnvironmentOrSecretValue; + POSTGRES_HOST: EnvironmentOrSecretValue; + POSTGRES_PORT: EnvironmentOrSecretValue; + POSTGRES_DB: EnvironmentOrSecretValue; + }; +} +export type BackupContainerConfig = ContainerInternalConfig & + BackupContainerPublicConfig; diff --git a/libs/terraform/service/postgres/src/lib/interfaces/index.ts b/libs/terraform/service/postgres/src/lib/interfaces/index.ts index 7716f498d..c9462c8fb 100644 --- a/libs/terraform/service/postgres/src/lib/interfaces/index.ts +++ b/libs/terraform/service/postgres/src/lib/interfaces/index.ts @@ -1 +1,2 @@ +export * from './backup.interface'; export * from './postgres.interface'; diff --git a/libs/terraform/service/postgres/src/lib/interfaces/postgres.interface.ts b/libs/terraform/service/postgres/src/lib/interfaces/postgres.interface.ts index 6699cd26c..924ece1ab 100644 --- a/libs/terraform/service/postgres/src/lib/interfaces/postgres.interface.ts +++ b/libs/terraform/service/postgres/src/lib/interfaces/postgres.interface.ts @@ -1,5 +1,7 @@ import { DeepPartial } from 'ts-essentials'; +import { BackupContainerPublicConfig } from './backup.interface'; + import { BackendServiceComponentInternalConfig, ContainerInternalConfig, @@ -21,6 +23,8 @@ export type PostgresContainerConfig = ContainerInternalConfig & export interface PostgresComponentDefaultConfig extends ServiceComponentDefaultConfig { containerConfig: PostgresContainerPublicConfig; + backupsConfig: BackupContainerPublicConfig; + enableBackups: boolean; } export type PostgresComponentPublicConfig = DeepPartial; diff --git a/libs/terraform/service/postgres/src/lib/postgres.component.ts b/libs/terraform/service/postgres/src/lib/postgres.component.ts index 2be0ecd71..fc0b87722 100644 --- a/libs/terraform/service/postgres/src/lib/postgres.component.ts +++ b/libs/terraform/service/postgres/src/lib/postgres.component.ts @@ -1,3 +1,4 @@ +import { BackupContainer } from './backup.container'; import { PostgresComponentConfig, PostgresComponentDefaultConfig, @@ -19,12 +20,32 @@ export class PostgresComponent extends BackendServiceComponent< } protected getContainers(): Container[] { - return [ + const containers: Container[] = [ new PostgresContainer(this, { ...this.config.containerConfig, name: 'postgres', }), ]; + + if (this.config.enableBackups) { + containers.push( + new BackupContainer(this, { + ...this.config.backupsConfig, + name: 'backup', + }), + ); + } + + return containers; + } + + /** + * If the backups are enabled, the backup container need the call this service + * in order to connect to postgres. Therefore the security group should accept + * incoming connection from itself. + */ + protected shouldAccessItself(): boolean { + return this.config.enableBackups; } protected getDefaultConfig(): PostgresComponentDefaultConfig { @@ -38,6 +59,31 @@ export class PostgresComponent extends BackendServiceComponent< POSTGRES_PASSWORD: Secret(), }, }, + enableBackups: false, + backupsConfig: { + imageTag: 'v1.7', + environments: { + VOLUMERIZE_SOURCE: '/source', + VOLUMERIZE_TARGET: 'file:///backups', + VOLUMERIZE_JOBBER_TIME: '0 0 */4 * * *', + VOLUMERIZE_FULL_IF_OLDER_THAN: '3D', + JOB_NAME2: 'RemoveOldBackups', + JOB_COMMAND2: '/etc/volumerize/remove-older-than 1M --force', + JOB_TIME2: '0 0 2 * * *', + JOB_NOTIFY_ERR2: 'true', + JOB_NOTIFY_FAIL2: 'true', + JOB_NAME3: 'CleanupBackups', + JOB_COMMAND3: '/etc/volumerize/cleanup --force', + JOB_TIME3: '0 0 3 * * *', + JOB_NOTIFY_ERR3: 'true', + JOB_NOTIFY_FAIL3: 'true', + POSTGRES_DB: 'api', + POSTGRES_USER: Secret(), + POSTGRES_PASSWORD: Secret(), + POSTGRES_HOST: (service) => service.getServiceDomainName('postgres'), + POSTGRES_PORT: '5432', + }, + }, }; } }