aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
635 lines • 83.4 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const client_ecs_1 = require("@aws-sdk/client-ecs");
const setup = require("./hotswap-test-setup");
const common_1 = require("../../../lib/api/hotswap/common");
const user_configuration_1 = require("../../../lib/cli/user-configuration");
const mock_sdk_1 = require("../../util/mock-sdk");
const silent_1 = require("../../util/silent");
let hotswapMockSdkProvider;
beforeEach(() => {
hotswapMockSdkProvider = setup.setupHotswapTests();
mock_sdk_1.mockECSClient
.on(client_ecs_1.UpdateServiceCommand)
.resolves({
service: {
clusterArn: 'arn:aws:ecs:region:account:service/my-cluster',
serviceArn: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
},
})
.on(client_ecs_1.DescribeServicesCommand)
.resolves({
services: [
{
deployments: [
{
desiredCount: 1,
runningCount: 1,
},
],
},
],
});
});
describe.each([common_1.HotswapMode.FALL_BACK, common_1.HotswapMode.HOTSWAP_ONLY])('%p mode', (hotswapMode) => {
(0, silent_1.silentTest)('should call registerTaskDefinition and updateService for a difference only in the TaskDefinition with a Family property', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image1' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image2' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [{ image: 'image2' }],
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: 0,
},
forceNewDeployment: true,
});
});
(0, silent_1.silentTest)('any other TaskDefinition property change besides ContainerDefinition cannot be hotswapped in CLASSIC mode but does not block HOTSWAP_ONLY mode deployments', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image1' }],
Cpu: '256',
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image2' }],
Cpu: '512',
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.RegisterTaskDefinitionCommand);
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.UpdateServiceCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [{ image: 'image2' }],
cpu: '256', // this uses the old value because a new value could cause a service replacement
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: 0,
},
forceNewDeployment: true,
});
}
});
(0, silent_1.silentTest)('deleting any other TaskDefinition property besides ContainerDefinition results in a full deployment in CLASSIC mode and a hotswap deployment in HOTSWAP_ONLY mode', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image1' }],
Cpu: '256',
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image2' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.RegisterTaskDefinitionCommand);
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.UpdateServiceCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [{ image: 'image2' }],
cpu: '256', // this uses the old value because a new value could cause a service replacement
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: 0,
},
forceNewDeployment: true,
});
}
});
(0, silent_1.silentTest)('should call registerTaskDefinition and updateService for a difference only in the TaskDefinition without a Family property', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
ContainerDefinitions: [{ Image: 'image1' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('TaskDef', 'AWS::ECS::TaskDefinition', 'arn:aws:ecs:region:account:task-definition/my-task-def:2'), setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
ContainerDefinitions: [{ Image: 'image2' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [{ image: 'image2' }],
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: 0,
},
forceNewDeployment: true,
});
});
(0, silent_1.silentTest)('a difference just in a TaskDefinition, without any services using it, is not hotswappable in FALL_BACK mode', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
ContainerDefinitions: [{ Image: 'image1' }],
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('TaskDef', 'AWS::ECS::TaskDefinition', 'arn:aws:ecs:region:account:task-definition/my-task-def:2'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
ContainerDefinitions: [{ Image: 'image2' }],
},
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.RegisterTaskDefinitionCommand);
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.UpdateServiceCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [{ image: 'image2' }],
});
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.UpdateServiceCommand);
}
});
(0, silent_1.silentTest)('if anything besides an ECS Service references the changed TaskDefinition, hotswapping is not possible in CLASSIC mode but is possible in HOTSWAP_ONLY', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image1' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
Function: {
Type: 'AWS::Lambda::Function',
Properties: {
Environment: {
Variables: {
TaskDefRevArn: { Ref: 'TaskDef' },
},
},
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image2' }],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
Function: {
Type: 'AWS::Lambda::Function',
Properties: {
Environment: {
Variables: {
TaskDefRevArn: { Ref: 'TaskDef' },
},
},
},
},
},
},
});
if (hotswapMode === common_1.HotswapMode.FALL_BACK) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).toBeUndefined();
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.RegisterTaskDefinitionCommand);
expect(mock_sdk_1.mockECSClient).not.toHaveReceivedCommand(client_ecs_1.UpdateServiceCommand);
}
else if (hotswapMode === common_1.HotswapMode.HOTSWAP_ONLY) {
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [{ image: 'image2' }],
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: 0,
},
forceNewDeployment: true,
});
}
});
(0, silent_1.silentTest)('should call registerTaskDefinition with certain properties not lowercased', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [{ Image: 'image1' }],
Volumes: [
{
DockerVolumeConfiguration: {
DriverOpts: { Option1: 'option1' },
Labels: { Label1: 'label1' },
},
},
],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [
{
Image: 'image2',
DockerLabels: { Label1: 'label1' },
FirelensConfiguration: {
Options: { Name: 'cloudwatch' },
},
LogConfiguration: {
Options: { Option1: 'option1' },
},
},
],
Volumes: [
{
DockerVolumeConfiguration: {
DriverOpts: { Option1: 'option1' },
Labels: { Label1: 'label1' },
},
},
],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
},
});
// WHEN
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(hotswapMode, cdkStackArtifact);
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [
{
image: 'image2',
dockerLabels: { Label1: 'label1' },
firelensConfiguration: {
options: {
Name: 'cloudwatch',
},
},
logConfiguration: {
options: { Option1: 'option1' },
},
},
],
volumes: [
{
dockerVolumeConfiguration: {
driverOpts: { Option1: 'option1' },
labels: { Label1: 'label1' },
},
},
],
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: 0,
},
forceNewDeployment: true,
});
});
});
describe.each([
new user_configuration_1.Configuration().settings.set(['hotswap'], { ecs: { minimumHealthyPercent: 10 } }),
new user_configuration_1.Configuration().settings.set(['hotswap'], { ecs: { minimumHealthyPercent: 10, maximumHealthyPercent: 100 } }),
])('hotswap properties', (settings) => {
test('should handle all possible hotswap properties', async () => {
// GIVEN
setup.setCurrentCfnStackTemplate({
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [
{ Image: 'image1' },
],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
});
setup.pushStackResourceSummaries(setup.stackSummaryOf('Service', 'AWS::ECS::Service', 'arn:aws:ecs:region:account:service/my-cluster/my-service'));
mock_sdk_1.mockECSClient.on(client_ecs_1.RegisterTaskDefinitionCommand).resolves({
taskDefinition: {
taskDefinitionArn: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
},
});
const cdkStackArtifact = setup.cdkStackArtifactOf({
template: {
Resources: {
TaskDef: {
Type: 'AWS::ECS::TaskDefinition',
Properties: {
Family: 'my-task-def',
ContainerDefinitions: [
{ Image: 'image2' },
],
},
},
Service: {
Type: 'AWS::ECS::Service',
Properties: {
TaskDefinition: { Ref: 'TaskDef' },
},
},
},
},
});
// WHEN
let ecsHotswapProperties = new common_1.EcsHotswapProperties(settings.get(['hotswap']).ecs.minimumHealthyPercent, settings.get(['hotswap']).ecs.maximumHealthyPercent);
const deployStackResult = await hotswapMockSdkProvider.tryHotswapDeployment(common_1.HotswapMode.HOTSWAP_ONLY, cdkStackArtifact, {}, new common_1.HotswapPropertyOverrides(ecsHotswapProperties));
// THEN
expect(deployStackResult).not.toBeUndefined();
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.RegisterTaskDefinitionCommand, {
family: 'my-task-def',
containerDefinitions: [
{ image: 'image2' },
],
});
expect(mock_sdk_1.mockECSClient).toHaveReceivedCommandWith(client_ecs_1.UpdateServiceCommand, {
service: 'arn:aws:ecs:region:account:service/my-cluster/my-service',
cluster: 'my-cluster',
taskDefinition: 'arn:aws:ecs:region:account:task-definition/my-task-def:3',
deploymentConfiguration: {
minimumHealthyPercent: settings.get(['hotswap']).ecs?.minimumHealthyPercent == undefined ?
0 : settings.get(['hotswap']).ecs?.minimumHealthyPercent,
maximumPercent: settings.get(['hotswap']).ecs?.maximumHealthyPercent,
},
forceNewDeployment: true,
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZWNzLXNlcnZpY2VzLWhvdHN3YXAtZGVwbG95bWVudHMudGVzdC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbImVjcy1zZXJ2aWNlcy1ob3Rzd2FwLWRlcGxveW1lbnRzLnRlc3QudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSxvREFBbUg7QUFDbkgsOENBQThDO0FBQzlDLDREQUE4RztBQUM5Ryw0RUFBb0U7QUFDcEUsa0RBQW9EO0FBQ3BELDhDQUErQztBQUUvQyxJQUFJLHNCQUFvRCxDQUFDO0FBRXpELFVBQVUsQ0FBQyxHQUFHLEVBQUU7SUFDZCxzQkFBc0IsR0FBRyxLQUFLLENBQUMsaUJBQWlCLEVBQUUsQ0FBQztJQUNuRCx3QkFBYTtTQUNWLEVBQUUsQ0FBQyxpQ0FBb0IsQ0FBQztTQUN4QixRQUFRLENBQUM7UUFDUixPQUFPLEVBQUU7WUFDUCxVQUFVLEVBQUUsK0NBQStDO1lBQzNELFVBQVUsRUFBRSwwREFBMEQ7U0FDdkU7S0FDRixDQUFDO1NBQ0QsRUFBRSxDQUFDLG9DQUF1QixDQUFDO1NBQzNCLFFBQVEsQ0FBQztRQUNSLFFBQVEsRUFBRTtZQUNSO2dCQUNFLFdBQVcsRUFBRTtvQkFDWDt3QkFDRSxZQUFZLEVBQUUsQ0FBQzt3QkFDZixZQUFZLEVBQUUsQ0FBQztxQkFDaEI7aUJBQ0Y7YUFDRjtTQUNGO0tBQ0YsQ0FBQyxDQUFDO0FBQ1AsQ0FBQyxDQUFDLENBQUM7QUFFSCxRQUFRLENBQUMsSUFBSSxDQUFDLENBQUMsb0JBQVcsQ0FBQyxTQUFTLEVBQUUsb0JBQVcsQ0FBQyxZQUFZLENBQUMsQ0FBQyxDQUFDLFNBQVMsRUFBRSxDQUFDLFdBQVcsRUFBRSxFQUFFO0lBQzFGLElBQUEsbUJBQVUsRUFDUix5SEFBeUgsRUFDekgsS0FBSyxJQUFJLEVBQUU7UUFDVCxRQUFRO1FBQ1IsS0FBSyxDQUFDLDBCQUEwQixDQUFDO1lBQy9CLFNBQVMsRUFBRTtnQkFDVCxPQUFPLEVBQUU7b0JBQ1AsSUFBSSxFQUFFLDBCQUEwQjtv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