@odigos/aws-cdk 0.0.13 → 0.0.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.jsii +415 -13
- package/README.md +206 -6
- package/lib/fargate-recipes.d.ts +108 -0
- package/lib/fargate-recipes.js +231 -0
- package/lib/fargate.d.ts +218 -0
- package/lib/fargate.js +400 -0
- package/lib/generated/fargate.d.ts +44 -0
- package/lib/generated/fargate.js +125 -0
- package/lib/generated/layers.d.ts +4 -4
- package/lib/generated/layers.js +149 -149
- package/lib/index.d.ts +3 -0
- package/lib/index.js +16 -2
- package/lib/instrumentation.js +2 -2
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +51 -0
- package/lib/types.js +41 -2
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
-
# Odigos CDK constructs for AWS
|
|
1
|
+
# Odigos CDK constructs for AWS
|
|
2
2
|
|
|
3
|
-
Instrument AWS Lambda functions
|
|
3
|
+
Instrument AWS Lambda functions and ECS Fargate services with OpenTelemetry, from your own CDK app.
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
6
|
npm install @odigos/aws-cdk # TypeScript / JavaScript
|
|
7
7
|
pip install odigos-aws-cdk # Python
|
|
8
8
|
```
|
|
9
9
|
|
|
10
|
+
| | Lambda | ECS Fargate |
|
|
11
|
+
|---|---|---|
|
|
12
|
+
| Construct | `OdigosLambdaInstrumentation` | `OdigosFargateInstrumentation` |
|
|
13
|
+
| Mechanism | a layer, plus an exec wrapper or loader variable | agents staged into a shared task volume by a run-to-completion container |
|
|
14
|
+
| Languages | python, nodejs, java, ruby | java, python, nodejs, dotnet, php, ruby |
|
|
15
|
+
| Where telemetry goes | a collector in the same Lambda sandbox, which forwards | your destination, from the application process |
|
|
16
|
+
| Language detection | from the function's declared runtime | none — you state the language |
|
|
17
|
+
|
|
18
|
+
Both write `ODIGOS_IAC` and never the connector's ownership marker: instrumented by Odigos, owned by you.
|
|
19
|
+
|
|
20
|
+
# AWS Lambda
|
|
21
|
+
|
|
10
22
|
```ts
|
|
11
23
|
import { OdigosLambdaInstrumentation } from '@odigos/aws-cdk';
|
|
12
24
|
|
|
@@ -224,18 +236,206 @@ Pick one per function. Discovery-only (manual) mode is the combination that work
|
|
|
224
236
|
reports, your stack owns the configuration. That is what `ODIGOS_IAC` expresses — instrumented by Odigos,
|
|
225
237
|
owned by you — and it is why these constructs never write the connector's ownership marker.
|
|
226
238
|
|
|
227
|
-
|
|
239
|
+
# ECS Fargate
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
import { OdigosFargateInstrumentation, OdigosFargateLanguage } from '@odigos/aws-cdk';
|
|
243
|
+
|
|
244
|
+
const odigos = new OdigosFargateInstrumentation(this, 'Odigos', {
|
|
245
|
+
destination: { httpEndpoint: 'https://otlp.example.com:4318' },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
odigos.instrument({
|
|
249
|
+
container: apiContainer, // the ecs.ContainerDefinition to instrument
|
|
250
|
+
language: OdigosFargateLanguage.JAVA,
|
|
251
|
+
service: apiService, // read OTEL_SERVICE_NAME off the ECS service
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
```python
|
|
256
|
+
from odigos_aws_cdk import OdigosFargateInstrumentation, OdigosFargateLanguage
|
|
257
|
+
|
|
258
|
+
odigos = OdigosFargateInstrumentation(self, "Odigos",
|
|
259
|
+
destination={"http_endpoint": "https://otlp.example.com:4318"},
|
|
260
|
+
)
|
|
261
|
+
odigos.instrument(
|
|
262
|
+
container=api_container,
|
|
263
|
+
language=OdigosFargateLanguage.JAVA,
|
|
264
|
+
service=api_service,
|
|
265
|
+
)
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## What it does to the task definition
|
|
269
|
+
|
|
270
|
+
Four moves, which are one mechanism and only work together:
|
|
271
|
+
|
|
272
|
+
1. a bare-name task volume, `odigos-agent` — on Fargate an ephemeral-storage bind mount scoped to the task
|
|
273
|
+
(there is no tmpfs);
|
|
274
|
+
2. a **non-essential** `odigos-agent` container that copies the language agents out of the agents bundle
|
|
275
|
+
into that volume and exits 0;
|
|
276
|
+
3. the volume mounted read-only at `/var/odigos` in your container, which `DependsOn` the staging container
|
|
277
|
+
with condition `SUCCESS` — legal only against a non-essential container, which is why (2) is
|
|
278
|
+
non-essential;
|
|
279
|
+
4. the language's activation environment, the OTLP export environment and the `ODIGOS_IAC` marker on your
|
|
280
|
+
container.
|
|
281
|
+
|
|
282
|
+
Marker presence alone is a false green here. A container can carry a complete, correct activation
|
|
283
|
+
environment while the staging container is gone — the application then starts, finds nothing at
|
|
284
|
+
`/var/odigos`, and exports nothing while reporting as instrumented. That is why all four are one construct.
|
|
285
|
+
|
|
286
|
+
## Where the telemetry goes
|
|
287
|
+
|
|
288
|
+
Straight to your destination, from inside the application process. **This is the substantive difference
|
|
289
|
+
from Lambda**, where the SDK hands batches to a collector in the same sandbox and the export happens after
|
|
290
|
+
the response has already been returned. A Fargate task has no such collector, so a slow or unreachable
|
|
291
|
+
destination is felt by the application's own exporter.
|
|
292
|
+
|
|
293
|
+
## Naming the service
|
|
294
|
+
|
|
295
|
+
`OTEL_SERVICE_NAME` **must equal the workload's name in Odigos, which for a Fargate workload is the ECS
|
|
296
|
+
service name** — not the task definition family and not the container name. A different value is reported
|
|
297
|
+
as instrumentation drift rather than treated as a naming preference, so there is no default: pass `service`
|
|
298
|
+
to read the name off an `ecs.FargateService`, or `serviceName` when the service is defined elsewhere.
|
|
299
|
+
|
|
300
|
+
The service's physical name has to be one you set. A name CloudFormation generates is a `Ref` to the
|
|
301
|
+
service, and the task definition that service runs cannot reference it — CloudFormation rejects the cycle
|
|
302
|
+
at deploy time — so a generated name throws at synth pointing you at `serviceName`.
|
|
303
|
+
|
|
304
|
+
## Languages, and stating them
|
|
305
|
+
|
|
306
|
+
| | Activated through | Needs `runtimeVersion` |
|
|
307
|
+
|---|---|---|
|
|
308
|
+
| `JAVA` | `JAVA_TOOL_OPTIONS` | no |
|
|
309
|
+
| `PYTHON` | `PYTHONPATH` | no |
|
|
310
|
+
| `NODEJS` | `NODE_OPTIONS` | no |
|
|
311
|
+
| `DOTNET` | `CORECLR_*` / `DOTNET_*` (glibc only) | no |
|
|
312
|
+
| `PHP` | `PHP_INI_SCAN_DIR` | **yes** — major.minor, e.g. `"8.3"` |
|
|
313
|
+
| `RUBY` | `RUBYOPT` | **yes** — major.minor, e.g. `"3.3"` |
|
|
314
|
+
|
|
315
|
+
There is no aspect and no detection, and unlike Lambda that is not a matter of taste. A function declares
|
|
316
|
+
its runtime; a container declares an image. A language guessed from an image would silently mis-instrument
|
|
317
|
+
whatever it got wrong — a Java agent staged into a Python container leaves it uninstrumented, and a
|
|
318
|
+
`JAVA_TOOL_OPTIONS` on a JVM-less image is simply ignored, so nothing fails loudly enough to notice.
|
|
319
|
+
|
|
320
|
+
php and ruby need `runtimeVersion` because their agents are laid down per runtime version in the bundle,
|
|
321
|
+
and the version selects the path their activation variable points at. It must be exactly `major.minor`;
|
|
322
|
+
anything else names a directory that does not exist, and the container starts with no agent loaded.
|
|
323
|
+
|
|
324
|
+
**Go cannot be instrumented on Fargate at all.** Its only zero-code instrumentation is eBPF, and Fargate
|
|
325
|
+
forbids the privileged containers and host PID an eBPF agent needs. Use the OpenTelemetry Go SDK.
|
|
326
|
+
|
|
327
|
+
## Loader variables your container already sets
|
|
328
|
+
|
|
329
|
+
The construct reads the value your container was created with and **appends** to it, joined by the
|
|
330
|
+
separator the language's own pattern specifies — a space for `JAVA_TOOL_OPTIONS` and `NODE_OPTIONS`, a
|
|
331
|
+
colon for `PYTHONPATH`:
|
|
332
|
+
|
|
333
|
+
```ts
|
|
334
|
+
taskDefinition.addContainer('App', {
|
|
335
|
+
image,
|
|
336
|
+
environment: { JAVA_TOOL_OPTIONS: '-Xmx512m' },
|
|
337
|
+
});
|
|
338
|
+
// becomes: -Xmx512m -javaagent:/var/odigos/java/javaagent.jar
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
Pass `existingLoaderValue` when the value is not visible at synth — set by an `environmentFiles` entry,
|
|
342
|
+
baked into the image's own `ENV`, or supplied through `secrets`.
|
|
343
|
+
|
|
344
|
+
`PHP_INI_SCAN_DIR` and `RUBYOPT` are the exception: Odigos compares them **exactly**, so they are set
|
|
345
|
+
outright and a value already there is reported as a synth warning rather than merged. Fold what your
|
|
346
|
+
application needs into the value Odigos sets.
|
|
347
|
+
|
|
348
|
+
## Transport
|
|
349
|
+
|
|
350
|
+
Each language's agent has a preferred transport, and giving the destination **both** endpoints lets each
|
|
351
|
+
one take it:
|
|
352
|
+
|
|
353
|
+
```ts
|
|
354
|
+
destination: {
|
|
355
|
+
endpoint: 'otlp.example.com:4317', // OTLP/gRPC, bare host:port
|
|
356
|
+
httpEndpoint: 'https://otlp.example.com:4318', // OTLP/HTTP, scheme-qualified
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
Every agent in the bundle prefers `http/protobuf`. java, python, nodejs and dotnet honour
|
|
361
|
+
`OTEL_EXPORTER_OTLP_PROTOCOL` and fall back to gRPC when that is all the destination offers; **php and
|
|
362
|
+
ruby cannot** — their community SDKs are `http/protobuf`-only, so instrumenting one against a gRPC-only
|
|
363
|
+
destination throws at synth rather than deploying a task that exports nothing.
|
|
364
|
+
|
|
365
|
+
Endpoint and protocol are one decision, and are compared as one. Pairing an endpoint with the other
|
|
366
|
+
protocol fails at runtime as a connection reset that points nowhere near the cause, so an endpoint on the
|
|
367
|
+
other protocol's standard port (4317 with `http/protobuf`, 4318 with `grpc`) is a synth warning.
|
|
368
|
+
|
|
369
|
+
Unlike Lambda, `destination.headers` is rendered into `OTEL_EXPORTER_OTLP_HEADERS` on the container. The
|
|
370
|
+
connector does not set or compare that variable when it instruments a Fargate task itself, so it is
|
|
371
|
+
neither reconciled nor reported as drift — it is here because a task with no collector of its own has
|
|
372
|
+
nowhere else to hold a credential. Same exposure caveat as Lambda: `ecs:DescribeTaskDefinition` and the
|
|
373
|
+
console can read it.
|
|
374
|
+
|
|
375
|
+
## Signals
|
|
376
|
+
|
|
377
|
+
Same defaults as Lambda — traces on, metrics and logs opt-in — and the same requirement that they match
|
|
378
|
+
what Odigos has configured for the destination. All three `OTEL_*_EXPORTER` variables are written
|
|
379
|
+
explicitly here, unlike Lambda where an enabled signal's variable is left unset, because the connector's
|
|
380
|
+
Fargate check compares all three by value.
|
|
381
|
+
|
|
382
|
+
## A task definition with more than one application
|
|
383
|
+
|
|
384
|
+
Six applications in six languages in one task definition is a real shape. Call `instrument()` once per
|
|
385
|
+
container; there is **one** staging container for the whole task definition, and its command is built
|
|
386
|
+
lazily from the union of every instrumented container's agents, in ascending directory order — which is
|
|
387
|
+
exactly what the connector compares it against.
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
odigos.instrumentAll([
|
|
391
|
+
{ container: apiContainer, language: OdigosFargateLanguage.JAVA, service: svc },
|
|
392
|
+
{ container: workerContainer, language: OdigosFargateLanguage.PYTHON, service: svc },
|
|
393
|
+
]);
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
AWS caps a task definition at 10 containers and the staging container needs one of them; a definition
|
|
397
|
+
already at the limit fails at synth rather than on `RegisterTaskDefinition`.
|
|
398
|
+
|
|
399
|
+
## The agents image
|
|
400
|
+
|
|
401
|
+
The staging container pulls `registry.odigos.io/odigos-fargate-agents` at this package's release tag — a
|
|
402
|
+
multi-arch manifest list, so amd64 and Graviton tasks both get a matching image. **It is pulled by your
|
|
403
|
+
task's execution role at every task start**, so a task in a private subnet with no NAT and no registry
|
|
404
|
+
endpoint cannot pull it. Mirror it into a registry your tasks can reach and pass `agentsImage`:
|
|
405
|
+
|
|
406
|
+
```ts
|
|
407
|
+
agentsImage: '111122223333.dkr.ecr.eu-west-1.amazonaws.com/odigos-fargate-agents:v0.0.13',
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
A mirror has to be the same bundle — the activation environment names exact paths inside it. The bundle is
|
|
411
|
+
Linux-only and glibc-based: a Windows task definition throws at synth, and musl (Alpine) images have no
|
|
412
|
+
.NET native profiler in it.
|
|
413
|
+
|
|
414
|
+
## Do not also instrument these services with the connector
|
|
415
|
+
|
|
416
|
+
Same rule as Lambda, with ECS's own cost. The connector instruments a live service by registering a new
|
|
417
|
+
task-definition revision and updating the service to it. A service instrumented here *and* enabled as an
|
|
418
|
+
automatic-mode source is owned by both: each `cdk deploy` reasserts your stack's definition, each
|
|
419
|
+
reconcile reasserts the connector's, and every round trip registers a revision and rolls the service.
|
|
420
|
+
Discovery-only (manual) mode is the combination that works.
|
|
421
|
+
|
|
422
|
+
# API
|
|
228
423
|
|
|
229
424
|
| Export | |
|
|
230
425
|
|---|---|
|
|
231
|
-
| `OdigosLambdaInstrumentation` | the construct; `instrument(fn)`, `instrumentAll(fns)` |
|
|
426
|
+
| `OdigosLambdaInstrumentation` | the Lambda construct; `instrument(fn)`, `instrumentAll(fns)` |
|
|
232
427
|
| `OdigosInstrumentationAspect` | applies it to every function in a scope |
|
|
233
|
-
| `
|
|
428
|
+
| `OdigosFargateInstrumentation` | the Fargate construct; `instrument(target)`, `instrumentAll(targets)` |
|
|
429
|
+
| `OdigosFargateTarget` | one container to instrument: `container`, `language`, `service` / `serviceName`, `runtimeVersion`, `existingLoaderValue` |
|
|
430
|
+
| `OdigosFargateLanguage` | `JAVA` \| `PYTHON` \| `NODEJS` \| `DOTNET` \| `PHP` \| `RUBY` |
|
|
431
|
+
| `OdigosDestination` | where telemetry goes; exactly one endpoint on Lambda, either or both on Fargate |
|
|
234
432
|
| `OdigosLayerSource` | `ODIGOS_PUBLIC` \| `IN_ACCOUNT` |
|
|
235
433
|
| `OdigosInstrumentationMode` | `HANDLER` \| `WEB_SERVER` |
|
|
236
434
|
| `OdigosJavaHandlerDistro` | the four Java wrappers |
|
|
237
|
-
| `renderCollectorConfig` | the collector config renderer, if you want to inspect what gets set |
|
|
435
|
+
| `renderCollectorConfig` | the Lambda collector config renderer, if you want to inspect what gets set |
|
|
436
|
+
| `sidecarCommand`, `evaluateAppendPattern` | the Fargate staging command and loader-value rendering |
|
|
238
437
|
| `LAYER_ARNS`, `ODIGOS_VERSION` | the baked layer table and the release it came from |
|
|
438
|
+
| `FARGATE_AGENTS_IMAGE`, `FARGATE_DISTROS_VERSION` | the agents image this build stages, and the distros release its recipes mirror |
|
|
239
439
|
|
|
240
440
|
## License
|
|
241
441
|
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { OdigosDestination, OdigosFargateLanguage } from './types';
|
|
2
|
+
/** `OTEL_EXPORTER_OTLP_PROTOCOL` values. Endpoint and protocol are always decided together. */
|
|
3
|
+
export declare const OTLP_PROTOCOL_GRPC = "grpc";
|
|
4
|
+
export declare const OTLP_PROTOCOL_HTTP = "http/protobuf";
|
|
5
|
+
/**
|
|
6
|
+
* An append-style loader variable — `JAVA_TOOL_OPTIONS`, `NODE_OPTIONS`, `PYTHONPATH` — kept as its
|
|
7
|
+
* pattern rather than a finished value, because the value depends on what the container already carries.
|
|
8
|
+
*/
|
|
9
|
+
export interface FargateAppendEnvVar {
|
|
10
|
+
/** Variable name. */
|
|
11
|
+
readonly name: string;
|
|
12
|
+
/** The distro's ReplacePattern, placeholders unresolved. */
|
|
13
|
+
readonly pattern: string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Resolves an append pattern for a given pre-existing value.
|
|
17
|
+
*
|
|
18
|
+
* Port of `distro.EvaluateReplacePattern`, INCLUDING the empty-original case: with nothing to join to,
|
|
19
|
+
* the placeholder and its adjacent delimiter are stripped, so the result never opens with a stray space
|
|
20
|
+
* or colon. `PYTHONPATH=":/var/odigos/python"` would put the current working directory on `sys.path`
|
|
21
|
+
* ahead of the agent, which is a different program than the one intended.
|
|
22
|
+
*/
|
|
23
|
+
export declare function evaluateAppendPattern(pattern: string, originalValue: string): string;
|
|
24
|
+
/**
|
|
25
|
+
* The delimiter a pattern joins the container's own value to ours with: the character immediately after
|
|
26
|
+
* the original-value placeholder.
|
|
27
|
+
*
|
|
28
|
+
* DERIVED from the pattern rather than declared, because the pattern is the authority — it comes from the
|
|
29
|
+
* upstream distro, differs by language (a space for `JAVA_TOOL_OPTIONS`/`NODE_OPTIONS`, a colon for
|
|
30
|
+
* `PYTHONPATH`) and can change on somebody else's release. Assuming one would be silently destructive in
|
|
31
|
+
* a way nothing here would catch: a space-joined `PYTHONPATH` is one unusable path rather than two, and
|
|
32
|
+
* the process still starts.
|
|
33
|
+
*/
|
|
34
|
+
export declare function appendSeparator(pattern: string): string;
|
|
35
|
+
/** One language's staging and activation, with every platform-supplied parameter already rendered. */
|
|
36
|
+
export interface FargateRecipe {
|
|
37
|
+
/** Canonical language key, and the value recorded in the `ODIGOS_IAC` marker's `lang`. */
|
|
38
|
+
readonly language: string;
|
|
39
|
+
/** Directories under the agents image the staging container copies into the shared volume. */
|
|
40
|
+
readonly agentDirs: string[];
|
|
41
|
+
/** Activation variables set outright. */
|
|
42
|
+
readonly staticEnv: {
|
|
43
|
+
[key: string]: string;
|
|
44
|
+
};
|
|
45
|
+
/** Activation variables our fragment is appended to. */
|
|
46
|
+
readonly appendEnv: FargateAppendEnvVar[];
|
|
47
|
+
/** The transport this agent prefers. */
|
|
48
|
+
readonly otlpProtocol: string;
|
|
49
|
+
/** Whether the agent can speak only `otlpProtocol` and cannot be switched to the other transport. */
|
|
50
|
+
readonly protocolFixed: boolean;
|
|
51
|
+
}
|
|
52
|
+
/** The canonical connector language key for an enum member. The enum's values ARE those keys. */
|
|
53
|
+
export declare function languageKeyOf(language: OdigosFargateLanguage): string;
|
|
54
|
+
/**
|
|
55
|
+
* The recipe for a language, with the runtime version rendered into the agents' paths where the language
|
|
56
|
+
* needs it.
|
|
57
|
+
*
|
|
58
|
+
* @param runtimeVersion the application's runtime `major.minor`. Required for php and ruby, whose agent
|
|
59
|
+
* files are laid down per version, and ignored by every other language.
|
|
60
|
+
*/
|
|
61
|
+
export declare function recipeFor(language: OdigosFargateLanguage, runtimeVersion?: string): FargateRecipe;
|
|
62
|
+
/**
|
|
63
|
+
* The staging container's command: create the shared directory, then copy each language's agent
|
|
64
|
+
* directory out of the image into it, then exit 0.
|
|
65
|
+
*
|
|
66
|
+
* ORDER AND SPELLING ARE THE CONTRACT. There is ONE staging container per task definition however many
|
|
67
|
+
* languages it holds, and the connector compares its command against the union of every instrumented
|
|
68
|
+
* container's agent directories in ascending order. A command that differs by a space is reported as
|
|
69
|
+
* drift on the whole task definition, so this is sorted and rendered exactly as `sidecarCommand` in the
|
|
70
|
+
* connector does it.
|
|
71
|
+
*/
|
|
72
|
+
export declare function sidecarCommand(agentDirs: string[]): string[];
|
|
73
|
+
/**
|
|
74
|
+
* The resource attributes every span, metric and log from an instrumented task carries. `service.name` is
|
|
75
|
+
* set separately, through `OTEL_SERVICE_NAME`.
|
|
76
|
+
*
|
|
77
|
+
* The region may be an unresolved token on an environment-agnostic stack, which is fine here and unlike
|
|
78
|
+
* the Lambda layer lookup: this value is an environment variable, so the token resolves to a `Ref` to
|
|
79
|
+
* `AWS::Region` and CloudFormation fills in the real region at deploy time.
|
|
80
|
+
*/
|
|
81
|
+
export declare function fargateResourceAttributes(region: string): string;
|
|
82
|
+
/** Where an instrumented container exports to, and over which transport. Always decided as a pair. */
|
|
83
|
+
export interface FargateExportTarget {
|
|
84
|
+
/** `scheme://host:port`. */
|
|
85
|
+
readonly endpoint: string;
|
|
86
|
+
/** `grpc` or `http/protobuf`. */
|
|
87
|
+
readonly protocol: string;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Resolves the endpoint and protocol for one recipe against one destination.
|
|
91
|
+
*
|
|
92
|
+
* Port of `exportTargetFor`. A Fargate task has no node-local collector — unlike Lambda, where the SDK
|
|
93
|
+
* exports to a collector in the same sandbox — so the agent exports OTLP straight to the destination, and
|
|
94
|
+
* which transport it can use is a property of the agent as much as of the destination.
|
|
95
|
+
*
|
|
96
|
+
* Each agent's PREFERRED transport is taken when the destination offers it. When it does not, an agent
|
|
97
|
+
* whose community SDK pins the protocol (php, ruby) cannot be switched and throws; every other agent
|
|
98
|
+
* honours `OTEL_EXPORTER_OTLP_PROTOCOL` and falls back to whichever transport exists.
|
|
99
|
+
*/
|
|
100
|
+
export declare function exportTargetFor(recipe: FargateRecipe, dest: OdigosDestination): FargateExportTarget;
|
|
101
|
+
/**
|
|
102
|
+
* A warning when an endpoint's port is the well-known port of the OTHER OTLP protocol, or "" when the
|
|
103
|
+
* pairing looks right — including a non-standard port, which cannot be judged.
|
|
104
|
+
*
|
|
105
|
+
* Worth a synth-time warning because the runtime symptom is a connection reset mid-export, which points
|
|
106
|
+
* nowhere near the cause: an HTTP request against an HTTP/2 gRPC listener, or the reverse.
|
|
107
|
+
*/
|
|
108
|
+
export declare function portProtocolMismatch(endpoint: string, protocol: string): string;
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.OTLP_PROTOCOL_HTTP = exports.OTLP_PROTOCOL_GRPC = void 0;
|
|
4
|
+
exports.evaluateAppendPattern = evaluateAppendPattern;
|
|
5
|
+
exports.appendSeparator = appendSeparator;
|
|
6
|
+
exports.languageKeyOf = languageKeyOf;
|
|
7
|
+
exports.recipeFor = recipeFor;
|
|
8
|
+
exports.sidecarCommand = sidecarCommand;
|
|
9
|
+
exports.fargateResourceAttributes = fargateResourceAttributes;
|
|
10
|
+
exports.exportTargetFor = exportTargetFor;
|
|
11
|
+
exports.portProtocolMismatch = portProtocolMismatch;
|
|
12
|
+
const fargate_1 = require("./generated/fargate");
|
|
13
|
+
/**
|
|
14
|
+
* How a language's agent is staged into an ECS Fargate task and activated in a container.
|
|
15
|
+
*
|
|
16
|
+
* A PORT of `recipeFor` / `applyInstrumentation` in the connector's ECS instrumentor
|
|
17
|
+
* (connectors/providers/aws/pkg/workload/instrumentor/ecs_recipes.go, ecs_inject.go). The data lives in
|
|
18
|
+
* ../../artifacts.json and reaches here through the generated module; the decisions live here.
|
|
19
|
+
*
|
|
20
|
+
* Equivalence with the connector is not cosmetic. Its conformance observer rebuilds each container's
|
|
21
|
+
* desired activation environment and compares it against what the container carries, component by
|
|
22
|
+
* component, so anything rendered differently here is reported to the customer as instrumentation drift
|
|
23
|
+
* on a stack that is in fact deployed exactly as they wrote it.
|
|
24
|
+
*/
|
|
25
|
+
/** The distro placeholder for the container's own pre-Odigos value of an append-style loader variable. */
|
|
26
|
+
const ORIGINAL_ENV_VALUE_PLACEHOLDER = '{{ORIGINAL_ENV_VALUE}}';
|
|
27
|
+
/** The distro placeholder for the shared volume's mount path. */
|
|
28
|
+
const AGENTS_DIR_PLACEHOLDER = '{{ODIGOS_AGENTS_DIR}}';
|
|
29
|
+
/**
|
|
30
|
+
* The distro parameter for the application's runtime `major.minor`, as a Go text/template action —
|
|
31
|
+
* verbatim from the distro yaml so ../../artifacts.json can be diffed against it by eye. Only php and
|
|
32
|
+
* ruby carry it: their agents are laid down per runtime version.
|
|
33
|
+
*/
|
|
34
|
+
const RUNTIME_VERSION_PLACEHOLDER = '{{.RUNTIME_VERSION_MAJOR_MINOR}}';
|
|
35
|
+
/** `OTEL_EXPORTER_OTLP_PROTOCOL` values. Endpoint and protocol are always decided together. */
|
|
36
|
+
exports.OTLP_PROTOCOL_GRPC = 'grpc';
|
|
37
|
+
exports.OTLP_PROTOCOL_HTTP = 'http/protobuf';
|
|
38
|
+
/** Standard OTLP receiver ports, used only to warn about an endpoint paired with the other protocol. */
|
|
39
|
+
const OTLP_DEFAULT_GRPC_PORT = '4317';
|
|
40
|
+
const OTLP_DEFAULT_HTTP_PORT = '4318';
|
|
41
|
+
/**
|
|
42
|
+
* Resolves an append pattern for a given pre-existing value.
|
|
43
|
+
*
|
|
44
|
+
* Port of `distro.EvaluateReplacePattern`, INCLUDING the empty-original case: with nothing to join to,
|
|
45
|
+
* the placeholder and its adjacent delimiter are stripped, so the result never opens with a stray space
|
|
46
|
+
* or colon. `PYTHONPATH=":/var/odigos/python"` would put the current working directory on `sys.path`
|
|
47
|
+
* ahead of the agent, which is a different program than the one intended.
|
|
48
|
+
*/
|
|
49
|
+
function evaluateAppendPattern(pattern, originalValue) {
|
|
50
|
+
const withDir = pattern.split(AGENTS_DIR_PLACEHOLDER).join(fargate_1.FARGATE_AGENTS_DIR);
|
|
51
|
+
if (originalValue.trim() === '') {
|
|
52
|
+
return withDir.split(ORIGINAL_ENV_VALUE_PLACEHOLDER).join('').replace(/^[ :]+/, '');
|
|
53
|
+
}
|
|
54
|
+
return withDir.split(ORIGINAL_ENV_VALUE_PLACEHOLDER).join(originalValue);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The delimiter a pattern joins the container's own value to ours with: the character immediately after
|
|
58
|
+
* the original-value placeholder.
|
|
59
|
+
*
|
|
60
|
+
* DERIVED from the pattern rather than declared, because the pattern is the authority — it comes from the
|
|
61
|
+
* upstream distro, differs by language (a space for `JAVA_TOOL_OPTIONS`/`NODE_OPTIONS`, a colon for
|
|
62
|
+
* `PYTHONPATH`) and can change on somebody else's release. Assuming one would be silently destructive in
|
|
63
|
+
* a way nothing here would catch: a space-joined `PYTHONPATH` is one unusable path rather than two, and
|
|
64
|
+
* the process still starts.
|
|
65
|
+
*/
|
|
66
|
+
function appendSeparator(pattern) {
|
|
67
|
+
const i = pattern.indexOf(ORIGINAL_ENV_VALUE_PLACEHOLDER);
|
|
68
|
+
if (i < 0) {
|
|
69
|
+
return '';
|
|
70
|
+
}
|
|
71
|
+
const rest = pattern.slice(i + ORIGINAL_ENV_VALUE_PLACEHOLDER.length);
|
|
72
|
+
return rest === '' ? '' : rest.charAt(0);
|
|
73
|
+
}
|
|
74
|
+
/** The canonical connector language key for an enum member. The enum's values ARE those keys. */
|
|
75
|
+
function languageKeyOf(language) {
|
|
76
|
+
return language;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The recipe for a language, with the runtime version rendered into the agents' paths where the language
|
|
80
|
+
* needs it.
|
|
81
|
+
*
|
|
82
|
+
* @param runtimeVersion the application's runtime `major.minor`. Required for php and ruby, whose agent
|
|
83
|
+
* files are laid down per version, and ignored by every other language.
|
|
84
|
+
*/
|
|
85
|
+
function recipeFor(language, runtimeVersion) {
|
|
86
|
+
const key = languageKeyOf(language);
|
|
87
|
+
const spec = fargate_1.FARGATE_LANGUAGES[key];
|
|
88
|
+
if (!spec) {
|
|
89
|
+
// Unreachable through the enum; reachable if the generated table and the enum ever disagree.
|
|
90
|
+
throw new Error(`Odigos: no ECS Fargate agent recipe for language "${key}".`);
|
|
91
|
+
}
|
|
92
|
+
if (spec.requiresRuntimeVersion && !runtimeVersion) {
|
|
93
|
+
throw new Error(`Odigos: the ${key} agent's files are laid down per runtime version, so instrumenting a ${key} ` +
|
|
94
|
+
'container needs `runtimeVersion` set to the application\'s major.minor (for example "8.3"). ' +
|
|
95
|
+
'The value selects the agent path the activation environment points at, so a wrong or missing ' +
|
|
96
|
+
'one leaves the container with an agent that is never loaded.');
|
|
97
|
+
}
|
|
98
|
+
// Rejected here rather than passed through: a "8.3.4" or "php8.3" would be rendered straight into
|
|
99
|
+
// PHP_INI_SCAN_DIR / RUBYOPT as a path that does not exist in the bundle, and the container would start
|
|
100
|
+
// with no agent loaded and no error anywhere near the cause.
|
|
101
|
+
if (runtimeVersion && !/^\d+\.\d+$/.test(runtimeVersion)) {
|
|
102
|
+
throw new Error(`Odigos: runtimeVersion "${runtimeVersion}" must be exactly major.minor (for example "8.3") — it ` +
|
|
103
|
+
'names a directory in the agents bundle, not a full version.');
|
|
104
|
+
}
|
|
105
|
+
const staticEnv = {};
|
|
106
|
+
for (const [k, v] of Object.entries(spec.staticEnv)) {
|
|
107
|
+
staticEnv[k] = runtimeVersion ? v.split(RUNTIME_VERSION_PLACEHOLDER).join(runtimeVersion) : v;
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
language: key,
|
|
111
|
+
agentDirs: spec.agentDirs,
|
|
112
|
+
staticEnv,
|
|
113
|
+
appendEnv: spec.appendEnv.map((a) => ({ name: a.name, pattern: a.pattern })),
|
|
114
|
+
otlpProtocol: spec.otlpProtocol,
|
|
115
|
+
protocolFixed: spec.protocolFixed,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* The staging container's command: create the shared directory, then copy each language's agent
|
|
120
|
+
* directory out of the image into it, then exit 0.
|
|
121
|
+
*
|
|
122
|
+
* ORDER AND SPELLING ARE THE CONTRACT. There is ONE staging container per task definition however many
|
|
123
|
+
* languages it holds, and the connector compares its command against the union of every instrumented
|
|
124
|
+
* container's agent directories in ascending order. A command that differs by a space is reported as
|
|
125
|
+
* drift on the whole task definition, so this is sorted and rendered exactly as `sidecarCommand` in the
|
|
126
|
+
* connector does it.
|
|
127
|
+
*/
|
|
128
|
+
function sidecarCommand(agentDirs) {
|
|
129
|
+
const dirs = [...new Set(agentDirs)].sort();
|
|
130
|
+
let script = `mkdir -p ${fargate_1.FARGATE_AGENTS_DIR}`;
|
|
131
|
+
for (const d of dirs) {
|
|
132
|
+
script += ` && cp -r ${fargate_1.FARGATE_IMAGE_AGENTS_DIR}/${d} ${fargate_1.FARGATE_AGENTS_DIR}/${d}`;
|
|
133
|
+
}
|
|
134
|
+
return ['sh', '-c', script];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The resource attributes every span, metric and log from an instrumented task carries. `service.name` is
|
|
138
|
+
* set separately, through `OTEL_SERVICE_NAME`.
|
|
139
|
+
*
|
|
140
|
+
* The region may be an unresolved token on an environment-agnostic stack, which is fine here and unlike
|
|
141
|
+
* the Lambda layer lookup: this value is an environment variable, so the token resolves to a `Ref` to
|
|
142
|
+
* `AWS::Region` and CloudFormation fills in the real region at deploy time.
|
|
143
|
+
*/
|
|
144
|
+
function fargateResourceAttributes(region) {
|
|
145
|
+
const attrs = 'cloud.provider=aws,cloud.platform=aws_ecs';
|
|
146
|
+
return region ? `${attrs},cloud.region=${region}` : attrs;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Turns a destination's OTLP/gRPC target (`host:port`) into the URL the OpenTelemetry SDK env accepts,
|
|
150
|
+
* taking the scheme from the destination's TLS setting. A value that is already a URL passes through.
|
|
151
|
+
*/
|
|
152
|
+
function grpcEndpointURL(endpoint, insecure) {
|
|
153
|
+
if (endpoint === '') {
|
|
154
|
+
return '';
|
|
155
|
+
}
|
|
156
|
+
if (endpoint.includes('://')) {
|
|
157
|
+
return endpoint;
|
|
158
|
+
}
|
|
159
|
+
return `${insecure === true ? 'http' : 'https'}://${endpoint}`;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Resolves the endpoint and protocol for one recipe against one destination.
|
|
163
|
+
*
|
|
164
|
+
* Port of `exportTargetFor`. A Fargate task has no node-local collector — unlike Lambda, where the SDK
|
|
165
|
+
* exports to a collector in the same sandbox — so the agent exports OTLP straight to the destination, and
|
|
166
|
+
* which transport it can use is a property of the agent as much as of the destination.
|
|
167
|
+
*
|
|
168
|
+
* Each agent's PREFERRED transport is taken when the destination offers it. When it does not, an agent
|
|
169
|
+
* whose community SDK pins the protocol (php, ruby) cannot be switched and throws; every other agent
|
|
170
|
+
* honours `OTEL_EXPORTER_OTLP_PROTOCOL` and falls back to whichever transport exists.
|
|
171
|
+
*/
|
|
172
|
+
function exportTargetFor(recipe, dest) {
|
|
173
|
+
const grpcURL = grpcEndpointURL(dest.endpoint ?? '', dest.insecure);
|
|
174
|
+
const httpURL = dest.httpEndpoint ?? '';
|
|
175
|
+
if (recipe.otlpProtocol === exports.OTLP_PROTOCOL_HTTP && httpURL !== '') {
|
|
176
|
+
return { endpoint: httpURL, protocol: exports.OTLP_PROTOCOL_HTTP };
|
|
177
|
+
}
|
|
178
|
+
if (recipe.otlpProtocol === exports.OTLP_PROTOCOL_GRPC && grpcURL !== '') {
|
|
179
|
+
return { endpoint: grpcURL, protocol: exports.OTLP_PROTOCOL_GRPC };
|
|
180
|
+
}
|
|
181
|
+
if (recipe.protocolFixed) {
|
|
182
|
+
throw new Error(`Odigos: the ${recipe.language} agent exports OTLP over ${recipe.otlpProtocol} only — its community ` +
|
|
183
|
+
'SDK cannot be switched to the other transport — but the destination configures no ' +
|
|
184
|
+
`${recipe.otlpProtocol === exports.OTLP_PROTOCOL_HTTP ? '`httpEndpoint`' : '`endpoint`'}. ` +
|
|
185
|
+
'Configure one, or leave this container uninstrumented.');
|
|
186
|
+
}
|
|
187
|
+
if (httpURL !== '') {
|
|
188
|
+
return { endpoint: httpURL, protocol: exports.OTLP_PROTOCOL_HTTP };
|
|
189
|
+
}
|
|
190
|
+
if (grpcURL !== '') {
|
|
191
|
+
return { endpoint: grpcURL, protocol: exports.OTLP_PROTOCOL_GRPC };
|
|
192
|
+
}
|
|
193
|
+
throw new Error('Odigos destination has neither `endpoint` (OTLP/gRPC) nor `httpEndpoint` (OTLP/HTTP), so an ' +
|
|
194
|
+
'instrumented Fargate task would have nowhere to export. Unlike Lambda, a Fargate task has no ' +
|
|
195
|
+
'in-sandbox collector to buffer for it — the agent exports to this destination directly.');
|
|
196
|
+
}
|
|
197
|
+
/** The port of a `scheme://host:port[/path]` endpoint, or "" when it names none. */
|
|
198
|
+
function endpointPort(endpoint) {
|
|
199
|
+
let rest = endpoint;
|
|
200
|
+
const scheme = rest.indexOf('://');
|
|
201
|
+
if (scheme >= 0) {
|
|
202
|
+
rest = rest.slice(scheme + 3);
|
|
203
|
+
}
|
|
204
|
+
const pathStart = rest.search(/[/?]/);
|
|
205
|
+
if (pathStart >= 0) {
|
|
206
|
+
rest = rest.slice(0, pathStart);
|
|
207
|
+
}
|
|
208
|
+
const colon = rest.lastIndexOf(':');
|
|
209
|
+
return colon < 0 ? '' : rest.slice(colon + 1);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* A warning when an endpoint's port is the well-known port of the OTHER OTLP protocol, or "" when the
|
|
213
|
+
* pairing looks right — including a non-standard port, which cannot be judged.
|
|
214
|
+
*
|
|
215
|
+
* Worth a synth-time warning because the runtime symptom is a connection reset mid-export, which points
|
|
216
|
+
* nowhere near the cause: an HTTP request against an HTTP/2 gRPC listener, or the reverse.
|
|
217
|
+
*/
|
|
218
|
+
function portProtocolMismatch(endpoint, protocol) {
|
|
219
|
+
const port = endpointPort(endpoint);
|
|
220
|
+
if (protocol === exports.OTLP_PROTOCOL_HTTP && port === OTLP_DEFAULT_GRPC_PORT) {
|
|
221
|
+
return (`endpoint ${endpoint} is on ${OTLP_DEFAULT_GRPC_PORT}, the standard OTLP/gRPC port, but this agent ` +
|
|
222
|
+
`exports over ${exports.OTLP_PROTOCOL_HTTP}; exports will fail as a connection reset. Point it at the ` +
|
|
223
|
+
`OTLP/HTTP port (${OTLP_DEFAULT_HTTP_PORT}).`);
|
|
224
|
+
}
|
|
225
|
+
if (protocol === exports.OTLP_PROTOCOL_GRPC && port === OTLP_DEFAULT_HTTP_PORT) {
|
|
226
|
+
return (`endpoint ${endpoint} is on ${OTLP_DEFAULT_HTTP_PORT}, the standard OTLP/HTTP port, but this agent ` +
|
|
227
|
+
`exports over gRPC; exports will fail. Point it at the OTLP/gRPC port (${OTLP_DEFAULT_GRPC_PORT}).`);
|
|
228
|
+
}
|
|
229
|
+
return '';
|
|
230
|
+
}
|
|
231
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFyZ2F0ZS1yZWNpcGVzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2ZhcmdhdGUtcmVjaXBlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUE0REEsc0RBTUM7QUFZRCwwQ0FPQztBQW1CRCxzQ0FFQztBQVNELDhCQXNDQztBQVlELHdDQU9DO0FBVUQsOERBR0M7QUFtQ0QsMENBNkJDO0FBd0JELG9EQWdCQztBQWpTRCxpREFJNkI7QUFHN0I7Ozs7Ozs7Ozs7O0dBV0c7QUFFSCwwR0FBMEc7QUFDMUcsTUFBTSw4QkFBOEIsR0FBRyx3QkFBd0IsQ0FBQztBQUVoRSxpRUFBaUU7QUFDakUsTUFBTSxzQkFBc0IsR0FBRyx1QkFBdUIsQ0FBQztBQUV2RDs7OztHQUlHO0FBQ0gsTUFBTSwyQkFBMkIsR0FBRyxrQ0FBa0MsQ0FBQztBQUV2RSwrRkFBK0Y7QUFDbEYsUUFBQSxrQkFBa0IsR0FBRyxNQUFNLENBQUM7QUFDNUIsUUFBQSxrQkFBa0IsR0FBRyxlQUFlLENBQUM7QUFFbEQsd0dBQXdHO0FBQ3hHLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDO0FBQ3RDLE1BQU0sc0JBQXNCLEdBQUcsTUFBTSxDQUFDO0FBYXRDOzs7Ozs7O0dBT0c7QUFDSCxTQUFnQixxQkFBcUIsQ0FBQyxPQUFlLEVBQUUsYUFBcUI7SUFDMUUsTUFBTSxPQUFPLEdBQUcsT0FBTyxDQUFDLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQyxDQUFDLElBQUksQ0FBQyw0QkFBa0IsQ0FBQyxDQUFDO0lBQy9FLElBQUksYUFBYSxDQUFDLElBQUksRUFBRSxLQUFLLEVBQUUsRUFBRSxDQUFDO1FBQ2hDLE9BQU8sT0FBTyxDQUFDLEtBQUssQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3RGLENBQUM7SUFDRCxPQUFPLE9BQU8sQ0FBQyxLQUFLLENBQUMsOEJBQThC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Q0FBQztBQUNaLENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBGQVJHQVRFX0FHRU5UU19ESVIsXG4gIEZBUkdBVEVfSU1BR0VfQUdFTlRTX0RJUixcbiAgRkFSR0FURV9MQU5HVUFHRVMsXG59IGZyb20gJy4vZ2VuZXJhdGVkL2ZhcmdhdGUnO1xuaW1wb3J0IHsgT2RpZ29zRGVzdGluYXRpb24sIE9kaWdvc0ZhcmdhdGVMYW5ndWFnZSB9IGZyb20gJy4vdHlwZXMnO1xuXG4vKipcbiAqIEhvdyBhIGxhbmd1YWdlJ3MgYWdlbnQgaXMgc3RhZ2VkIGludG8gYW4gRUNTIEZhcmdhdGUgdGFzayBhbmQgYWN0aXZhdGVkIGluIGEgY29udGFpbmVyLlxuICpcbiAqIEEgUE9SVCBvZiBgcmVjaXBlRm9yYCAvIGBhcHBseUluc3RydW1lbnRhdGlvbmAgaW4gdGhlIGNvbm5lY3RvcidzIEVDUyBpbnN0cnVtZW50b3JcbiAqIChjb25uZWN0b3JzL3Byb3ZpZGVycy9hd3MvcGtnL3dvcmtsb2FkL2luc3RydW1lbnRvci9lY3NfcmVjaXBlcy5nbywgZWNzX2luamVjdC5nbykuIFRoZSBkYXRhIGxpdmVzIGluXG4gKiAuLi8uLi9hcnRpZmFjdHMuanNvbiBhbmQgcmVhY2hlcyBoZXJlIHRocm91Z2ggdGhlIGdlbmVyYXRlZCBtb2R1bGU7IHRoZSBkZWNpc2lvbnMgbGl2ZSBoZXJlLlxuICpcbiAqIEVxdWl2YWxlbmNlIHdpdGggdGhlIGNvbm5lY3RvciBpcyBub3QgY29zbWV0aWMuIEl0cyBjb25mb3JtYW5jZSBvYnNlcnZlciByZWJ1aWxkcyBlYWNoIGNvbnRhaW5lcidzXG4gKiBkZXNpcmVkIGFjdGl2YXRpb24gZW52aXJvbm1lbnQgYW5kIGNvbXBhcmVzIGl0IGFnYWluc3Qgd2hhdCB0aGUgY29udGFpbmVyIGNhcnJpZXMsIGNvbXBvbmVudCBieVxuICogY29tcG9uZW50LCBzbyBhbnl0aGluZyByZW5kZXJlZCBkaWZmZXJlbnRseSBoZXJlIGlzIHJlcG9ydGVkIHRvIHRoZSBjdXN0b21lciBhcyBpbnN0cnVtZW50YXRpb24gZHJpZnRcbiAqIG9uIGEgc3RhY2sgdGhhdCBpcyBpbiBmYWN0IGRlcGxveWVkIGV4YWN0bHkgYXMgdGhleSB3cm90ZSBpdC5cbiAqL1xuXG4vKiogVGhlIGRpc3RybyBwbGFjZWhvbGRlciBmb3IgdGhlIGNvbnRhaW5lcidzIG93biBwcmUtT2RpZ29zIHZhbHVlIG9mIGFuIGFwcGVuZC1zdHlsZSBsb2FkZXIgdmFyaWFibGUuICovXG5jb25zdCBPUklHSU5BTF9FTlZfVkFMVUVfUExBQ0VIT0xERVIgPSAne3tPUklHSU5BTF9FTlZfVkFMVUV9fSc7XG5cbi8qKiBUaGUgZGlzdHJvIHBsYWNlaG9sZGVyIGZvciB0aGUgc2hhcmVkIHZvbHVtZSdzIG1vdW50IHBhdGguICovXG5jb25zdCBBR0VOVFNfRElSX1BMQUNFSE9MREVSID0gJ3t7T0RJR09TX0FHRU5UU19ESVJ9fSc7XG5cbi8qKlxuICogVGhlIGRpc3RybyBwYXJhbWV0ZXIgZm9yIHRoZSBhcHBsaWNhdGlvbidzIHJ1bnRpbWUgYG1ham9yLm1pbm9yYCwgYXMgYSBHbyB0ZXh0L3RlbXBsYXRlIGFjdGlvbiDigJRcbiAqIHZlcmJhdGltIGZyb20gdGhlIGRpc3RybyB5YW1sIHNvIC4uLy4uL2FydGlmYWN0cy5qc29uIGNhbiBiZSBkaWZmZWQgYWdhaW5zdCBpdCBieSBleWUuIE9ubHkgcGhwIGFuZFxuICogcnVieSBjYXJyeSBpdDogdGhlaXIgYWdlbnRzIGFyZSBsYWlkIGRvd24gcGVyIHJ1bnRpbWUgdmVyc2lvbi5cbiAqL1xuY29uc3QgUlVOVElNRV9WRVJTSU9OX1BMQUNFSE9MREVSID0gJ3t7LlJVTlRJTUVfVkVSU0lPTl9NQUpPUl9NSU5PUn19JztcblxuLyoqIGBPVEVMX0VYUE9SVEVSX09UTFBfUFJPVE9DT0xgIHZhbHVlcy4gRW5kcG9pbnQgYW5kIHByb3RvY29sIGFyZSBhbHdheXMgZGVjaWRlZCB0b2dldGhlci4gKi9cbmV4cG9ydCBjb25zdCBPVExQX1BST1RPQ09MX0dSUEMgPSAnZ3JwYyc7XG5leHBvcnQgY29uc3QgT1RMUF9QUk9UT0NPTF9IVFRQID0gJ2h0dHAvcHJvdG9idWYnO1xuXG4vKiogU3RhbmRhcmQgT1RMUCByZWNlaXZlciBwb3J0cywgdXNlZCBvbmx5IHRvIHdhcm4gYWJvdXQgYW4gZW5kcG9pbnQgcGFpcmVkIHdpdGggdGhlIG90aGVyIHByb3RvY29sLiAqL1xuY29uc3QgT1RMUF9ERUZBVUxUX0dSUENfUE9SVCA9ICc0MzE3JztcbmNvbnN0IE9UTFBfREVGQVVMVF9IVFRQX1BPUlQgPSAnNDMxOCc7XG5cbi8qKlxuICogQW4gYXBwZW5kLXN0eWxlIGxvYWRlciB2YXJpYWJsZSDigJQgYEpBVkFfVE9PTF9PUFRJT05TYCwgYE5PREVfT1BUSU9OU2AsIGBQWVRIT05QQVRIYCDigJQga2VwdCBhcyBpdHNcbiAqIHBhdHRlcm4gcmF0aGVyIHRoYW4gYSBmaW5pc2hlZCB2YWx1ZSwgYmVjYXVzZSB0aGUgdmFsdWUgZGVwZW5kcyBvbiB3aGF0IHRoZSBjb250YWluZXIgYWxyZWFkeSBjYXJyaWVzLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIEZhcmdhdGVBcHBlbmRFbnZWYXIge1xuICAvKiogVmFyaWFibGUgbmFtZS4gKi9cbiAgcmVhZG9ubHkgbmFtZTogc3RyaW5nO1xuICAvKiogVGhlIGRpc3RybydzIFJlcGxhY2VQYXR0ZXJuLCBwbGFjZWhvbGRlcnMgdW5yZXNvbHZlZC4gKi9cbiAgcmVhZG9ubHkgcGF0dGVybjogc3RyaW5nO1xufVxuXG4vKipcbiAqIFJlc29sdmVzIGFuIGFwcGVuZCBwYXR0ZXJuIGZvciBhIGdpdmVuIHByZS1leGlzdGluZyB2YWx1ZS5cbiAqXG4gKiBQb3J0IG9mIGBkaXN0cm8uRXZhbHVhdGVSZXBsYWNlUGF0dGVybmAsIElOQ0xVRElORyB0aGUgZW1wdHktb3JpZ2luYWwgY2FzZTogd2l0aCBub3RoaW5nIHRvIGpvaW4gdG8sXG4gKiB0aGUgcGxhY2Vob2xkZXIgYW5kIGl0cyBhZGphY2VudCBkZWxpbWl0ZXIgYXJlIHN0cmlwcGVkLCBzbyB0aGUgcmVzdWx0IG5ldmVyIG9wZW5zIHdpdGggYSBzdHJheSBzcGFjZVxuICogb3IgY29sb24uIGBQWVRIT05QQVRIPVwiOi92YXIvb2RpZ29zL3B5dGhvblwiYCB3b3VsZCBwdXQgdGhlIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnkgb24gYHN5cy5wYXRoYFxuICogYWhlYWQgb2YgdGhlIGFnZW50LCB3aGljaCBpcyBhIGRpZmZlcmVudCBwcm9ncmFtIHRoYW4gdGhlIG9uZSBpbnRlbmRlZC5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV2YWx1YXRlQXBwZW5kUGF0dGVybihwYXR0ZXJuOiBzdHJpbmcsIG9yaWdpbmFsVmFsdWU6IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IHdpdGhEaXIgPSBwYXR0ZXJuLnNwbGl0KEFHRU5UU19ESVJfUExBQ0VIT0xERVIpLmpvaW4oRkFSR0FURV9BR0VOVFNfRElSKTtcbiAgaWYgKG9yaWdpbmFsVmFsdWUudHJpbSgpID09PSAnJykge1xuICAgIHJldHVybiB3aXRoRGlyLnNwbGl0KE9SSUdJTkFMX0VOVl9WQUxVRV9QTEFDRUhPTERFUikuam9pbignJykucmVwbGFjZSgvXlsgOl0rLywgJycpO1xuICB9XG4gIHJldHVybiB3aXRoRGlyLnNwbGl0KE9SSUdJTkFMX0VOVl9WQUxVRV9QTEFDRUhPTERFUikuam9pbihvcmlnaW5hbFZhbHVlKTtcbn1cblxuLyoqXG4gKiBUaGUgZGVsaW1pdGVyIGEgcGF0dGVybiBqb2lucyB0aGUgY29udGFpbmVyJ3Mgb3duIHZhbHVlIHRvIG91cnMgd2l0aDogdGhlIGNoYXJhY3RlciBpbW1lZGlhdGVseSBhZnRlclxuICogdGhlIG9yaWdpbmFsLXZhbHVlIHBsYWNlaG9sZGVyLlxuICpcbiAqIERFUklWRUQgZnJvbSB0aGUgcGF0dGVybiByYXRoZXIgdGhhbiBkZWNsYXJlZCwgYmVjYXVzZSB0aGUgcGF0dGVybiBpcyB0aGUgYXV0aG9yaXR5IOKAlCBpdCBjb21lcyBmcm9tIHRoZVxuICogdXBzdHJlYW0gZGlzdHJvLCBkaWZmZXJzIGJ5IGxhbmd1YWdlIChhIHNwYWNlIGZvciBgSkFWQV9UT09MX09QVElPTlNgL2BOT0RFX09QVElPTlNgLCBhIGNvbG9uIGZvclxuICogYFBZVEhPTlBBVEhgKSBhbmQgY2FuIGNoYW5nZSBvbiBzb21lYm9keSBlbHNlJ3MgcmVsZWFzZS4gQXNzdW1pbmcgb25lIHdvdWxkIGJlIHNpbGVudGx5IGRlc3RydWN0aXZlIGluXG4gKiBhIHdheSBub3RoaW5nIGhlcmUgd291bGQgY2F0Y2g6IGEgc3BhY2Utam9pbmVkIGBQWVRIT05QQVRIYCBpcyBvbmUgdW51c2FibGUgcGF0aCByYXRoZXIgdGhhbiB0d28sIGFuZFxuICogdGhlIHByb2Nlc3Mgc3RpbGwgc3RhcnRzLlxuICovXG5leHBvcnQgZnVuY3Rpb24gYXBwZW5kU2VwYXJhdG9yKHBhdHRlcm46IHN0cmluZyk6IHN0cmluZyB7XG4gIGNvbnN0IGkgPSBwYXR0ZXJuLmluZGV4T2YoT1JJR0lOQUxfRU5WX1ZBTFVFX1BMQUNFSE9MREVSKTtcbiAgaWYgKGkgPCAwKSB7XG4gICAgcmV0dXJuICcnO1xuICB9XG4gIGNvbnN0IHJlc3QgPSBwYXR0ZXJuLnNsaWNlKGkgKyBPUklHSU5BTF9FTlZfVkFMVUVfUExBQ0VIT0xERVIubGVuZ3RoKTtcbiAgcmV0dXJuIHJlc3QgPT09ICcnID8gJycgOiByZXN0LmNoYXJBdCgwKTtcbn1cblxuLyoqIE9uZSBsYW5ndWFnZSdzIHN0YWdpbmcgYW5kIGFjdGl2YXRpb24sIHdpdGggZXZlcnkgcGxhdGZvcm0tc3VwcGxpZWQgcGFyYW1ldGVyIGFscmVhZHkgcmVuZGVyZWQuICovXG5leHBvcnQgaW50ZXJmYWNlIEZhcmdhdGVSZWNpcGUge1xuICAvKiogQ2Fub25pY2FsIGxhbmd1YWdlIGtleSwgYW5kIHRoZSB2YWx1ZSByZWNvcmRlZCBpbiB0aGUgYE9ESUdPU19JQUNgIG1hcmtlcidzIGBsYW5nYC4gKi9cbiAgcmVhZG9ubHkgbGFuZ3VhZ2U6IHN0cmluZztcbiAgLyoqIERpcmVjdG9yaWVzIHVuZGVyIHRoZSBhZ2VudHMgaW1hZ2UgdGhlIHN0YWdpbmcgY29udGFpbmVyIGNvcGllcyBpbnRvIHRoZSBzaGFyZWQgdm9sdW1lLiAqL1xuICByZWFkb25seSBhZ2VudERpcnM6IHN0cmluZ1tdO1xuICAvKiogQWN0aXZhdGlvbiB2YXJpYWJsZXMgc2V0IG91dHJpZ2h0LiAqL1xuICByZWFkb25seSBzdGF0aWNFbnY6IHsgW2tleTogc3RyaW5nXTogc3RyaW5nIH07XG4gIC8qKiBBY3RpdmF0aW9uIHZhcmlhYmxlcyBvdXIgZnJhZ21lbnQgaXMgYXBwZW5kZWQgdG8uICovXG4gIHJlYWRvbmx5IGFwcGVuZEVudjogRmFyZ2F0ZUFwcGVuZEVudlZhcltdO1xuICAvKiogVGhlIHRyYW5zcG9ydCB0aGlzIGFnZW50IHByZWZlcnMuICovXG4gIHJlYWRvbmx5IG90bHBQcm90b2NvbDogc3RyaW5nO1xuICAvKiogV2hldGhlciB0aGUgYWdlbnQgY2FuIHNwZWFrIG9ubHkgYG90bHBQcm90b2NvbGAgYW5kIGNhbm5vdCBiZSBzd2l0Y2hlZCB0byB0aGUgb3RoZXIgdHJhbnNwb3J0LiAqL1xuICByZWFkb25seSBwcm90b2NvbEZpeGVkOiBib29sZWFuO1xufVxuXG4vKiogVGhlIGNhbm9uaWNhbCBjb25uZWN0b3IgbGFuZ3VhZ2Uga2V5IGZvciBhbiBlbnVtIG1lbWJlci4gVGhlIGVudW0ncyB2YWx1ZXMgQVJFIHRob3NlIGtleXMuICovXG5leHBvcnQgZnVuY3Rpb24gbGFuZ3VhZ2VLZXlPZihsYW5ndWFnZTogT2RpZ29zRmFyZ2F0ZUxhbmd1YWdlKTogc3RyaW5nIHtcbiAgcmV0dXJuIGxhbmd1YWdlO1xufVxuXG4vKipcbiAqIFRoZSByZWNpcGUgZm9yIGEgbGFuZ3VhZ2UsIHdpdGggdGhlIHJ1bnRpbWUgdmVyc2lvbiByZW5kZXJlZCBpbnRvIHRoZSBhZ2VudHMnIHBhdGhzIHdoZXJlIHRoZSBsYW5ndWFnZVxuICogbmVlZHMgaXQuXG4gKlxuICogQHBhcmFtIHJ1bnRpbWVWZXJzaW9uIHRoZSBhcHBsaWNhdGlvbidzIHJ1bnRpbWUgYG1ham9yLm1pbm9yYC4gUmVxdWlyZWQgZm9yIHBocCBhbmQgcnVieSwgd2hvc2UgYWdlbnRcbiAqIGZpbGVzIGFyZSBsYWlkIGRvd24gcGVyIHZlcnNpb24sIGFuZCBpZ25vcmVkIGJ5IGV2ZXJ5IG90aGVyIGxhbmd1YWdlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVjaXBlRm9yKGxhbmd1YWdlOiBPZGlnb3NGYXJnYXRlTGFuZ3VhZ2UsIHJ1bnRpbWVWZXJzaW9uPzogc3RyaW5nKTogRmFyZ2F0ZVJlY2lwZSB7XG4gIGNvbnN0IGtleSA9IGxhbmd1YWdlS2V5T2YobGFuZ3VhZ2UpO1xuICBjb25zdCBzcGVjID0gRkFSR0FURV9MQU5HVUFHRVNba2V5XTtcbiAgaWYgKCFzcGVjKSB7XG4gICAgLy8gVW5yZWFjaGFibGUgdGhyb3VnaCB0aGUgZW51bTsgcmVhY2hhYmxlIGlmIHRoZSBnZW5lcmF0ZWQgdGFibGUgYW5kIHRoZSBlbnVtIGV2ZXIgZGlzYWdyZWUuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBPZGlnb3M6IG5vIEVDUyBGYXJnYXRlIGFnZW50IHJlY2lwZSBmb3IgbGFuZ3VhZ2UgXCIke2tleX1cIi5gKTtcbiAgfVxuICBpZiAoc3BlYy5yZXF1aXJlc1J1bnRpbWVWZXJzaW9uICYmICFydW50aW1lVmVyc2lvbikge1xuICAgIHRocm93IG5ldyBFcnJvcihcbiAgICAgIGBPZGlnb3M6IHRoZSAke2tleX0gYWdlbnQncyBmaWxlcyBhcmUgbGFpZCBkb3duIHBlciBydW50aW1lIHZlcnNpb24sIHNvIGluc3RydW1lbnRpbmcgYSAke2tleX0gYCArXG4gICAgICAgICdjb250YWluZXIgbmVlZHMgYHJ1bnRpbWVWZXJzaW9uYCBzZXQgdG8gdGhlIGFwcGxpY2F0aW9uXFwncyBtYWpvci5taW5vciAoZm9yIGV4YW1wbGUgXCI4LjNcIikuICcgK1xuICAgICAgICAnVGhlIHZhbHVlIHNlbGVjdHMgdGhlIGFnZW50IHBhdGggdGhlIGFjdGl2YXRpb24gZW52aXJvbm1lbnQgcG9pbnRzIGF0LCBzbyBhIHdyb25nIG9yIG1pc3NpbmcgJyArXG4gICAgICAgICdvbmUgbGVhdmVzIHRoZSBjb250YWluZXIgd2l0aCBhbiBhZ2VudCB0aGF0IGlzIG5ldmVyIGxvYWRlZC4nLFxuICAgICk7XG4gIH1cbiAgLy8gUmVqZWN0ZWQgaGVyZSByYXRoZXIgdGhhbiBwYXNzZWQgdGhyb3VnaDogYSBcIjguMy40XCIgb3IgXCJwaHA4LjNcIiB3b3VsZCBiZSByZW5kZXJlZCBzdHJhaWdodCBpbnRvXG4gIC8vIFBIUF9JTklfU0NBTl9ESVIgLyBSVUJZT1BUIGFzIGEgcGF0aCB0aGF0IGRvZXMgbm90IGV4aXN0IGluIHRoZSBidW5kbGUsIGFuZCB0aGUgY29udGFpbmVyIHdvdWxkIHN0YXJ0XG4gIC8vIHdpdGggbm8gYWdlbnQgbG9hZGVkIGFuZCBubyBlcnJvciBhbnl3aGVyZSBuZWFyIHRoZSBjYXVzZS5cbiAgaWYgKHJ1bnRpbWVWZXJzaW9uICYmICEvXlxcZCtcXC5cXGQrJC8udGVzdChydW50aW1lVmVyc2lvbikpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgT2RpZ29zOiBydW50aW1lVmVyc2lvbiBcIiR7cnVudGltZVZlcnNpb259XCIgbXVzdCBiZSBleGFjdGx5IG1ham9yLm1pbm9yIChmb3IgZXhhbXBsZSBcIjguM1wiKSDigJQgaXQgYCArXG4gICAgICAgICduYW1lcyBhIGRpcmVjdG9yeSBpbiB0aGUgYWdlbnRzIGJ1bmRsZSwgbm90IGEgZnVsbCB2ZXJzaW9uLicsXG4gICAgKTtcbiAgfVxuXG4gIGNvbnN0IHN0YXRpY0VudjogeyBba2V5OiBzdHJpbmddOiBzdHJpbmcgfSA9IHt9O1xuICBmb3IgKGNvbnN0IFtrLCB2XSBvZiBPYmplY3QuZW50cmllcyhzcGVjLnN0YXRpY0VudikpIHtcbiAgICBzdGF0aWNFbnZba10gPSBydW50aW1lVmVyc2lvbiA/IHYuc3BsaXQoUlVOVElNRV9WRVJTSU9OX1BMQUNFSE9MREVSKS5qb2luKHJ1bnRpbWVWZXJzaW9uKSA6IHY7XG4gIH1cblxuICByZXR1cm4ge1xuICAgIGxhbmd1YWdlOiBrZXksXG4gICAgYWdlbnREaXJzOiBzcGVjLmFnZW50RGlycyxcbiAgICBzdGF0aWNFbnYsXG4gICAgYXBwZW5kRW52OiBzcGVjLmFwcGVuZEVudi5tYXAoKGEpID0+ICh7IG5hbWU6IGEubmFtZSwgcGF0dGVybjogYS5wYXR0ZXJuIH0pKSxcbiAgICBvdGxwUHJvdG9jb2w6IHNwZWMub3RscFByb3RvY29sLFxuICAgIHByb3RvY29sRml4ZWQ6IHNwZWMucHJvdG9jb2xGaXhlZCxcbiAgfTtcbn1cblxuLyoqXG4gKiBUaGUgc3RhZ2luZyBjb250YWluZXIncyBjb21tYW5kOiBjcmVhdGUgdGhlIHNoYXJlZCBkaXJlY3RvcnksIHRoZW4gY29weSBlYWNoIGxhbmd1YWdlJ3MgYWdlbnRcbiAqIGRpcmVjdG9yeSBvdXQgb2YgdGhlIGltYWdlIGludG8gaXQsIHRoZW4gZXhpdCAwLlxuICpcbiAqIE9SREVSIEFORCBTUEVMTElORyBBUkUgVEhFIENPTlRSQUNULiBUaGVyZSBpcyBPTkUgc3RhZ2luZyBjb250YWluZXIgcGVyIHRhc2sgZGVmaW5pdGlvbiBob3dldmVyIG1hbnlcbiAqIGxhbmd1YWdlcyBpdCBob2xkcywgYW5kIHRoZSBjb25uZWN0b3IgY29tcGFyZXMgaXRzIGNvbW1hbmQgYWdhaW5zdCB0aGUgdW5pb24gb2YgZXZlcnkgaW5zdHJ1bWVudGVkXG4gKiBjb250YWluZXIncyBhZ2VudCBkaXJlY3RvcmllcyBpbiBhc2NlbmRpbmcgb3JkZXIuIEEgY29tbWFuZCB0aGF0IGRpZmZlcnMgYnkgYSBzcGFjZSBpcyByZXBvcnRlZCBhc1xuICogZHJpZnQgb24gdGhlIHdob2xlIHRhc2sgZGVmaW5pdGlvbiwgc28gdGhpcyBpcyBzb3J0ZWQgYW5kIHJlbmRlcmVkIGV4YWN0bHkgYXMgYHNpZGVjYXJDb21tYW5kYCBpbiB0aGVcbiAqIGNvbm5lY3RvciBkb2VzIGl0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gc2lkZWNhckNvbW1hbmQoYWdlbnREaXJzOiBzdHJpbmdbXSk6IHN0cmluZ1tdIHtcbiAgY29uc3QgZGlycyA9IFsuLi5uZXcgU2V0KGFnZW50RGlycyldLnNvcnQoKTtcbiAgbGV0IHNjcmlwdCA9IGBta2RpciAtcCAke0ZBUkdBVEVfQUdFTlRTX0RJUn1gO1xuICBmb3IgKGNvbnN0IGQgb2YgZGlycykge1xuICAgIHNjcmlwdCArPSBgICYmIGNwIC1yICR7RkFSR0FURV9JTUFHRV9BR0VOVFNfRElSfS8ke2R9ICR7RkFSR0FURV9BR0VOVFNfRElSfS8ke2R9YDtcbiAgfVxuICByZXR1cm4gWydzaCcsICctYycsIHNjcmlwdF07XG59XG5cbi8qKlxuICogVGhlIHJlc291cmNlIGF0dHJpYnV0ZXMgZXZlcnkgc3BhbiwgbWV0cmljIGFuZCBsb2cgZnJvbSBhbiBpbnN0cnVtZW50ZWQgdGFzayBjYXJyaWVzLiBgc2VydmljZS5uYW1lYCBpc1xuICogc2V0IHNlcGFyYXRlbHksIHRocm91Z2ggYE9URUxfU0VSVklDRV9OQU1FYC5cbiAqXG4gKiBUaGUgcmVnaW9uIG1heSBiZSBhbiB1bnJlc29sdmVkIHRva2VuIG9uIGFuIGVudmlyb25tZW50LWFnbm9zdGljIHN0YWNrLCB3aGljaCBpcyBmaW5lIGhlcmUgYW5kIHVubGlrZVxuICogdGhlIExhbWJkYSBsYXllciBsb29rdXA6IHRoaXMgdmFsdWUgaXMgYW4gZW52aXJvbm1lbnQgdmFyaWFibGUsIHNvIHRoZSB0b2tlbiByZXNvbHZlcyB0byBhIGBSZWZgIHRvXG4gKiBgQVdTOjpSZWdpb25gIGFuZCBDbG91ZEZvcm1hdGlvbiBmaWxscyBpbiB0aGUgcmVhbCByZWdpb24gYXQgZGVwbG95IHRpbWUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBmYXJnYXRlUmVzb3VyY2VBdHRyaWJ1dGVzKHJlZ2lvbjogc3RyaW5nKTogc3RyaW5nIHtcbiAgY29uc3QgYXR0cnMgPSAnY2xvdWQucHJvdmlkZXI9YXdzLGNsb3VkLnBsYXRmb3JtPWF3c19lY3MnO1xuICByZXR1cm4gcmVnaW9uID8gYCR7YXR0cnN9LGNsb3VkLnJlZ2lvbj0ke3JlZ2lvbn1gIDogYXR0cnM7XG59XG5cbi8qKiBXaGVyZSBhbiBpbnN0cnVtZW50ZWQgY29udGFpbmVyIGV4cG9ydHMgdG8sIGFuZCBvdmVyIHdoaWNoIHRyYW5zcG9ydC4gQWx3YXlzIGRlY2lkZWQgYXMgYSBwYWlyLiAqL1xuZXhwb3J0IGludGVyZmFjZSBGYXJnYXRlRXhwb3J0VGFyZ2V0IHtcbiAgLyoqIGBzY2hlbWU6Ly9ob3N0OnBvcnRgLiAqL1xuICByZWFkb25seSBlbmRwb2ludDogc3RyaW5nO1xuICAvKiogYGdycGNgIG9yIGBodHRwL3Byb3RvYnVmYC4gKi9cbiAgcmVhZG9ubHkgcHJvdG9jb2w6IHN0cmluZztcbn1cblxuLyoqXG4gKiBUdXJucyBhIGRlc3RpbmF0aW9uJ3MgT1RMUC9nUlBDIHRhcmdldCAoYGhvc3Q6cG9ydGApIGludG8gdGhlIFVSTCB0aGUgT3BlblRlbGVtZXRyeSBTREsgZW52IGFjY2VwdHMsXG4gKiB0YWtpbmcgdGhlIHNjaGVtZSBmcm9tIHRoZSBkZXN0aW5hdGlvbidzIFRMUyBzZXR0aW5nLiBBIHZhbHVlIHRoYXQgaXMgYWxyZWFkeSBhIFVSTCBwYXNzZXMgdGhyb3VnaC5cbiAqL1xuZnVuY3Rpb24gZ3JwY0VuZHBvaW50VVJMKGVuZHBvaW50OiBzdHJpbmcsIGluc2VjdXJlPzogYm9vbGVhbik6IHN0cmluZyB7XG4gIGlmIChlbmRwb2ludCA9PT0gJycpIHtcbiAgICByZXR1cm4gJyc7XG4gIH1cbiAgaWYgKGVuZHBvaW50LmluY2x1ZGVzKCc6Ly8nKSkge1xuICAgIHJldHVybiBlbmRwb2ludDtcbiAgfVxuICByZXR1cm4gYCR7aW5zZWN1cmUgPT09IHRydWUgPyAnaHR0cCcgOiAnaHR0cHMnfTovLyR7ZW5kcG9pbnR9YDtcbn1cblxuLyoqXG4gKiBSZXNvbHZlcyB0aGUgZW5kcG9pbnQgYW5kIHByb3RvY29sIGZvciBvbmUgcmVjaXBlIGFnYWluc3Qgb25lIGRlc3RpbmF0aW9uLlxuICpcbiAqIFBvcnQgb2YgYGV4cG9ydFRhcmdldEZvcmAuIEEgRmFyZ2F0ZSB0YXNrIGhhcyBubyBub2RlLWxvY2FsIGNvbGxlY3RvciDigJQgdW5saWtlIExhbWJkYSwgd2hlcmUgdGhlIFNES1xuICogZXhwb3J0cyB0byBhIGNvbGxlY3RvciBpbiB0aGUgc2FtZSBzYW5kYm94IOKAlCBzbyB0aGUgYWdlbnQgZXhwb3J0cyBPVExQIHN0cmFpZ2h0IHRvIHRoZSBkZXN0aW5hdGlvbiwgYW5kXG4gKiB3aGljaCB0cmFuc3BvcnQgaXQgY2FuIHVzZSBpcyBhIHByb3BlcnR5IG9mIHRoZSBhZ2VudCBhcyBtdWNoIGFzIG9mIHRoZSBkZXN0aW5hdGlvbi5cbiAqXG4gKiBFYWNoIGFnZW50J3MgUFJFRkVSUkVEIHRyYW5zcG9ydCBpcyB0YWtlbiB3aGVuIHRoZSBkZXN0aW5hdGlvbiBvZmZlcnMgaXQuIFdoZW4gaXQgZG9lcyBub3QsIGFuIGFnZW50XG4gKiB3aG9zZSBjb21tdW5pdHkgU0RLIHBpbnMgdGhlIHByb3RvY29sIChwaHAsIHJ1YnkpIGNhbm5vdCBiZSBzd2l0Y2hlZCBhbmQgdGhyb3dzOyBldmVyeSBvdGhlciBhZ2VudFxuICogaG9ub3VycyBgT1RFTF9FWFBPUlRFUl9PVExQX1BST1RPQ09MYCBhbmQgZmFsbHMgYmFjayB0byB3aGljaGV2ZXIgdHJhbnNwb3J0IGV4aXN0cy5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGV4cG9ydFRhcmdldEZvcihyZWNpcGU6IEZhcmdhdGVSZWNpcGUsIGRlc3Q6IE9kaWdvc0Rlc3RpbmF0aW9uKTogRmFyZ2F0ZUV4cG9ydFRhcmdldCB7XG4gIGNvbnN0IGdycGNVUkwgPSBncnBjRW5kcG9pbnRVUkwoZGVzdC5lbmRwb2ludCA/PyAnJywgZGVzdC5pbnNlY3VyZSk7XG4gIGNvbnN0IGh0dHBVUkwgPSBkZXN0Lmh0dHBFbmRwb2ludCA/PyAnJztcblxuICBpZiAocmVjaXBlLm90bHBQcm90b2NvbCA9PT0gT1RMUF9QUk9UT0NPTF9IVFRQICYmIGh0dHBVUkwgIT09ICcnKSB7XG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IGh0dHBVUkwsIHByb3RvY29sOiBPVExQX1BST1RPQ09MX0hUVFAgfTtcbiAgfVxuICBpZiAocmVjaXBlLm90bHBQcm90b2NvbCA9PT0gT1RMUF9QUk9UT0NPTF9HUlBDICYmIGdycGNVUkwgIT09ICcnKSB7XG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IGdycGNVUkwsIHByb3RvY29sOiBPVExQX1BST1RPQ09MX0dSUEMgfTtcbiAgfVxuICBpZiAocmVjaXBlLnByb3RvY29sRml4ZWQpIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICBgT2RpZ29zOiB0aGUgJHtyZWNpcGUubGFuZ3VhZ2V9IGFnZW50IGV4cG9ydHMgT1RMUCBvdmVyICR7cmVjaXBlLm90bHBQcm90b2NvbH0gb25seSDigJQgaXRzIGNvbW11bml0eSBgICtcbiAgICAgICAgJ1NESyBjYW5ub3QgYmUgc3dpdGNoZWQgdG8gdGhlIG90aGVyIHRyYW5zcG9ydCDigJQgYnV0IHRoZSBkZXN0aW5hdGlvbiBjb25maWd1cmVzIG5vICcgK1xuICAgICAgICBgJHtyZWNpcGUub3RscFByb3RvY29sID09PSBPVExQX1BST1RPQ09MX0hUVFAgPyAnYGh0dHBFbmRwb2ludGAnIDogJ2BlbmRwb2ludGAnfS4gYCArXG4gICAgICAgICdDb25maWd1cmUgb25lLCBvciBsZWF2ZSB0aGlzIGNvbnRhaW5lciB1bmluc3RydW1lbnRlZC4nLFxuICAgICk7XG4gIH1cbiAgaWYgKGh0dHBVUkwgIT09ICcnKSB7XG4gICAgcmV0dXJuIHsgZW5kcG9pbnQ6IGh0dHBVUkwsIHByb3RvY29sOiBPVExQX1BST1RPQ09MX0hUVFAgfTtcbiAgfVxuICBpZiAoZ3JwY1VSTCAhPT0gJycpIHtcbiAgICByZXR1cm4geyBlbmRwb2ludDogZ3JwY1VSTCwgcHJvdG9jb2w6IE9UTFBfUFJPVE9DT0xfR1JQQyB9O1xuICB9XG4gIHRocm93IG5ldyBFcnJvcihcbiAgICAnT2RpZ29zIGRlc3RpbmF0aW9uIGhhcyBuZWl0aGVyIGBlbmRwb2ludGAgKE9UTFAvZ1JQQykgbm9yIGBodHRwRW5kcG9pbnRgIChPVExQL0hUVFApLCBzbyBhbiAnICtcbiAgICAgICdpbnN0cnVtZW50ZWQgRmFyZ2F0ZSB0YXNrIHdvdWxkIGhhdmUgbm93aGVyZSB0byBleHBvcnQuIFVubGlrZSBMYW1iZGEsIGEgRmFyZ2F0ZSB0YXNrIGhhcyBubyAnICtcbiAgICAgICdpbi1zYW5kYm94IGNvbGxlY3RvciB0byBidWZmZXIgZm9yIGl0IOKAlCB0aGUgYWdlbnQgZXhwb3J0cyB0byB0aGlzIGRlc3RpbmF0aW9uIGRpcmVjdGx5LicsXG4gICk7XG59XG5cbi8qKiBUaGUgcG9ydCBvZiBhIGBzY2hlbWU6Ly9ob3N0OnBvcnRbL3BhdGhdYCBlbmRwb2ludCwgb3IgXCJcIiB3aGVuIGl0IG5hbWVzIG5vbmUuICovXG5mdW5jdGlvbiBlbmRwb2ludFBvcnQoZW5kcG9pbnQ6IHN0cmluZyk6IHN0cmluZyB7XG4gIGxldCByZXN0ID0gZW5kcG9pbnQ7XG4gIGNvbnN0IHNjaGVtZSA9IHJlc3QuaW5kZXhPZignOi8vJyk7XG4gIGlmIChzY2hlbWUgPj0gMCkge1xuICAgIHJlc3QgPSByZXN0LnNsaWNlKHNjaGVtZSArIDMpO1xuICB9XG4gIGNvbnN0IHBhdGhTdGFydCA9IHJlc3Quc2VhcmNoKC9bLz9dLyk7XG4gIGlmIChwYXRoU3RhcnQgPj0gMCkge1xuICAgIHJlc3QgPSByZXN0LnNsaWNlKDAsIHBhdGhTdGFydCk7XG4gIH1cbiAgY29uc3QgY29sb24gPSByZXN0Lmxhc3RJbmRleE9mKCc6Jyk7XG4gIHJldHVybiBjb2xvbiA8IDAgPyAnJyA6IHJlc3Quc2xpY2UoY29sb24gKyAxKTtcbn1cblxuLyoqXG4gKiBBIHdhcm5pbmcgd2hlbiBhbiBlbmRwb2ludCdzIHBvcnQgaXMgdGhlIHdlbGwta25vd24gcG9ydCBvZiB0aGUgT1RIRVIgT1RMUCBwcm90b2NvbCwgb3IgXCJcIiB3aGVuIHRoZVxuICogcGFpcmluZyBsb29rcyByaWdodCDigJQgaW5jbHVkaW5nIGEgbm9uLXN0YW5kYXJkIHBvcnQsIHdoaWNoIGNhbm5vdCBiZSBqdWRnZWQuXG4gKlxuICogV29ydGggYSBzeW50aC10aW1lIHdhcm5pbmcgYmVjYXVzZSB0aGUgcnVudGltZSBzeW1wdG9tIGlzIGEgY29ubmVjdGlvbiByZXNldCBtaWQtZXhwb3J0LCB3aGljaCBwb2ludHNcbiAqIG5vd2hlcmUgbmVhciB0aGUgY2F1c2U6IGFuIEhUVFAgcmVxdWVzdCBhZ2FpbnN0IGFuIEhUVFAvMiBnUlBDIGxpc3RlbmVyLCBvciB0aGUgcmV2ZXJzZS5cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIHBvcnRQcm90b2NvbE1pc21hdGNoKGVuZHBvaW50OiBzdHJpbmcsIHByb3RvY29sOiBzdHJpbmcpOiBzdHJpbmcge1xuICBjb25zdCBwb3J0ID0gZW5kcG9pbnRQb3J0KGVuZHBvaW50KTtcbiAgaWYgKHByb3RvY29sID09PSBPVExQX1BST1RPQ09MX0hUVFAgJiYgcG9ydCA9PT0gT1RMUF9ERUZBVUxUX0dSUENfUE9SVCkge1xuICAgIHJldHVybiAoXG4gICAgICBgZW5kcG9pbnQgJHtlbmRwb2ludH0gaXMgb24gJHtPVExQX0RFRkFVTFRfR1JQQ19QT1JUfSwgdGhlIHN0YW5kYXJkIE9UTFAvZ1JQQyBwb3J0LCBidXQgdGhpcyBhZ2VudCBgICtcbiAgICAgIGBleHBvcnRzIG92ZXIgJHtPVExQX1BST1RPQ09MX0hUVFB9OyBleHBvcnRzIHdpbGwgZmFpbCBhcyBhIGNvbm5lY3Rpb24gcmVzZXQuIFBvaW50IGl0IGF0IHRoZSBgICtcbiAgICAgIGBPVExQL0hUVFAgcG9ydCAoJHtPVExQX0RFRkFVTFRfSFRUUF9QT1JUfSkuYFxuICAgICk7XG4gIH1cbiAgaWYgKHByb3RvY29sID09PSBPVExQX1BST1RPQ09MX0dSUEMgJiYgcG9ydCA9PT0gT1RMUF9ERUZBVUxUX0hUVFBfUE9SVCkge1xuICAgIHJldHVybiAoXG4gICAgICBgZW5kcG9pbnQgJHtlbmRwb2ludH0gaXMgb24gJHtPVExQX0RFRkFVTFRfSFRUUF9QT1JUfSwgdGhlIHN0YW5kYXJkIE9UTFAvSFRUUCBwb3J0LCBidXQgdGhpcyBhZ2VudCBgICtcbiAgICAgIGBleHBvcnRzIG92ZXIgZ1JQQzsgZXhwb3J0cyB3aWxsIGZhaWwuIFBvaW50IGl0IGF0IHRoZSBPVExQL2dSUEMgcG9ydCAoJHtPVExQX0RFRkFVTFRfR1JQQ19QT1JUfSkuYFxuICAgICk7XG4gIH1cbiAgcmV0dXJuICcnO1xufVxuIl19
|