diff --git a/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts b/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts index 52773f1f0bbea..3ad45964b992f 100644 --- a/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts +++ b/packages/aws-cdk-lib/aws-rds/lib/instance-engine.ts @@ -467,6 +467,132 @@ class MariaDbInstanceEngine extends InstanceEngineBase { } } +interface Db2InstanceEngineBaseProps { + readonly engineType: string; + readonly version?: EngineVersion; +} + +abstract class Db2InstanceEngineBase extends InstanceEngineBase { + constructor(props: Db2InstanceEngineBaseProps) { + super({ + ...props, + singleUserRotationApplication: secretsmanager.SecretRotationApplication.DB2_ROTATION_SINGLE_USER, + multiUserRotationApplication: secretsmanager.SecretRotationApplication.DB2_ROTATION_MULTI_USER, + parameterGroupFamily: props.version ? `${props.engineType}-${props.version.majorVersion}` : undefined, + features: { + s3Import: 'S3_INTEGRATION', + s3Export: 'S3_INTEGRATION', + }, + }); + } + + public bindToInstance(scope: Construct, options: InstanceEngineBindOptions): InstanceEngineConfig { + const config = super.bindToInstance(scope, options); + + let optionGroup = options.optionGroup; + if (options.s3ImportRole || options.s3ExportRole) { + if (!optionGroup) { + optionGroup = new OptionGroup(scope, 'InstanceOptionGroup', { + engine: this, + configurations: [], + }); + } + // https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/oracle-s3-integration.html + optionGroup.addConfiguration({ + name: 'S3_INTEGRATION', + version: '1.0', + }); + } + + return { + ...config, + optionGroup, + }; + } +} + +/** + * Properties for DB2 Standard Edition instance engines. + * Used in `DatabaseInstanceEngine.db2Se`. + */ +export interface Db2SeInstanceEngineProps extends Db2InstanceEngineProps { +} + +class Db2SeInstanceEngine extends Db2InstanceEngineBase { + constructor(version?: Db2EngineVersion) { + super({ + engineType: 'db2-se', + version: version + ? { + fullVersion: version.db2FullVersion, + majorVersion: version.db2MajorVersion, + } + : undefined, + }); + } +} + +/** + * Properties for DB2 Advanced Edition instance engines. + * Used in `DatabaseInstanceEngine.db2Ae`. + */ +export interface Db2AeInstanceEngineProps extends Db2InstanceEngineProps { +} + +class Db2AeInstanceEngine extends Db2InstanceEngineBase { + constructor(version?: Db2EngineVersion) { + super({ + engineType: 'db2-ae', + version: version + ? { + fullVersion: version.db2FullVersion, + majorVersion: version.db2MajorVersion, + } + : undefined, + }); + } +} + +/** + * The versions for the DB2 instance engines + * (those returned by `DatabaseInstanceEngine.mariaDb`). + */ +export class Db2EngineVersion { + /** Version "11.5". */ + public static readonly VER_11_5 = Db2EngineVersion.of('11.5', '11.5'); + + /** + * Create a new Db2EngineVersion with an arbitrary version. + * + * @param db2FullVersion the full version string, + * for example "10.5.28" + * @param db2MajorVersion the major version of the engine, + * for example "10.5" + */ + public static of(db2FullVersion: string, db2MajorVersion: string): Db2EngineVersion { + return new Db2EngineVersion(db2FullVersion, db2MajorVersion); + } + + /** The full version string, for example, "10.5.28". */ + public readonly db2FullVersion: string; + /** The major version of the engine, for example, "10.5". */ + public readonly db2MajorVersion: string; + + private constructor(db2FullVersion: string, db2MajorVersion: string) { + this.db2FullVersion = db2FullVersion; + this.db2MajorVersion = db2MajorVersion; + } +} + +/** + * Properties for DB2 instance engines. + * Used in `DatabaseInstanceEngine.mariaDb`. + */ +export interface Db2InstanceEngineProps { + /** The exact version of the engine to use. */ + readonly version: Db2EngineVersion; +} + /** * The versions for the MySQL instance engines * (those returned by `DatabaseInstanceEngine.mysql`). @@ -2139,6 +2265,22 @@ class SqlServerEeInstanceEngine extends SqlServerInstanceEngineBase { * secret rotation. */ export class DatabaseInstanceEngine { + /** + * The unversioned 'db2-se' instance engine. + * + * NOTE: using unversioned engines is an availability risk. + * We recommend using versioned engines created using the `db2Se()` methods + */ + public static readonly DB2_SE: IInstanceEngine = new Db2SeInstanceEngine(); + + /** + * The unversioned 'db2-ae' instance engine. + * + * NOTE: using unversioned engines is an availability risk. + * We recommend using versioned engines created using the `db2Ae()` methods + */ + public static readonly DB2_AE: IInstanceEngine = new Db2AeInstanceEngine(); + /** * The unversioned 'mariadb' instance engine. * @@ -2241,6 +2383,16 @@ export class DatabaseInstanceEngine { */ public static readonly SQL_SERVER_WEB: IInstanceEngine = new SqlServerWebInstanceEngine(); + /** Creates a new DB2 Standard Edition instance engine. */ + public static db2Se(props: Db2SeInstanceEngineProps): IInstanceEngine { + return new Db2SeInstanceEngine(props.version); + } + + /** Creates a new DB2 Advanced Edition instance engine. */ + public static db2Ae(props: Db2AeInstanceEngineProps): IInstanceEngine { + return new Db2AeInstanceEngine(props.version); + } + /** Creates a new MariaDB instance engine. */ public static mariaDb(props: MariaDbInstanceEngineProps): IInstanceEngine { return new MariaDbInstanceEngine(props.version); diff --git a/packages/aws-cdk-lib/aws-rds/test/db2/db2.instance-engine.test.ts b/packages/aws-cdk-lib/aws-rds/test/db2/db2.instance-engine.test.ts new file mode 100644 index 0000000000000..fe70199cca363 --- /dev/null +++ b/packages/aws-cdk-lib/aws-rds/test/db2/db2.instance-engine.test.ts @@ -0,0 +1,28 @@ +import { Template } from '../../../assertions'; +import * as core from '../../../core'; +import * as rds from '../../lib'; + +describe('DB2 server instance engine', () => { + describe('DB2 instance engine versions', () => { + test("has MajorEngineVersion ending in '11.5' for major version 11.5", () => { + const stack = new core.Stack(); + new rds.OptionGroup(stack, 'OptionGroup', { + engine: rds.DatabaseInstanceEngine.db2Se({ + version: rds.Db2EngineVersion.VER_11_5, + }), + configurations: [ + { + name: 'DB2_BACKUP_RESTORE', + settings: { + IAM_ROLE_ARN: 'some-role-arn', + }, + }, + ], + }); + + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { + MajorEngineVersion: '11.5', + }); + }); + }); +}); \ No newline at end of file diff --git a/packages/aws-cdk-lib/aws-rds/test/instance-engine.test.ts b/packages/aws-cdk-lib/aws-rds/test/instance-engine.test.ts index 6da65fd2d64d3..acb028d644c58 100644 --- a/packages/aws-cdk-lib/aws-rds/test/instance-engine.test.ts +++ b/packages/aws-cdk-lib/aws-rds/test/instance-engine.test.ts @@ -60,6 +60,22 @@ describe('instance engine', () => { expect(family).toEqual(undefined); }); + test('default parameterGroupFamily for versionless DB2 SE instance engine is not defined', () => { + const engine = rds.DatabaseInstanceEngine.DB2_SE; + + const family = engine.parameterGroupFamily; + + expect(family).toEqual(undefined); + }); + + test('default parameterGroupFamily for versionless DB2 AE instance engine is not defined', () => { + const engine = rds.DatabaseInstanceEngine.DB2_AE; + + const family = engine.parameterGroupFamily; + + expect(family).toEqual(undefined); + }); + test('default parameterGroupFamily for versionless Oracle EE instance engine is not defined', () => { const engine = rds.DatabaseInstanceEngine.ORACLE_EE; @@ -262,4 +278,62 @@ describe('instance engine', () => { expect(engineConfig.features?.s3Export).toEqual('s3Export'); }); }); + + describe('DB2 engine bindToInstance', () => { + + test('returns s3 integration feature', () => { + const engine = rds.DatabaseInstanceEngine.db2Se({ version: rds.Db2EngineVersion.VER_11_5 }); + + const engineConfig = engine.bindToInstance(new cdk.Stack(), {}); + expect(engineConfig.features?.s3Import).toEqual('S3_INTEGRATION'); + expect(engineConfig.features?.s3Export).toEqual('S3_INTEGRATION'); + }); + + test('s3 import/export - creates an option group if needed', () => { + const stack = new cdk.Stack(); + const engine = rds.DatabaseInstanceEngine.db2Se({ version: rds.Db2EngineVersion.VER_11_5 }); + + const engineConfig = engine.bindToInstance(stack, { + optionGroup: undefined, + s3ImportRole: new iam.Role(stack, 'ImportRole', { assumedBy: new iam.AccountRootPrincipal() }), + }); + + expect(engineConfig.optionGroup).toBeDefined(); + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { + EngineName: 'db2-se', + OptionConfigurations: [{ + OptionName: 'S3_INTEGRATION', + OptionVersion: '1.0', + }], + }); + }); + + test('s3 import/export - appends to an existing option group if it exists', () => { + const stack = new cdk.Stack(); + const engine = rds.DatabaseInstanceEngine.db2Ae({ version: rds.Db2EngineVersion.VER_11_5 }); + const optionGroup = new rds.OptionGroup(stack, 'OptionGroup', { + engine, + configurations: [{ + name: 'MY_OPTION_CONFIG', + }], + }); + + const engineConfig = engine.bindToInstance(stack, { + optionGroup, + s3ImportRole: new iam.Role(stack, 'ImportRole', { assumedBy: new iam.AccountRootPrincipal() }), + }); + + expect(engineConfig.optionGroup).toEqual(optionGroup); + Template.fromStack(stack).hasResourceProperties('AWS::RDS::OptionGroup', { + EngineName: 'db2-ae', + OptionConfigurations: [{ + OptionName: 'MY_OPTION_CONFIG', + }, + { + OptionName: 'S3_INTEGRATION', + OptionVersion: '1.0', + }], + }); + }); + }); }); diff --git a/packages/aws-cdk-lib/aws-rds/test/instance.test.ts b/packages/aws-cdk-lib/aws-rds/test/instance.test.ts index 92f9d41cccc1e..c43d3e3c656a7 100644 --- a/packages/aws-cdk-lib/aws-rds/test/instance.test.ts +++ b/packages/aws-cdk-lib/aws-rds/test/instance.test.ts @@ -1030,7 +1030,8 @@ describe('instance', () => { const tzSupportedEngines = [rds.DatabaseInstanceEngine.SQL_SERVER_EE, rds.DatabaseInstanceEngine.SQL_SERVER_EX, rds.DatabaseInstanceEngine.SQL_SERVER_SE, rds.DatabaseInstanceEngine.SQL_SERVER_WEB]; const tzUnsupportedEngines = [rds.DatabaseInstanceEngine.MYSQL, rds.DatabaseInstanceEngine.POSTGRES, - rds.DatabaseInstanceEngine.ORACLE_EE, rds.DatabaseInstanceEngine.MARIADB]; + rds.DatabaseInstanceEngine.ORACLE_EE, rds.DatabaseInstanceEngine.MARIADB, rds.DatabaseInstanceEngine.DB2_SE, + rds.DatabaseInstanceEngine.DB2_AE]; // THEN tzSupportedEngines.forEach((engine) => { @@ -1349,7 +1350,8 @@ describe('instance', () => { test('throws when domain is set for mariadb database engine', () => { const domainSupportedEngines = [rds.DatabaseInstanceEngine.SQL_SERVER_EE, rds.DatabaseInstanceEngine.SQL_SERVER_EX, rds.DatabaseInstanceEngine.SQL_SERVER_SE, rds.DatabaseInstanceEngine.SQL_SERVER_WEB, rds.DatabaseInstanceEngine.MYSQL, - rds.DatabaseInstanceEngine.POSTGRES, rds.DatabaseInstanceEngine.ORACLE_EE]; + rds.DatabaseInstanceEngine.POSTGRES, rds.DatabaseInstanceEngine.ORACLE_EE, rds.DatabaseInstanceEngine.DB2_SE, + rds.DatabaseInstanceEngine.DB2_AE]; const domainUnsupportedEngines = [rds.DatabaseInstanceEngine.MARIADB]; // THEN diff --git a/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts b/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts index aa8ae986f407b..1deefd6c61a1c 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/lib/rotation-schedule.ts @@ -249,6 +249,16 @@ export class HostedRotation implements ec2.IConnectable { return new HostedRotation(HostedRotationType.ORACLE_MULTI_USER, options, options.masterSecret); } + /** DB2 Single User */ + public static db2SingleUser(options: SingleUserHostedRotationOptions = {}) { + return new HostedRotation(HostedRotationType.DB2_SINGLE_USER, options); + } + + /** DB2 Multi User */ + public static db2MultiUser(options: MultiUserHostedRotationOptions) { + return new HostedRotation(HostedRotationType.DB2_MULTI_USER, options, options.masterSecret); + } + /** MariaDB Single User */ public static mariaDbSingleUser(options: SingleUserHostedRotationOptions = {}) { return new HostedRotation(HostedRotationType.MARIADB_SINGLE_USER, options); @@ -411,6 +421,12 @@ export class HostedRotationType { /** MongoDB Multi User */ public static readonly MONGODB_MULTI_USER = new HostedRotationType('MongoDBMultiUser', true); + /** DB2 Single User */ + public static readonly DB2_SINGLE_USER = new HostedRotationType('Db2SingleUser'); + + /** DB2 Multi User */ + public static readonly DB2_MULTI_USER = new HostedRotationType('Db2MultiUser', true); + /** * @param name The type of rotation * @param isMultiUser Whether the rotation uses the mutli user scheme diff --git a/packages/aws-cdk-lib/aws-secretsmanager/lib/secret-rotation.ts b/packages/aws-cdk-lib/aws-secretsmanager/lib/secret-rotation.ts index 6a05e03627465..f8992b6745e52 100644 --- a/packages/aws-cdk-lib/aws-secretsmanager/lib/secret-rotation.ts +++ b/packages/aws-cdk-lib/aws-secretsmanager/lib/secret-rotation.ts @@ -57,6 +57,18 @@ export class SecretRotationApplication { isMultiUser: true, }); + /** + * Conducts an AWS SecretsManager secret rotation for RDS DB2 using the single user rotation scheme + */ + public static readonly DB2_ROTATION_SINGLE_USER = new SecretRotationApplication('SecretsManagerRDSDb2RotationSingleUser', '1.1.367'); + + /** + * Conducts an AWS SecretsManager secret rotation for RDS DB2 using the multi user rotation scheme + */ + public static readonly DB2_ROTATION_MULTI_USER = new SecretRotationApplication('SecretsManagerRDSDb2RotationMultiUser', '1.1.367', { + isMultiUser: true, + }); + /** * Conducts an AWS SecretsManager secret rotation for RDS PostgreSQL using the single user rotation scheme */