Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(opensearchservice): L2 properties for offPeakWindowOptions and softwareUpdateOptions #26403

Merged
merged 11 commits into from
Jul 27, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,18 @@
},
"NodeToNodeEncryptionOptions": {
"Enabled": true
},
"OffPeakWindowOptions": {
"Enabled": true,
"OffPeakWindow": {
"WindowStartTime": {
"Hours": 20,
"Minutes": 0
}
}
},
"SoftwareUpdateOptions": {
"AutoSoftwareUpdateEnabled": true
}
},
"DependsOn": [
Expand Down Expand Up @@ -458,6 +470,18 @@
},
"NodeToNodeEncryptionOptions": {
"Enabled": true
},
"OffPeakWindowOptions": {
"Enabled": true,
"OffPeakWindow": {
"WindowStartTime": {
"Hours": 20,
"Minutes": 0
}
}
},
"SoftwareUpdateOptions": {
"AutoSoftwareUpdateEnabled": true
}
},
"DependsOn": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ class TestStack extends Stack {
capacity: {
multiAzWithStandbyEnabled: false,
},
offPeakWindowEnabled: true,
offPeakWindowStart: {
hours: 20,
minutes: 0,
},
enableAutoSoftwareUpdate: true,
};

// create 2 domains to ensure that Cloudwatch Log Group policy names dont conflict
Expand Down
36 changes: 36 additions & 0 deletions packages/aws-cdk-lib/aws-opensearchservice/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -396,4 +396,40 @@ const domain = new Domain(this, 'Domain', {
dataNodes: 3,
},
});
```

## Define off-peak windows

The domain can be configured to use a daily 10-hour window considered as off-peak hours.

Off-peak windows were introduced on February 16, 2023.
All domains created before this date have the off-peak window disabled by default.
You must manually enable and configure the off-peak window for these domains.
All domains created after this date will have the off-peak window enabled by default.
You can't disable the off-peak window for a domain after it's enabled.

> Visit [Defining off-peak windows for Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/off-peak.html) for more details.

```ts
const domain = new Domain(this, 'Domain', {
version: EngineVersion.OPENSEARCH_1_3,
offPeakWindowEnabled: true, // can be omitted if offPeakWindowStart is set
offPeakWindowStart: {
hours: 20,
minutes: 0,
},
});
```

## Configuring service software updates

The domain can be configured to use service software updates.

> Visit [Service software updates in Amazon OpenSearch Service](https://docs.aws.amazon.com/opensearch-service/latest/developerguide/service-software.html) for more details.

```ts
const domain = new Domain(this, 'Domain', {
version: EngineVersion.OPENSEARCH_1_3,
enableAutoSoftwareUpdate: true,
});
```
84 changes: 83 additions & 1 deletion packages/aws-cdk-lib/aws-opensearchservice/lib/domain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,22 @@ export interface CustomEndpointOptions {
readonly hostedZone?: route53.IHostedZone;
}

export interface WindowStartTime {
/**
* The start hour of the window in Coordinated Universal Time (UTC), using 24-hour time.
* For example, 17 refers to 5:00 P.M. UTC.
*
* @default - 22
*/
readonly hours: number;
/**
* The start minute of the window, in UTC.
*
* @default - 0
*/
readonly minutes: number;
}

/**
* Properties for an Amazon OpenSearch Service domain.
*/
Expand Down Expand Up @@ -493,6 +509,7 @@ export interface DomainProps {
* domain resource, use the EnableVersionUpgrade update policy.
*
* @see https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-attribute-updatepolicy.html#cfn-attributes-updatepolicy-upgradeopensearchdomain
*
* @default - false
*/
readonly enableVersionUpgrade?: boolean;
Expand All @@ -508,9 +525,44 @@ export interface DomainProps {
* To configure a custom domain configure these options
*
* If you specify a Route53 hosted zone it will create a CNAME record and use DNS validation for the certificate
*
* @default - no custom domain endpoint will be configured
*/
readonly customEndpoint?: CustomEndpointOptions;

/**
* Options for enabling a domain's off-peak window, during which OpenSearch Service can perform mandatory
* configuration changes on the domain.
*
* Off-peak windows were introduced on February 16, 2023.
* All domains created before this date have the off-peak window disabled by default.
* You must manually enable and configure the off-peak window for these domains.
* All domains created after this date will have the off-peak window enabled by default.
* You can't disable the off-peak window for a domain after it's enabled.
*
* @see https://docs.aws.amazon.com/it_it/AWSCloudFormation/latest/UserGuide/aws-properties-opensearchservice-domain-offpeakwindow.html
*
* @default - Disabled for domains created before February 16, 2023. Enabled for domains created after. Enabled if `offPeakWindowStart` is set.
*/
readonly offPeakWindowEnabled?: boolean;

/**
* Start time for the off-peak window, in Coordinated Universal Time (UTC).
* The window length will always be 10 hours, so you can't specify an end time.
* For example, if you specify 11:00 P.M. UTC as a start time, the end time will automatically be set to 9:00 A.M.
*
* @default - 10:00 P.M. local time
*/
readonly offPeakWindowStart?: WindowStartTime;

/**
* Specifies whether automatic service software updates are enabled for the domain.
*
* @see https://docs.aws.amazon.com/it_it/AWSCloudFormation/latest/UserGuide/aws-properties-opensearchservice-domain-softwareupdateoptions.html
*
* @default - false
*/
readonly enableAutoSoftwareUpdate?: boolean;
}

/**
Expand Down Expand Up @@ -1089,7 +1141,6 @@ abstract class DomainBase extends cdk.Resource implements IDomain {

return grant;
}

}

/**
Expand Down Expand Up @@ -1558,6 +1609,11 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
}
}

const offPeakWindowEnabled = props.offPeakWindowEnabled ?? props.offPeakWindowStart !== undefined;
if (offPeakWindowEnabled) {
this.validateWindowStartTime(props.offPeakWindowStart);
}

// Create the domain
this.domain = new CfnDomain(this, 'Resource', {
domainName: this.physicalName,
Expand Down Expand Up @@ -1632,6 +1688,18 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
}
: undefined,
advancedOptions: props.advancedOptions,
offPeakWindowOptions: offPeakWindowEnabled ? {
enabled: offPeakWindowEnabled,
offPeakWindow: {
windowStartTime: props.offPeakWindowStart ?? {
hours: 22,
minutes: 0,
},
},
} : undefined,
softwareUpdateOptions: props.enableAutoSoftwareUpdate ? {
autoSoftwareUpdateEnabled: props.enableAutoSoftwareUpdate,
} : undefined,
});
this.domain.applyRemovalPolicy(props.removalPolicy);

Expand Down Expand Up @@ -1689,6 +1757,20 @@ export class Domain extends DomainBase implements IDomain, ec2.IConnectable {
}
}

/**
* Validate windowStartTime property according to
* https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-properties-opensearchservice-domain-windowstarttime.html
*/
private validateWindowStartTime(windowStartTime?: WindowStartTime) {
if (!windowStartTime) return;
if (windowStartTime.hours < 0 || windowStartTime.hours > 23) {
throw new Error(`Hours must be a value between 0 and 23, but got ${windowStartTime.hours}.`);
}
if (windowStartTime.minutes < 0 || windowStartTime.minutes > 59) {
throw new Error(`Minutes must be a value between 0 and 59, but got ${windowStartTime.minutes}.`);
}
}

/**
* Manages network connections to the domain. This will throw an error in case the domain
* is not placed inside a VPC.
Expand Down
107 changes: 107 additions & 0 deletions packages/aws-cdk-lib/aws-opensearchservice/test/domain.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1980,6 +1980,113 @@ each(testedOpenSearchVersions).describe('cognito dashboards auth', (engineVersio
});
});

each(testedOpenSearchVersions).describe('offPeakWindow and softwareUpdateOptions', (engineVersion) => {
test('with offPeakWindowStart and offPeakWindowEnabled', () => {
new Domain(stack, 'Domain', {
version: engineVersion,
offPeakWindowEnabled: true,
offPeakWindowStart: {
hours: 10,
minutes: 30,
},
});

Template.fromStack(stack).hasResourceProperties('AWS::OpenSearchService::Domain', {
OffPeakWindowOptions: {
Enabled: true,
OffPeakWindow: {
WindowStartTime: {
Hours: 10,
Minutes: 30,
},
},
},
});
});

test('with offPeakWindowStart only', () => {
new Domain(stack, 'Domain', {
version: engineVersion,
offPeakWindowStart: {
hours: 10,
minutes: 30,
},
});

Template.fromStack(stack).hasResourceProperties('AWS::OpenSearchService::Domain', {
OffPeakWindowOptions: {
Enabled: true,
OffPeakWindow: {
WindowStartTime: {
Hours: 10,
Minutes: 30,
},
},
},
});
});

test('with offPeakWindowOptions default start time', () => {
new Domain(stack, 'Domain', {
version: engineVersion,
offPeakWindowEnabled: true,
});

Template.fromStack(stack).hasResourceProperties('AWS::OpenSearchService::Domain', {
OffPeakWindowOptions: {
Enabled: true,
OffPeakWindow: {
WindowStartTime: {
Hours: 22,
Minutes: 0,
},
},
},
});
});

test('with autoSoftwareUpdateEnabled', () => {
new Domain(stack, 'Domain', {
version: engineVersion,
enableAutoSoftwareUpdate: true,
});

Template.fromStack(stack).hasResourceProperties('AWS::OpenSearchService::Domain', {
SoftwareUpdateOptions: {
AutoSoftwareUpdateEnabled: true,
},
});
});

test('with invalid offPeakWindowStart', () => {
expect(() => {
new Domain(stack, 'Domain1', {
version: engineVersion,
offPeakWindowEnabled: true,
offPeakWindowStart: {
hours: 50,
minutes: 0,
},
});
}).toThrow(
/Hours must be a value between 0 and 23/,
);

expect(() => {
new Domain(stack, 'Domain2', {
version: engineVersion,
offPeakWindowEnabled: true,
offPeakWindowStart: {
hours: 10,
minutes: 90,
},
});
}).toThrow(
/Minutes must be a value between 0 and 59/,
);
});
});

function testGrant(
expectedActions: string[],
invocation: (user: iam.IPrincipal, domain: Domain) => void,
Expand Down