UNPKG

28.3 kBJavaScriptView Raw
1"use strict";
2var _a;
3Object.defineProperty(exports, "__esModule", { value: true });
4exports.VpcEndpointServiceDomainName = void 0;
5const jsiiDeprecationWarnings = require("../.warnings.jsii.js");
6const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
7const crypto = require("crypto");
8const core_1 = require("@aws-cdk/core");
9const custom_resources_1 = require("@aws-cdk/custom-resources");
10const lib_1 = require("../lib");
11// v2 - keep this import as a separate section to reduce merge conflict when forward merging with the v2 branch.
12// eslint-disable-next-line
13const core_2 = require("@aws-cdk/core");
14/**
15 * A Private DNS configuration for a VPC endpoint service.
16 */
17class VpcEndpointServiceDomainName extends core_2.Construct {
18 // The way this class works is by using three custom resources and a TxtRecord in conjunction
19 // The first custom resource tells the VPC endpoint service to use the given DNS name
20 // The VPC endpoint service will then say:
21 // "ok, create a TXT record using these two values to prove you own the domain"
22 // The second custom resource retrieves these two values from the service
23 // The TxtRecord is created from these two values
24 // The third custom resource tells the VPC Endpoint Service to verify the domain ownership
25 constructor(scope, id, props) {
26 super(scope, id);
27 try {
28 jsiiDeprecationWarnings._aws_cdk_aws_route53_VpcEndpointServiceDomainNameProps(props);
29 }
30 catch (error) {
31 if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
32 Error.captureStackTrace(error, VpcEndpointServiceDomainName);
33 }
34 throw error;
35 }
36 const serviceUniqueId = core_1.Names.nodeUniqueId(props.endpointService.node);
37 const serviceId = props.endpointService.vpcEndpointServiceId;
38 this.domainName = props.domainName;
39 // Make sure a user doesn't accidentally add multiple domains
40 this.validateProps(props);
41 VpcEndpointServiceDomainName.endpointServicesMap[serviceUniqueId] = this.domainName;
42 VpcEndpointServiceDomainName.endpointServices.push(props.endpointService);
43 // Enable Private DNS on the endpoint service and retrieve the AWS-generated configuration
44 const privateDnsConfiguration = this.getPrivateDnsConfiguration(serviceUniqueId, serviceId, this.domainName);
45 // Tell AWS to verify that this account owns the domain attached to the service
46 this.verifyPrivateDnsConfiguration(privateDnsConfiguration, props.publicHostedZone);
47 // Finally, don't do any of the above before the endpoint service is created
48 this.node.addDependency(props.endpointService);
49 }
50 validateProps(props) {
51 const serviceUniqueId = core_1.Names.nodeUniqueId(props.endpointService.node);
52 if (serviceUniqueId in VpcEndpointServiceDomainName.endpointServicesMap) {
53 const endpoint = VpcEndpointServiceDomainName.endpointServicesMap[serviceUniqueId];
54 throw new Error(`Cannot create a VpcEndpointServiceDomainName for service ${serviceUniqueId}, another VpcEndpointServiceDomainName (${endpoint}) is already associated with it`);
55 }
56 }
57 /**
58 * Sets up Custom Resources to make AWS calls to set up Private DNS on an endpoint service,
59 * returning the values to use in a TxtRecord, which AWS uses to verify domain ownership.
60 */
61 getPrivateDnsConfiguration(serviceUniqueId, serviceId, privateDnsName) {
62 // The custom resource which tells AWS to enable Private DNS on the given service, using the given domain name
63 // AWS will generate a name/value pair for use in a TxtRecord, which is used to verify domain ownership.
64 const enablePrivateDnsAction = {
65 service: 'EC2',
66 action: 'modifyVpcEndpointServiceConfiguration',
67 parameters: {
68 ServiceId: serviceId,
69 PrivateDnsName: privateDnsName,
70 },
71 physicalResourceId: custom_resources_1.PhysicalResourceId.of(serviceUniqueId),
72 };
73 const removePrivateDnsAction = {
74 service: 'EC2',
75 action: 'modifyVpcEndpointServiceConfiguration',
76 parameters: {
77 ServiceId: serviceId,
78 RemovePrivateDnsName: true,
79 },
80 };
81 const enable = new custom_resources_1.AwsCustomResource(this, 'EnableDns', {
82 onCreate: enablePrivateDnsAction,
83 onUpdate: enablePrivateDnsAction,
84 onDelete: removePrivateDnsAction,
85 policy: custom_resources_1.AwsCustomResourcePolicy.fromSdkCalls({
86 resources: [
87 core_1.Fn.join(':', [
88 'arn',
89 core_1.Stack.of(this).partition,
90 'ec2',
91 core_1.Stack.of(this).region,
92 core_1.Stack.of(this).account,
93 core_1.Fn.join('/', [
94 'vpc-endpoint-service',
95 serviceId,
96 ]),
97 ]),
98 ],
99 }),
100 });
101 // Look up the name/value pair if the domain changes, or the service changes,
102 // which would cause the values to be different. If the unique ID changes,
103 // the resource may be entirely recreated, so we will need to look it up again.
104 const lookup = hashcode(core_1.Names.uniqueId(this) + serviceUniqueId + privateDnsName);
105 // Create the custom resource to look up the name/value pair generated by AWS
106 // after the previous API call
107 const retrieveNameValuePairAction = {
108 service: 'EC2',
109 action: 'describeVpcEndpointServiceConfigurations',
110 parameters: {
111 ServiceIds: [serviceId],
112 },
113 physicalResourceId: custom_resources_1.PhysicalResourceId.of(lookup),
114 };
115 const getNames = new custom_resources_1.AwsCustomResource(this, 'GetNames', {
116 onCreate: retrieveNameValuePairAction,
117 onUpdate: retrieveNameValuePairAction,
118 // describeVpcEndpointServiceConfigurations can't take an ARN for granular permissions
119 policy: custom_resources_1.AwsCustomResourcePolicy.fromSdkCalls({
120 resources: custom_resources_1.AwsCustomResourcePolicy.ANY_RESOURCE,
121 }),
122 });
123 // We only want to call and get the name/value pair after we've told AWS to enable Private DNS
124 // If we call before then, we'll get an empty pair of values.
125 getNames.node.addDependency(enable);
126 // Get the references to the name/value pair associated with the endpoint service
127 const name = getNames.getResponseField('ServiceConfigurations.0.PrivateDnsNameConfiguration.Name');
128 const value = getNames.getResponseField('ServiceConfigurations.0.PrivateDnsNameConfiguration.Value');
129 return { name, value, serviceId };
130 }
131 /**
132 * Creates a Route53 entry and a Custom Resource which explicitly tells AWS to verify ownership
133 * of the domain name attached to an endpoint service.
134 */
135 verifyPrivateDnsConfiguration(config, publicHostedZone) {
136 // Create the TXT record in the provided hosted zone
137 const verificationRecord = new lib_1.TxtRecord(this, 'DnsVerificationRecord', {
138 recordName: config.name,
139 values: [config.value],
140 zone: publicHostedZone,
141 });
142 // Tell the endpoint service to verify the domain ownership
143 const startVerificationAction = {
144 service: 'EC2',
145 action: 'startVpcEndpointServicePrivateDnsVerification',
146 parameters: {
147 ServiceId: config.serviceId,
148 },
149 physicalResourceId: custom_resources_1.PhysicalResourceId.of(core_1.Fn.join(':', [config.name, config.value])),
150 };
151 const startVerification = new custom_resources_1.AwsCustomResource(this, 'StartVerification', {
152 onCreate: startVerificationAction,
153 onUpdate: startVerificationAction,
154 policy: custom_resources_1.AwsCustomResourcePolicy.fromSdkCalls({
155 resources: [
156 core_1.Fn.join(':', [
157 'arn',
158 core_1.Stack.of(this).partition,
159 'ec2',
160 core_1.Stack.of(this).region,
161 core_1.Stack.of(this).account,
162 core_1.Fn.join('/', [
163 'vpc-endpoint-service',
164 config.serviceId,
165 ]),
166 ]),
167 ],
168 }),
169 });
170 // Only verify after the record has been created
171 startVerification.node.addDependency(verificationRecord);
172 }
173}
174exports.VpcEndpointServiceDomainName = VpcEndpointServiceDomainName;
175_a = JSII_RTTI_SYMBOL_1;
176VpcEndpointServiceDomainName[_a] = { fqn: "@aws-cdk/aws-route53.VpcEndpointServiceDomainName", version: "1.160.0" };
177// Track all domain names created, so someone doesn't accidentally associate two domains with a single service
178VpcEndpointServiceDomainName.endpointServices = [];
179// Track all domain names created, so someone doesn't accidentally associate two domains with a single service
180VpcEndpointServiceDomainName.endpointServicesMap = {};
181/**
182 * Hash a string
183 */
184function hashcode(s) {
185 const hash = crypto.createHash('md5');
186 hash.update(s);
187 return hash.digest('hex');
188}
189;
190//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidnBjLWVuZHBvaW50LXNlcnZpY2UtZG9tYWluLW5hbWUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJ2cGMtZW5kcG9pbnQtc2VydmljZS1kb21haW4tbmFtZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxpQ0FBaUM7QUFFakMsd0NBQWlEO0FBQ2pELGdFQUEyRztBQUUzRyxnQ0FBc0Q7QUFFdEQsZ0hBQWdIO0FBQ2hILDJCQUEyQjtBQUMzQix3Q0FBMkQ7QUE0QjNEOztHQUVHO0FBQ0gsTUFBYSw0QkFBNkIsU0FBUSxnQkFBYTtJQWE3RCw2RkFBNkY7SUFDN0YscUZBQXFGO0lBQ3JGLDBDQUEwQztJQUMxQywrRUFBK0U7SUFDL0UseUVBQXlFO0lBQ3pFLGlEQUFpRDtJQUNqRCwwRkFBMEY7SUFDMUYsWUFBWSxLQUFnQixFQUFFLEVBQVUsRUFBRSxLQUF3QztRQUNoRixLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDOzs7Ozs7K0NBckJSLDRCQUE0Qjs7OztRQXVCckMsTUFBTSxlQUFlLEdBQUcsWUFBSyxDQUFDLFlBQVksQ0FBQyxLQUFLLENBQUMsZUFBZSxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ3ZFLE1BQU0sU0FBUyxHQUFHLEtBQUssQ0FBQyxlQUFlLENBQUMsb0JBQW9CLENBQUM7UUFDN0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxLQUFLLENBQUMsVUFBVSxDQUFDO1FBRW5DLDZEQUE2RDtRQUM3RCxJQUFJLENBQUMsYUFBYSxDQUFDLEtBQUssQ0FBQyxDQUFDO1FBRTFCLDRCQUE0QixDQUFDLG1CQUFtQixDQUFDLGVBQWUsQ0FBQyxHQUFHLElBQUksQ0FBQyxVQUFVLENBQUM7UUFDcEYsNEJBQTRCLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxDQUFDLEtBQUssQ0FBQyxlQUFlLENBQUMsQ0FBQztRQUUxRSwwRkFBMEY7UUFDMUYsTUFBTSx1QkFBdUIsR0FBRyxJQUFJLENBQUMsMEJBQTBCLENBQUMsZUFBZSxF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cm9tICdjcnlwdG8nO1xuaW1wb3J0IHsgSVZwY0VuZHBvaW50U2VydmljZSB9IGZyb20gJ0Bhd3MtY2RrL2F3cy1lYzInO1xuaW1wb3J0IHsgRm4sIE5hbWVzLCBTdGFjayB9IGZyb20gJ0Bhd3MtY2RrL2NvcmUnO1xuaW1wb3J0IHsgQXdzQ3VzdG9tUmVzb3VyY2UsIEF3c0N1c3RvbVJlc291cmNlUG9saWN5LCBQaHlzaWNhbFJlc291cmNlSWQgfSBmcm9tICdAYXdzLWNkay9jdXN0b20tcmVzb3VyY2VzJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuaW1wb3J0IHsgSVB1YmxpY0hvc3RlZFpvbmUsIFR4dFJlY29yZCB9IGZyb20gJy4uL2xpYic7XG5cbi8vIHYyIC0ga2VlcCB0aGlzIGltcG9ydCBhcyBhIHNlcGFyYXRlIHNlY3Rpb24gdG8gcmVkdWNlIG1lcmdlIGNvbmZsaWN0IHdoZW4gZm9yd2FyZCBtZXJnaW5nIHdpdGggdGhlIHYyIGJyYW5jaC5cbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZVxuaW1wb3J0IHsgQ29uc3RydWN0IGFzIENvcmVDb25zdHJ1Y3QgfSBmcm9tICdAYXdzLWNkay9jb3JlJztcblxuLyoqXG4gKiBQcm9wZXJ0aWVzIHRvIGNvbmZpZ3VyZSBhIFZQQyBFbmRwb2ludCBTZXJ2aWNlIGRvbWFpbiBuYW1lXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgVnBjRW5kcG9pbnRTZXJ2aWNlRG9tYWluTmFtZVByb3BzIHtcblxuICAvKipcbiAgICogVGhlIFZQQyBFbmRwb2ludCBTZXJ2aWNlIHRvIGNvbmZpZ3VyZSBQcml2YXRlIEROUyBmb3JcbiAgICovXG4gIHJlYWRvbmx5IGVuZHBvaW50U2VydmljZTogSVZwY0VuZHBvaW50U2VydmljZTtcblxuICAvKipcbiAgICogVGhlIGRvbWFpbiBuYW1lIHRvIHVzZS5cbiAgICpcbiAgICogVGhpcyBkb21haW4gbmFtZSBtdXN0IGJlIG93bmVkIGJ5IHRoaXMgYWNjb3VudCAocmVnaXN0ZXJlZCB0aHJvdWdoIFJvdXRlNTMpLFxuICAgKiBvciBkZWxlZ2F0ZWQgdG8gdGhpcyBhY2NvdW50LiBEb21haW4gb3duZXJzaGlwIHdpbGwgYmUgdmVyaWZpZWQgYnkgQVdTIGJlZm9yZVxuICAgKiBwcml2YXRlIEROUyBjYW4gYmUgdXNlZC5cbiAgICogQHNlZSBodHRwczovL2RvY3MuYXdzLmFtYXpvbi5jb20vdnBjL2xhdGVzdC91c2VyZ3VpZGUvZW5kcG9pbnQtc2VydmljZXMtZG5zLXZhbGlkYXRpb24uaHRtbFxuICAgKi9cbiAgcmVhZG9ubHkgZG9tYWluTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgcHVibGljIGhvc3RlZCB6b25lIHRvIHVzZSBmb3IgdGhlIGRvbWFpbi5cbiAgICovXG4gIHJlYWRvbmx5IHB1YmxpY0hvc3RlZFpvbmU6IElQdWJsaWNIb3N0ZWRab25lO1xufVxuXG4vKipcbiAqIEEgUHJpdmF0ZSBETlMgY29uZmlndXJhdGlvbiBmb3IgYSBWUEMgZW5kcG9pbnQgc2VydmljZS5cbiAqL1xuZXhwb3J0IGNsYXNzIFZwY0VuZHBvaW50U2VydmljZURvbWFpbk5hbWUgZXh0ZW5kcyBDb3JlQ29uc3RydWN0IHtcblxuICAvLyBUcmFjayBhbGwgZG9tYWluIG5hbWVzIGNyZWF0ZWQsIHNvIHNvbWVvbmUgZG9lc24ndCBhY2NpZGVudGFsbHkgYXNzb2NpYXRlIHR3byBkb21haW5zIHdpdGggYSBzaW5nbGUgc2VydmljZVxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBlbmRwb2ludFNlcnZpY2VzOiBJVnBjRW5kcG9pbnRTZXJ2aWNlW10gPSBbXTtcblxuICAvLyBUcmFjayBhbGwgZG9tYWluIG5hbWVzIGNyZWF0ZWQsIHNvIHNvbWVvbmUgZG9lc24ndCBhY2NpZGVudGFsbHkgYXNzb2NpYXRlIHR3byBkb21haW5zIHdpdGggYSBzaW5nbGUgc2VydmljZVxuICBwcml2YXRlIHN0YXRpYyByZWFkb25seSBlbmRwb2ludFNlcnZpY2VzTWFwOiB7IFtlbmRwb2ludFNlcnZpY2U6IHN0cmluZ106IHN0cmluZ30gPSB7fTtcblxuICAvKipcbiAgICogVGhlIGRvbWFpbiBuYW1lIGFzc29jaWF0ZWQgd2l0aCB0aGUgcHJpdmF0ZSBETlMgY29uZmlndXJhdGlvblxuICAgKi9cbiAgcHVibGljIGRvbWFpbk5hbWU6IHN0cmluZztcblxuICAvLyBUaGUgd2F5IHRoaXMgY2xhc3Mgd29ya3MgaXMgYnkgdXNpbmcgdGhyZWUgY3VzdG9tIHJlc291cmNlcyBhbmQgYSBUeHRSZWNvcmQgaW4gY29uanVuY3Rpb25cbiAgLy8gVGhlIGZpcnN0IGN1c3RvbSByZXNvdXJjZSB0ZWxscyB0aGUgVlBDIGVuZHBvaW50IHNlcnZpY2UgdG8gdXNlIHRoZSBnaXZlbiBETlMgbmFtZVxuICAvLyBUaGUgVlBDIGVuZHBvaW50IHNlcnZpY2Ugd2lsbCB0aGVuIHNheTpcbiAgLy8gXCJvaywgY3JlYXRlIGEgVFhUIHJlY29yZCB1c2luZyB0aGVzZSB0d28gdmFsdWVzIHRvIHByb3ZlIHlvdSBvd24gdGhlIGRvbWFpblwiXG4gIC8vIFRoZSBzZWNvbmQgY3VzdG9tIHJlc291cmNlIHJldHJpZXZlcyB0aGVzZSB0d28gdmFsdWVzIGZyb20gdGhlIHNlcnZpY2VcbiAgLy8gVGhlIFR4dFJlY29yZCBpcyBjcmVhdGVkIGZyb20gdGhlc2UgdHdvIHZhbHVlc1xuICAvLyBUaGUgdGhpcmQgY3VzdG9tIHJlc291cmNlIHRlbGxzIHRoZSBWUEMgRW5kcG9pbnQgU2VydmljZSB0byB2ZXJpZnkgdGhlIGRvbWFpbiBvd25lcnNoaXBcbiAgY29uc3RydWN0b3Ioc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgcHJvcHM6IFZwY0VuZHBvaW50U2VydmljZURvbWFpbk5hbWVQcm9wcykge1xuICAgIHN1cGVyKHNjb3BlLCBpZCk7XG5cbiAgICBjb25zdCBzZXJ2aWNlVW5pcXVlSWQgPSBOYW1lcy5ub2RlVW5pcXVlSWQocHJvcHMuZW5kcG9pbnRTZXJ2aWNlLm5vZGUpO1xuICAgIGNvbnN0IHNlcnZpY2VJZCA9IHByb3BzLmVuZHBvaW50U2VydmljZS52cGNFbmRwb2ludFNlcnZpY2VJZDtcbiAgICB0aGlzLmRvbWFpbk5hbWUgPSBwcm9wcy5kb21haW5OYW1lO1xuXG4gICAgLy8gTWFrZSBzdXJlIGEgdXNlciBkb2Vzbid0IGFjY2lkZW50YWxseSBhZGQgbXVsdGlwbGUgZG9tYWluc1xuICAgIHRoaXMudmFsaWRhdGVQcm9wcyhwcm9wcyk7XG5cbiAgICBWcGNFbmRwb2ludFNlcnZpY2VEb21haW5OYW1lLmVuZHBvaW50U2VydmljZXNNYXBbc2VydmljZVVuaXF1ZUlkXSA9IHRoaXMuZG9tYWluTmFtZTtcbiAgICBWcGNFbmRwb2ludFNlcnZpY2VEb21haW5OYW1lLmVuZHBvaW50U2VydmljZXMucHVzaChwcm9wcy5lbmRwb2ludFNlcnZpY2UpO1xuXG4gICAgLy8gRW5hYmxlIFByaXZhdGUgRE5TIG9uIHRoZSBlbmRwb2ludCBzZXJ2aWNlIGFuZCByZXRyaWV2ZSB0aGUgQVdTLWdlbmVyYXRlZCBjb25maWd1cmF0aW9uXG4gICAgY29uc3QgcHJpdmF0ZURuc0NvbmZpZ3VyYXRpb24gPSB0aGlzLmdldFByaXZhdGVEbnNDb25maWd1cmF0aW9uKHNlcnZpY2VVbmlxdWVJZCwgc2VydmljZUlkLCB0aGlzLmRvbWFpbk5hbWUpO1xuXG4gICAgLy8gVGVsbCBBV1MgdG8gdmVyaWZ5IHRoYXQgdGhpcyBhY2NvdW50IG93bnMgdGhlIGRvbWFpbiBhdHRhY2hlZCB0byB0aGUgc2VydmljZVxuICAgIHRoaXMudmVyaWZ5UHJpdmF0ZURuc0NvbmZpZ3VyYXRpb24ocHJpdmF0ZURuc0NvbmZpZ3VyYXRpb24sIHByb3BzLnB1YmxpY0hvc3RlZFpvbmUpO1xuXG4gICAgLy8gRmluYWxseSwgZG9uJ3QgZG8gYW55IG9mIHRoZSBhYm92ZSBiZWZvcmUgdGhlIGVuZHBvaW50IHNlcnZpY2UgaXMgY3JlYXRlZFxuICAgIHRoaXMubm9kZS5hZGREZXBlbmRlbmN5KHByb3BzLmVuZHBvaW50U2VydmljZSk7XG4gIH1cblxuICBwcml2YXRlIHZhbGlkYXRlUHJvcHMocHJvcHM6IFZwY0VuZHBvaW50U2VydmljZURvbWFpbk5hbWVQcm9wcyk6IHZvaWQge1xuICAgIGNvbnN0IHNlcnZpY2VVbmlxdWVJZCA9IE5hbWVzLm5vZGVVbmlxdWVJZChwcm9wcy5lbmRwb2ludFNlcnZpY2Uubm9kZSk7XG4gICAgaWYgKHNlcnZpY2VVbmlxdWVJZCBpbiBWcGNFbmRwb2ludFNlcnZpY2VEb21haW5OYW1lLmVuZHBvaW50U2VydmljZXNNYXApIHtcbiAgICAgIGNvbnN0IGVuZHBvaW50ID0gVnBjRW5kcG9pbnRTZXJ2aWNlRG9tYWluTmFtZS5lbmRwb2ludFNlcnZpY2VzTWFwW3NlcnZpY2VVbmlxdWVJZF07XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAgIGBDYW5ub3QgY3JlYXRlIGEgVnBjRW5kcG9pbnRTZXJ2aWNlRG9tYWluTmFtZSBmb3Igc2VydmljZSAke3NlcnZpY2VVbmlxdWVJZH0sIGFub3RoZXIgVnBjRW5kcG9pbnRTZXJ2aWNlRG9tYWluTmFtZSAoJHtlbmRwb2ludH0pIGlzIGFscmVhZHkgYXNzb2NpYXRlZCB3aXRoIGl0YCk7XG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNldHMgdXAgQ3VzdG9tIFJlc291cmNlcyB0byBtYWtlIEFXUyBjYWxscyB0byBzZXQgdXAgUHJpdmF0ZSBETlMgb24gYW4gZW5kcG9pbnQgc2VydmljZSxcbiAgICogcmV0dXJuaW5nIHRoZSB2YWx1ZXMgdG8gdXNlIGluIGEgVHh0UmVjb3JkLCB3aGljaCBBV1MgdXNlcyB0byB2ZXJpZnkgZG9tYWluIG93bmVyc2hpcC5cbiAgICovXG4gIHByaXZhdGUgZ2V0UHJpdmF0ZURuc0NvbmZpZ3VyYXRpb24oc2VydmljZVVuaXF1ZUlkOiBzdHJpbmcsIHNlcnZpY2VJZDogc3RyaW5nLCBwcml2YXRlRG5zTmFtZTogc3RyaW5nKTogUHJpdmF0ZURuc0NvbmZpZ3VyYXRpb24ge1xuXG4gICAgLy8gVGhlIGN1c3RvbSByZXNvdXJjZSB3aGljaCB0ZWxscyBBV1MgdG8gZW5hYmxlIFByaXZhdGUgRE5TIG9uIHRoZSBnaXZlbiBzZXJ2aWNlLCB1c2luZyB0aGUgZ2l2ZW4gZG9tYWluIG5hbWVcbiAgICAvLyBBV1Mgd2lsbCBnZW5lcmF0ZSBhIG5hbWUvdmFsdWUgcGFpciBmb3IgdXNlIGluIGEgVHh0UmVjb3JkLCB3aGljaCBpcyB1c2VkIHRvIHZlcmlmeSBkb21haW4gb3duZXJzaGlwLlxuICAgIGNvbnN0IGVuYWJsZVByaXZhdGVEbnNBY3Rpb24gPSB7XG4gICAgICBzZXJ2aWNlOiAnRUMyJyxcbiAgICAgIGFjdGlvbjogJ21vZGlmeVZwY0VuZHBvaW50U2VydmljZUNvbmZpZ3VyYXRpb24nLFxuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICBTZXJ2aWNlSWQ6IHNlcnZpY2VJZCxcbiAgICAgICAgUHJpdmF0ZURuc05hbWU6IHByaXZhdGVEbnNOYW1lLFxuICAgICAgfSxcbiAgICAgIHBoeXNpY2FsUmVzb3VyY2VJZDogUGh5c2ljYWxSZXNvdXJjZUlkLm9mKHNlcnZpY2VVbmlxdWVJZCksXG4gICAgfTtcbiAgICBjb25zdCByZW1vdmVQcml2YXRlRG5zQWN0aW9uID0ge1xuICAgICAgc2VydmljZTogJ0VDMicsXG4gICAgICBhY3Rpb246ICdtb2RpZnlWcGNFbmRwb2ludFNlcnZpY2VDb25maWd1cmF0aW9uJyxcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgU2VydmljZUlkOiBzZXJ2aWNlSWQsXG4gICAgICAgIFJlbW92ZVByaXZhdGVEbnNOYW1lOiB0cnVlLFxuICAgICAgfSxcbiAgICB9O1xuICAgIGNvbnN0IGVuYWJsZSA9IG5ldyBBd3NDdXN0b21SZXNvdXJjZSh0aGlzLCAnRW5hYmxlRG5zJywge1xuICAgICAgb25DcmVhdGU6IGVuYWJsZVByaXZhdGVEbnNBY3Rpb24sXG4gICAgICBvblVwZGF0ZTogZW5hYmxlUHJpdmF0ZURuc0FjdGlvbixcbiAgICAgIG9uRGVsZXRlOiByZW1vdmVQcml2YXRlRG5zQWN0aW9uLFxuICAgICAgcG9saWN5OiBBd3NDdXN0b21SZXNvdXJjZVBvbGljeS5mcm9tU2RrQ2FsbHMoe1xuICAgICAgICByZXNvdXJjZXM6IFtcbiAgICAgICAgICBGbi5qb2luKCc6JywgW1xuICAgICAgICAgICAgJ2FybicsXG4gICAgICAgICAgICBTdGFjay5vZih0aGlzKS5wYXJ0aXRpb24sXG4gICAgICAgICAgICAnZWMyJyxcbiAgICAgICAgICAgIFN0YWNrLm9mKHRoaXMpLnJlZ2lvbixcbiAgICAgICAgICAgIFN0YWNrLm9mKHRoaXMpLmFjY291bnQsXG4gICAgICAgICAgICBGbi5qb2luKCcvJywgW1xuICAgICAgICAgICAgICAndnBjLWVuZHBvaW50LXNlcnZpY2UnLFxuICAgICAgICAgICAgICBzZXJ2aWNlSWQsXG4gICAgICAgICAgICBdKSxcbiAgICAgICAgICBdKSxcbiAgICAgICAgXSxcbiAgICAgIH0pLFxuICAgIH0pO1xuXG4gICAgLy8gTG9vayB1cCB0aGUgbmFtZS92YWx1ZSBwYWlyIGlmIHRoZSBkb21haW4gY2hhbmdlcywgb3IgdGhlIHNlcnZpY2UgY2hhbmdlcyxcbiAgICAvLyB3aGljaCB3b3VsZCBjYXVzZSB0aGUgdmFsdWVzIHRvIGJlIGRpZmZlcmVudC4gSWYgdGhlIHVuaXF1ZSBJRCBjaGFuZ2VzLFxuICAgIC8vIHRoZSByZXNvdXJjZSBtYXkgYmUgZW50aXJlbHkgcmVjcmVhdGVkLCBzbyB3ZSB3aWxsIG5lZWQgdG8gbG9vayBpdCB1cCBhZ2Fpbi5cbiAgICBjb25zdCBsb29rdXAgPSBoYXNoY29kZShOYW1lcy51bmlxdWVJZCh0aGlzKSArIHNlcnZpY2VVbmlxdWVJZCArIHByaXZhdGVEbnNOYW1lKTtcblxuICAgIC8vIENyZWF0ZSB0aGUgY3VzdG9tIHJlc291cmNlIHRvIGxvb2sgdXAgdGhlIG5hbWUvdmFsdWUgcGFpciBnZW5lcmF0ZWQgYnkgQVdTXG4gICAgLy8gYWZ0ZXIgdGhlIHByZXZpb3VzIEFQSSBjYWxsXG4gICAgY29uc3QgcmV0cmlldmVOYW1lVmFsdWVQYWlyQWN0aW9uID0ge1xuICAgICAgc2VydmljZTogJ0VDMicsXG4gICAgICBhY3Rpb246ICdkZXNjcmliZVZwY0VuZHBvaW50U2VydmljZUNvbmZpZ3VyYXRpb25zJyxcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgU2VydmljZUlkczogW3NlcnZpY2VJZF0sXG4gICAgICB9LFxuICAgICAgcGh5c2ljYWxSZXNvdXJjZUlkOiBQaHlzaWNhbFJlc291cmNlSWQub2YobG9va3VwKSxcbiAgICB9O1xuICAgIGNvbnN0IGdldE5hbWVzID0gbmV3IEF3c0N1c3RvbVJlc291cmNlKHRoaXMsICdHZXROYW1lcycsIHtcbiAgICAgIG9uQ3JlYXRlOiByZXRyaWV2ZU5hbWVWYWx1ZVBhaXJBY3Rpb24sXG4gICAgICBvblVwZGF0ZTogcmV0cmlldmVOYW1lVmFsdWVQYWlyQWN0aW9uLFxuICAgICAgLy8gZGVzY3JpYmVWcGNFbmRwb2ludFNlcnZpY2VDb25maWd1cmF0aW9ucyBjYW4ndCB0YWtlIGFuIEFSTiBmb3IgZ3JhbnVsYXIgcGVybWlzc2lvbnNcbiAgICAgIHBvbGljeTogQXdzQ3VzdG9tUmVzb3VyY2VQb2xpY3kuZnJvbVNka0NhbGxzKHtcbiAgICAgICAgcmVzb3VyY2VzOiBBd3NDdXN0b21SZXNvdXJjZVBvbGljeS5BTllfUkVTT1VSQ0UsXG4gICAgICB9KSxcbiAgICB9KTtcblxuICAgIC8vIFdlIG9ubHkgd2FudCB0byBjYWxsIGFuZCBnZXQgdGhlIG5hbWUvdmFsdWUgcGFpciBhZnRlciB3ZSd2ZSB0b2xkIEFXUyB0byBlbmFibGUgUHJpdmF0ZSBETlNcbiAgICAvLyBJZiB3ZSBjYWxsIGJlZm9yZSB0aGVuLCB3ZSdsbCBnZXQgYW4gZW1wdHkgcGFpciBvZiB2YWx1ZXMuXG4gICAgZ2V0TmFtZXMubm9kZS5hZGREZXBlbmRlbmN5KGVuYWJsZSk7XG5cbiAgICAvLyBHZXQgdGhlIHJlZmVyZW5jZXMgdG8gdGhlIG5hbWUvdmFsdWUgcGFpciBhc3NvY2lhdGVkIHdpdGggdGhlIGVuZHBvaW50IHNlcnZpY2VcbiAgICBjb25zdCBuYW1lID0gZ2V0TmFtZXMuZ2V0UmVzcG9uc2VGaWVsZCgnU2VydmljZUNvbmZpZ3VyYXRpb25zLjAuUHJpdmF0ZURuc05hbWVDb25maWd1cmF0aW9uLk5hbWUnKTtcbiAgICBjb25zdCB2YWx1ZSA9IGdldE5hbWVzLmdldFJlc3BvbnNlRmllbGQoJ1NlcnZpY2VDb25maWd1cmF0aW9ucy4wLlByaXZhdGVEbnNOYW1lQ29uZmlndXJhdGlvbi5WYWx1ZScpO1xuXG4gICAgcmV0dXJuIHsgbmFtZSwgdmFsdWUsIHNlcnZpY2VJZCB9O1xuICB9XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBSb3V0ZTUzIGVudHJ5IGFuZCBhIEN1c3RvbSBSZXNvdXJjZSB3aGljaCBleHBsaWNpdGx5IHRlbGxzIEFXUyB0byB2ZXJpZnkgb3duZXJzaGlwXG4gICAqIG9mIHRoZSBkb21haW4gbmFtZSBhdHRhY2hlZCB0byBhbiBlbmRwb2ludCBzZXJ2aWNlLlxuICAgKi9cbiAgcHJpdmF0ZSB2ZXJpZnlQcml2YXRlRG5zQ29uZmlndXJhdGlvbihjb25maWc6IFByaXZhdGVEbnNDb25maWd1cmF0aW9uLCBwdWJsaWNIb3N0ZWRab25lOiBJUHVibGljSG9zdGVkWm9uZSkge1xuICAgIC8vIENyZWF0ZSB0aGUgVFhUIHJlY29yZCBpbiB0aGUgcHJvdmlkZWQgaG9zdGVkIHpvbmVcbiAgICBjb25zdCB2ZXJpZmljYXRpb25SZWNvcmQgPSBuZXcgVHh0UmVjb3JkKHRoaXMsICdEbnNWZXJpZmljYXRpb25SZWNvcmQnLCB7XG4gICAgICByZWNvcmROYW1lOiBjb25maWcubmFtZSxcbiAgICAgIHZhbHVlczogW2NvbmZpZy52YWx1ZV0sXG4gICAgICB6b25lOiBwdWJsaWNIb3N0ZWRab25lLFxuICAgIH0pO1xuXG4gICAgLy8gVGVsbCB0aGUgZW5kcG9pbnQgc2VydmljZSB0byB2ZXJpZnkgdGhlIGRvbWFpbiBvd25lcnNoaXBcbiAgICBjb25zdCBzdGFydFZlcmlmaWNhdGlvbkFjdGlvbiA9IHtcbiAgICAgIHNlcnZpY2U6ICdFQzInLFxuICAgICAgYWN0aW9uOiAnc3RhcnRWcGNFbmRwb2ludFNlcnZpY2VQcml2YXRlRG5zVmVyaWZpY2F0aW9uJyxcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgU2VydmljZUlkOiBjb25maWcuc2VydmljZUlkLFxuICAgICAgfSxcbiAgICAgIHBoeXNpY2FsUmVzb3VyY2VJZDogUGh5c2ljYWxSZXNvdXJjZUlkLm9mKEZuLmpvaW4oJzonLCBbY29uZmlnLm5hbWUsIGNvbmZpZy52YWx1ZV0pKSxcbiAgICB9O1xuICAgIGNvbnN0IHN0YXJ0VmVyaWZpY2F0aW9uID0gbmV3IEF3c0N1c3RvbVJlc291cmNlKHRoaXMsICdTdGFydFZlcmlmaWNhdGlvbicsIHtcbiAgICAgIG9uQ3JlYXRlOiBzdGFydFZlcmlmaWNhdGlvbkFjdGlvbixcbiAgICAgIG9uVXBkYXRlOiBzdGFydFZlcmlmaWNhdGlvbkFjdGlvbixcbiAgICAgIHBvbGljeTogQXdzQ3VzdG9tUmVzb3VyY2VQb2xpY3kuZnJvbVNka0NhbGxzKHtcbiAgICAgICAgcmVzb3VyY2VzOiBbXG4gICAgICAgICAgRm4uam9pbignOicsIFtcbiAgICAgICAgICAgICdhcm4nLFxuICAgICAgICAgICAgU3RhY2sub2YodGhpcykucGFydGl0aW9uLFxuICAgICAgICAgICAgJ2VjMicsXG4gICAgICAgICAgICBTdGFjay5vZih0aGlzKS5yZWdpb24sXG4gICAgICAgICAgICBTdGFjay5vZih0aGlzKS5hY2NvdW50LFxuICAgICAgICAgICAgRm4uam9pbignLycsIFtcbiAgICAgICAgICAgICAgJ3ZwYy1lbmRwb2ludC1zZXJ2aWNlJyxcbiAgICAgICAgICAgICAgY29uZmlnLnNlcnZpY2VJZCxcbiAgICAgICAgICAgIF0pLFxuICAgICAgICAgIF0pLFxuICAgICAgICBdLFxuICAgICAgfSksXG4gICAgfSk7XG4gICAgLy8gT25seSB2ZXJpZnkgYWZ0ZXIgdGhlIHJlY29yZCBoYXMgYmVlbiBjcmVhdGVkXG4gICAgc3RhcnRWZXJpZmljYXRpb24ubm9kZS5hZGREZXBlbmRlbmN5KHZlcmlmaWNhdGlvblJlY29yZCk7XG4gIH1cbn1cblxuLyoqXG4gKiBSZXByZXNlbnQgdGhlIG5hbWUvdmFsdWUgcGFpciBhc3NvY2lhdGVkIHdpdGggYSBQcml2YXRlIEROUyBlbmFibGVkIGVuZHBvaW50IHNlcnZpY2VcbiAqL1xuaW50ZXJmYWNlIFByaXZhdGVEbnNDb25maWd1cmF0aW9uIHtcbiAgcmVhZG9ubHkgbmFtZTogc3RyaW5nO1xuICByZWFkb25seSB2YWx1ZTogc3RyaW5nO1xuICByZWFkb25seSBzZXJ2aWNlSWQ6IHN0cmluZztcbn1cblxuLyoqXG4gKiBIYXNoIGEgc3RyaW5nXG4gKi9cbmZ1bmN0aW9uIGhhc2hjb2RlKHM6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGhhc2ggPSBjcnlwdG8uY3JlYXRlSGFzaCgnbWQ1Jyk7XG4gIGhhc2gudXBkYXRlKHMpO1xuICByZXR1cm4gaGFzaC5kaWdlc3QoJ2hleCcpO1xufTsiXX0=
\No newline at end of file