UNPKG

@cloudsnorkel/cdk-github-runners

Version:

CDK construct to create GitHub Actions self-hosted runners. Creates ephemeral runners on demand. Easy to deploy and highly customizable.

212 lines 27 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.LambdaAccess = void 0; const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti"); const child_process_1 = require("child_process"); const cdk = require("aws-cdk-lib"); const aws_cdk_lib_1 = require("aws-cdk-lib"); const aws_iam_1 = require("aws-cdk-lib/aws-iam"); const aws_lambda_1 = require("aws-cdk-lib/aws-lambda"); /** * Access configuration options for Lambda functions like setup and webhook function. Use this to limit access to these functions. * * If you need a custom access point, you can implement this abstract class yourself. Note that the Lambda functions expect API Gateway v1 or v2 input. They also expect every URL under the constructed URL to point to the function. */ class LambdaAccess { /** * Disables access to the configured Lambda function. This is useful for the setup function after setup is done. */ static noAccess() { return new NoAccess(); } /** * Provide access using Lambda URL. This is the default and simplest option. It puts no limits on the requester, but the Lambda functions themselves authenticate every request. */ static lambdaUrl() { return new LambdaUrl(); } /** * Provide access using API Gateway. This is the most secure option, but requires additional configuration. It allows you to limit access to specific IP addresses and even to a specific VPC. * * To limit access to GitHub.com use: * * ``` * LambdaAccess.apiGateway({ * allowedIps: LambdaAccess.githubWebhookIps(), * }); * ``` * * Alternatively, get and manually update the list manually with: * * ``` * curl https://api.github.com/meta | jq .hooks * ``` */ static apiGateway(props) { return new ApiGateway(props); } /** * Downloads the list of IP addresses used by GitHub.com for webhooks. * * Note that downloading dynamic data during deployment is not recommended in CDK. This is a workaround for the lack of a better solution. */ static githubWebhookIps() { const githubMeta = (0, child_process_1.execFileSync)('curl', ['-fsSL', 'https://api.github.com/meta']).toString(); const githubMetaJson = JSON.parse(githubMeta); return githubMetaJson.hooks; } } exports.LambdaAccess = LambdaAccess; _a = JSII_RTTI_SYMBOL_1; LambdaAccess[_a] = { fqn: "@cloudsnorkel/cdk-github-runners.LambdaAccess", version: "0.14.11" }; /** * @internal */ class NoAccess extends LambdaAccess { bind(_construct, _id, _lambdaFunction) { return ''; } } /** * @internal */ class LambdaUrl extends LambdaAccess { bind(_construct, _id, lambdaFunction) { return lambdaFunction.addFunctionUrl({ authType: aws_lambda_1.FunctionUrlAuthType.NONE, }).url; } } /** * @internal */ class ApiGateway { constructor(props) { this.props = props; } bind(scope, id, lambdaFunction) { let policy; let endpointConfig = undefined; let vpcEndpoint = undefined; const region = cdk.Stack.of(scope).region; if (this.props?.allowedVpcEndpoints) { // private api gateway with existing vpc endpoints if (this.props?.allowedSecurityGroups) { cdk.Annotations.of(scope).addError('allowedSecurityGroups cannot be used when allowedVpcEndpoints is specified.'); } if (this.props?.allowedIps) { cdk.Annotations.of(scope).addError('allowedIps cannot be used when allowedVpcEndpoints is specified.'); } if (this.props?.allowedVpc) { cdk.Annotations.of(scope).addError('allowedVpc cannot be used when allowedVpcEndpoints is specified.'); } endpointConfig = { types: [aws_cdk_lib_1.aws_apigateway.EndpointType.PRIVATE], vpcEndpoints: this.props.allowedVpcEndpoints, }; policy = aws_iam_1.PolicyDocument.fromJson({ Version: '2012-10-17', Statement: [ { Effect: 'Allow', Principal: '*', Action: 'execute-api:Invoke', Resource: 'execute-api:/*/*/*', Condition: { StringEquals: { 'aws:SourceVpce': this.props.allowedVpcEndpoints.map(ve => ve.vpcEndpointId), }, }, }, ], }); vpcEndpoint = this.props.allowedVpcEndpoints[0]; } else if (this.props?.allowedVpc) { // private api gateway const sg = new aws_cdk_lib_1.aws_ec2.SecurityGroup(scope, `${id}/SG`, { vpc: this.props.allowedVpc, allowAllOutbound: true, }); for (const otherSg of this.props?.allowedSecurityGroups ?? []) { sg.connections.allowFrom(otherSg, aws_cdk_lib_1.aws_ec2.Port.tcp(443)); } for (const ip of this.props?.allowedIps ?? []) { try { sg.connections.allowFrom(aws_cdk_lib_1.aws_ec2.Peer.ipv4(ip), aws_cdk_lib_1.aws_ec2.Port.tcp(443)); } catch { // poor attempt at supporting both IPv4 and IPv6 // we can't accept ec2.IPeer because that doesn't work for public API Gateway sg.connections.allowFrom(aws_cdk_lib_1.aws_ec2.Peer.ipv6(ip), aws_cdk_lib_1.aws_ec2.Port.tcp(443)); } } vpcEndpoint = new aws_cdk_lib_1.aws_ec2.InterfaceVpcEndpoint(scope, `${id}/VpcEndpoint`, { vpc: this.props.allowedVpc, service: aws_cdk_lib_1.aws_ec2.InterfaceVpcEndpointAwsService.APIGATEWAY, privateDnsEnabled: false, securityGroups: [sg], open: false, }); endpointConfig = { types: [aws_cdk_lib_1.aws_apigateway.EndpointType.PRIVATE], vpcEndpoints: [vpcEndpoint], }; policy = aws_iam_1.PolicyDocument.fromJson({ Version: '2012-10-17', Statement: [ { Effect: 'Allow', Principal: '*', Action: 'execute-api:Invoke', Resource: 'execute-api:/*/*/*', Condition: { StringEquals: { 'aws:SourceVpce': vpcEndpoint.vpcEndpointId, }, }, }, ], }); } else { // public api gateway if (this.props?.allowedSecurityGroups) { cdk.Annotations.of(scope).addError('allowedSecurityGroups cannot be used when allowedVpc is not specified.'); } policy = aws_iam_1.PolicyDocument.fromJson({ Version: '2012-10-17', Statement: [ { Effect: 'Allow', Principal: '*', Action: 'execute-api:Invoke', Resource: 'execute-api:/*/*/*', Condition: { IpAddress: { 'aws:SourceIp': this.props?.allowedIps ?? ['0.0.0.0/0'], }, }, }, ], }); } const api = new aws_cdk_lib_1.aws_apigateway.LambdaRestApi(scope, id, { handler: lambdaFunction, proxy: true, cloudWatchRole: false, endpointConfiguration: endpointConfig, policy, }); // remove CfnOutput api.node.tryRemoveChild('Endpoint'); if (vpcEndpoint) { // enabling private DNS affects the entire VPC, so we use the Route53 alias instead // https://docs.aws.amazon.com/apigateway/latest/developerguide/apigateway-private-api-test-invoke-url.html return `https://${api.restApiId}-${vpcEndpoint.vpcEndpointId}.execute-api.${region}.amazonaws.com/${api.deploymentStage.stageName}`; } return api.url; } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYWNjZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2FjY2Vzcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7OztBQUFBLGlEQUE2QztBQUM3QyxtQ0FBbUM7QUFDbkMsNkNBQWlIO0FBQ2pILGlEQUFxRDtBQUNyRCx1REFBNkQ7QUFzQzdEOzs7O0dBSUc7QUFDSCxNQUFzQixZQUFZO0lBQ2hDOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFFBQVE7UUFDYixPQUFPLElBQUksUUFBUSxFQUFFLENBQUM7SUFDeEIsQ0FBQztJQUVEOztPQUVHO0lBQ0gsTUFBTSxDQUFDLFNBQVM7UUFDZCxPQUFPLElBQUksU0FBUyxFQUFFLENBQUM7SUFDekIsQ0FBQztJQUVEOzs7Ozs7Ozs7Ozs7Ozs7O09BZ0JHO0lBQ0gsTUFBTSxDQUFDLFVBQVUsQ0FBQyxLQUE2QjtRQUM3QyxPQUFPLElBQUksVUFBVSxDQUFDLEtBQUssQ0FBQyxDQUFDO0lBQy9CLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsTUFBTSxDQUFDLGdCQUFnQjtRQUNyQixNQUFNLFVBQVUsR0FBRyxJQUFBLDRCQUFZLEVBQUMsTUFBTSxFQUFFLENBQUMsT0FBTyxFQUFFLDZCQUE2QixDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsQ0FBQztRQUM3RixNQUFNLGNBQWMsR0FBRyxJQUFJLENBQUMsS0FBSyxDQUFDLFVBQVUsQ0FBQyxDQUFDO1FBQzlDLE9BQU8sY0FBYyxDQUFDLEtBQUssQ0FBQztJQUM5QixDQUFDOztBQTdDSCxvQ0FxREM7OztBQUVEOztHQUVHO0FBQ0gsTUFBTSxRQUFTLFNBQVEsWUFBWTtJQUMxQixJQUFJLENBQUMsVUFBcUIsRUFBRSxHQUFXLEVBQUUsZUFBZ0M7UUFDOUUsT0FBTyxFQUFFLENBQUM7SUFDWixDQUFDO0NBQ0Y7QUFFRDs7R0FFRztBQUNILE1BQU0sU0FBVSxTQUFRLFlBQVk7SUFDM0IsSUFBSSxDQUFDLFVBQXFCLEVBQUUsR0FBVyxFQUFFLGNBQStCO1FB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QVcsRUFBRSxDQUFDO1lBQ2hCLG1GQUFtRjtZQUNuRiwyR0FBMkc7WUFDM0csT0FBTyxXQUFXLEdBQUcsQ0FBQyxTQUFTLElBQUksV0FBVyxDQUFDLGFBQWEsZ0JBQWdCLE1BQU0sa0JBQWtCLEdBQUcsQ0FBQyxlQUFlLENBQUMsU0FBUyxFQUFFLENBQUM7UUFDdEksQ0FBQztRQUVELE9BQU8sR0FBRyxDQUFDLEdBQUcsQ0FBQztJQUNqQixDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBleGVjRmlsZVN5bmMgfSBmcm9tICdjaGlsZF9wcm9jZXNzJztcbmltcG9ydCAqIGFzIGNkayBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBhd3NfYXBpZ2F0ZXdheSBhcyBhcGlnYXRld2F5LCBhd3NfZWMyIGFzIGVjMiwgYXdzX2lhbSBhcyBpYW0sIGF3c19sYW1iZGEgYXMgbGFtYmRhIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgUG9saWN5RG9jdW1lbnQgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtaWFtJztcbmltcG9ydCB7IEZ1bmN0aW9uVXJsQXV0aFR5cGUgfSBmcm9tICdhd3MtY2RrLWxpYi9hd3MtbGFtYmRhJztcbmltcG9ydCB7IENvbnN0cnVjdCB9IGZyb20gJ2NvbnN0cnVjdHMnO1xuXG5cbmV4cG9ydCBpbnRlcmZhY2UgQXBpR2F0ZXdheUFjY2Vzc1Byb3BzIHtcbiAgLyoqXG4gICAqIENyZWF0ZSBhIHByaXZhdGUgQVBJIEdhdGV3YXkgYW5kIGFsbG93IGFjY2VzcyBmcm9tIHRoZSBzcGVjaWZpZWQgVlBDIGVuZHBvaW50cy5cbiAgICpcbiAgICogVXNlIHRoaXMgdG8gbWFrZSB1c2Ugb2YgZXhpc3RpbmcgVlBDIGVuZHBvaW50cyBvciB0byBzaGFyZSBhbiBlbmRwb2ludCBiZXR3ZWVuIG11bHRpcGxlIGZ1bmN0aW9ucy4gVGhlIFZQQyBlbmRwb2ludCBtdXN0IHBvaW50IHRvIGBlYzIuSW50ZXJmYWNlVnBjRW5kcG9pbnRBd3NTZXJ2aWNlLkFQSUdBVEVXQVlgLlxuICAgKlxuICAgKiBObyBvdGhlciBzZXR0aW5ncyBhcmUgc3VwcG9ydGVkIHdoZW4gdXNpbmcgdGhpcyBvcHRpb24uXG4gICAqXG4gICAqIEFsbCBlbmRwb2ludHMgd2lsbCBiZSBhbGxvd2VkIGFjY2VzcywgYnV0IG9ubHkgdGhlIGZpcnN0IG9uZSB3aWxsIGJlIHVzZWQgYXMgdGhlIFVSTCBieSB0aGUgcnVubmVyIHN5c3RlbSBmb3Igc2V0dGluZyB1cCB0aGUgd2ViaG9vaywgYW5kIGFzIHNldHVwIFVSTC5cbiAgICovXG4gIHJlYWRvbmx5IGFsbG93ZWRWcGNFbmRwb2ludHM/OiBlYzIuSVZwY0VuZHBvaW50W107XG5cbiAgLyoqXG4gICAqIExpc3Qgb2YgSVAgYWRkcmVzc2VzIGluIENJRFIgbm90YXRpb24gdGhhdCBhcmUgYWxsb3dlZCB0byBhY2Nlc3MgdGhlIEFQSSBHYXRld2F5LlxuICAgKlxuICAgKiBJZiBub3Qgc3BlY2lmaWVkIG9uIHB1YmxpYyBBUEkgR2F0ZXdheSwgYWxsIElQIGFkZHJlc3NlcyBhcmUgYWxsb3dlZC5cbiAgICpcbiAgICogSWYgbm90IHNwZWNpZmllZCBvbiBwcml2YXRlIEFQSSBHYXRld2F5LCBubyBJUCBhZGRyZXNzZXMgYXJlIGFsbG93ZWQgKGJ1dCBzcGVjaWZpZWQgc2VjdXJpdHkgZ3JvdXBzIGFyZSkuXG4gICAqL1xuICByZWFkb25seSBhbGxvd2VkSXBzPzogc3RyaW5nW107XG5cbiAgLyoqXG4gICAqIENyZWF0ZSBhIHByaXZhdGUgQVBJIEdhdGV3YXkgYW5kIGFsbG93IGFjY2VzcyBmcm9tIHRoZSBzcGVjaWZpZWQgVlBDLlxuICAgKi9cbiAgcmVhZG9ubHkgYWxsb3dlZFZwYz86IGVjMi5JVnBjO1xuXG4gIC8qKlxuICAgKiBMaXN0IG9mIHNlY3VyaXR5IGdyb3VwcyB0aGF0IGFyZSBhbGxvd2VkIHRvIGFjY2VzcyB0aGUgQVBJIEdhdGV3YXkuXG4gICAqXG4gICAqIE9ubHkgd29ya3MgZm9yIHByaXZhdGUgQVBJIEdhdGV3YXlzIHdpdGgge0BsaW5rIGFsbG93ZWRWcGN9LlxuICAgKi9cbiAgcmVhZG9ubHkgYWxsb3dlZFNlY3VyaXR5R3JvdXBzPzogZWMyLklTZWN1cml0eUdyb3VwW107XG59XG5cbi8qKlxuICogQWNjZXNzIGNvbmZpZ3VyYXRpb24gb3B0aW9ucyBmb3IgTGFtYmRhIGZ1bmN0aW9ucyBsaWtlIHNldHVwIGFuZCB3ZWJob29rIGZ1bmN0aW9uLiBVc2UgdGhpcyB0byBsaW1pdCBhY2Nlc3MgdG8gdGhlc2UgZnVuY3Rpb25zLlxuICpcbiAqIElmIHlvdSBuZWVkIGEgY3VzdG9tIGFjY2VzcyBwb2ludCwgeW91IGNhbiBpbXBsZW1lbnQgdGhpcyBhYnN0cmFjdCBjbGFzcyB5b3Vyc2VsZi4gTm90ZSB0aGF0IHRoZSBMYW1iZGEgZnVuY3Rpb25zIGV4cGVjdCBBUEkgR2F0ZXdheSB2MSBvciB2MiBpbnB1dC4gVGhleSBhbHNvIGV4cGVjdCBldmVyeSBVUkwgdW5kZXIgdGhlIGNvbnN0cnVjdGVkIFVSTCB0byBwb2ludCB0byB0aGUgZnVuY3Rpb24uXG4gKi9cbmV4cG9ydCBhYnN0cmFjdCBjbGFzcyBMYW1iZGFBY2Nlc3Mge1xuICAvKipcbiAgICogRGlzYWJsZXMgYWNjZXNzIHRvIHRoZSBjb25maWd1cmVkIExhbWJkYSBmdW5jdGlvbi4gVGhpcyBpcyB1c2VmdWwgZm9yIHRoZSBzZXR1cCBmdW5jdGlvbiBhZnRlciBzZXR1cCBpcyBkb25lLlxuICAgKi9cbiAgc3RhdGljIG5vQWNjZXNzKCk6IExhbWJkYUFjY2VzcyB7XG4gICAgcmV0dXJuIG5ldyBOb0FjY2VzcygpO1xuICB9XG5cbiAgLyoqXG4gICAqIFByb3ZpZGUgYWNjZXNzIHVzaW5nIExhbWJkYSBVUkwuIFRoaXMgaXMgdGhlIGRlZmF1bHQgYW5kIHNpbXBsZXN0IG9wdGlvbi4gSXQgcHV0cyBubyBsaW1pdHMgb24gdGhlIHJlcXVlc3RlciwgYnV0IHRoZSBMYW1iZGEgZnVuY3Rpb25zIHRoZW1zZWx2ZXMgYXV0aGVudGljYXRlIGV2ZXJ5IHJlcXVlc3QuXG4gICAqL1xuICBzdGF0aWMgbGFtYmRhVXJsKCk6IExhbWJkYUFjY2VzcyB7XG4gICAgcmV0dXJuIG5ldyBMYW1iZGFVcmwoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBQcm92aWRlIGFjY2VzcyB1c2luZyBBUEkgR2F0ZXdheS4gVGhpcyBpcyB0aGUgbW9zdCBzZWN1cmUgb3B0aW9uLCBidXQgcmVxdWlyZXMgYWRkaXRpb25hbCBjb25maWd1cmF0aW9uLiBJdCBhbGxvd3MgeW91IHRvIGxpbWl0IGFjY2VzcyB0byBzcGVjaWZpYyBJUCBhZGRyZXNzZXMgYW5kIGV2ZW4gdG8gYSBzcGVjaWZpYyBWUEMuXG4gICAqXG4gICAqIFRvIGxpbWl0IGFjY2VzcyB0byBHaXRIdWIuY29tIHVzZTpcbiAgICpcbiAgICogYGBgXG4gICAqIExhbWJkYUFjY2Vzcy5hcGlHYXRld2F5KHtcbiAgICogICBhbGxvd2VkSXBzOiBMYW1iZGFBY2Nlc3MuZ2l0aHViV2ViaG9va0lwcygpLFxuICAgKiB9KTtcbiAgICogYGBgXG4gICAqXG4gICAqIEFsdGVybmF0aXZlbHksIGdldCBhbmQgbWFudWFsbHkgdXBkYXRlIHRoZSBsaXN0IG1hbnVhbGx5IHdpdGg6XG4gICAqXG4gICAqIGBgYFxuICAgKiBjdXJsIGh0dHBzOi8vYXBpLmdpdGh1Yi5jb20vbWV0YSB8IGpxIC5ob29rc1xuICAgKiBgYGBcbiAgICovXG4gIHN0YXRpYyBhcGlHYXRld2F5KHByb3BzPzogQXBpR2F0ZXdheUFjY2Vzc1Byb3BzKTogTGFtYmRhQWNjZXNzIHtcbiAgICByZXR1cm4gbmV3IEFwaUdhdGV3YXkocHJvcHMpO1xuICB9XG5cbiAgLyoqXG4gICAqIERvd25sb2FkcyB0aGUgbGlzdCBvZiBJUCBhZGRyZXNzZXMgdXNlZCBieSBHaXRIdWIuY29tIGZvciB3ZWJob29rcy5cbiAgICpcbiAgICogTm90ZSB0aGF0IGRvd25sb2FkaW5nIGR5bmFtaWMgZGF0YSBkdXJpbmcgZGVwbG95bWVudCBpcyBub3QgcmVjb21tZW5kZWQgaW4gQ0RLLiBUaGlzIGlzIGEgd29ya2Fyb3VuZCBmb3IgdGhlIGxhY2sgb2YgYSBiZXR0ZXIgc29sdXRpb24uXG4gICAqL1xuICBzdGF0aWMgZ2l0aHViV2ViaG9va0lwcygpOiBzdHJpbmdbXSB7XG4gICAgY29uc3QgZ2l0aHViTWV0YSA9IGV4ZWNGaWxlU3luYygnY3VybCcsIFsnLWZzU0wnLCAnaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS9tZXRhJ10pLnRvU3RyaW5nKCk7XG4gICAgY29uc3QgZ2l0aHViTWV0YUpzb24gPSBKU09OLnBhcnNlKGdpdGh1Yk1ldGEpO1xuICAgIHJldHVybiBnaXRodWJNZXRhSnNvbi5ob29rcztcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGFsbCByZXF1aXJlZCByZXNvdXJjZXMgYW5kIHJldHVybnMgYWNjZXNzIFVSTCBvciBlbXB0eSBzdHJpbmcgaWYgZGlzYWJsZWQuXG4gICAqXG4gICAqIEByZXR1cm4gYWNjZXNzIFVSTCBvciBlbXB0eSBzdHJpbmcgaWYgZGlzYWJsZWRcbiAgICovXG4gIHB1YmxpYyBhYnN0cmFjdCBiaW5kKHNjb3BlOiBDb25zdHJ1Y3QsIGlkOiBzdHJpbmcsIGxhbWJkYUZ1bmN0aW9uOiBsYW1iZGEuRnVuY3Rpb24pOiBzdHJpbmc7XG59XG5cbi8qKlxuICogQGludGVybmFsXG4gKi9cbmNsYXNzIE5vQWNjZXNzIGV4dGVuZHMgTGFtYmRhQWNjZXNzIHtcbiAgcHVibGljIGJpbmQoX2NvbnN0cnVjdDogQ29uc3RydWN0LCBfaWQ6IHN0cmluZywgX2xhbWJkYUZ1bmN0aW9uOiBsYW1iZGEuRnVuY3Rpb24pOiBzdHJpbmcge1xuICAgIHJldHVybiAnJztcbiAgfVxufVxuXG4vKipcbiAqIEBpbnRlcm5hbFxuICovXG5jbGFzcyBMYW1iZGFVcmwgZXh0ZW5kcyBMYW1iZGFBY2Nlc3Mge1xuICBwdWJsaWMgYmluZChfY29uc3RydWN0OiBDb25zdHJ1Y3QsIF9pZDogc3RyaW5nLCBsYW1iZGFGdW5jdGlvbjogbGFtYmRhLkZ1bmN0aW9uKTogc3RyaW5nIHtcbiAgICByZXR1cm4gbGFtYmRhRnVuY3Rpb24uYWRkRnVuY3Rpb25Vcmwoe1xuICAgICAgYXV0aFR5cGU6IEZ1bmN0aW9uVXJsQXV0aFR5cGUuTk9ORSxcbiAgICB9KS51cmw7XG4gIH1cbn1cblxuLyoqXG4gKiBAaW50ZXJuYWxcbiAqL1xuY2xhc3MgQXBpR2F0ZXdheSB7XG4gIGNvbnN0cnVjdG9yKHByaXZhdGUgcmVhZG9ubHkgcHJvcHM/OiBBcGlHYXRld2F5QWNjZXNzUHJvcHMpIHt9XG5cbiAgcHVibGljIGJpbmQoc2NvcGU6IENvbnN0cnVjdCwgaWQ6IHN0cmluZywgbGFtYmRhRnVuY3Rpb246IGxhbWJkYS5GdW5jdGlvbik6IHN0cmluZyB7XG4gICAgbGV0IHBvbGljeTogaWFtLlBvbGljeURvY3VtZW50O1xuICAgIGxldCBlbmRwb2ludENvbmZpZzogYXBpZ2F0ZXdheS5FbmRwb2ludENvbmZpZ3VyYXRpb24gfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG4gICAgbGV0IHZwY0VuZHBvaW50OiBlYzIuSVZwY0VuZHBvaW50IHwgdW5kZWZpbmVkID0gdW5kZWZpbmVkO1xuICAgIGNvbnN0IHJlZ2lvbiA9IGNkay5TdGFjay5vZihzY29wZSkucmVnaW9uO1xuXG4gICAgaWYgKHRoaXMucHJvcHM/LmFsbG93ZWRWcGNFbmRwb2ludHMpIHtcbiAgICAgIC8vIHByaXZhdGUgYXBpIGdhdGV3YXkgd2l0aCBleGlzdGluZyB2cGMgZW5kcG9pbnRzXG4gICAgICBpZiAodGhpcy5wcm9wcz8uYWxsb3dlZFNlY3VyaXR5R3JvdXBzKSB7XG4gICAgICAgIGNkay5Bbm5vdGF0aW9ucy5vZihzY29wZSkuYWRkRXJyb3IoJ2FsbG93ZWRTZWN1cml0eUdyb3VwcyBjYW5ub3QgYmUgdXNlZCB3aGVuIGFsbG93ZWRWcGNFbmRwb2ludHMgaXMgc3BlY2lmaWVkLicpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMucHJvcHM/LmFsbG93ZWRJcHMpIHtcbiAgICAgICAgY2RrLkFubm90YXRpb25zLm9mKHNjb3BlKS5hZGRFcnJvcignYWxsb3dlZElwcyBjYW5ub3QgYmUgdXNlZCB3aGVuIGFsbG93ZWRWcGNFbmRwb2ludHMgaXMgc3BlY2lmaWVkLicpO1xuICAgICAgfVxuICAgICAgaWYgKHRoaXMucHJvcHM/LmFsbG93ZWRWcGMpIHtcbiAgICAgICAgY2RrLkFubm90YXRpb25zLm9mKHNjb3BlKS5hZGRFcnJvcignYWxsb3dlZFZwYyBjYW5ub3QgYmUgdXNlZCB3aGVuIGFsbG93ZWRWcGNFbmRwb2ludHMgaXMgc3BlY2lmaWVkLicpO1xuICAgICAgfVxuXG4gICAgICBlbmRwb2ludENvbmZpZyA9IHtcbiAgICAgICAgdHlwZXM6IFthcGlnYXRld2F5LkVuZHBvaW50VHlwZS5QUklWQVRFXSxcbiAgICAgICAgdnBjRW5kcG9pbnRzOiB0aGlzLnByb3BzLmFsbG93ZWRWcGNFbmRwb2ludHMsXG4gICAgICB9O1xuXG4gICAgICBwb2xpY3kgPSBQb2xpY3lEb2N1bWVudC5mcm9tSnNvbih7XG4gICAgICAgIFZlcnNpb246ICcyMDEyLTEwLTE3JyxcbiAgICAgICAgU3RhdGVtZW50OiBbXG4gICAgICAgICAge1xuICAgICAgICAgICAgRWZmZWN0OiAnQWxsb3cnLFxuICAgICAgICAgICAgUHJpbmNpcGFsOiAnKicsXG4gICAgICAgICAgICBBY3Rpb246ICdleGVjdXRlLWFwaTpJbnZva2UnLFxuICAgICAgICAgICAgUmVzb3VyY2U6ICdleGVjdXRlLWFwaTovKi8qLyonLFxuICAgICAgICAgICAgQ29uZGl0aW9uOiB7XG4gICAgICAgICAgICAgIFN0cmluZ0VxdWFsczoge1xuICAgICAgICAgICAgICAgICdhd3M6U291cmNlVnBjZSc6IHRoaXMucHJvcHMuYWxsb3dlZFZwY0VuZHBvaW50cy5tYXAodmUgPT4gdmUudnBjRW5kcG9pbnRJZCksXG4gICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICB9LFxuICAgICAgICAgIH0sXG4gICAgICAgIF0sXG4gICAgICB9KTtcblxuICAgICAgdnBjRW5kcG9pbnQgPSB0aGlzLnByb3BzLmFsbG93ZWRWcGNFbmRwb2ludHNbMF07XG4gICAgfSBlbHNlIGlmICh0aGlzLnByb3BzPy5hbGxvd2VkVnBjKSB7XG4gICAgICAvLyBwcml2YXRlIGFwaSBnYXRld2F5XG4gICAgICBjb25zdCBzZyA9IG5ldyBlYzIuU2VjdXJpdHlHcm91cChzY29wZSwgYCR7aWR9L1NHYCwge1xuICAgICAgICB2cGM6IHRoaXMucHJvcHMuYWxsb3dlZFZwYyxcbiAgICAgICAgYWxsb3dBbGxPdXRib3VuZDogdHJ1ZSxcbiAgICAgIH0pO1xuXG4gICAgICBmb3IgKGNvbnN0IG90aGVyU2cgb2YgdGhpcy5wcm9wcz8uYWxsb3dlZFNlY3VyaXR5R3JvdXBzID8/IFtdKSB7XG4gICAgICAgIHNnLmNvbm5lY3Rpb25zLmFsbG93RnJvbShvdGhlclNnLCBlYzIuUG9ydC50Y3AoNDQzKSk7XG4gICAgICB9XG5cbiAgICAgIGZvciAoY29uc3QgaXAgb2YgdGhpcy5wcm9wcz8uYWxsb3dlZElwcyA/PyBbXSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIHNnLmNvbm5lY3Rpb25zLmFsbG93RnJvbShlYzIuUGVlci5pcHY0KGlwKSwgZWMyLlBvcnQudGNwKDQ0MykpO1xuICAgICAgICB9IGNhdGNoIHtcbiAgICAgICAgICAvLyBwb29yIGF0dGVtcHQgYXQgc3VwcG9ydGluZyBib3RoIElQdjQgYW5kIElQdjZcbiAgICAgICAgICAvLyB3ZSBjYW4ndCBhY2NlcHQgZWMyLklQZWVyIGJlY2F1c2UgdGhhdCBkb2Vzbid0IHdvcmsgZm9yIHB1YmxpYyBBUEkgR2F0ZXdheVxuICAgICAgICAgIHNnLmNvbm5lY3Rpb25zLmFsbG93RnJvbShlYzIuUGVlci5pcHY2KGlwKSwgZWMyLlBvcnQudGNwKDQ0MykpO1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIHZwY0VuZHBvaW50ID0gbmV3IGVjMi5JbnRlcmZhY2VWcGNFbmRwb2ludChzY29wZSwgYCR7aWR9L1ZwY0VuZHBvaW50YCwge1xuICAgICAgICB2cGM6IHRoaXMucHJvcHMuYWxsb3dlZFZwYyxcbiAgICAgICAgc2VydmljZTogZWMyLkludGVyZmFjZVZwY0VuZHBvaW50QXdzU2VydmljZS5BUElHQVRFV0FZLFxuICAgICAgICBwcml2YXRlRG5zRW5hYmxlZDogZmFsc2UsXG4gICAgICAgIHNlY3VyaXR5R3JvdXBzOiBbc2ddLFxuICAgICAgICBvcGVuOiBmYWxzZSxcbiAgICAgIH0pO1xuXG4gICAgICBlbmRwb2ludENvbmZpZyA9IHtcbiAgICAgICAgdHlwZXM6IFthcGlnYXRld2F5LkVuZHBvaW50VHlwZS5QUklWQVRFXSxcbiAgICAgICAgdnBjRW5kcG9pbnRzOiBbdnBjRW5kcG9pbnRdLFxuICAgICAgfTtcblxuICAgICAgcG9saWN5ID0gUG9saWN5RG9jdW1lbnQuZnJvbUpzb24oe1xuICAgICAgICBWZXJzaW9uOiAnMjAxMi0xMC0xNycsXG4gICAgICAgIFN0YXRlbWVudDogW1xuICAgICAgICAgIHtcbiAgICAgICAgICAgIEVmZmVjdDogJ0FsbG93JyxcbiAgICAgICAgICAgIFByaW5jaXBhbDogJyonLFxuICAgICAgICAgICAgQWN0aW9uOiAnZXhlY3V0ZS1hcGk6SW52b2tlJyxcbiAgICAgICAgICAgIFJlc291cmNlOiAnZXhlY3V0ZS1hcGk6LyovKi8qJyxcbiAgICAgICAgICAgIENvbmRpdGlvbjoge1xuICAgICAgICAgICAgICBTdHJpbmdFcXVhbHM6IHtcbiAgICAgICAgICAgICAgICAnYXdzOlNvdXJjZVZwY2UnOiB2cGNFbmRwb2ludC52cGNFbmRwb2ludElkLFxuICAgICAgICAgICAgICB9LFxuICAgICAgICAgICAgfSxcbiAgICAgICAgICB9LFxuICAgICAgICBdLFxuICAgICAgfSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIC8vIHB1YmxpYyBhcGkgZ2F0ZXdheVxuICAgICAgaWYgKHRoaXMucHJvcHM/LmFsbG93ZWRTZWN1cml0eUdyb3Vwcykge1xuICAgICAgICBjZGsuQW5ub3RhdGlvbnMub2Yoc2NvcGUpLmFkZEVycm9yKCdhbGxvd2VkU2VjdXJpdHlHcm91cHMgY2Fubm90IGJlIHVzZWQgd2hlbiBhbGxvd2VkVnBjIGlzIG5vdCBzcGVjaWZpZWQuJyk7XG4gICAgICB9XG5cbiAgICAgIHBvbGljeSA9IFBvbGljeURvY3VtZW50LmZyb21Kc29uKHtcbiAgICAgICAgVmVyc2lvbjogJzIwMTItMTAtMTcnLFxuICAgICAgICBTdGF0ZW1lbnQ6IFtcbiAgICAgICAgICB7XG4gICAgICAgICAgICBFZmZlY3Q6ICdBbGxvdycsXG4gICAgICAgICAgICBQcmluY2lwYWw6ICcqJyxcbiAgICAgICAgICAgIEFjdGlvbjogJ2V4ZWN1dGUtYXBpOkludm9rZScsXG4gICAgICAgICAgICBSZXNvdXJjZTogJ2V4ZWN1dGUtYXBpOi8qLyovKicsXG4gICAgICAgICAgICBDb25kaXRpb246IHtcbiAgICAgICAgICAgICAgSXBBZGRyZXNzOiB7XG4gICAgICAgICAgICAgICAgJ2F3czpTb3VyY2VJcCc6IHRoaXMucHJvcHM/LmFsbG93ZWRJcHMgPz8gWycwLjAuMC4wLzAnXSxcbiAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgIH0sXG4gICAgICAgICAgfSxcbiAgICAgICAgXSxcbiAgICAgIH0pO1xuICAgIH1cblxuICAgIGNvbnN0IGFwaSA9IG5ldyBhcGlnYXRld2F5LkxhbWJkYVJlc3RBcGkoc2NvcGUsIGlkLCB7XG4gICAgICBoYW5kbGVyOiBsYW1iZGFGdW5jdGlvbixcbiAgICAgIHByb3h5OiB0cnVlLFxuICAgICAgY2xvdWRXYXRjaFJvbGU6IGZhbHNlLFxuICAgICAgZW5kcG9pbnRDb25maWd1cmF0aW9uOiBlbmRwb2ludENvbmZpZyxcbiAgICAgIHBvbGljeSxcbiAgICB9KTtcblxuICAgIC8vIHJlbW92ZSBDZm5PdXRwdXRcbiAgICBhcGkubm9kZS50cnlSZW1vdmVDaGlsZCgnRW5kcG9pbnQnKTtcblxuICAgIGlmICh2cGNFbmRwb2ludCkge1xuICAgICAgLy8gZW5hYmxpbmcgcHJpdmF0ZSBETlMgYWZmZWN0cyB0aGUgZW50aXJlIFZQQywgc28gd2UgdXNlIHRoZSBSb3V0ZTUzIGFsaWFzIGluc3RlYWRcbiAgICAgIC8vIGh0dHBzOi8vZG9jcy5hd3MuYW1hem9uLmNvbS9hcGlnYXRld2F5L2xhdGVzdC9kZXZlbG9wZXJndWlkZS9hcGlnYXRld2F5LXByaXZhdGUtYXBpLXRlc3QtaW52b2tlLXVybC5odG1sXG4gICAgICByZXR1cm4gYGh0dHBzOi8vJHthcGkucmVzdEFwaUlkfS0ke3ZwY0VuZHBvaW50LnZwY0VuZHBvaW50SWR9LmV4ZWN1dGUtYXBpLiR7cmVnaW9ufS5hbWF6b25hd3MuY29tLyR7YXBpLmRlcGxveW1lbnRTdGFnZS5zdGFnZU5hbWV9YDtcbiAgICB9XG5cbiAgICByZXR1cm4gYXBpLnVybDtcbiAgfVxufVxuIl19