1 | ;
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | const ava_1 = require("ava");
|
4 | const uuid_1 = require("uuid");
|
5 | const index_1 = require("../index");
|
6 | const aws_faast_1 = require("../src/aws/aws-faast");
|
7 | const util_aws_1 = require("./fixtures/util-aws");
|
8 | const functions = require("./fixtures/functions");
|
9 | const util_1 = require("./fixtures/util");
|
10 | const assert = require("assert");
|
11 | async function waitForLogGroupCreation(cloudwatch, logGroupName, message) {
|
12 | let n = 0;
|
13 | while (true) {
|
14 | await (0, util_1.sleep)(1000);
|
15 | try {
|
16 | n++;
|
17 | const described = await cloudwatch
|
18 | .describeLogGroups({ logGroupNamePrefix: logGroupName })
|
19 | .promise();
|
20 | if (!described.logGroups) {
|
21 | continue;
|
22 | }
|
23 | const retrievedLogGroup = described.logGroups?.[0]?.logGroupName;
|
24 | if (!retrievedLogGroup) {
|
25 | continue;
|
26 | }
|
27 | assert(retrievedLogGroup === logGroupName, `Unexpected logGroupName: ${retrievedLogGroup}, expecting ${logGroupName}`);
|
28 | const logResult = await cloudwatch
|
29 | .filterLogEvents({ logGroupName })
|
30 | .promise();
|
31 | const events = logResult.events ?? [];
|
32 | // console.log(`[${n}] Found log group: ${logGroupName}`);
|
33 | let foundMessage = false;
|
34 | for (const event of events) {
|
35 | // console.log(
|
36 | // `[${n}] ${event.logStreamName} ${event.eventId} ${event.timestamp} ${event.message}`
|
37 | // );
|
38 | if (event.message.includes(message)) {
|
39 | foundMessage = true;
|
40 | }
|
41 | if (foundMessage && event.message.match(/REPORT RequestId/)) {
|
42 | return;
|
43 | }
|
44 | }
|
45 | }
|
46 | catch (err) {
|
47 | console.error(`Transient error waiting for log group creation: ${err}`);
|
48 | }
|
49 | }
|
50 | }
|
51 | ava_1.default.serial((0, util_1.title)("aws", "garbage collects functions that are called"), async (t) => {
|
52 | // Idea behind this test: create a faast module and make a call. Then
|
53 | // cleanup while leaving the resources in place. Then create another faast
|
54 | // module and set its retention to 0, and use a synthetic gc worker to
|
55 | // observe and verify the garbage collector actually cleans up.
|
56 | const mod = await (0, index_1.faastAws)(functions, {
|
57 | gc: "off",
|
58 | mode: "queue",
|
59 | description: t.title,
|
60 | packageJson: {
|
61 | name: (0, uuid_1.v4)(),
|
62 | dependencies: {
|
63 | tslib: "^1.9.1"
|
64 | }
|
65 | },
|
66 | maxRetries: 0
|
67 | });
|
68 | try {
|
69 | await mod.functions.consoleLog("gc-test-string");
|
70 | const { cloudwatch } = mod.state.services;
|
71 | const { logGroupName } = mod.state.resources;
|
72 | await waitForLogGroupCreation(cloudwatch, logGroupName, "gc-test-string");
|
73 | await mod.cleanup({ deleteResources: false, gcTimeout: 0 });
|
74 | // Create some work for gc to do by removing the log retention policy, gc
|
75 | // should add it back.
|
76 | const deleteRetentionPolicy = (0, index_1.throttle)({ concurrency: 1, retry: 5 }, () => cloudwatch.deleteRetentionPolicy({ logGroupName }).promise());
|
77 | await deleteRetentionPolicy();
|
78 | let deletedLayer = false;
|
79 | const { layer, FunctionName } = mod.state.resources;
|
80 | // log.gc.enabled = true;
|
81 | const mod2 = await (0, index_1.faastAws)(functions, {
|
82 | gc: "force",
|
83 | retentionInDays: 0,
|
84 | description: t.title,
|
85 | _gcWorker: async (work, services) => {
|
86 | switch (work.type) {
|
87 | case "SetLogRetention":
|
88 | // checkResourcesCleanedUp will verify the log group is
|
89 | // deleted.
|
90 | await (0, aws_faast_1.defaultGcWorker)(work, services);
|
91 | break;
|
92 | case "DeleteLayerVersion":
|
93 | if (work.LayerName === layer.LayerName) {
|
94 | index_1.log.gc(`deleting layer ${work.LayerName}`);
|
95 | await (0, aws_faast_1.defaultGcWorker)(work, services);
|
96 | deletedLayer = true;
|
97 | }
|
98 | break;
|
99 | case "DeleteResources":
|
100 | if (work.resources.FunctionName === FunctionName) {
|
101 | index_1.log.gc(`deleting resources for ${FunctionName}`);
|
102 | await (0, aws_faast_1.defaultGcWorker)(work, services);
|
103 | }
|
104 | }
|
105 | }
|
106 | });
|
107 | await mod2.cleanup({ gcTimeout: 0 });
|
108 | t.true(deletedLayer, "Deleted layer is true");
|
109 | await (0, util_1.checkResourcesCleanedUp)(t, await (0, util_aws_1.getAWSResources)(mod, true));
|
110 | }
|
111 | finally {
|
112 | index_1.log.gc.enabled = false;
|
113 | await mod.cleanup({ deleteResources: true, deleteCaches: true, gcTimeout: 0 });
|
114 | }
|
115 | });
|
116 | ava_1.default.serial((0, util_1.title)("aws", "garbage collects functions that are never called"), async (t) => {
|
117 | const mod = await (0, index_1.faastAws)(functions, {
|
118 | gc: "off",
|
119 | mode: "queue",
|
120 | description: t.title,
|
121 | maxRetries: 0
|
122 | });
|
123 | try {
|
124 | await mod.cleanup({ deleteResources: false, gcTimeout: 0 });
|
125 | const { FunctionName } = mod.state.resources;
|
126 | const mod2 = await (0, index_1.faastAws)(functions, {
|
127 | gc: "force",
|
128 | retentionInDays: 0,
|
129 | description: t.title,
|
130 | _gcWorker: async (work, services) => {
|
131 | switch (work.type) {
|
132 | case "DeleteResources":
|
133 | if (work.resources.FunctionName === FunctionName) {
|
134 | index_1.log.gc(`deleting resources for ${FunctionName}`);
|
135 | await (0, aws_faast_1.defaultGcWorker)(work, services);
|
136 | }
|
137 | }
|
138 | }
|
139 | });
|
140 | await mod2.cleanup({ gcTimeout: 0 });
|
141 | // Don't fail if a log group exists because we didn't wait for its
|
142 | // creation; it might be created by AWS after the cleanup occurs. The
|
143 | // reason is that the log group will only be created if there's an
|
144 | // invocation test, which only happens in the special case that the role
|
145 | // was recently created. Which is true for the nightly testsuite but
|
146 | // rarely elsewhere.
|
147 | const { logGroupResult, ...resources } = await (0, util_aws_1.getAWSResources)(mod, true);
|
148 | await (0, util_1.checkResourcesCleanedUp)(t, resources);
|
149 | }
|
150 | finally {
|
151 | await mod.cleanup({ deleteResources: true, deleteCaches: true, gcTimeout: 0 });
|
152 | }
|
153 | });
|
154 | ava_1.default.skip((0, util_1.title)("aws", "garbage collection caching"), async (t) => {
|
155 | {
|
156 | // Run a real gc so the build account doesn't accumulate garbage.
|
157 | const mod = await (0, index_1.faastAws)(functions, { gc: "force", description: t.title });
|
158 | await mod.cleanup({ gcTimeout: 0 });
|
159 | t.is(await mod.state.gcPromise, "done");
|
160 | }
|
161 | {
|
162 | // Test the in-memory cache that prevents gc from multiple faast.js
|
163 | // instances from running at the same time.
|
164 | const mod = await (0, index_1.faastAws)(functions, { description: t.title });
|
165 | await mod.cleanup();
|
166 | t.is(await mod.state.gcPromise, "skipped");
|
167 | }
|
168 | {
|
169 | // Test the persistent cache that prevents gc from running too often
|
170 | // even across processes.
|
171 | (0, aws_faast_1.clearLastGc)();
|
172 | const mod = await (0, index_1.faastAws)(functions, { description: t.title });
|
173 | await mod.cleanup();
|
174 | t.is(await mod.state.gcPromise, "skipped");
|
175 | }
|
176 | });
|
177 | //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXdzLWdjLnRlc3QuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi90ZXN0L2F3cy1nYy50ZXN0LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUEsNkJBQXVCO0FBRXZCLCtCQUFrQztBQUNsQyxvQ0FBbUQ7QUFDbkQsb0RBQW9FO0FBQ3BFLGtEQUFzRDtBQUN0RCxrREFBa0Q7QUFDbEQsMENBQXdFO0FBQ3hFLGlDQUFpQztBQUVqQyxLQUFLLFVBQVUsdUJBQXVCLENBQ2xDLFVBQTBCLEVBQzFCLFlBQW9CLEVBQ3BCLE9BQWU7SUFFZixJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDVixPQUFPLElBQUksRUFBRTtRQUNULE1BQU0sSUFBQSxZQUFLLEVBQUMsSUFBSSxDQUFDLENBQUM7UUFDbEIsSUFBSTtZQUNBLENBQUMsRUFBRSxDQUFDO1lBQ0osTUFBTSxTQUFTLEdBQUcsTUFBTSxVQUFVO2lCQUM3QixpQkFBaUIsQ0FBQyxFQUFFLGtCQUFrQixFQUFFLFlBQVksRUFBRSxDQUFDO2lCQUN2RCxPQUFPLEVBQUUsQ0FBQztZQUNmLElBQUksQ0FBQyxTQUFTLENBQUMsU0FBUyxFQUFFO2dCQUN0QixTQUFTO2FBQ1o7WUFDRCxNQUFNLGlCQUFpQixHQUFHLFNBQVMsQ0FBQyxTQUFTLEVBQUUsQ0FBQyxDQUFDLENBQUMsRUFBRSxZQUFZLENBQUM7WUFDakUsSUFBSSxDQUFDLGlCQUFpQixFQUFFO2dCQUNwQixTQUFTO2FBQ1o7WUFDRCxNQUFNLENBQ0YsaUJBQWlCLEtBQUssWUFBWSxFQUNsQyw0QkFBNEIsaUJBQWlCLGVBQWUsWUFBWSxFQUFFLENBQzdFLENBQUM7WUFDRixNQUFNLFNBQVMsR0FBRyxNQUFNLFVBQVU7aUJBQzdCLGVBQWUsQ0FBQyxFQUFFLFlBQVksRUFBRSxDQUFDO2lCQUNqQyxPQUFPLEVBQUUsQ0FBQztZQUNmLE1BQU0sTUFBTSxHQUFHLFNBQVMsQ0FBQyxNQUFNLElBQUksRUFBRSxDQUFDO1lBQ3RDLDBEQUEw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QUNJLG9FQUFvRTtRQUNwRSx5QkFBeUI7UUFDekIsSUFBQSx1QkFBVyxHQUFFLENBQUM7UUFDZCxNQUFNLEdBQUcsR0FBRyxNQUFNLElBQUEsZ0JBQVEsRUFBQyxTQUFTLEVBQUUsRUFBRSxXQUFXLEVBQUUsQ0FBQyxDQUFDLEtBQUssRUFBRSxDQUFDLENBQUM7UUFDaEUsTUFBTSxHQUFHLENBQUMsT0FBTyxFQUFFLENBQUM7UUFDcEIsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxNQUFNLEdBQUcsQ0FBQyxLQUFLLENBQUMsU0FBUyxFQUFFLFNBQVMsQ0FBQyxDQUFDO0tBQzlDO0FBQ0wsQ0FBQyxDQUFDLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgdGVzdCBmcm9tIFwiYXZhXCI7XG5pbXBvcnQgeyBDbG91ZFdhdGNoTG9ncyB9IGZyb20gXCJhd3Mtc2RrXCI7XG5pbXBvcnQgeyB2NCBhcyB1dWlkIH0gZnJvbSBcInV1aWRcIjtcbmltcG9ydCB7IGZhYXN0QXdzLCBsb2csIHRocm90dGxlIH0gZnJvbSBcIi4uL2luZGV4XCI7XG5pbXBvcnQgeyBkZWZhdWx0R2NXb3JrZXIsIGNsZWFyTGFzdEdjIH0gZnJvbSBcIi4uL3NyYy9hd3MvYXdzLWZhYXN0XCI7XG5pbXBvcnQgeyBnZXRBV1NSZXNvdXJjZXMgfSBmcm9tIFwiLi9maXh0dXJlcy91dGlsLWF3c1wiO1xuaW1wb3J0ICogYXMgZnVuY3Rpb25zIGZyb20gXCIuL2ZpeHR1cmVzL2Z1bmN0aW9uc1wiO1xuaW1wb3J0IHsgY2hlY2tSZXNvdXJjZXNDbGVhbmVkVXAsIHNsZWVwLCB0aXRsZSB9IGZyb20gXCIuL2ZpeHR1cmVzL3V0aWxcIjtcbmltcG9ydCAqIGFzIGFzc2VydCBmcm9tIFwiYXNzZXJ0XCI7XG5cbmFzeW5jIGZ1bmN0aW9uIHdhaXRGb3JMb2dHcm91cENyZWF0aW9uKFxuICAgIGNsb3Vkd2F0Y2g6IENsb3VkV2F0Y2hMb2dzLFxuICAgIGxvZ0dyb3VwTmFtZTogc3RyaW5nLFxuICAgIG1lc3NhZ2U6IHN0cmluZ1xuKSB7XG4gICAgbGV0IG4gPSAwO1xuICAgIHdoaWxlICh0cnVlKSB7XG4gICAgICAgIGF3YWl0IHNsZWVwKDEwMDApO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgbisrO1xuICAgICAgICAgICAgY29uc3QgZGVzY3JpYmVkID0gYXdhaXQgY2xvdWR3YXRjaFxuICAgICAgICAgICAgICAgIC5kZXNjcmliZUxvZ0dyb3Vwcyh7IGxvZ0dyb3VwTmFtZVByZWZpeDogbG9nR3JvdXBOYW1lIH0pXG4gICAgICAgICAgICAgICAgLnByb21pc2UoKTtcbiAgICAgICAgICAgIGlmICghZGVzY3JpYmVkLmxvZ0dyb3Vwcykge1xuICAgICAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgY29uc3QgcmV0cmlldmVkTG9nR3JvdXAgPSBkZXNjcmliZWQubG9nR3JvdXBzPy5bMF0/LmxvZ0dyb3VwTmFtZTtcbiAgICAgICAgICAgIGlmICghcmV0cmlldmVkTG9nR3JvdXApIHtcbiAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIGFzc2VydChcbiAgICAgICAgICAgICAgICByZXRyaWV2ZWRMb2dHcm91cCA9PT0gbG9nR3JvdXBOYW1lLFxuICAgICAgICAgICAgICAgIGBVbmV4cGVjdGVkIGxvZ0dyb3VwTmFtZTogJHtyZXRyaWV2ZWRMb2dHcm91cH0sIGV4cGVjdGluZyAke2xvZ0dyb3VwTmFtZX1gXG4gICAgICAgICAgICApO1xuICAgICAgICAgICAgY29uc3QgbG9nUmVzdWx0ID0gYXdhaXQgY2xvdWR3YXRjaFxuICAgICAgICAgICAgICAgIC5maWx0ZXJMb2dFdmVudHMoeyBsb2dHcm91cE5hbWUgfSlcbiAgICAgICAgICAgICAgICAucHJvbWlzZSgpO1xuICAgICAgICAgICAgY29uc3QgZXZlbnRzID0gbG9nUmVzdWx0LmV2ZW50cyA/PyBbXTtcbiAgICAgICAgICAgIC8vIGNvbnNvbGUubG9nKGBbJHtufV0gRm91bmQgbG9nIGdyb3VwOiAke2xvZ0dyb3VwTmFtZX1gKTtcbiAgICAgICAgICAgIGxldCBmb3VuZE1lc3NhZ2UgPSBmYWxzZTtcbiAgICAgICAgICAgIGZvciAoY29uc3QgZXZlbnQgb2YgZXZlbnRzKSB7XG4gICAgICAgICAgICAgICAgLy8gY29uc29sZS5sb2coXG4gICAgICAgICAgICAgICAgLy8gICAgIGBbJHtufV0gJHtldmVudC5sb2dTdHJlYW1OYW1lfSAke2V2ZW50LmV2ZW50SWR9ICR7ZXZlbnQudGltZXN0YW1wfSAke2V2ZW50Lm1lc3NhZ2V9YFxuICAgICAgICAgICAgICAgIC8vICk7XG4gICAgICAgICAgICAgICAgaWYgKGV2ZW50Lm1lc3NhZ2UhLmluY2x1ZGVzKG1lc3NhZ2UpKSB7XG4gICAgICAgICAgICAgICAgICAgIGZvdW5kTWVzc2FnZSA9IHRydWU7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIGlmIChmb3VuZE1lc3NhZ2UgJiYgZXZlbnQubWVzc2FnZSEubWF0Y2goL1JFUE9SVCBSZXF1ZXN0SWQvKSkge1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlcnI6IGFueSkge1xuICAgICAgICAgICAgY29uc29sZS5lcnJvcihgVHJhbnNpZW50IGVycm9yIHdhaXRpbmcgZm9yIGxvZyBncm91cCBjcmVhdGlvbjogJHtlcnJ9YCk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbnRlc3Quc2VyaWFsKHRpdGxlKFwiYXdzXCIsIFwiZ2FyYmFnZSBjb2xsZWN0cyBmdW5jdGlvbnMgdGhhdCBhcmUgY2FsbGVkXCIpLCBhc3luYyB0ID0+IHtcbiAgICAvLyBJZGVhIGJlaGluZCB0aGlzIHRlc3Q6IGNyZWF0ZSBhIGZhYXN0IG1vZHVsZSBhbmQgbWFrZSBhIGNhbGwuIFRoZW5cbiAgICAvLyBjbGVhbnVwIHdoaWxlIGxlYXZpbmcgdGhlIHJlc291cmNlcyBpbiBwbGFjZS4gVGhlbiBjcmVhdGUgYW5vdGhlciBmYWFzdFxuICAgIC8vIG1vZHVsZSBhbmQgc2V0IGl0cyByZXRlbnRpb24gdG8gMCwgYW5kIHVzZSBhIHN5bnRoZXRpYyBnYyB3b3JrZXIgdG9cbiAgICAvLyBvYnNlcnZlIGFuZCB2ZXJpZnkgdGhlIGdhcmJhZ2UgY29sbGVjdG9yIGFjdHVhbGx5IGNsZWFucyB1cC5cbiAgICBjb25zdCBtb2QgPSBhd2FpdCBmYWFzdEF3cyhmdW5jdGlvbnMsIHtcbiAgICAgICAgZ2M6IFwib2ZmXCIsXG4gICAgICAgIG1vZGU6IFwicXVldWVcIixcbiAgICAgICAgZGVzY3JpcHRpb246IHQudGl0bGUsXG4gICAgICAgIHBhY2thZ2VKc29uOiB7XG4gICAgICAgICAgICBuYW1lOiB1dWlkKCksXG4gICAgICAgICAgICBkZXBlbmRlbmNpZXM6IHtcbiAgICAgICAgICAgICAgICB0c2xpYjogXCJeMS45LjFcIlxuICAgICAgICAgICAgfVxuICAgICAgICB9LFxuICAgICAgICBtYXhSZXRyaWVzOiAwXG4gICAgfSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgbW9kLmZ1bmN0aW9ucy5jb25zb2xlTG9nKFwiZ2MtdGVzdC1zdHJpbmdcIik7XG4gICAgICAgIGNvbnN0IHsgY2xvdWR3YXRjaCB9ID0gbW9kLnN0YXRlLnNlcnZpY2VzO1xuICAgICAgICBjb25zdCB7IGxvZ0dyb3VwTmFtZSB9ID0gbW9kLnN0YXRlLnJlc291cmNlcztcbiAgICAgICAgYXdhaXQgd2FpdEZvckxvZ0dyb3VwQ3JlYXRpb24oY2xvdWR3YXRjaCwgbG9nR3JvdXBOYW1lLCBcImdjLXRlc3Qtc3RyaW5nXCIpO1xuICAgICAgICBhd2FpdCBtb2QuY2xlYW51cCh7IGRlbGV0ZVJlc291cmNlczogZmFsc2UsIGdjVGltZW91dDogMCB9KTtcblxuICAgICAgICAvLyBDcmVhdGUgc29tZSB3b3JrIGZvciBnYyB0byBkbyBieSByZW1vdmluZyB0aGUgbG9nIHJldGVudGlvbiBwb2xpY3ksIGdjXG4gICAgICAgIC8vIHNob3VsZCBhZGQgaXQgYmFjay5cbiAgICAgICAgY29uc3QgZGVsZXRlUmV0ZW50aW9uUG9saWN5ID0gdGhyb3R0bGUoeyBjb25jdXJyZW5jeTogMSwgcmV0cnk6IDUgfSwgKCkgPT5cbiAgICAgICAgICAgIGNsb3Vkd2F0Y2guZGVsZXRlUmV0ZW50aW9uUG9saWN5KHsgbG9nR3JvdXBOYW1lIH0pLnByb21pc2UoKVxuICAgICAgICApO1xuICAgICAgICBhd2FpdCBkZWxldGVSZXRlbnRpb25Qb2xpY3koKTtcblxuICAgICAgICBsZXQgZGVsZXRlZExheWVyID0gZmFsc2U7XG4gICAgICAgIGNvbnN0IHsgbGF5ZXIsIEZ1bmN0aW9uTmFtZSB9ID0gbW9kLnN0YXRlLnJlc291cmNlcztcbiAgICAgICAgLy8gbG9nLmdjLmVuYWJsZWQgPSB0cnVlO1xuICAgICAgICBjb25zdCBtb2QyID0gYXdhaXQgZmFhc3RBd3MoZnVuY3Rpb25zLCB7XG4gICAgICAgICAgICBnYzogXCJmb3JjZVwiLFxuICAgICAgICAgICAgcmV0ZW50aW9uSW5EYXlzOiAwLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246IHQudGl0bGUsXG4gICAgICAgICAgICBfZ2NXb3JrZXI6IGFzeW5jICh3b3JrLCBzZXJ2aWNlcykgPT4ge1xuICAgICAgICAgICAgICAgIHN3aXRjaCAod29yay50eXBlKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgXCJTZXRMb2dSZXRlbnRpb25cIjpcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNoZWNrUmVzb3VyY2VzQ2xlYW5lZFVwIHdpbGwgdmVyaWZ5IHRoZSBsb2cgZ3JvdXAgaXNcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGRlbGV0ZWQuXG4gICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCBkZWZhdWx0R2NXb3JrZXIod29yaywgc2VydmljZXMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWs7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgXCJEZWxldGVMYXllclZlcnNpb25cIjpcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh3b3JrLkxheWVyTmFtZSA9PT0gbGF5ZXIhLkxheWVyTmFtZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5nYyhgZGVsZXRpbmcgbGF5ZXIgJHt3b3JrLkxheWVyTmFtZX1gKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCBkZWZhdWx0R2NXb3JrZXIod29yaywgc2VydmljZXMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlbGV0ZWRMYXllciA9IHRydWU7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICBicmVhaztcbiAgICAgICAgICAgICAgICAgICAgY2FzZSBcIkRlbGV0ZVJlc291cmNlc1wiOlxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHdvcmsucmVzb3VyY2VzLkZ1bmN0aW9uTmFtZSA9PT0gRnVuY3Rpb25OYW1lKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgbG9nLmdjKGBkZWxldGluZyByZXNvdXJjZXMgZm9yICR7RnVuY3Rpb25OYW1lfWApO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGF3YWl0IGRlZmF1bHRHY1dvcmtlcih3b3JrLCBzZXJ2aWNlcyk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuICAgICAgICB9KTtcblxuICAgICAgICBhd2FpdCBtb2QyLmNsZWFudXAoeyBnY1RpbWVvdXQ6IDAgfSk7XG4gICAgICAgIHQudHJ1ZShkZWxldGVkTGF5ZXIsIFwiRGVsZXRlZCBsYXllciBpcyB0cnVlXCIpO1xuICAgICAgICBhd2FpdCBjaGVja1Jlc291cmNlc0NsZWFuZWRVcCh0LCBhd2FpdCBnZXRBV1NSZXNvdXJjZXMobW9kLCB0cnVlKSk7XG4gICAgfSBmaW5hbGx5IHtcbiAgICAgICAgbG9nLmdjLmVuYWJsZWQgPSBmYWxzZTtcbiAgICAgICAgYXdhaXQgbW9kLmNsZWFudXAoeyBkZWxldGVSZXNvdXJjZXM6IHRydWUsIGRlbGV0ZUNhY2hlczogdHJ1ZSwgZ2NUaW1lb3V0OiAwIH0pO1xuICAgIH1cbn0pO1xuXG50ZXN0LnNlcmlhbCh0aXRsZShcImF3c1wiLCBcImdhcmJhZ2UgY29sbGVjdHMgZnVuY3Rpb25zIHRoYXQgYXJlIG5ldmVyIGNhbGxlZFwiKSwgYXN5bmMgdCA9PiB7XG4gICAgY29uc3QgbW9kID0gYXdhaXQgZmFhc3RBd3MoZnVuY3Rpb25zLCB7XG4gICAgICAgIGdjOiBcIm9mZlwiLFxuICAgICAgICBtb2RlOiBcInF1ZXVlXCIsXG4gICAgICAgIGRlc2NyaXB0aW9uOiB0LnRpdGxlLFxuICAgICAgICBtYXhSZXRyaWVzOiAwXG4gICAgfSk7XG4gICAgdHJ5IHtcbiAgICAgICAgYXdhaXQgbW9kLmNsZWFudXAoeyBkZWxldGVSZXNvdXJjZXM6IGZhbHNlLCBnY1RpbWVvdXQ6IDAgfSk7XG4gICAgICAgIGNvbnN0IHsgRnVuY3Rpb25OYW1lIH0gPSBtb2Quc3RhdGUucmVzb3VyY2VzO1xuICAgICAgICBjb25zdCBtb2QyID0gYXdhaXQgZmFhc3RBd3MoZnVuY3Rpb25zLCB7XG4gICAgICAgICAgICBnYzogXCJmb3JjZVwiLFxuICAgICAgICAgICAgcmV0ZW50aW9uSW5EYXlzOiAwLFxuICAgICAgICAgICAgZGVzY3JpcHRpb246IHQudGl0bGUsXG4gICAgICAgICAgICBfZ2NXb3JrZXI6IGFzeW5jICh3b3JrLCBzZXJ2aWNlcykgPT4ge1xuICAgICAgICAgICAgICAgIHN3aXRjaCAod29yay50eXBlKSB7XG4gICAgICAgICAgICAgICAgICAgIGNhc2UgXCJEZWxldGVSZXNvdXJjZXNcIjpcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICh3b3JrLnJlc291cmNlcy5GdW5jdGlvbk5hbWUgPT09IEZ1bmN0aW9uTmFtZSkge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZy5nYyhgZGVsZXRpbmcgcmVzb3VyY2VzIGZvciAke0Z1bmN0aW9uTmFtZX1gKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhd2FpdCBkZWZhdWx0R2NXb3JrZXIod29yaywgc2VydmljZXMpO1xuICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfSk7XG5cbiAgICAgICAgYXdhaXQgbW9kMi5jbGVhbnVwKHsgZ2NUaW1lb3V0OiAwIH0pO1xuICAgICAgICAvLyBEb24ndCBmYWlsIGlmIGEgbG9nIGdyb3VwIGV4aXN0cyBiZWNhdXNlIHdlIGRpZG4ndCB3YWl0IGZvciBpdHNcbiAgICAgICAgLy8gY3JlYXRpb247IGl0IG1pZ2h0IGJlIGNyZWF0ZWQgYnkgQVdTIGFmdGVyIHRoZSBjbGVhbnVwIG9jY3Vycy4gVGhlXG4gICAgICAgIC8vIHJlYXNvbiBpcyB0aGF0IHRoZSBsb2cgZ3JvdXAgd2lsbCBvbmx5IGJlIGNyZWF0ZWQgaWYgdGhlcmUncyBhblxuICAgICAgICAvLyBpbnZvY2F0aW9uIHRlc3QsIHdoaWNoIG9ubHkgaGFwcGVucyBpbiB0aGUgc3BlY2lhbCBjYXNlIHRoYXQgdGhlIHJvbGVcbiAgICAgICAgLy8gd2FzIHJlY2VudGx5IGNyZWF0ZWQuIFdoaWNoIGlzIHRydWUgZm9yIHRoZSBuaWdodGx5IHRlc3RzdWl0ZSBidXRcbiAgICAgICAgLy8gcmFyZWx5IGVsc2V3aGVyZS5cbiAgICAgICAgY29uc3QgeyBsb2dHcm91cFJlc3VsdCwgLi4ucmVzb3VyY2VzIH0gPSBhd2FpdCBnZXRBV1NSZXNvdXJjZXMobW9kLCB0cnVlKTtcbiAgICAgICAgYXdhaXQgY2hlY2tSZXNvdXJjZXNDbGVhbmVkVXAodCwgcmVzb3VyY2VzKTtcbiAgICB9IGZpbmFsbHkge1xuICAgICAgICBhd2FpdCBtb2QuY2xlYW51cCh7IGRlbGV0ZVJlc291cmNlczogdHJ1ZSwgZGVsZXRlQ2FjaGVzOiB0cnVlLCBnY1RpbWVvdXQ6IDAgfSk7XG4gICAgfVxufSk7XG5cbnRlc3Quc2tpcCh0aXRsZShcImF3c1wiLCBcImdhcmJhZ2UgY29sbGVjdGlvbiBjYWNoaW5nXCIpLCBhc3luYyB0ID0+IHtcbiAgICB7XG4gICAgICAgIC8vIFJ1biBhIHJlYWwgZ2Mgc28gdGhlIGJ1aWxkIGFjY291bnQgZG9lc24ndCBhY2N1bXVsYXRlIGdhcmJhZ2UuXG4gICAgICAgIGNvbnN0IG1vZCA9IGF3YWl0IGZhYXN0QXdzKGZ1bmN0aW9ucywgeyBnYzogXCJmb3JjZVwiLCBkZXNjcmlwdGlvbjogdC50aXRsZSB9KTtcbiAgICAgICAgYXdhaXQgbW9kLmNsZWFudXAoeyBnY1RpbWVvdXQ6IDAgfSk7XG4gICAgICAgIHQuaXMoYXdhaXQgbW9kLnN0YXRlLmdjUHJvbWlzZSwgXCJkb25lXCIpO1xuICAgIH1cblxuICAgIHtcbiAgICAgICAgLy8gVGVzdCB0aGUgaW4tbWVtb3J5IGNhY2hlIHRoYXQgcHJldmVudHMgZ2MgZnJvbSBtdWx0aXBsZSBmYWFzdC5qc1xuICAgICAgICAvLyBpbnN0YW5jZXMgZnJvbSBydW5uaW5nIGF0IHRoZSBzYW1lIHRpbWUuXG4gICAgICAgIGNvbnN0IG1vZCA9IGF3YWl0IGZhYXN0QXdzKGZ1bmN0aW9ucywgeyBkZXNjcmlwdGlvbjogdC50aXRsZSB9KTtcbiAgICAgICAgYXdhaXQgbW9kLmNsZWFudXAoKTtcbiAgICAgICAgdC5pcyhhd2FpdCBtb2Quc3RhdGUuZ2NQcm9taXNlLCBcInNraXBwZWRcIik7XG4gICAgfVxuXG4gICAge1xuICAgICAgICAvLyBUZXN0IHRoZSBwZXJzaXN0ZW50IGNhY2hlIHRoYXQgcHJldmVudHMgZ2MgZnJvbSBydW5uaW5nIHRvbyBvZnRlblxuICAgICAgICAvLyBldmVuIGFjcm9zcyBwcm9jZXNzZXMuXG4gICAgICAgIGNsZWFyTGFzdEdjKCk7XG4gICAgICAgIGNvbnN0IG1vZCA9IGF3YWl0IGZhYXN0QXdzKGZ1bmN0aW9ucywgeyBkZXNjcmlwdGlvbjogdC50aXRsZSB9KTtcbiAgICAgICAgYXdhaXQgbW9kLmNsZWFudXAoKTtcbiAgICAgICAgdC5pcyhhd2FpdCBtb2Quc3RhdGUuZ2NQcm9taXNlLCBcInNraXBwZWRcIik7XG4gICAgfVxufSk7XG4iXX0= |
\ | No newline at end of file |