UNPKG

aws-cdk

Version:

CDK Toolkit, the command line tool for CDK apps

433 lines 52.4 kB
"use strict"; /* eslint-disable import/order */ Object.defineProperty(exports, "__esModule", { value: true }); const mockDeployStack = jest.fn(); jest.mock('../../lib/api/deploy-stack', () => ({ deployStack: mockDeployStack, })); const api_1 = require("../../lib/api"); const mock_sdk_1 = require("../util/mock-sdk"); let bootstrapper; let mockGetPolicyIamCode; let mockCreatePolicyIamCode; let stderrMock; beforeEach(() => { bootstrapper = new api_1.Bootstrapper({ source: 'default' }); stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => { return true; }); }); afterEach(() => { stderrMock.mockRestore(); }); function mockTheToolkitInfo(stackProps) { const sdk = new mock_sdk_1.MockSdk(); api_1.ToolkitInfo.lookup = jest.fn().mockResolvedValue(api_1.ToolkitInfo.fromStack((0, mock_sdk_1.mockBootstrapStack)(sdk, stackProps))); } describe('Bootstrapping v2', () => { const env = { account: '123456789012', region: 'us-east-1', name: 'mock', }; let sdk; beforeEach(() => { sdk = new mock_sdk_1.MockSdkProvider({ realSdk: false }); // 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', }, }; mockGetPolicyIamCode = jest.fn().mockReturnValue(value); mockCreatePolicyIamCode = jest.fn().mockReturnValue(value); sdk.stubIam({ createPolicy: mockCreatePolicyIamCode, getPolicy: mockGetPolicyIamCode, }); }); 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('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; }); 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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYm9vdHN0cmFwMi50ZXN0LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYm9vdHN0cmFwMi50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxpQ0FBaUM7O0FBRWpDLE1BQU0sZUFBZSxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQztBQUVsQyxJQUFJLENBQUMsSUFBSSxDQUFDLDRCQUE0QixFQUFFLEdBQUcsRUFBRSxDQUFDLENBQUM7SUFDN0MsV0FBVyxFQUFFLGVBQWU7Q0FDN0IsQ0FBQyxDQUFDLENBQUM7QUFHSix1Q0FBOEU7QUFDOUUsK0NBQWdGO0FBRWhGLElBQUksWUFBMEIsQ0FBQztBQUMvQixJQUFJLG9CQUF5RixDQUFDO0FBQzlGLElBQUksdUJBQWtHLENBQUM7QUFDdkcsSUFBSSxVQUE0QixDQUFDO0FBRWpDLFVBQVUsQ0FBQyxHQUFHLEVBQUU7SUFDZCxZQUFZLEdBQUcsSUFBSSxrQkFBWSxDQUFDLEVBQUUsTUFBTSxFQUFFLFNBQVMsRUFBRSxDQUFDLENBQUM7SUFDdkQsVUFBVSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQyxrQkFBa0IsQ0FBQyxHQUFHLEVBQUUsR0FBRyxPQUFPLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQzlGLENBQUMsQ0FBQyxDQUFDO0FBRUgsU0FBUyxDQUFDLEdBQUcsRUFBRTtJQUNiLFVBQVUsQ0FBQyxXQUFXLEVBQUUsQ0FBQztBQUMzQixDQUFDLENBQUMsQ0FBQztBQUVILFNBQVMsa0JBQWtCLENBQUMsVUFBNkM7SUFDdkUsTUFBTSxHQUFHLEdBQUcsSUFBSSxrQkFBTyxFQUFFLENBQUM7SUFDekIsaUJBQW1CLENBQUMsTUFBTSxHQUFHLElBQUksQ0FBQyxFQUFFLEVBQUUsQ0FBQyxpQkFBaUIsQ0FBQyxpQkFBVyxDQUFDLFNBQVMsQ0FBQyxJQUFBLDZCQUFrQixFQUFDLEdBQUcsRUFBRSxVQUFVLENB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b3JkZXIgKi9cblxuY29uc3QgbW9ja0RlcGxveVN0YWNrID0gamVzdC5mbigpO1xuXG5qZXN0Lm1vY2soJy4uLy4uL2xpYi9hcGkvZGVwbG95LXN0YWNrJywgKCkgPT4gKHtcbiAgZGVwbG95U3RhY2s6IG1vY2tEZXBsb3lTdGFjayxcbn0pKTtcblxuaW1wb3J0IHsgSUFNIH0gZnJvbSAnYXdzLXNkayc7XG5pbXBvcnQgeyBCb290c3RyYXBwZXIsIERlcGxveVN0YWNrT3B0aW9ucywgVG9vbGtpdEluZm8gfSBmcm9tICcuLi8uLi9saWIvYXBpJztcbmltcG9ydCB7IG1vY2tCb290c3RyYXBTdGFjaywgTW9ja1NkaywgTW9ja1Nka1Byb3ZpZGVyIH0gZnJvbSAnLi4vdXRpbC9tb2NrLXNkayc7XG5cbmxldCBib290c3RyYXBwZXI6IEJvb3RzdHJhcHBlcjtcbmxldCBtb2NrR2V0UG9saWN5SWFtQ29kZTogKHBhcmFtczogSUFNLlR5cGVzLkdldFBvbGljeVJlcXVlc3QpID0+IElBTS5UeXBlcy5HZXRQb2xpY3lSZXNwb25zZTtcbmxldCBtb2NrQ3JlYXRlUG9saWN5SWFtQ29kZTogKHBhcmFtczogSUFNLlR5cGVzLkNyZWF0ZVBvbGljeVJlcXVlc3QpID0+IElBTS5UeXBlcy5DcmVhdGVQb2xpY3lSZXNwb25zZTtcbmxldCBzdGRlcnJNb2NrOiBqZXN0LlNweUluc3RhbmNlO1xuXG5iZWZvcmVFYWNoKCgpID0+IHtcbiAgYm9vdHN0cmFwcGVyID0gbmV3IEJvb3RzdHJhcHBlcih7IHNvdXJjZTogJ2RlZmF1bHQnIH0pO1xuICBzdGRlcnJNb2NrID0gamVzdC5zcHlPbihwcm9jZXNzLnN0ZGVyciwgJ3dyaXRlJykubW9ja0ltcGxlbWVudGF0aW9uKCgpID0+IHsgcmV0dXJuIHRydWU7IH0pO1xufSk7XG5cbmFmdGVyRWFjaCgoKSA9PiB7XG4gIHN0ZGVyck1vY2subW9ja1Jlc3RvcmUoKTtcbn0pO1xuXG5mdW5jdGlvbiBtb2NrVGhlVG9vbGtpdEluZm8oc3RhY2tQcm9wczogUGFydGlhbDxBV1MuQ2xvdWRGb3JtYXRpb24uU3RhY2s+KSB7XG4gIGNvbnN0IHNkayA9IG5ldyBNb2NrU2RrKCk7XG4gIChUb29sa2l0SW5mbyBhcyBhbnkpLmxvb2t1cCA9IGplc3QuZm4oKS5tb2NrUmVzb2x2ZWRWYWx1ZShUb29sa2l0SW5mby5mcm9tU3RhY2sobW9ja0Jvb3RzdHJhcFN0YWNrKHNkaywgc3RhY2tQcm9wcykpKTtcbn1cblxuZGVzY3JpYmUoJ0Jvb3RzdHJhcHBpbmcgdjInLCAoKSA9PiB7XG4gIGNvbnN0IGVudiA9IHtcbiAgICBhY2NvdW50OiAnMTIzNDU2Nzg5MDEyJyxcbiAgICByZWdpb246ICd1cy1lYXN0LTEnLFxuICAgIG5hbWU6ICdtb2NrJyxcbiAgfTtcblxuICBsZXQgc2RrOiBNb2NrU2RrUHJvdmlkZXI7XG4gIGJlZm9yZUVhY2goKCkgPT4ge1xuICAgIHNkayA9IG5ldyBNb2NrU2RrUHJvdmlkZXIoeyByZWFsU2RrOiBmYWxzZSB9KTtcbiAgICAvLyBCeSBkZWZhdWx0LCB3ZSdsbCByZXR1cm4gYSBub24tZm91bmQgdG9vbGtpdCBpbmZvXG4gICAgKFRvb2xraXRJbmZvIGFzIGFueSkubG9va3VwID0gamVzdC5mbigpLm1vY2tSZXNvbHZlZFZhbHVlKFRvb2xraXRJbmZvLmJvb3RzdHJhcFN0YWNrTm90Rm91bmRJbmZvKCdCb290c3RyYXBTdGFjaycpKTtcbiAgICBjb25zdCB2YWx1ZSA9IHtcbiAgICAgIFBvbGljeToge1xuICAgICAgICBQb2xpY3lOYW1lOiAnbXktcG9saWN5JyxcbiAgICAgICAgQXJuOiAnYXJuOmF3czppYW06OjAxMjM0NTY3ODkwMTI6cG9saWN5L215LXBvbGljeScsXG4gICAgICB9LFxuICAgIH07XG4gICAgbW9ja0dldFBvbGljeUlhbUNvZGUgPSBqZXN0LmZuKCkubW9ja1JldHVyblZhbHVlKHZhbHVlKTtcbiAgICBtb2NrQ3JlYXRlUG9saWN5SWFtQ29kZSA9IGplc3QuZm4oKS5tb2NrUmV0dXJuVmFsdWUodmFsdWUpO1xuICAgIHNkay5zdHViSWFtKHtcbiAgICAgIGNyZWF0ZVBvbGljeTogbW9ja0NyZWF0ZVBvbGljeUlhbUNvZGUsXG4gICAgICBnZXRQb2xpY3k6IG1vY2tHZXRQb2xpY3lJYW1Db2RlLFxuICAgIH0pO1xuICB9KTtcblxuICBhZnRlckVhY2goKCkgPT4ge1xuICAgIG1vY2tEZXBsb3lTdGFjay5tb2NrQ2xlYXIoKTtcbiAgfSk7XG5cbiAgdGVzdCgncGFzc2VzIHRoZSBidWNrZXQgbmFtZSBhcyBhIENGTiBwYXJhbWV0ZXInLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGJ1Y2tldE5hbWU6ICdteS1idWNrZXQtbmFtZScsXG4gICAgICAgIGNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXM6IFsnYXJuOnBvbGljeSddLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChtb2NrRGVwbG95U3RhY2spLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgIHBhcmFtZXRlcnM6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgRmlsZUFzc2V0c0J1Y2tldE5hbWU6ICdteS1idWNrZXQtbmFtZScsXG4gICAgICAgIFB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbjogJ3RydWUnLFxuICAgICAgfSksXG4gICAgfSkpO1xuICB9KTtcblxuICB0ZXN0KCdwYXNzZXMgdGhlIEtNUyBrZXkgSUQgYXMgYSBDRk4gcGFyYW1ldGVyJywgYXN5bmMgKCkgPT4ge1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICBjbG91ZEZvcm1hdGlvbkV4ZWN1dGlvblBvbGljaWVzOiBbJ2Fybjpwb2xpY3knXSxcbiAgICAgICAga21zS2V5SWQ6ICdteS1rbXMta2V5LWlkJyxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICBwYXJhbWV0ZXJzOiBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIEZpbGVBc3NldHNCdWNrZXRLbXNLZXlJZDogJ215LWttcy1rZXktaWQnLFxuICAgICAgICBQdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb246ICd0cnVlJyxcbiAgICAgIH0pLFxuICAgIH0pKTtcbiAgfSk7XG5cbiAgdGVzdCgncGFzc2VzIGZhbHNlIHRvIFB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbicsIGFzeW5jICgpID0+IHtcbiAgICBhd2FpdCBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogWydhcm46cG9saWN5J10sXG4gICAgICAgIHB1YmxpY0FjY2Vzc0Jsb2NrQ29uZmlndXJhdGlvbjogZmFsc2UsXG4gICAgICB9LFxuICAgIH0pO1xuXG4gICAgZXhwZWN0KG1vY2tEZXBsb3lTdGFjaykudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgcGFyYW1ldGVyczogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICBQdWJsaWNBY2Nlc3NCbG9ja0NvbmZpZ3VyYXRpb246ICdmYWxzZScsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NlcyB0cnVlIHRvIFBlcm1pc3Npb25zQm91bmRhcnknLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGV4YW1wbGVQZXJtaXNzaW9uc0JvdW5kYXJ5OiB0cnVlLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChtb2NrRGVwbG95U3RhY2spLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgIHBhcmFtZXRlcnM6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5OiAnY2RrLWhuYjY1OWZkcy1wZXJtaXNzaW9ucy1ib3VuZGFyeScsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NlcyB2YWx1ZSB0byBQZXJtaXNzaW9uc0JvdW5kYXJ5JywgYXN5bmMgKCkgPT4ge1xuICAgIG1vY2tUaGVUb29sa2l0SW5mbyh7XG4gICAgICBQYXJhbWV0ZXJzOiBbXG4gICAgICAgIHtcbiAgICAgICAgICBQYXJhbWV0ZXJLZXk6ICdJbnB1dFBlcm1pc3Npb25zQm91bmRhcnknLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnZXhpc3RpbmctcGInLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcbiAgICBhd2FpdCBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgY3VzdG9tUGVybWlzc2lvbnNCb3VuZGFyeTogJ3Blcm1pc3Npb25zLWJvdW5kYXJ5LW5hbWUnLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChtb2NrRGVwbG95U3RhY2spLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgIHBhcmFtZXRlcnM6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5OiAncGVybWlzc2lvbnMtYm91bmRhcnktbmFtZScsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gICAgZXhwZWN0KHN0ZGVyck1vY2subW9jay5jYWxscykudG9FcXVhbChleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgIGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW1xuICAgICAgICBleHBlY3Quc3RyaW5nTWF0Y2hpbmcoL0NoYW5naW5nIHBlcm1pc3Npb25zIGJvdW5kYXJ5IGZyb20gZXhpc3RpbmctcGIgdG8gcGVybWlzc2lvbnMtYm91bmRhcnktbmFtZS8pLFxuICAgICAgXSksXG4gICAgXSkpO1xuICB9KTtcblxuICB0ZXN0KCdwZXJtaXNzaW9uIGJvdW5kYXJ5IHN3aXRjaCBtZXNzYWdlIGRvZXMgbm90IGFwcGVhcicsIGFzeW5jICgpID0+IHtcbiAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgUGFyYW1ldGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgUGFyYW1ldGVyS2V5OiAnSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5JyxcbiAgICAgICAgICBQYXJhbWV0ZXJWYWx1ZTogJycsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkayk7XG5cbiAgICBleHBlY3Qoc3RkZXJyTW9jay5tb2NrLmNhbGxzKS50b0VxdWFsKGV4cGVjdC5hcnJheUNvbnRhaW5pbmcoW1xuICAgICAgZXhwZWN0Lm5vdC5hcnJheUNvbnRhaW5pbmcoW1xuICAgICAgICBleHBlY3Quc3RyaW5nTWF0Y2hpbmcoL0NoYW5naW5nIHBlcm1pc3Npb25zIGJvdW5kYXJ5LyksXG4gICAgICBdKSxcbiAgICBdKSk7XG4gIH0pO1xuXG4gIHRlc3QoJ2FkZGluZyBuZXcgcGVybWlzc2lvbnMgYm91bmRhcnknLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIFBhcmFtZXRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFBhcmFtZXRlcktleTogJ0lucHV0UGVybWlzc2lvbnNCb3VuZGFyeScsXG4gICAgICAgICAgUGFyYW1ldGVyVmFsdWU6ICcnLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcbiAgICBhd2FpdCBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgY3VzdG9tUGVybWlzc2lvbnNCb3VuZGFyeTogJ3Blcm1pc3Npb25zLWJvdW5kYXJ5LW5hbWUnLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChzdGRlcnJNb2NrLm1vY2suY2FsbHMpLnRvRXF1YWwoZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgICAgZXhwZWN0LnN0cmluZ01hdGNoaW5nKC9BZGRpbmcgbmV3IHBlcm1pc3Npb25zIGJvdW5kYXJ5IHBlcm1pc3Npb25zLWJvdW5kYXJ5LW5hbWUvKSxcbiAgICAgIF0pLFxuICAgIF0pKTtcbiAgfSk7XG5cbiAgdGVzdCgncmVtb3ZpbmcgZXhpc3RpbmcgcGVybWlzc2lvbnMgYm91bmRhcnknLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIFBhcmFtZXRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFBhcmFtZXRlcktleTogJ0lucHV0UGVybWlzc2lvbnNCb3VuZGFyeScsXG4gICAgICAgICAgUGFyYW1ldGVyVmFsdWU6ICdwZXJtaXNzaW9ucy1ib3VuZGFyeS1uYW1lJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7fSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChzdGRlcnJNb2NrLm1vY2suY2FsbHMpLnRvRXF1YWwoZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgICAgZXhwZWN0LnN0cmluZ01hdGNoaW5nKC9SZW1vdmluZyBleGlzdGluZyBwZXJtaXNzaW9ucyBib3VuZGFyeSBwZXJtaXNzaW9ucy1ib3VuZGFyeS1uYW1lLyksXG4gICAgICBdKSxcbiAgICBdKSk7XG4gIH0pO1xuXG4gIHRlc3QoJ2FkZGluZyBwZXJtaXNzaW9uIGJvdW5kYXJ5IHdpdGggcGF0aCBpbiBwb2xpY3kgbmFtZScsIGFzeW5jICgpID0+IHtcbiAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgUGFyYW1ldGVyczogW1xuICAgICAgICB7XG4gICAgICAgICAgUGFyYW1ldGVyS2V5OiAnSW5wdXRQZXJtaXNzaW9uc0JvdW5kYXJ5JyxcbiAgICAgICAgICBQYXJhbWV0ZXJWYWx1ZTogJycsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICBjdXN0b21QZXJtaXNzaW9uc0JvdW5kYXJ5OiAncGVybWlzc2lvbnMtYm91bmRhcnktbmFtZS93aXRoL3BhdGgnLFxuICAgICAgfSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChzdGRlcnJNb2NrLm1vY2suY2FsbHMpLnRvRXF1YWwoZXhwZWN0LmFycmF5Q29udGFpbmluZyhbXG4gICAgICBleHBlY3QuYXJyYXlDb250YWluaW5nKFtcbiAgICAgICAgZXhwZWN0LnN0cmluZ01hdGNoaW5nKC9BZGRpbmcgbmV3IHBlcm1pc3Npb25zIGJvdW5kYXJ5IHBlcm1pc3Npb25zLWJvdW5kYXJ5LW5hbWVcXC93aXRoXFwvcGF0aC8pLFxuICAgICAgXSksXG4gICAgXSkpO1xuICB9KTtcblxuICB0ZXN0KCdwYXNzaW5nIHRydXN0ZWQgYWNjb3VudHMgd2l0aG91dCBDRk4gbWFuYWdlZCBwb2xpY2llcyByZXN1bHRzIGluIGFuIGVycm9yJywgYXN5bmMgKCkgPT4ge1xuICAgIGF3YWl0IGV4cGVjdChib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgdHJ1c3RlZEFjY291bnRzOiBbJzEyMzQ1Njc4OTAxMiddLFxuICAgICAgfSxcbiAgICB9KSlcbiAgICAgIC5yZWplY3RzXG4gICAgICAudG9UaHJvdygvLS1jbG91ZGZvcm1hdGlvbi1leGVjdXRpb24tcG9saWNpZXMvKTtcbiAgfSk7XG5cbiAgdGVzdCgncGFzc2luZyB0cnVzdGVkIGFjY291bnRzIHdpdGhvdXQgQ0ZOIG1hbmFnZWQgcG9saWNpZXMgb24gdGhlIGV4aXN0aW5nIHN0YWNrIHJlc3VsdHMgaW4gYW4gZXJyb3InLCBhc3luYyAoKSA9PiB7XG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIFBhcmFtZXRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFBhcmFtZXRlcktleTogJ0Nsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXMnLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICBhd2FpdCBleHBlY3QoYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHRydXN0ZWRBY2NvdW50czogWycxMjM0NTY3ODkwMTInXSxcbiAgICAgIH0sXG4gICAgfSkpXG4gICAgICAucmVqZWN0c1xuICAgICAgLnRvVGhyb3coLy0tY2xvdWRmb3JtYXRpb24tZXhlY3V0aW9uLXBvbGljaWVzLyk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3Npbmcgbm8gQ0ZOIG1hbmFnZWQgcG9saWNpZXMgd2l0aG91dCB0cnVzdGVkIGFjY291bnRzIGlzIG9rYXknLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7fSxcbiAgICB9KTtcblxuICAgIGV4cGVjdChtb2NrRGVwbG95U3RhY2spLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgIHBhcmFtZXRlcnM6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgQ2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogJycsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gIH0pO1xuXG4gIHRlc3QoJ3Bhc3NpbmcgdHJ1c3RlZCBhY2NvdW50cyBmb3IgbG9va3VwIGdlbmVyYXRlcyB0aGUgY29ycmVjdCBzdGFjayBwYXJhbWV0ZXInLCBhc3luYyAoKSA9PiB7XG4gICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIHRydXN0ZWRBY2NvdW50c0Zvckxvb2t1cDogWycxMjM0NTY3ODkwMTInXSxcbiAgICAgICAgY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogWydhd3M6Ly9mb28nXSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICBwYXJhbWV0ZXJzOiBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIFRydXN0ZWRBY2NvdW50c0Zvckxvb2t1cDogJzEyMzQ1Njc4OTAxMicsXG4gICAgICB9KSxcbiAgICB9KSk7XG4gIH0pO1xuXG4gIHRlc3QoJ2FsbG93IGFkZGluZyB0cnVzdGVkIGFjY291bnQgaWYgdGhlcmUgd2FzIGFscmVhZHkgYSBwb2xpY3kgb24gdGhlIHN0YWNrJywgYXN5bmMgKCkgPT4ge1xuICAgIC8vIEdJVkVOXG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIFBhcmFtZXRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFBhcmFtZXRlcktleTogJ0Nsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXMnLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnYXJuOmF3czpzb21ldGhpbmcnLFxuICAgICAgICB9LFxuICAgICAgXSxcbiAgICB9KTtcblxuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICB0cnVzdGVkQWNjb3VudHM6IFsnMTIzNDU2Nzg5MDEyJ10sXG4gICAgICB9LFxuICAgIH0pO1xuICAgIC8vIERpZCBub3QgdGhyb3dcbiAgfSk7XG5cbiAgdGVzdCgnRG8gbm90IGFsbG93IGRvd25ncmFkaW5nIGJvb3RzdHJhcCBzdGFjayB2ZXJzaW9uJywgYXN5bmMgKCkgPT4ge1xuICAgIC8vIEdJVkVOXG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIE91dHB1dHM6IFtcbiAgICAgICAge1xuICAgICAgICAgIE91dHB1dEtleTogJ0Jvb3RzdHJhcFZlcnNpb24nLFxuICAgICAgICAgIE91dHB1dFZhbHVlOiAnOTk5JyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICBhd2FpdCBleHBlY3QoYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgIGNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXM6IFsnYXJuOnBvbGljeSddLFxuICAgICAgfSxcbiAgICB9KSkucmVzb2x2ZXMudG9FcXVhbChleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7IG5vT3A6IHRydWUgfSkpO1xuICB9KTtcblxuICB0ZXN0KCdEbyBub3QgYWxsb3cgb3ZlcndyaXRpbmcgYm9vdHN0cmFwIHN0YWNrIGZyb20gYSBkaWZmZXJlbnQgdmVuZG9yJywgYXN5bmMgKCkgPT4ge1xuICAgIC8vIEdJVkVOXG4gICAgbW9ja1RoZVRvb2xraXRJbmZvKHtcbiAgICAgIFBhcmFtZXRlcnM6IFtcbiAgICAgICAge1xuICAgICAgICAgIFBhcmFtZXRlcktleTogJ0Jvb3RzdHJhcFZhcmlhbnQnLFxuICAgICAgICAgIFBhcmFtZXRlclZhbHVlOiAnSm9lU2NobW9lJyxcbiAgICAgICAgfSxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICBhd2FpdCBleHBlY3QoYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgfSkpLnJlc29sdmVzLnRvRXF1YWwoZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoeyBub09wOiB0cnVlIH0pKTtcbiAgfSk7XG5cbiAgdGVzdCgnYm9vdHN0cmFwIHRlbXBsYXRlIGhhcyB0aGUgcmlnaHQgZXhwb3J0cycsIGFzeW5jICgpID0+IHtcbiAgICBsZXQgdGVtcGxhdGU6IGFueTtcbiAgICBtb2NrRGVwbG95U3RhY2subW9ja0ltcGxlbWVudGF0aW9uKChhcmdzOiBEZXBsb3lTdGFja09wdGlvbnMpID0+IHtcbiAgICAgIHRlbXBsYXRlID0gYXJncy5zdGFjay50ZW1wbGF0ZTtcbiAgICB9KTtcblxuICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICBjbG91ZEZvcm1hdGlvbkV4ZWN1dGlvblBvbGljaWVzOiBbJ2Fybjpwb2xpY3knXSxcbiAgICAgIH0sXG4gICAgfSk7XG5cbiAgICBjb25zdCBleHBvcnRzID0gT2JqZWN0LnZhbHVlcyh0ZW1wbGF0ZS5PdXRwdXRzID8/IHt9KVxuICAgICAgLmZpbHRlcigobzogYW55KSA9PiBvLkV4cG9ydCAhPT0gdW5kZWZpbmVkKVxuICAgICAgLm1hcCgobzogYW55KSA9PiBvLkV4cG9ydC5OYW1lKTtcblxuICAgIGV4cGVjdChleHBvcnRzKS50b0VxdWFsKFtcbiAgICAgIC8vIFRoaXMgdXNlZCB0byBiZSB1c2VkIGJ5IGF3cy1zMy1hc3NldHNcbiAgICAgIHsgJ0ZuOjpTdWInOiAnQ2RrQm9vdHN0cmFwLSR7UXVhbGlmaWVyfS1GaWxlQXNzZXRLZXlBcm4nIH0sXG4gICAgXSk7XG4gIH0pO1xuXG4gIGRlc2NyaWJlKCd0ZXJtaW5hdGlvbiBwcm90ZWN0aW9uJywgKCkgPT4ge1xuICAgIHRlc3QoJ3N0YWNrIGlzIG5vdCB0ZXJtaW5hdGlvbiBwcm90ZWN0ZWQgYnkgZGVmYXVsdCcsIGFzeW5jICgpID0+IHtcbiAgICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgICBwYXJhbWV0ZXJzOiB7XG4gICAgICAgICAgY2xvdWRGb3JtYXRpb25FeGVjdXRpb25Qb2xpY2llczogWydhcm46cG9saWN5J10sXG4gICAgICAgIH0sXG4gICAgICB9KTtcblxuICAgICAgZXhwZWN0KG1vY2tEZXBsb3lTdGFjaykudG9IYXZlQmVlbkNhbGxlZFdpdGgoZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICBzdGFjazogZXhwZWN0Lm9iamVjdENvbnRhaW5pbmcoe1xuICAgICAgICAgIHRlcm1pbmF0aW9uUHJvdGVjdGlvbjogZmFsc2UsXG4gICAgICAgIH0pLFxuICAgICAgfSkpO1xuICAgIH0pO1xuXG4gICAgdGVzdCgnc3RhY2sgaXMgdGVybWluYXRpb24gcHJvdGVjdGVkIHdoZW4gb3B0aW9uIGlzIHNldCcsIGFzeW5jICgpID0+IHtcbiAgICAgIGF3YWl0IGJvb3RzdHJhcHBlci5ib290c3RyYXBFbnZpcm9ubWVudChlbnYsIHNkaywge1xuICAgICAgICB0ZXJtaW5hdGlvblByb3RlY3Rpb246IHRydWUsXG4gICAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgICBjbG91ZEZvcm1hdGlvbkV4ZWN1dGlvblBvbGljaWVzOiBbJ2Fybjpwb2xpY3knXSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuXG4gICAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIHN0YWNrOiBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgICAgdGVybWluYXRpb25Qcm90ZWN0aW9uOiB0cnVlLFxuICAgICAgICB9KSxcbiAgICAgIH0pKTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ3Rlcm1pbmF0aW9uIHByb3RlY3Rpb24gaXMgbGVmdCBhbG9uZSB3aGVuIG9wdGlvbiBpcyBub3QgZ2l2ZW4nLCBhc3luYyAoKSA9PiB7XG4gICAgICBtb2NrVGhlVG9vbGtpdEluZm8oe1xuICAgICAgICBFbmFibGVUZXJtaW5hdGlvblByb3RlY3Rpb246IHRydWUsXG4gICAgICB9KTtcblxuICAgICAgYXdhaXQgYm9vdHN0cmFwcGVyLmJvb3RzdHJhcEVudmlyb25tZW50KGVudiwgc2RrLCB7XG4gICAgICAgIHBhcmFtZXRlcnM6IHtcbiAgICAgICAgICBjbG91ZEZvcm1hdGlvbkV4ZWN1dGlvblBvbGljaWVzOiBbJ2Fybjpwb2xpY3knXSxcbiAgICAgICAgfSxcbiAgICAgIH0pO1xuXG4gICAgICBleHBlY3QobW9ja0RlcGxveVN0YWNrKS50b0hhdmVCZWVuQ2FsbGVkV2l0aChleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgIHN0YWNrOiBleHBlY3Qub2JqZWN0Q29udGFpbmluZyh7XG4gICAgICAgICAgdGVybWluYXRpb25Qcm90ZWN0aW9uOiB0cnVlLFxuICAgICAgICB9KSxcbiAgICAgIH0pKTtcbiAgICB9KTtcblxuICAgIHRlc3QoJ3Rlcm1pbmF0aW9uIHByb3RlY3Rpb24gY2FuIGJlIHN3aXRjaGVkIG9mZicsIGFzeW5jICgpID0+IHtcbiAgICAgIG1vY2tUaGVUb29sa2l0SW5mbyh7XG4gICAgICAgIEVuYWJsZVRlcm1pbmF0aW9uUHJvdGVjdGlvbjogdHJ1ZSxcbiAgICAgIH0pO1xuXG4gICAgICBhd2FpdCBib290c3RyYXBwZXIuYm9vdHN0cmFwRW52aXJvbm1lbnQoZW52LCBzZGssIHtcbiAgICAgICAgdGVybWluYXRpb25Qcm90ZWN0aW9uOiBmYWxzZSxcbiAgICAgICAgcGFyYW1ldGVyczoge1xuICAgICAgICAgIGNsb3VkRm9ybWF0aW9uRXhlY3V0aW9uUG9saWNpZXM6IFsnYXJuOnBvbGljeSddLFxuICAgICAgICB9LFxuICAgICAgfSk7XG5cbiAgICAgIGV4cGVjdChtb2NrRGVwbG95U3RhY2spLnRvSGF2ZUJlZW5DYWxsZWRXaXRoKGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgc3RhY2s6IGV4cGVjdC5vYmplY3RDb250YWluaW5nKHtcbiAgICAgICAgICB0ZXJtaW5hdGlvblByb3RlY3Rpb246IGZhbHNlLFxuICAgICAgICB9KSxcbiAgICAgIH0pKTtcbiAgICB9KTtcbiAgfSk7XG5cbiAgZGVzY3JpYmUoJ0tNUyBrZXknLCAoKSA9PiB7XG4gICAgdGVzdC5lYWNoKFtcbiAgICAgIC8vIERlZmF1bHQgY2FzZVxuICAgICAgW3VuZGVmaW5lZCwgJ0FXU19NQU5BR0VEX0tFWSddLFxuICAgICAgLy8gQ3JlYXRlIGEgbmV3IGtleVxuICAgICAgW3RydWUsICcnXSxcbiAgICAgIC8vIERvbid0IGNyZWF0ZSBhIG5ldyBrZXlcbiAgICAgIFtmYWxzZSwgJ0FXU19NQU5BR0VEX0tFWSddLFxuICAgIF0pKCcobmV3IHN0YWNrKSBjcmVhdGVDdXN0b21lck1hc3RlcktleT0lcCA9PiBwYXJhbWV0ZXIgYmVjb21lcyAl