aws-cdk
Version:
CDK Toolkit, the command line tool for CDK apps
493 lines • 60.6 kB
JavaScript
"use strict";
/* eslint-disable import/order */
Object.defineProperty(exports, "__esModule", { value: true });
const deployStack = require("../../lib/api/deployments/deploy-stack");
const client_iam_1 = require("@aws-sdk/client-iam");
const api_1 = require("../../lib/api");
const mock_sdk_1 = require("../util/mock-sdk");
const cli_io_host_1 = require("../../lib/toolkit/cli-io-host");
const mockDeployStack = jest.spyOn(deployStack, 'deployStack');
let bootstrapper;
let stderrMock;
beforeEach(() => {
cli_io_host_1.CliIoHost.instance().isCI = false;
bootstrapper = new api_1.Bootstrapper({ source: 'default' });
stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => {
return true;
});
});
afterEach(() => {
stderrMock.mockRestore();
});
function mockTheToolkitInfo(stackProps) {
api_1.ToolkitInfo.lookup = jest.fn().mockResolvedValue(api_1.ToolkitInfo.fromStack((0, mock_sdk_1.mockBootstrapStack)(stackProps)));
}
describe('Bootstrapping v2', () => {
const env = {
account: '123456789012',
region: 'us-east-1',
name: 'mock',
};
let sdk;
beforeEach(() => {
sdk = new mock_sdk_1.MockSdkProvider();
// By default, we'll return a non-found toolkit info
api_1.ToolkitInfo.lookup = jest.fn().mockResolvedValue(api_1.ToolkitInfo.bootstrapStackNotFoundInfo('BootstrapStack'));
const value = {
Policy: {
PolicyName: 'my-policy',
Arn: 'arn:aws:iam::0123456789012:policy/my-policy',
},
};
(0, mock_sdk_1.restoreSdkMocksToDefault)();
(0, mock_sdk_1.setDefaultSTSMocks)();
mock_sdk_1.mockIAMClient.on(client_iam_1.GetPolicyCommand).resolves(value);
mock_sdk_1.mockIAMClient.on(client_iam_1.CreatePolicyCommand).resolves(value);
mockDeployStack.mockResolvedValue({
type: 'did-deploy-stack',
noOp: false,
outputs: {},
stackArn: 'arn:stack',
});
});
afterEach(() => {
mockDeployStack.mockClear();
});
test('passes the bucket name as a CFN parameter', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
bucketName: 'my-bucket-name',
cloudFormationExecutionPolicies: ['arn:policy'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
FileAssetsBucketName: 'my-bucket-name',
PublicAccessBlockConfiguration: 'true',
}),
}));
});
test('passes the KMS key ID as a CFN parameter', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
kmsKeyId: 'my-kms-key-id',
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
FileAssetsBucketKmsKeyId: 'my-kms-key-id',
PublicAccessBlockConfiguration: 'true',
}),
}));
});
test('passes false to PublicAccessBlockConfiguration', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
publicAccessBlockConfiguration: false,
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
PublicAccessBlockConfiguration: 'false',
}),
}));
});
test('passes true to PermissionsBoundary', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
examplePermissionsBoundary: true,
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
InputPermissionsBoundary: 'cdk-hnb659fds-permissions-boundary',
}),
}));
});
test('passes value to PermissionsBoundary', async () => {
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'InputPermissionsBoundary',
ParameterValue: 'existing-pb',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
customPermissionsBoundary: 'permissions-boundary-name',
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
InputPermissionsBoundary: 'permissions-boundary-name',
}),
}));
expect(stderrMock.mock.calls).toEqual(expect.arrayContaining([
expect.arrayContaining([
expect.stringMatching(/Changing permissions boundary from existing-pb to permissions-boundary-name/),
]),
]));
});
test('permission boundary switch message does not appear', async () => {
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'InputPermissionsBoundary',
ParameterValue: '',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk);
expect(stderrMock.mock.calls).toEqual(expect.arrayContaining([expect.not.arrayContaining([expect.stringMatching(/Changing permissions boundary/)])]));
});
test('adding new permissions boundary', async () => {
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'InputPermissionsBoundary',
ParameterValue: '',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
customPermissionsBoundary: 'permissions-boundary-name',
},
});
expect(stderrMock.mock.calls).toEqual(expect.arrayContaining([
expect.arrayContaining([expect.stringMatching(/Adding new permissions boundary permissions-boundary-name/)]),
]));
});
test('removing existing permissions boundary', async () => {
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'InputPermissionsBoundary',
ParameterValue: 'permissions-boundary-name',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {},
});
expect(stderrMock.mock.calls).toEqual(expect.arrayContaining([
expect.arrayContaining([
expect.stringMatching(/Removing existing permissions boundary permissions-boundary-name/),
]),
]));
});
test('adding permission boundary with path in policy name', async () => {
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'InputPermissionsBoundary',
ParameterValue: '',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
customPermissionsBoundary: 'permissions-boundary-name/with/path',
},
});
expect(stderrMock.mock.calls).toEqual(expect.arrayContaining([
expect.arrayContaining([
expect.stringMatching(/Adding new permissions boundary permissions-boundary-name\/with\/path/),
]),
]));
});
test('passing trusted accounts without CFN managed policies results in an error', async () => {
await expect(bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
trustedAccounts: ['123456789012'],
},
})).rejects.toThrow(/--cloudformation-execution-policies/);
});
test('passing trusted accounts without CFN managed policies on the existing stack results in an error', async () => {
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'CloudFormationExecutionPolicies',
ParameterValue: '',
},
],
});
await expect(bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
trustedAccounts: ['123456789012'],
},
})).rejects.toThrow(/--cloudformation-execution-policies/);
});
test('passing no CFN managed policies without trusted accounts is okay', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
CloudFormationExecutionPolicies: '',
}),
}));
});
test('passing trusted accounts for lookup generates the correct stack parameter', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
trustedAccountsForLookup: ['123456789012'],
cloudFormationExecutionPolicies: ['aws://foo'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
TrustedAccountsForLookup: '123456789012',
}),
}));
});
test('allow adding trusted account if there was already a policy on the stack', async () => {
// GIVEN
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'CloudFormationExecutionPolicies',
ParameterValue: 'arn:aws:something',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
trustedAccounts: ['123456789012'],
},
});
// Did not throw
});
test('removes trusted account when it is listed as untrusted', async () => {
// GIVEN
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'CloudFormationExecutionPolicies',
ParameterValue: 'arn:aws:something',
},
{
ParameterKey: 'TrustedAccounts',
ParameterValue: '111111111111,222222222222',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
untrustedAccounts: ['111111111111'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
TrustedAccounts: '222222222222',
}),
}));
});
test('removes trusted account for lookup when it is listed as untrusted', async () => {
// GIVEN
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'CloudFormationExecutionPolicies',
ParameterValue: 'arn:aws:something',
},
{
ParameterKey: 'TrustedAccountsForLookup',
ParameterValue: '111111111111,222222222222',
},
],
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
untrustedAccounts: ['111111111111'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
TrustedAccountsForLookup: '222222222222',
}),
}));
});
test('do not allow accounts to be listed as both trusted and untrusted', async () => {
await expect(bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
trustedAccountsForLookup: ['123456789012'],
untrustedAccounts: ['123456789012'],
},
})).rejects.toThrow('Accounts cannot be both trusted and untrusted. Found: 123456789012');
});
test('Do not allow downgrading bootstrap stack version', async () => {
// GIVEN
mockTheToolkitInfo({
Outputs: [
{
OutputKey: 'BootstrapVersion',
OutputValue: '999',
},
],
});
await expect(bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
},
})).resolves.toEqual(expect.objectContaining({ noOp: true }));
});
test('Do not allow overwriting bootstrap stack from a different vendor', async () => {
// GIVEN
mockTheToolkitInfo({
Parameters: [
{
ParameterKey: 'BootstrapVariant',
ParameterValue: 'JoeSchmoe',
},
],
});
await expect(bootstrapper.bootstrapEnvironment(env, sdk, {})).resolves.toEqual(expect.objectContaining({ noOp: true }));
});
test('bootstrap template has the right exports', async () => {
let template;
mockDeployStack.mockImplementation((args) => {
template = args.stack.template;
return Promise.resolve({
type: 'did-deploy-stack',
noOp: false,
outputs: {},
stackArn: 'arn:stack',
});
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
},
});
const exports = Object.values(template.Outputs ?? {})
.filter((o) => o.Export !== undefined)
.map((o) => o.Export.Name);
expect(exports).toEqual([
// This used to be used by aws-s3-assets
{ 'Fn::Sub': 'CdkBootstrap-${Qualifier}-FileAssetKeyArn' },
]);
});
describe('termination protection', () => {
test('stack is not termination protected by default', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
stack: expect.objectContaining({
terminationProtection: false,
}),
}));
});
test('stack is termination protected when option is set', async () => {
await bootstrapper.bootstrapEnvironment(env, sdk, {
terminationProtection: true,
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
stack: expect.objectContaining({
terminationProtection: true,
}),
}));
});
test('termination protection is left alone when option is not given', async () => {
mockTheToolkitInfo({
EnableTerminationProtection: true,
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
stack: expect.objectContaining({
terminationProtection: true,
}),
}));
});
test('termination protection can be switched off', async () => {
mockTheToolkitInfo({
EnableTerminationProtection: true,
});
await bootstrapper.bootstrapEnvironment(env, sdk, {
terminationProtection: false,
parameters: {
cloudFormationExecutionPolicies: ['arn:policy'],
},
});
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
stack: expect.objectContaining({
terminationProtection: false,
}),
}));
});
});
describe('KMS key', () => {
test.each([
// Default case
[undefined, 'AWS_MANAGED_KEY'],
// Create a new key
[true, ''],
// Don't create a new key
[false, 'AWS_MANAGED_KEY'],
])('(new stack) createCustomerMasterKey=%p => parameter becomes %p ', async (createCustomerMasterKey, paramKeyId) => {
// GIVEN: no existing stack
// WHEN
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
createCustomerMasterKey,
cloudFormationExecutionPolicies: ['arn:booh'],
},
});
// THEN
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
FileAssetsBucketKmsKeyId: paramKeyId,
}),
}));
});
test.each([
// Old bootstrap stack being upgraded to new one
[undefined, undefined, 'AWS_MANAGED_KEY'],
// There is a value, user doesn't request a change
['arn:aws:key', undefined, undefined],
// Switch off existing key
['arn:aws:key', false, 'AWS_MANAGED_KEY'],
// Switch on existing key
['AWS_MANAGED_KEY', true, ''],
])('(upgrading) current param %p, createCustomerMasterKey=%p => parameter becomes %p ', async (currentKeyId, createCustomerMasterKey, paramKeyId) => {
// GIVEN
mockTheToolkitInfo({
Parameters: currentKeyId
? [
{
ParameterKey: 'FileAssetsBucketKmsKeyId',
ParameterValue: currentKeyId,
},
]
: undefined,
});
// WHEN
await bootstrapper.bootstrapEnvironment(env, sdk, {
parameters: {
createCustomerMasterKey,
cloudFormationExecutionPolicies: ['arn:booh'],
},
});
// THEN
expect(mockDeployStack).toHaveBeenCalledWith(expect.objectContaining({
parameters: expect.objectContaining({
FileAssetsBucketKmsKeyId: paramKeyId,
}),
}));
});
});
});
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9vdHN0cmFwMi50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYm9vdHN0cmFwMi50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxpQ0FBaUM7O0FBRWpDLHNFQUFzRTtBQUV0RSxvREFBNEU7QUFDNUUsdUNBQTBEO0FBQzFELCtDQUswQjtBQUMxQiwrREFBMEQ7QUFFMUQsTUFBTSxlQUFlLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxXQUFXLEVBQUUsYUFBYSxDQUFDLENBQUM7QUFFL0QsSUFBSSxZQUEwQixDQUFDO0FBQy9CLElBQUksVUFBNEIsQ0FBQztBQUVqQyxVQUFVLENBQUMsR0FBRyxFQUFFO0lBQ2QsdUJBQVMsQ0FBQyxRQUFRLEVBQUUsQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDO0lBQ2xDLFlBQVksR0FBRyxJQUFJLGtCQUFZLENBQUMsRUFBRSxNQUFNLEVBQUUsU0FBUyxFQUFFLENBQUMsQ0FBQztJQUN2RCxVQUFVLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsTUFBTSxFQUFFLE9BQU8sQ0FBQyxDQUFDLGtCQUFrQixDQUFDLEdBQUcsRUFBRTtRQUN2RSxPQUFPLElBQUksQ0FBQztJQUNkLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyxDQUFDLENBQUM7QUFFSCxTQUFTLENBQUMsR0FBRyxFQUFFO0lBQ2IsVUFBVSxDQUFDLFdBQVcsRUFBRSxDQUFDO0FBQzNCLENBQUMsQ0FBQyxDQUFDO0FBRUgsU0FBUyxrQkFBa0IsQ0FBQyxVQUEwQjtJQUNuRCxpQkFBbUIsQ0FBQyxNQUFNLEdBQUcsSUFBSSxDQUFDLEVBQUUsRUFBRSxDQUFDLGlCQUFpQixDQUFDLGlCQUFXLENBQUMsU0FBUyxDQUFDLElBQUEsNkJBQWtCLEVBQUMsVUFBVSxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ25ILENBQUM7QUFFRCxRQUFRLENBQUMsa0JBQWtCLEVBQUUsR0FBRyxFQUFFO0lBQ2hDLE1BQU0sR0FBRyxHQUFH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LFVBQVUsRUFBRSxZQUFZO29CQUN0QixDQUFDLENBQUM7d0JBQ0E7NEJBQ0UsWUFBWSxFQUFFLDBCQUEwQjs0QkFDeEMsY0FBYyxFQUFFLFlBQVk7eUJBQzdCO3FCQUNGO29CQUNELENBQUMsQ0FBQyxTQUFTO2FBQ2QsQ0FBQyxDQUFDO1lBRUgsT0FBTztZQUNQLE1BQU0sWUFBWSxDQUFDLG9CQUFvQixDQUFDLEdBQUcsRUFBRSxHQUFHLEVBQUU7Z0JBQ2hELFVBQVUsRUFBRTtvQkFDVix1QkFBdUI7b0JBQ3ZCLCtCQUErQixFQUFFLENBQUMsVUFBVSxDQUFDO2lCQUM5QzthQUNGLENBQUMsQ0FBQztZQUVILE9BQU87WUFDUCxNQUFNLENBQUMsZUFBZSxDQUFDLENBQUMsb0JBQW9CLENBQzFDLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztnQkFDdEIsVUFBVSxFQUFFLE1BQU0sQ0FBQyxnQkFBZ0IsQ0FBQztvQkFDbEMsd0JBQXdCLEVBQUUsVUFBVTtpQkFDckMsQ0FBQzthQUNILENBQUMsQ0FDSCxDQUFDO1FBQ0osQ0FBQyxDQUNGLENBQUM7SUFDSixDQUFDLENBQUMsQ0FBQztBQUNMLENBQUMsQ0FBQyxDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiLyogZXNsaW50LWRpc2FibGUgaW1wb3J0L29yZGVyICovXG5cbmltcG9ydCAqIGFzIGRlcGxveVN0YWNrIGZyb20gJy4uLy4uL2xpYi9hcGkvZGVwbG95bWVudHMvZGVwbG95LXN0YWNrJztcbmltcG9ydCB7IFN0YWNrIH0gZnJvbSAnQGF3cy1zZGsvY2xpZW50LWNsb3VkZm9ybWF0aW9uJztcbmltcG9ydCB7IENyZWF0ZVBvbGljeUNvbW1hbmQsIEdldFBvbGljeUNvbW1hbmQgfSBmcm9tICdAYXdzLXNkay9jbGllbnQtaWFtJztcbmltcG9ydCB7IEJvb3RzdHJhcHBlciwgVG9vbGtpdEluZm8gfSBmcm9tICcuLi8uLi9saWIvYXBpJztcbmltcG9ydCB7XG4gIG1vY2tCb290c3RyYXBTdGFjayxcbiAgbW9ja0lBTUNsaWVudCxcbiAgTW9ja1Nka1Byb3ZpZGVyLFxuICByZXN0b3JlU2RrTW9ja3NUb0RlZmF1bHQsIHNldERlZmF1bHRTVFNNb2Nrcyxcbn0gZnJvbSAnLi4vdXRpbC9tb2NrLXNkayc7XG5pbXBvcnQgeyBDbGlJb0hvc3QgfSBmcm9tICcuLi8uLi9saWIvdG9vbGtpdC9jbGktaW8taG9zdCc7XG5cbmNvbnN0IG1vY2tEZXBsb3lTdGFjayA9IGplc3Quc3B5T24oZGVwbG95U3RhY2ssICdkZXBsb3lTdGFjaycpO1xuXG5sZXQgYm9vdHN0cmFwcGVyOiBCb290c3RyYXBwZXI7XG5sZXQgc3RkZXJyTW9jazogamVzdC5TcHlJbnN0YW5jZTtcblxuYmVmb3JlRWFjaCgoKSA9PiB7XG4gIENsaUlvSG9zdC5pbnN0YW5jZSgpLmlzQ0kgPSBmYWxzZTtcbiAgYm9vdHN0cmFwcGVyID0gbmV3IEJvb3RzdHJhcHBlcih7IHNvdXJjZTogJ2RlZmF1bHQnIH0pO1xuICBzdGRlcnJNb2NrID0gamVzdC5zcHlPbihwcm9jZXNzLnN0ZGVyciwgJ3dyaXRlJykubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+IHtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfSk7XG59KTtcblxuYWZ0ZXJFYWNoKCgpID0+IHtcbiAgc3RkZXJyTW9jay5tb2NrUmVzdG9yZSgpO1xufSk7XG5cbmZ1bmN0aW9uIG1vY2tUaGVUb29sa2l0SW5mbyhzdGFja1Byb3BzOiBQYXJ0aWFsPFN0YWNrPikge1xuICAoVG9vbGtpdEluZm8gYXMgYW55KS5sb29rdXAgPSBqZXN0LmZuKCkubW9ja1Jlc29sdmVkVmFsdWUoVG9vbGtpdEluZm8uZnJvbVN0YWNrKG1vY2tCb290c3RyYXBTdGFjayhzdGFja1Byb3BzKSkpO1xufVxuXG5kZXNjcmliZSgnQm9vdHN0cmFwcGluZyB2MicsICgpID0+IHtcbiAgY29uc3QgZW52ID0ge1xuICAgIGFjY291bnQ6ICcxMjM0NTY3ODkwMTInLFxuICAgIHJlZ2lvbjogJ3VzLWVhc3QtMScsXG4gICAgbmFtZTogJ21vY2snLFxuICB9O1xuXG4gIGxldCBzZGs6IE1vY2tTZGtQcm92aWRlcjtcbiAgYmVmb3JlRWFjaCgoKSA9PiB7XG4gICAgc2RrID0gbmV3IE1vY2tTZGtQcm92aWRlcigpO1xuICAgIC8vIEJ5IGRlZmF1bHQsIHdlJ2xsIHJldHVybiBhIG5vbi1mb3VuZCB0b29sa2l0IGluZm9cbiAgICAoVG9vbGtpdEluZm8gYXMgYW55KS5sb29rdXAgPSBqZXN0LmZuKCkubW9ja1Jlc29sdmVkVmFsdWUoVG9vbGtpdEluZm8uYm9vdHN0cmFwU3RhY2tOb3RGb3VuZEluZm8oJ0Jvb3RzdHJhcFN0YWNrJykpO1xuICAgIGNvbnN0IHZhbHVlID0ge1xuICAgICAgUG9saWN5OiB7XG4gICAgICAgIFBvbGljeU5hbWU6ICdteS1wb2xpY3knLFxuICAgICAgICBBcm46ICdhcm46YXdzOmlhbTo6MDEyMzQ1Njc4OTAxMjpwb2xpY3kvbXktcG9saWN5JyxcbiAgICAgIH0sXG4gICAgfTtcbiAgICByZXN0b3JlU2RrTW9ja3NUb0RlZmF1bHQoKTtcbiAgICBzZXREZWZhdWx0U1RTTW9ja3MoKTtcbiAgICBtb2NrSUFNQ2xpZW50Lm9uKEdldFBvbGljeUNvbW1hbmQpLnJlc29sdmVzKHZhbHVlKTtcbiAgICBtb2NrSUFNQ2xpZW50Lm9uKENyZWF0ZVBvbGljeUNvbW1hbmQpLnJlc29sdmVzKHZhbHVlKTtcbiAgICBtb2NrRGVwbG95U3RhY2subW9ja1Jlc29sdmVkVmFsdWUoe1xuICAgICAgdHlwZTogJ2RpZC1kZXBsb3ktc3RhY2snLFxuICAgICAgbm9PcDogZmFsc2UsXG4gICAgICBvdXRwdXRzOiB7fSxcbiAgICAgIHN0YWNrQXJuOiAnYXJuOnN0YWNrJyxcbiAgICB9KTtcbiAgfSk7XG5cbiAgYWZ0ZXJFYWNoKCgpID0+IHtcbiAgICBtb2NrRGVwbG95U3RhY2subW9ja0NsZWFyKCk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NlcyB0aGUgYnVja2V0IG5hbWUgYXMgYSBDRk4gcGFyYW1ldGVyJywgYXN5bmMgKCkgPT4ge1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICBidWNrZXROYW1lOiAnbXktYnVja2V0LW5hbWUnLFxuICAgICAgICBjbG91ZEZvcm1hdGlvbkV4ZWN1dGlvblBvbGljaWVzOiBbJ2Fybjpwb2xpY3knXSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgcGFyYW1ldGVyczogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgIEZpbGVBc3NldHNCdWNrZXROYW1lOiAnbXktYnVja2V0LW5hbWUnLFxuICAgICAgICAgIFB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbjogJ3RydWUnLFxuICAgICAgICB9KSxcbiAgICAgIH0pLFxuICAgICk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NlcyB0aGUgS01TIGtleSBJRCBhcyBhIENGTiBwYXJhbWV0ZXInLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXM6IFsnYXJuOnBvbGljeSddLFxuICAgICAgICBrbXNLZXlJZDogJ215LWttcy1rZXktaWQnLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChtb2NrRGVwbG95U3RhY2spLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKFxuICAgICAgZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICBwYXJhbWV0ZXJzOiBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgICAgRmlsZUFzc2V0c0J1Y2tldEttc0tleUlkOiAnbXkta21zLWtleS1pZCcsXG4gICAgICAgICAgUHVibGljQWNjZXNzQmxvY2tDb25maWd1cmF0aW9uOiAndHJ1ZScsXG4gICAgICAgIH0pLFxuICAgICAgfSksXG4gICAgKTtcbiAgfSk7XG5cbiAgdGVzdCgncGFzc2VzIGZhbHNlIHRvIFB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbicsIGFzeW5jICgpID0+IHtcbiAgICBhd2FpdCBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogWydhcm46cG9saWN5J10sXG4gICAgICAgIHB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbjogZmFsc2UsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgZXhwZWN0KG1vY2tEZXBsb3lTdGFjaykudG9IYXZlQmVlbkNhbGxlZFdpdGgoXG4gICAgICBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIHBhcmFtZXRlcnM6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgICBQdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb246ICdmYWxzZScsXG4gICAgICAgIH0pLFxuICAgICAgfSksXG4gICAgKTtcbiAgfSk7XG5cbiAgdGVzdCgncGFzc2VzIHRydWUgdG8gUGVybWlzc2lvbnNCb3VuZGFyeScsIGFzeW5jICgpID0+IHtcbiAgICBhd2FpdCBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgZXhhbXBsZVBlcm1pc3Npb25zQm91bmRhcnk6IHRydWUsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgZXhwZWN0KG1vY2tEZXBsb3lTdGFjaykudG9IYXZlQmVlbkNhbGxlZFdpdGgoXG4gICAgICBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIHBhcmFtZXRlcnM6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgICBJbnB1dFBlcm1pc3Npb25zQm91bmRhcnk6ICdjZGstaG5iNjU5ZmRzLXBlcm1pc3Npb25zLWJvdW5kYXJ5JyxcbiAgICAgICAgfSksXG4gICAgICB9KSxcbiAgICApO1xuICB9KTtcblxuICB0ZXN0KCdwYXNzZXMgdmFsdWUgdG8gUGVybWlzc2lvbnNCb3VuZGFyeScsIGFzeW5jICgpID0+IHtcbiAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgUGFyYW1ldGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgUGFyYW1ldGVyS2V5OiAnSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5JyxcbiAgICAgICAgICBQYXJhbWV0ZXJWYWx1ZTogJ2V4aXN0aW5nLXBiJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGN1c3RvbVBlcm1pc3Npb25zQm91bmRhcnk6ICdwZXJtaXNzaW9ucy1ib3VuZGFyeS1uYW1lJyxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgcGFyYW1ldGVyczogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgIElucHV0UGVybWlzc2lvbnNCb3VuZGFyeTogJ3Blcm1pc3Npb25zLWJvdW5kYXJ5LW5hbWUnLFxuICAgICAgICB9KSxcbiAgICAgIH0pLFxuICAgICk7XG4gICAgZXhwZWN0KHN0ZGVyck1vY2subW9jay5jYWxscykudG9FcXVhbChcbiAgICAgIGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW1xuICAgICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgICAgICBleHBlY3Quc3RyaW5nTWF0Y2hpbmcoL0NoYW5naW5nIHBlcm1pc3Npb25zIGJvdW5kYXJ5IGZyb20gZXhpc3RpbmctcGIgdG8gcGVybWlzc2lvbnMtYm91bmRhcnktbmFtZS8pLFxuICAgICAgICBdKSxcbiAgICAgIF0pLFxuICAgICk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Blcm1pc3Npb24gYm91bmRhcnkgc3dpdGNoIG1lc3NhZ2UgZG9lcyBub3QgYXBwZWFyJywgYXN5bmMgKCkgPT4ge1xuICAgIG1vY2tUaGVUb29sa2l0SW5mbyh7XG4gICAgICBQYXJhbWV0ZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBQYXJhbWV0ZXJLZXk6ICdJbnB1dFBlcm1pc3Npb25zQm91bmRhcnknLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrKTtcblxuICAgIGV4cGVjdChzdGRlcnJNb2NrLm1vY2suY2FsbHMpLnRvRXF1YWwoXG4gICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtleHBlY3Qubm90LmFycmF5Q29udGFpbmluZyhbZXhwZWN0LnN0cmluZ01hdGNoaW5nKC9DaGFuZ2luZyBwZXJtaXNzaW9ucyBib3VuZGFyeS8pXSldKSxcbiAgICApO1xuICB9KTtcblxuICB0ZXN0KCdhZGRpbmcgbmV3IHBlcm1pc3Npb25zIGJvdW5kYXJ5JywgYXN5bmMgKCkgPT4ge1xuICAgIG1vY2tUaGVUb29sa2l0SW5mbyh7XG4gICAgICBQYXJhbWV0ZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBQYXJhbWV0ZXJLZXk6ICdJbnB1dFBlcm1pc3Npb25zQm91bmRhcnknLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGN1c3RvbVBlcm1pc3Npb25zQm91bmRhcnk6ICdwZXJtaXNzaW9ucy1ib3VuZGFyeS1uYW1lJyxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBleHBlY3Qoc3RkZXJyTW9jay5tb2NrLmNhbGxzKS50b0VxdWFsKFxuICAgICAgZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICAgIGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW2V4cGVjdC5zdHJpbmdNYXRjaGluZygvQWRkaW5nIG5ldyBwZXJtaXNzaW9ucyBib3VuZGFyeSBwZXJtaXNzaW9ucy1ib3VuZGFyeS1uYW1lLyldKSxcbiAgICAgIF0pLFxuICAgICk7XG4gIH0pO1xuXG4gIHRlc3QoJ3JlbW92aW5nIGV4aXN0aW5nIHBlcm1pc3Npb25zIGJvdW5kYXJ5JywgYXN5bmMgKCkgPT4ge1xuICAgIG1vY2tUaGVUb29sa2l0SW5mbyh7XG4gICAgICBQYXJhbWV0ZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBQYXJhbWV0ZXJLZXk6ICdJbnB1dFBlcm1pc3Npb25zQm91bmRhcnknLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAncGVybWlzc2lvbnMtYm91bmRhcnktbmFtZScsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge30sXG4gICAgfSk7XG5cbiAgICBleHBlY3Qoc3RkZXJyTW9jay5tb2NrLmNhbGxzKS50b0VxdWFsKFxuICAgICAgZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICAgIGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW1xuICAgICAgICAgIGV4cGVjdC5zdHJpbmdNYXRjaGluZygvUmVtb3ZpbmcgZXhpc3RpbmcgcGVybWlzc2lvbnMgYm91bmRhcnkgcGVybWlzc2lvbnMtYm91bmRhcnktbmFtZS8pLFxuICAgICAgICBdKSxcbiAgICAgIF0pLFxuICAgICk7XG4gIH0pO1xuXG4gIHRlc3QoJ2FkZGluZyBwZXJtaXNzaW9uIGJvdW5kYXJ5IHdpdGggcGF0aCBpbiBwb2xpY3kgbmFtZScsIGFzeW5jICgpID0+IHtcbiAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgUGFyYW1ldGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgUGFyYW1ldGVyS2V5OiAnSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5JyxcbiAgICAgICAgICBQYXJhbWV0ZXJWYWx1ZTogJycsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICBjdXN0b21QZXJtaXNzaW9uc0JvdW5kYXJ5OiAncGVybWlzc2lvbnMtYm91bmRhcnktbmFtZS93aXRoL3BhdGgnLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChzdGRlcnJNb2NrLm1vY2suY2FsbHMpLnRvRXF1YWwoXG4gICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgICAgZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICAgICAgZXhwZWN0LnN0cmluZ01hdGNoaW5nKC9BZGRpbmcgbmV3IHBlcm1pc3Npb25zIGJvdW5kYXJ5IHBlcm1pc3Npb25zLWJvdW5kYXJ5LW5hbWVcXC93aXRoXFwvcGF0aC8pLFxuICAgICAgICBdKSxcbiAgICAgIF0pLFxuICAgICk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NpbmcgdHJ1c3RlZCBhY2NvdW50cyB3aXRob3V0IENGTiBtYW5hZ2VkIHBvbGljaWVzIHJlc3VsdHMgaW4gYW4gZXJyb3InLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgZXhwZWN0KFxuICAgICAgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgICB0cnVzdGVkQWNjb3VudHM6IFsnMTIzNDU2Nzg5MDEyJ10sXG4gICAgICAgIH0sXG4gICAgICB9KSxcbiAgICApLnJlamVjdHMudG9UaHJvdygvLS1jbG91ZGZvcm1hdGlvbi1leGVjdXRpb24tcG9saWNpZXMvKTtcbiAgfSk7XG5cbiAgdGVzdCgncGFzc2luZyB0cnVzdGVkIGFjY291bnRzIHdpdGhvdXQgQ0ZOIG1hbmFnZWQgcG9saWNpZXMgb24gdGhlIGV4aXN0aW5nIHN0YWNrIHJlc3VsdHMgaW4gYW4gZXJyb3InLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIFBhcmFtZXRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFBhcmFtZXRlcktleTogJ0Nsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXMnLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICBhd2FpdCBleHBlY3QoXG4gICAgICBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICAgIHRydXN0ZWRBY2NvdW50czogWycxMjM0NTY3ODkwMTInXSxcbiAgICAgICAgfSxcbiAgICAgIH0pLFxuICAgICkucmVqZWN0cy50b1Rocm93KC8tLWNsb3VkZm9ybWF0aW9uLWV4ZWN1dGlvbi1wb2xpY2llcy8pO1xuICB9KTtcblxuICB0ZXN0KCdwYXNzaW5nIG5vIENGTiBtYW5hZ2VkIHBvbGljaWVzIHdpdGhvdXQgdHJ1c3RlZCBhY2NvdW50cyBpcyBva2F5JywgYXN5bmMgKCkgPT4ge1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge30sXG4gICAgfSk7XG5cbiAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgcGFyYW1ldGVyczogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgIENsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXM6ICcnLFxuICAgICAgICB9KSxcbiAgICAgIH0pLFxuICAgICk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NpbmcgdHJ1c3RlZCBhY2NvdW50cyBmb3IgbG9va3VwIGdlbmVyYXRlcyB0aGUgY29ycmVjdCBzdGFjayBwYXJhbWV0ZXInLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHRydXN0ZWRBY2NvdW50c0Zvckxvb2t1cDogWycxMjM0NTY3ODkwMTInXSxcbiAgICAgICAgY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogWydhd3M6Ly9mb28nXSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChcbiAgICAgIGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgcGFyYW1ldGVyczogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgIFRydXN0ZWRBY2NvdW50c0Zvckxvb2t1cDogJzEyMzQ1Njc4OTAxMicsXG4gICAgICAgIH0pLFxuICAgICAgfSksXG4gICAgKTtcbiAgfSk7XG5cbiAgdGVzdCgnYWxsb3cgYWRkaW5nIHRydXN0ZWQgYWNjb3VudCBpZiB0aGVyZSB3YXMgYWxyZWFkeSBhIHBvbGljeSBvbiB0aGUgc3RhY2snLCBhc3luYyAoKSA9PiB7XG4gICAgLy8gR0lWRU5cbiAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgUGFyYW1ldGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgUGFyYW1ldGVyS2V5OiAnQ2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llcycsXG4gICAgICAgICAgUGFyYW1ldGVyVmFsdWU6ICdhcm46YXdzOnNvbWV0aGluZycsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuXG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHRydXN0ZWRBY2NvdW50czogWycxMjM0NTY3ODkwMTInXSxcbiAgICAgIH0sXG4gICAgfSk7XG4gICAgLy8gRGlkIG5vdCB0aHJvd1xuICB9KTtcblxuICB0ZXN0KCdyZW1vdmVzIHRydXN0ZWQgYWNjb3VudCB3aGVuIGl0IGlzIGxpc3RlZCBhcyB1bnRydXN0ZWQnLCBhc3luYyAoKSA9PiB7XG4gICAgLy8gR0lWRU5cbiAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgUGFyYW1ldGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgUGFyYW1ldGVyS2V5OiAnQ2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llcycsXG4gICAgICAgICAgUGFyYW1ldGVyVmFsdWU6ICdh