@scloud/cdk-patterns
Version:
Serverless CDK patterns for common infrastructure needs
97 lines • 16.2 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.FargateService = void 0;
const aws_certificatemanager_1 = require("aws-cdk-lib/aws-certificatemanager");
const aws_cdk_lib_1 = require("aws-cdk-lib");
const aws_elasticloadbalancingv2_1 = require("aws-cdk-lib/aws-elasticloadbalancingv2");
const aws_ec2_1 = require("aws-cdk-lib/aws-ec2");
const aws_ecs_patterns_1 = require("aws-cdk-lib/aws-ecs-patterns");
const aws_ecs_1 = require("aws-cdk-lib/aws-ecs");
const aws_logs_1 = require("aws-cdk-lib/aws-logs");
const constructs_1 = require("constructs");
const EcrRepository_1 = require("./EcrRepository");
/**
* Builds an ApplicationLoadBalancedFargateService that runs a container on ECS Fargate.
*
* Warning! This pattern is not 'pure' serverless! It gennerates 24x7 running costs per container (rather than being billed on traffic/storage).
*
* Warning! If you don't pass a vpc, this construct creates a vpc for you and limits the nuber of NAT gateways to 1 to reduce cost. This is less resilient, but NAT gateways are costly!
*
* If you'd like to avoid this tradeoof, pass in a vpc you've createed that ha zero NAT gateways and is configured with PrivateEndpoint(s) that will allow ECS to pull container images.
*
* @param serviceName Name for the service
* @param zone DNS zone
* @param domain Optional: by default the zone name will be used as the DNS name for the service (e.g. 'example.com') but you can specify a different domain here (e.g. 'subdomain.example.com').
* @param environment Any environment variables for the container
* @param repository Optional: if you want to use an existing container image repository
* @param tag Optional: defaults to 'latest'
* @param vpc Optional: if you want to use an existing VPC. In not set, a vpc will be created for you
* @param cpu Optional: defaults to 512
* @param memory Optional: defaults to 1024
* @param taskCount Optional: defaults to 2 for redundancy. Set to 1 if you want to reduce cost.
* @param zeroTasks Sets task count to zero. Pass true if you don't have an image in ECR yet, otherwise this construct will fail to build.
* @param containerPort Optional: defaults to 3000. This is the port the application in your container listens on.
* @returns Deplyment detais
*/
class FargateService extends constructs_1.Construct {
constructor(scope, id, serviceName, zone, domain, environment = {}, repository = undefined, tag = 'latest', vpc = undefined, cpu = 512, memory = 1024, taskCount = 2, zeroTasks = false, containerPort = 3000) {
super(scope, `${id}FargateService`);
// Container repository
this.repository = repository || new EcrRepository_1.EcrRepository(scope, id);
// It seems like NAT gateways are costly, so I've set this up to avoid that - only creating one.
// At some point we may want to figure out a privte endpoint so that we can retire the NAT.
// Based on: https://www.binarythinktank.com/blog/truly-serverless-container
// and https://stackoverflow.com/questions/64299664/how-to-configure-aws-cdk-applicationloadbalancedfargateservice-to-log-parsed-jso
this.vpc = vpc || new aws_ec2_1.Vpc(scope, `${id}Vpc`, {
natGateways: 1,
subnetConfiguration: [{
name: id,
subnetType: aws_ec2_1.SubnetType.PUBLIC,
}],
});
// Fargate
this.albFargateService = new aws_ecs_patterns_1.ApplicationLoadBalancedFargateService(scope, `${id}AlbFargateService`, {
loadBalancerName: id,
serviceName,
domainZone: zone,
domainName: domain || zone.zoneName,
certificate: new aws_certificatemanager_1.DnsValidatedCertificate(scope, id, {
domainName: domain || zone.zoneName,
hostedZone: zone,
}),
protocol: aws_elasticloadbalancingv2_1.ApplicationProtocol.HTTPS,
cpu,
memoryLimitMiB: memory,
taskImageOptions: {
containerName: id,
image: aws_ecs_1.ContainerImage.fromEcrRepository(this.repository, tag),
containerPort,
environment,
logDriver: aws_ecs_1.LogDrivers.awsLogs({
streamPrefix: id,
logGroup: new aws_logs_1.LogGroup(scope, `${id}LogGroup`, {
// Ensure the log group is deleted when the stack is deleted
// and that logs aren't retained indefinitely
logGroupName: `/${aws_cdk_lib_1.Stack.of(scope).stackName}/ecs/${id}`,
removalPolicy: aws_cdk_lib_1.RemovalPolicy.DESTROY,
retention: aws_logs_1.RetentionDays.THREE_MONTHS,
}),
}),
},
desiredCount: taskCount,
vpc: this.vpc,
// ? https://stackoverflow.com/questions/67301268/aws-fargate-resourceinitializationerror-unable-to-pull-secrets-or-registry-auth
assignPublicIp: true,
});
this.albFargateService.loadBalancer.addRedirect(); // http -> https
if (zeroTasks) {
// On the first deploy, when there's no image in the repository, setting desired tasks to zero allows this construct to build, otherwise :
// https://github.com/aws/aws-cdk/issues/3646#issuecomment-623919242
const { node } = this.albFargateService.service;
const cfnService = node.findChild('Service');
cfnService.desiredCount = 0;
}
}
}
exports.FargateService = FargateService;
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRmFyZ2F0ZUNvbnRhaW5lci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9GYXJnYXRlQ29udGFpbmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLCtFQUE2RTtBQUM3RSw2Q0FBbUQ7QUFDbkQsdUZBQTZFO0FBQzdFLGlEQUFzRDtBQUN0RCxtRUFBcUY7QUFHckYsaURBQTZFO0FBQzdFLG1EQUErRDtBQUMvRCwyQ0FBdUM7QUFDdkMsbURBQWdEO0FBRWhEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0dBc0JHO0FBQ0gsTUFBYSxjQUFlLFNBQVEsc0JBQVM7SUFTM0MsWUFDRSxLQUFnQixFQUNoQixFQUFVLEVBQ1YsV0FBbUIsRUFDbkIsSUFBaUIsRUFDakIsTUFBZSxFQUNmLGNBQTBDLEVBQUUsRUFDNUMsYUFBcUMsU0FBUyxFQUM5QyxNQUFjLFFBQVEsRUFDdEIsTUFBdUIsU0FBUyxFQUNoQyxNQUFjLEdBQUcsRUFDakIsU0FBaUIsSUFBSSxFQUNyQixZQUFvQixDQUFDLEVBQ3JCLFlBQXFCLEtBQUssRUFDMUIsZ0JBQXdCLElBQUk7UUFFNUIsS0FBSyxDQUFDLEtBQUssRUFBRSxHQUFHLEVBQUUsZ0JBQWdCLENBQUMsQ0FBQztRQUVwQyx1QkFBdUI7UUFDdkIsSUFBSSxDQUFDLFVBQVUsR0FBRyxVQUFVLElBQUksSUFBSSw2QkFBYSxDQUFDLEtBQUssRUFBRSxFQUFFLENBQUMsQ0FBQztRQUM3RCxnR0FBZ0c7UUFDaEcsMkZBQTJGO1FBQzNGLDRFQUE0RTtRQUM1RSxvSUFBb0k7UUFDcEksSUFBSSxDQUFDLEdBQUcsR0FBRyxHQUFHLElBQUksSUFBSSxhQUFHLENBQUMsS0FBSyxFQUFFLEdBQUcsRUFBRSxLQUFLLEVBQUU7WUFDM0MsV0FBVyxFQUFFLENBQUM7WUFDZCxtQkFBbUIsRUFBRSxDQUFDO29CQUNwQixJQUFJLEVBQUUsRUFBRTtvQkFDUixVQUFVLEVBQUUsb0JBQVUsQ0FBQyxNQUFNO2lCQUM5QixDQUFDO1NBQ0gsQ0FBQyxDQUFDO1FBRUgsVUFBVTtRQUNWLElBQUksQ0FBQyxpQkFBaUIsR0FBRyxJQUFJLHdEQUFxQyxDQUNoRSxLQUFLLEVBQ0wsR0FBRyxFQUFFLG1CQUFtQixFQUN4QjtZQUNFLGdCQUFnQixFQUFFLEVBQUU7WUFDcEIsV0FBVztZQUNYLFVBQVUsRUFBRSxJQUFJO1lBQ2hCLFVBQVUsRUFBRSxNQUFNLElBQUksSUFBSSxDQUFDLFFBQVE7WUFDbkMsV0FBVyxFQUFFLElBQUksZ0RBQXVCLENBQUMsS0FBSyxFQUFFLEVBQUUsRUFBRTtnQkFDbEQsVUFBVSxFQUFFLE1BQU0sSUFBSSxJQUFJLENBQUMsUUFBUTtnQkFDbkMsVUFBVSxFQUFFLElBQUk7YUFDakIsQ0FBQztZQUNGLFFBQVEsRUFBRSxnREFBbUIsQ0FBQyxLQUFLO1lBQ25DLEdBQUc7WUFDSCxjQUFjLEVBQUUsTUFBTTtZQUN0QixnQkFBZ0IsRUFBRTtnQkFDaEIsYUFBYSxFQUFFLEVBQUU7Z0JBQ2pCLEtBQUssRUFBRSx3QkFBYyxDQUFDLGlCQUFpQixDQUFDLElBQUksQ0FBQyxVQUFVLEVBQUUsR0FBRyxDQUFDO2dCQUM3RCxhQUFhO2dCQUNiLFdBQVc7Z0JBQ1gsU0FBUyxFQUFFLG9CQUFVLENBQUMsT0FBTyxDQUFDO29CQUM1QixZQUFZLEVBQUUsRUFBRTtvQkFDaEIsUUFBUSxFQUFFLElBQUksbUJBQVEsQ0FBQyxLQUFLLEVBQUUsR0FBRyxFQUFFLFVBQVUsRUFBRTt3QkFDN0MsNERBQTREO3dCQUM1RCw2Q0FBNkM7d0JBQzdDLFlBQVksRUFBRSxJQUFJLG1CQUFLLENBQUMsRUFBRSxDQUFDLEtBQUssQ0FBQyxDQUFDLFNBQVMsUUFBUSxFQUFFLEVBQUU7d0JBQ3ZELGFBQWEsRUFBRSwyQkFBYSxDQUFDLE9BQU87d0JBQ3BDLFNBQVMsRUFBRSx3QkFBYSxDQUFDLFlBQVk7cUJBQ3RDLENBQUM7aUJBQ0gsQ0FBQzthQUNIO1lBQ0QsWUFBWSxFQUFFLFNBQVM7WUFDdkIsR0FBRyxFQUFFLElBQUksQ0FBQyxHQUFHO1lBQ2IsaUlBQWlJO1lBQ2pJLGNBQWMsRUFBRSxJQUFJO1NBQ3JCLENBQ0YsQ0FBQztRQUNGLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLENBQUMsV0FBVyxFQUFFLENBQUMsQ0FBQyxnQkFBZ0I7UUFFbkUsSUFBSSxTQUFTLEVBQUUsQ0FBQztZQUNkLDBJQUEwSTtZQUMxSSxvRUFBb0U7WUFDcEUsTUFBTSxFQUFFLElBQUksRUFBRSxHQUFHLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxPQUFPLENBQUM7WUFDaEQsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxTQUFTLENBQWUsQ0FBQztZQUMzRCxVQUFVLENBQUMsWUFBWSxHQUFHLENBQUMsQ0FBQztRQUM5QixDQUFDO0lBQ0gsQ0FBQztDQUNGO0FBekZELHdDQXlGQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IERuc1ZhbGlkYXRlZENlcnRpZmljYXRlIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWNlcnRpZmljYXRlbWFuYWdlcic7XG5pbXBvcnQgeyBSZW1vdmFsUG9saWN5LCBTdGFjayB9IGZyb20gJ2F3cy1jZGstbGliJztcbmltcG9ydCB7IEFwcGxpY2F0aW9uUHJvdG9jb2wgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWxhc3RpY2xvYWRiYWxhbmNpbmd2Mic7XG5pbXBvcnQgeyBTdWJuZXRUeXBlLCBWcGMgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWMyJztcbmltcG9ydCB7IEFwcGxpY2F0aW9uTG9hZEJhbGFuY2VkRmFyZ2F0ZVNlcnZpY2UgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWNzLXBhdHRlcm5zJztcbmltcG9ydCB7IFJlcG9zaXRvcnkgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWNyJztcbmltcG9ydCB7IElIb3N0ZWRab25lIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLXJvdXRlNTMnO1xuaW1wb3J0IHsgQ2ZuU2VydmljZSwgQ29udGFpbmVySW1hZ2UsIExvZ0RyaXZlcnMgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtZWNzJztcbmltcG9ydCB7IExvZ0dyb3VwLCBSZXRlbnRpb25EYXlzIH0gZnJvbSAnYXdzLWNkay1saWIvYXdzLWxvZ3MnO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBFY3JSZXBvc2l0b3J5IH0gZnJvbSAnLi9FY3JSZXBvc2l0b3J5JztcblxuLyoqXG4gKiBCdWlsZHMgYW4gQXBwbGljYXRpb25Mb2FkQmFsYW5jZWRGYXJnYXRlU2VydmljZSB0aGF0IHJ1bnMgYSBjb250YWluZXIgb24gRUNTIEZhcmdhdGUuXG4gKlxuICogV2FybmluZyEgVGhpcyBwYXR0ZXJuIGlzIG5vdCAncHVyZScgc2VydmVybGVzcyEgSXQgZ2VubmVyYXRlcyAyNHg3IHJ1bm5pbmcgY29zdHMgcGVyIGNvbnRhaW5lciAocmF0aGVyIHRoYW4gYmVpbmcgYmlsbGVkIG9uIHRyYWZmaWMvc3RvcmFnZSkuXG4gKlxuICogV2FybmluZyEgSWYgeW91IGRvbid0IHBhc3MgYSB2cGMsIHRoaXMgY29uc3RydWN0IGNyZWF0ZXMgYSB2cGMgZm9yIHlvdSBhbmQgbGltaXRzIHRoZSBudWJlciBvZiBOQVQgZ2F0ZXdheXMgdG8gMSB0byByZWR1Y2UgY29zdC4gVGhpcyBpcyBsZXNzIHJlc2lsaWVudCwgYnV0IE5BVCBnYXRld2F5cyBhcmUgY29zdGx5IVxuICpcbiAqIElmIHlvdSdkIGxpa2UgdG8gYXZvaWQgdGhpcyB0cmFkZW9vZiwgcGFzcyBpbiBhIHZwYyB5b3UndmUgY3JlYXRlZWQgdGhhdCBoYSB6ZXJvIE5BVCBnYXRld2F5cyBhbmQgaXMgY29uZmlndXJlZCB3aXRoIFByaXZhdGVFbmRwb2ludChzKSB0aGF0IHdpbGwgYWxsb3cgRUNTIHRvIHB1bGwgY29udGFpbmVyIGltYWdlcy5cbiAqXG4gKiBAcGFyYW0gc2VydmljZU5hbWUgTmFtZSBmb3IgdGhlIHNlcnZpY2VcbiAqIEBwYXJhbSB6b25lIEROUyB6b25lXG4gKiBAcGFyYW0gZG9tYWluIE9wdGlvbmFsOiBieSBkZWZhdWx0IHRoZSB6b25lIG5hbWUgd2lsbCBiZSB1c2VkIGFzIHRoZSBETlMgbmFtZSBmb3IgdGhlIHNlcnZpY2UgKGUuZy4gJ2V4YW1wbGUuY29tJykgYnV0IHlvdSBjYW4gc3BlY2lmeSBhIGRpZmZlcmVudCBkb21haW4gaGVyZSAoZS5nLiAnc3ViZG9tYWluLmV4YW1wbGUuY29tJykuXG4gKiBAcGFyYW0gZW52aXJvbm1lbnQgQW55IGVudmlyb25tZW50IHZhcmlhYmxlcyBmb3IgdGhlIGNvbnRhaW5lclxuICogQHBhcmFtIHJlcG9zaXRvcnkgT3B0aW9uYWw6IGlmIHlvdSB3YW50IHRvIHVzZSBhbiBleGlzdGluZyBjb250YWluZXIgaW1hZ2UgcmVwb3NpdG9yeVxuICogQHBhcmFtIHRhZyBPcHRpb25hbDogZGVmYXVsdHMgdG8gJ2xhdGVzdCdcbiAqIEBwYXJhbSB2cGMgT3B0aW9uYWw6IGlmIHlvdSB3YW50IHRvIHVzZSBhbiBleGlzdGluZyBWUEMuIEluIG5vdCBzZXQsIGEgdnBjIHdpbGwgYmUgY3JlYXRlZCBmb3IgeW91XG4gKiBAcGFyYW0gY3B1IE9wdGlvbmFsOiBkZWZhdWx0cyB0byA1MTJcbiAqIEBwYXJhbSBtZW1vcnkgT3B0aW9uYWw6IGRlZmF1bHRzIHRvIDEwMjRcbiAqIEBwYXJhbSB0YXNrQ291bnQgT3B0aW9uYWw6IGRlZmF1bHRzIHRvIDIgZm9yIHJlZHVuZGFuY3kuIFNldCB0byAxIGlmIHlvdSB3YW50IHRvIHJlZHVjZSBjb3N0LlxuICogQHBhcmFtIHplcm9UYXNrcyBTZXRzIHRhc2sgY291bnQgdG8gemVyby4gUGFzcyB0cnVlIGlmIHlvdSBkb24ndCBoYXZlIGFuIGltYWdlIGluIEVDUiB5ZXQsIG90aGVyd2lzZSB0aGlzIGNvbnN0cnVjdCB3aWxsIGZhaWwgdG8gYnVpbGQuXG4gKiBAcGFyYW0gY29udGFpbmVyUG9ydCBPcHRpb25hbDogZGVmYXVsdHMgdG8gMzAwMC4gVGhpcyBpcyB0aGUgcG9ydCB0aGUgYXBwbGljYXRpb24gaW4geW91ciBjb250YWluZXIgbGlzdGVucyBvbi5cbiAqIEByZXR1cm5zIERlcGx5bWVudCBkZXRhaXNcbiAqL1xuZXhwb3J0IGNsYXNzIEZhcmdhdGVTZXJ2aWNlIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgY2VydGlmaWNhdGU6IERuc1ZhbGlkYXRlZENlcnRpZmljYXRlO1xuXG4gIHJlcG9zaXRvcnk6IFJlcG9zaXRvcnk7XG5cbiAgYWxiRmFyZ2F0ZVNlcnZpY2U6IEFwcGxpY2F0aW9uTG9hZEJhbGFuY2VkRmFyZ2F0ZVNlcnZpY2U7XG5cbiAgdnBjOiBWcGM7XG5cbiAgY29uc3RydWN0b3IoXG4gICAgc2NvcGU6IENvbnN0cnVjdCxcbiAgICBpZDogc3RyaW5nLFxuICAgIHNlcnZpY2VOYW1lOiBzdHJpbmcsXG4gICAgem9uZTogSUhvc3RlZFpvbmUsXG4gICAgZG9tYWluPzogc3RyaW5nLFxuICAgIGVudmlyb25tZW50OiB7IFtrZXk6IHN0cmluZ106IHN0cmluZzsgfSA9IHt9LFxuICAgIHJlcG9zaXRvcnk6IFJlcG9zaXRvcnkgfCB1bmRlZmluZWQgPSB1bmRlZmluZWQsXG4gICAgdGFnOiBzdHJpbmcgPSAnbGF0ZXN0JyxcbiAgICB2cGM6IFZwYyB8IHVuZGVmaW5lZCA9IHVuZGVmaW5lZCxcbiAgICBjcHU6IG51bWJlciA9IDUxMixcbiAgICBtZW1vcnk6IG51bWJlciA9IDEwMjQsXG4gICAgdGFza0NvdW50OiBudW1iZXIgPSAyLFxuICAgIHplcm9UYXNrczogYm9vbGVhbiA9IGZhbHNlLFxuICAgIGNvbnRhaW5lclBvcnQ6IG51bWJlciA9IDMwMDAsXG4gICkge1xuICAgIHN1cGVyKHNjb3BlLCBgJHtpZH1GYXJnYXRlU2VydmljZWApO1xuXG4gICAgLy8gQ29udGFpbmVyIHJlcG9zaXRvcnlcbiAgICB0aGlzLnJlcG9zaXRvcnkgPSByZXBvc2l0b3J5IHx8IG5ldyBFY3JSZXBvc2l0b3J5KHNjb3BlLCBpZCk7XG4gICAgLy8gSXQgc2VlbXMgbGlrZSBOQVQgZ2F0ZXdheXMgYXJlIGNvc3RseSwgc28gSSd2ZSBzZXQgdGhpcyB1cCB0byBhdm9pZCB0aGF0IC0gb25seSBjcmVhdGluZyBvbmUuXG4gICAgLy8gQXQgc29tZSBwb2ludCB3ZSBtYXkgd2FudCB0byBmaWd1cmUgb3V0IGEgcHJpdnRlIGVuZHBvaW50IHNvIHRoYXQgd2UgY2FuIHJldGlyZSB0aGUgTkFULlxuICAgIC8vIEJhc2VkIG9uOiBodHRwczovL3d3dy5iaW5hcnl0aGlua3RhbmsuY29tL2Jsb2cvdHJ1bHktc2VydmVybGVzcy1jb250YWluZXJcbiAgICAvLyBhbmQgaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjQyOTk2NjQvaG93LXRvLWNvbmZpZ3VyZS1hd3MtY2RrLWFwcGxpY2F0aW9ubG9hZGJhbGFuY2VkZmFyZ2F0ZXNlcnZpY2UtdG8tbG9nLXBhcnNlZC1qc29cbiAgICB0aGlzLnZwYyA9IHZwYyB8fCBuZXcgVnBjKHNjb3BlLCBgJHtpZH1WcGNgLCB7XG4gICAgICBuYXRHYXRld2F5czogMSxcbiAgICAgIHN1Ym5ldENvbmZpZ3VyYXRpb246IFt7XG4gICAgICAgIG5hbWU6IGlkLFxuICAgICAgICBzdWJuZXRUeXBlOiBTdWJuZXRUeXBlLlBVQkxJQyxcbiAgICAgIH1dLFxuICAgIH0pO1xuXG4gICAgLy8gRmFyZ2F0ZVxuICAgIHRoaXMuYWxiRmFyZ2F0ZVNlcnZpY2UgPSBuZXcgQXBwbGljYXRpb25Mb2FkQmFsYW5jZWRGYXJnYXRlU2VydmljZShcbiAgICAgIHNjb3BlLFxuICAgICAgYCR7aWR9QWxiRmFyZ2F0ZVNlcnZpY2VgLFxuICAgICAge1xuICAgICAgICBsb2FkQmFsYW5jZXJOYW1lOiBpZCxcbiAgICAgICAgc2VydmljZU5hbWUsXG4gICAgICAgIGRvbWFpblpvbmU6IHpvbmUsXG4gICAgICAgIGRvbWFpbk5hbWU6IGRvbWFpbiB8fCB6b25lLnpvbmVOYW1lLFxuICAgICAgICBjZXJ0aWZpY2F0ZTogbmV3IERuc1ZhbGlkYXRlZENlcnRpZmljYXRlKHNjb3BlLCBpZCwge1xuICAgICAgICAgIGRvbWFpbk5hbWU6IGRvbWFpbiB8fCB6b25lLnpvbmVOYW1lLFxuICAgICAgICAgIGhvc3RlZFpvbmU6IHpvbmUsXG4gICAgICAgIH0pLFxuICAgICAgICBwcm90b2NvbDogQXBwbGljYXRpb25Qcm90b2NvbC5IVFRQUyxcbiAgICAgICAgY3B1LFxuICAgICAgICBtZW1vcnlMaW1pdE1pQjogbWVtb3J5LFxuICAgICAgICB0YXNrSW1hZ2VPcHRpb25zOiB7XG4gICAgICAgICAgY29udGFpbmVyTmFtZTogaWQsXG4gICAgICAgICAgaW1hZ2U6IENvbnRhaW5lckltYWdlLmZyb21FY3JSZXBvc2l0b3J5KHRoaXMucmVwb3NpdG9yeSwgdGFnKSxcbiAgICAgICAgICBjb250YWluZXJQb3J0LFxuICAgICAgICAgIGVudmlyb25tZW50LFxuICAgICAgICAgIGxvZ0RyaXZlcjogTG9nRHJpdmVycy5hd3NMb2dzKHtcbiAgICAgICAgICAgIHN0cmVhbVByZWZpeDogaWQsXG4gICAgICAgICAgICBsb2dHcm91cDogbmV3IExvZ0dyb3VwKHNjb3BlLCBgJHtpZH1Mb2dHcm91cGAsIHtcbiAgICAgICAgICAgICAgLy8gRW5zdXJlIHRoZSBsb2cgZ3JvdXAgaXMgZGVsZXRlZCB3aGVuIHRoZSBzdGFjayBpcyBkZWxldGVkXG4gICAgICAgICAgICAgIC8vIGFuZCB0aGF0IGxvZ3MgYXJlbid0IHJldGFpbmVkIGluZGVmaW5pdGVseVxuICAgICAgICAgICAgICBsb2dHcm91cE5hbWU6IGAvJHtTdGFjay5vZihzY29wZSkuc3RhY2tOYW1lfS9lY3MvJHtpZH1gLFxuICAgICAgICAgICAgICByZW1vdmFsUG9saWN5OiBSZW1vdmFsUG9saWN5LkRFU1RST1ksXG4gICAgICAgICAgICAgIHJldGVudGlvbjogUmV0ZW50aW9uRGF5cy5USFJFRV9NT05USFMsXG4gICAgICAgICAgICB9KSxcbiAgICAgICAgICB9KSxcbiAgICAgICAgfSxcbiAgICAgICAgZGVzaXJlZENvdW50OiB0YXNrQ291bnQsXG4gICAgICAgIHZwYzogdGhpcy52cGMsXG4gICAgICAgIC8vID8gaHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNjczMDEyNjgvYXdzLWZhcmdhdGUtcmVzb3VyY2Vpbml0aWFsaXphdGlvbmVycm9yLXVuYWJsZS10by1wdWxsLXNlY3JldHMtb3ItcmVnaXN0cnktYXV0aFxuICAgICAgICBhc3NpZ25QdWJsaWNJcDogdHJ1ZSxcbiAgICAgIH0sXG4gICAgKTtcbiAgICB0aGlzLmFsYkZhcmdhdGVTZXJ2aWNlLmxvYWRCYWxhbmNlci5hZGRSZWRpcmVjdCgpOyAvLyBodHRwIC0+IGh0dHBzXG5cbiAgICBpZiAoemVyb1Rhc2tzKSB7XG4gICAgICAvLyBPbiB0aGUgZmlyc3QgZGVwbG95LCB3aGVuIHRoZXJlJ3Mgbm8gaW1hZ2UgaW4gdGhlIHJlcG9zaXRvcnksIHNldHRpbmcgZGVzaXJlZCB0YXNrcyB0byB6ZXJvIGFsbG93cyB0aGlzIGNvbnN0cnVjdCB0byBidWlsZCwgb3RoZXJ3aXNlIDpcbiAgICAgIC8vIGh0dHBzOi8vZ2l0aHViLmNvbS9hd3MvYXdzLWNkay9pc3N1ZXMvMzY0NiNpc3N1ZWNvbW1lbnQtNjIzOTE5MjQyXG4gICAgICBjb25zdCB7IG5vZGUgfSA9IHRoaXMuYWxiRmFyZ2F0ZVNlcnZpY2Uuc2VydmljZTtcbiAgICAgIGNvbnN0IGNmblNlcnZpY2UgPSBub2RlLmZpbmRDaGlsZCgnU2VydmljZScpIGFzIENmblNlcnZpY2U7XG4gICAgICBjZm5TZXJ2aWNlLmRlc2lyZWRDb3VudCA9IDA7XG4gICAgfVxuICB9XG59XG4iXX0=