@intentius/chant-lexicon-otel 0.124.45 → 0.124.48
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/README.md +2 -2
- package/dist/build/capability-plugin.d.ts +10 -0
- package/dist/build/capability-plugin.d.ts.map +1 -0
- package/dist/build/capability-plugin.js +19 -0
- package/dist/build/ocb-build.d.ts +38 -0
- package/dist/build/ocb-build.d.ts.map +1 -0
- package/dist/build/ocb-build.js +49 -0
- package/dist/clickhouse-tables.d.ts +73 -0
- package/dist/clickhouse-tables.d.ts.map +1 -0
- package/dist/clickhouse-tables.js +199 -0
- package/dist/codegen/docs.d.ts.map +1 -1
- package/dist/codegen/docs.js +4 -2
- package/dist/codegen/generate.d.ts +29 -10
- package/dist/codegen/generate.d.ts.map +1 -1
- package/dist/codegen/generate.js +82 -17
- package/dist/components/common.d.ts +3 -1
- package/dist/components/common.d.ts.map +1 -1
- package/dist/components/common.js +2 -0
- package/dist/components/connectors.d.ts +25 -90
- package/dist/components/connectors.d.ts.map +1 -1
- package/dist/components/connectors.js +3 -0
- package/dist/components/exporters.d.ts +78 -34
- package/dist/components/exporters.d.ts.map +1 -1
- package/dist/components/exporters.js +26 -1
- package/dist/components/extensions.d.ts +16 -33
- package/dist/components/extensions.d.ts.map +1 -1
- package/dist/components/filtering.d.ts +13 -60
- package/dist/components/filtering.d.ts.map +1 -1
- package/dist/components/k8s-receivers.d.ts +5 -53
- package/dist/components/k8s-receivers.d.ts.map +1 -1
- package/dist/components/overlay.d.ts +25 -0
- package/dist/components/overlay.d.ts.map +1 -0
- package/dist/components/overlay.js +19 -0
- package/dist/components/processors.d.ts +18 -79
- package/dist/components/processors.d.ts.map +1 -1
- package/dist/components/receivers.d.ts +17 -71
- package/dist/components/receivers.d.ts.map +1 -1
- package/dist/components/receivers.js +3 -1
- package/dist/components/sampling.d.ts +19 -35
- package/dist/components/sampling.d.ts.map +1 -1
- package/dist/config-hygiene.d.ts +10 -8
- package/dist/config-hygiene.d.ts.map +1 -1
- package/dist/config-hygiene.js +36 -13
- package/dist/coverage.d.ts +50 -0
- package/dist/coverage.d.ts.map +1 -0
- package/dist/coverage.js +92 -0
- package/dist/define.d.ts +8 -5
- package/dist/define.d.ts.map +1 -1
- package/dist/define.js +10 -6
- package/dist/distribution.d.ts +125 -0
- package/dist/distribution.d.ts.map +1 -0
- package/dist/distribution.js +227 -0
- package/dist/generated/index.d.ts +4234 -0
- package/dist/generated/index.d.ts.map +1 -0
- package/dist/generated/index.js +23 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +9 -1
- package/dist/init-templates.js +2 -2
- package/dist/integrity.json +11 -9
- package/dist/lint/audit-catalog.d.ts +3 -0
- package/dist/lint/audit-catalog.d.ts.map +1 -1
- package/dist/lint/audit-catalog.js +5 -0
- package/dist/lint/post-synth/index.d.ts.map +1 -1
- package/dist/lint/post-synth/index.js +4 -0
- package/dist/lint/post-synth/otel-helpers.d.ts +2 -1
- package/dist/lint/post-synth/otel-helpers.d.ts.map +1 -1
- package/dist/lint/post-synth/otel-helpers.js +1 -1
- package/dist/lint/post-synth/otel119.d.ts +1 -1
- package/dist/lint/post-synth/otel119.js +1 -1
- package/dist/lint/post-synth/otel125.d.ts +1 -1
- package/dist/lint/post-synth/otel125.js +1 -1
- package/dist/lint/post-synth/otel126.d.ts +1 -1
- package/dist/lint/post-synth/otel126.js +1 -1
- package/dist/lint/post-synth/otel127.d.ts +1 -1
- package/dist/lint/post-synth/otel127.js +1 -1
- package/dist/lint/post-synth/otel128.d.ts +8 -0
- package/dist/lint/post-synth/otel128.d.ts.map +1 -0
- package/dist/lint/post-synth/otel128.js +17 -0
- package/dist/lint/post-synth/otel129.d.ts +15 -0
- package/dist/lint/post-synth/otel129.d.ts.map +1 -0
- package/dist/lint/post-synth/otel129.js +92 -0
- package/dist/lsp/completions.d.ts +10 -1
- package/dist/lsp/completions.d.ts.map +1 -1
- package/dist/lsp/completions.js +29 -1
- package/dist/lsp/hover.d.ts +5 -1
- package/dist/lsp/hover.d.ts.map +1 -1
- package/dist/lsp/hover.js +16 -3
- package/dist/lsp/properties.d.ts +34 -0
- package/dist/lsp/properties.d.ts.map +1 -0
- package/dist/lsp/properties.js +135 -0
- package/dist/manifest.json +1 -1
- package/dist/meta.json +5 -0
- package/dist/metric-names.d.ts +36 -8
- package/dist/metric-names.d.ts.map +1 -1
- package/dist/metric-names.js +42 -9
- package/dist/model.d.ts +1 -1
- package/dist/model.js +1 -1
- package/dist/okf/index.md +3 -0
- package/dist/okf/rules/OTEL128.md +11 -0
- package/dist/okf/rules/OTEL129.md +11 -0
- package/dist/okf/types/ClickHouseExporter.md +9 -0
- package/dist/op/activities/collector-audit.d.ts +18 -6
- package/dist/op/activities/collector-audit.d.ts.map +1 -1
- package/dist/op/activities/collector-audit.js +37 -21
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +51 -8
- package/dist/rules/otel-helpers.ts +1 -1
- package/dist/rules/otel119.ts +1 -1
- package/dist/rules/otel125.ts +1 -1
- package/dist/rules/otel126.ts +1 -1
- package/dist/rules/otel127.ts +1 -1
- package/dist/rules/otel128.ts +22 -0
- package/dist/rules/otel129.ts +99 -0
- package/dist/serializer.d.ts +3 -0
- package/dist/serializer.d.ts.map +1 -1
- package/dist/serializer.js +12 -2
- package/dist/spec/builtins.d.ts +17 -0
- package/dist/spec/builtins.d.ts.map +1 -0
- package/dist/spec/builtins.js +45 -0
- package/dist/spec/fetch.d.ts +120 -0
- package/dist/spec/fetch.d.ts.map +1 -0
- package/dist/spec/fetch.js +282 -0
- package/dist/spec/parse.d.ts +77 -0
- package/dist/spec/parse.d.ts.map +1 -0
- package/dist/spec/parse.js +337 -0
- package/dist/spec/pin.d.ts +82 -0
- package/dist/spec/pin.d.ts.map +1 -0
- package/dist/spec/pin.js +126 -0
- package/dist/spec/snapshot-cli.d.ts +13 -0
- package/dist/spec/snapshot-cli.d.ts.map +1 -0
- package/dist/spec/snapshot-cli.js +31 -0
- package/dist/spec/snapshot.d.ts +9 -0
- package/dist/spec/snapshot.d.ts.map +1 -0
- package/dist/spec/snapshot.js +14 -0
- package/dist/types/index.d.ts +3 -2
- package/dist/validate-config.d.ts +1 -1
- package/dist/validate-config.d.ts.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +1 -0
- package/dist/workspace-links.d.ts +25 -0
- package/dist/workspace-links.d.ts.map +1 -0
- package/dist/workspace-links.js +49 -0
- package/package.json +3 -2
- package/src/build/capability-plugin.ts +30 -0
- package/src/build/ocb-build.ts +73 -0
- package/src/clickhouse-exporter.e2e.test.ts +208 -0
- package/src/clickhouse-tables.test.ts +216 -0
- package/src/clickhouse-tables.ts +223 -0
- package/src/codegen/docs.ts +4 -2
- package/src/codegen/generate.ts +95 -19
- package/src/components/common.ts +4 -2
- package/src/components/connectors.ts +30 -77
- package/src/components/exporters.ts +128 -61
- package/src/components/extensions.ts +22 -36
- package/src/components/filtering.ts +17 -58
- package/src/components/k8s-receivers.test.ts +1 -1
- package/src/components/k8s-receivers.ts +3 -45
- package/src/components/overlay.ts +24 -0
- package/src/components/processors.ts +18 -63
- package/src/components/receivers.ts +20 -60
- package/src/components/sampling.ts +24 -37
- package/src/config-hygiene.ts +36 -13
- package/src/coverage.test.ts +37 -0
- package/src/coverage.ts +121 -0
- package/src/define.test.ts +2 -0
- package/src/define.ts +10 -6
- package/src/distribution.test.ts +250 -0
- package/src/distribution.ts +305 -0
- package/src/generated/index.ts +4576 -0
- package/src/generated/lexicon-otel.json +5 -0
- package/src/generated/properties.json +1 -0
- package/src/import/roundtrip.test.ts +16 -0
- package/src/import/testdata/clickhouse.yaml +37 -0
- package/src/import/testdata/fixtures.ts +17 -2
- package/src/import/testdata/upstream/clickhouse-exporter.yaml +60 -0
- package/src/index.ts +69 -0
- package/src/init-templates.ts +2 -2
- package/src/lint/audit-catalog.ts +15 -0
- package/src/lint/post-synth/index.ts +4 -0
- package/src/lint/post-synth/otel-helpers.ts +1 -1
- package/src/lint/post-synth/otel119.ts +1 -1
- package/src/lint/post-synth/otel125.ts +1 -1
- package/src/lint/post-synth/otel126.ts +1 -1
- package/src/lint/post-synth/otel127.ts +1 -1
- package/src/lint/post-synth/otel128.ts +22 -0
- package/src/lint/post-synth/otel129.ts +99 -0
- package/src/lsp/completions.ts +30 -1
- package/src/lsp/hover.ts +14 -3
- package/src/lsp/properties.test.ts +72 -0
- package/src/lsp/properties.ts +141 -0
- package/src/metric-names.test.ts +11 -0
- package/src/metric-names.ts +64 -16
- package/src/model.ts +1 -1
- package/src/op/activities/collector-audit.test.ts +29 -22
- package/src/op/activities/collector-audit.ts +38 -18
- package/src/plugin.test.ts +2 -0
- package/src/plugin.ts +57 -6
- package/src/serializer.ts +11 -2
- package/src/spec/builtins.ts +56 -0
- package/src/spec/fetch.ts +332 -0
- package/src/spec/otel-schemas.snapshot.json +13932 -0
- package/src/spec/parse.ts +364 -0
- package/src/spec/pin.ts +155 -0
- package/src/spec/snapshot-cli.ts +32 -0
- package/src/spec/snapshot.ts +19 -0
- package/src/spec/spec.test.ts +144 -0
- package/src/validate-config.ts +4 -3
- package/src/validate.ts +1 -0
- package/src/workspace-links.e2e.test.ts +91 -0
- package/src/workspace-links.ts +50 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { load } from "js-yaml";
|
|
6
|
+
import { makePostSynthCtx } from "@intentius/chant-test-utils";
|
|
7
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
8
|
+
import type { ProcessRunner } from "@intentius/chant/components/verbs/process-runner";
|
|
9
|
+
import { BUILTIN_CATALOG } from "./catalog";
|
|
10
|
+
import { COLLECTOR_PIN, defineComponent } from "./define";
|
|
11
|
+
import { otelSerializer } from "./serializer";
|
|
12
|
+
import { otel128 } from "./lint/post-synth/otel128";
|
|
13
|
+
import {
|
|
14
|
+
BUILTIN_MODULES,
|
|
15
|
+
DISTRIBUTION_MANIFEST_FILE,
|
|
16
|
+
Distribution,
|
|
17
|
+
distributionManifest,
|
|
18
|
+
distributionYaml,
|
|
19
|
+
moduleComponentType,
|
|
20
|
+
} from "./distribution";
|
|
21
|
+
import { createOcbBuildCapability, ocbOutputPath } from "./build/ocb-build";
|
|
22
|
+
import { otelCapabilityPlugin, ocbBuild } from "./build/capability-plugin";
|
|
23
|
+
import {
|
|
24
|
+
BatchProcessor,
|
|
25
|
+
DebugExporter,
|
|
26
|
+
OtlpExporter,
|
|
27
|
+
OtlpReceiver,
|
|
28
|
+
Pipeline,
|
|
29
|
+
SpanMetricsConnector,
|
|
30
|
+
TransformProcessor,
|
|
31
|
+
otelPlugin,
|
|
32
|
+
} from "./index";
|
|
33
|
+
|
|
34
|
+
const V = COLLECTOR_PIN.version;
|
|
35
|
+
const CORE = "go.opentelemetry.io/collector";
|
|
36
|
+
const CONTRIB = "github.com/open-telemetry/opentelemetry-collector-contrib";
|
|
37
|
+
|
|
38
|
+
const AcmeExporter = defineComponent<{ endpoint: string }>()({
|
|
39
|
+
kind: "exporter",
|
|
40
|
+
type: "acme",
|
|
41
|
+
pin: { source: "github.com/acme/otel/exporter/acmeexporter", version: "v1.4.2" },
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* The keys of the builder's `Config`, `Distribution` and `Module` structs at
|
|
46
|
+
* v0.130.0 (`cmd/builder/internal/builder/config.go`). ocb reads the manifest
|
|
47
|
+
* strictly, so a key outside these fails the build.
|
|
48
|
+
*/
|
|
49
|
+
const OCB_CONFIG_KEYS = ["dist", "receivers", "processors", "exporters", "connectors", "extensions", "providers", "converters", "replaces", "excludes", "conf_resolver"];
|
|
50
|
+
const OCB_DIST_KEYS = ["module", "name", "go", "description", "output_path", "version", "build_tags", "debug_compilation", "cgo_enabled", "use_absolute_replace_paths"];
|
|
51
|
+
const OCB_MODULE_KEYS = ["gomod", "import", "name", "path"];
|
|
52
|
+
|
|
53
|
+
function entities(record: Record<string, unknown>): Map<string, Declarable> {
|
|
54
|
+
return new Map(Object.entries(record) as Array<[string, Declarable]>);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function distribution(extra: Partial<ConstructorParameters<typeof Distribution>[0]> = {}) {
|
|
58
|
+
return new Distribution({
|
|
59
|
+
dist: { name: "otelcol-acme", description: "Acme collector", output_path: "./_build", version: "1.0.0" },
|
|
60
|
+
receivers: [OtlpReceiver],
|
|
61
|
+
processors: [BatchProcessor, TransformProcessor],
|
|
62
|
+
exporters: [OtlpExporter, DebugExporter, AcmeExporter],
|
|
63
|
+
...extra,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
describe("Distribution manifest (#3374)", () => {
|
|
68
|
+
test("every built-in has a Go module, and the table names nothing else", () => {
|
|
69
|
+
const builtins = BUILTIN_CATALOG.filter((e) => e.type).map((e) => `${e.kind}:${e.type}`).sort();
|
|
70
|
+
expect(Object.keys(BUILTIN_MODULES).sort()).toEqual(builtins);
|
|
71
|
+
for (const path of Object.values(BUILTIN_MODULES)) expect(path.startsWith(CORE) || path.startsWith(CONTRIB)).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("built-ins write their module at COLLECTOR_PIN, a custom component its own pin", () => {
|
|
75
|
+
const m = distributionManifest(distribution({ providers: [{ gomod: `${CORE}/confmap/provider/envprovider v1.36.0` }] }));
|
|
76
|
+
expect(m).toEqual({
|
|
77
|
+
dist: { name: "otelcol-acme", description: "Acme collector", output_path: "./_build", version: "1.0.0" },
|
|
78
|
+
receivers: [{ gomod: `${CORE}/receiver/otlpreceiver ${V}` }],
|
|
79
|
+
processors: [{ gomod: `${CORE}/processor/batchprocessor ${V}` }, { gomod: `${CONTRIB}/processor/transformprocessor ${V}` }],
|
|
80
|
+
exporters: [
|
|
81
|
+
{ gomod: `${CORE}/exporter/otlpexporter ${V}` },
|
|
82
|
+
{ gomod: `${CORE}/exporter/debugexporter ${V}` },
|
|
83
|
+
{ gomod: "github.com/acme/otel/exporter/acmeexporter v1.4.2" },
|
|
84
|
+
],
|
|
85
|
+
providers: [{ gomod: `${CORE}/confmap/provider/envprovider v1.36.0` }],
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("the YAML holds only keys ocb's config knows, and reads back to the manifest", () => {
|
|
90
|
+
const d = distribution({
|
|
91
|
+
dist: { module: "github.com/acme/otelcol", name: "otelcol-acme", go: "/usr/local/go/bin/go", output_path: "./_build", build_tags: "netgo", debug_compilation: false, cgo_enabled: false, use_absolute_replace_paths: true },
|
|
92
|
+
connectors: [SpanMetricsConnector],
|
|
93
|
+
extensions: [{ gomod: "github.com/acme/otel/extension/acmeauthextension v1.4.2", import: "github.com/acme/otel/extension/acmeauthextension/v2", name: "acmeauth", path: "./acmeauth" }],
|
|
94
|
+
converters: [{ gomod: "github.com/acme/otel/converter v0.1.0" }],
|
|
95
|
+
replaces: ["github.com/acme/otel => ../otel"],
|
|
96
|
+
excludes: ["github.com/bad/mod v1.0.0"],
|
|
97
|
+
conf_resolver: { default_uri_scheme: "env" },
|
|
98
|
+
});
|
|
99
|
+
const doc = load(distributionYaml(d)) as Record<string, unknown>;
|
|
100
|
+
expect(doc).toEqual(distributionManifest(d));
|
|
101
|
+
for (const k of Object.keys(doc)) expect(OCB_CONFIG_KEYS).toContain(k);
|
|
102
|
+
for (const k of Object.keys(doc.dist as object)) expect(OCB_DIST_KEYS).toContain(k);
|
|
103
|
+
for (const list of ["receivers", "processors", "exporters", "connectors", "extensions", "providers", "converters"]) {
|
|
104
|
+
for (const mod of (doc[list] as Array<Record<string, unknown>> | undefined) ?? []) {
|
|
105
|
+
for (const k of Object.keys(mod)) expect(OCB_MODULE_KEYS).toContain(k);
|
|
106
|
+
expect(mod.gomod).toMatch(/^\S+ v\d+\.\d+\.\d+\S*$/);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("a component listed under the wrong kind is refused", () => {
|
|
112
|
+
const d = new Distribution({ dist: {}, receivers: [OtlpExporter as never] });
|
|
113
|
+
expect(() => distributionManifest(d)).toThrow(/otlp is a exporter, listed under the distribution's receivers/);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("a module's component type is read from a built-in's path or a contrib-style name", () => {
|
|
117
|
+
expect(moduleComponentType("receiver", `${CORE}/receiver/otlpreceiver ${V}`)).toBe("otlp");
|
|
118
|
+
expect(moduleComponentType("extension", `${CONTRIB}/extension/k8sleaderelector ${V}`)).toBe("k8s_leader_elector");
|
|
119
|
+
expect(moduleComponentType("exporter", `${CONTRIB}/exporter/datadogexporter ${V}`)).toBe("datadog");
|
|
120
|
+
expect(moduleComponentType("exporter", "github.com/acme/otel/vendor-out v1.0.0")).toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
describe("otel serializer with a Distribution (#3374)", () => {
|
|
125
|
+
const config = () => {
|
|
126
|
+
const otlp = new OtlpReceiver({ protocols: { grpc: { endpoint: "0.0.0.0:4317" } } });
|
|
127
|
+
const debug = new DebugExporter({});
|
|
128
|
+
return { otlp, debug, traces: new Pipeline({ signal: "traces", receivers: [otlp], exporters: [debug] }) };
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
test("writes the builder manifest beside the config", () => {
|
|
132
|
+
const out = otelSerializer.serialize(entities({ ...config(), dist: distribution() }));
|
|
133
|
+
expect(typeof out).toBe("object");
|
|
134
|
+
const files = (out as { files?: Record<string, string> }).files!;
|
|
135
|
+
expect(Object.keys(files)).toEqual([DISTRIBUTION_MANIFEST_FILE]);
|
|
136
|
+
expect(load(files[DISTRIBUTION_MANIFEST_FILE])).toEqual(distributionManifest(distribution()));
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test("a project without a Distribution serializes as before", () => {
|
|
140
|
+
const out = otelSerializer.serialize(entities(config()));
|
|
141
|
+
expect(typeof out).toBe("string");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("the Distribution is not part of the collector config", () => {
|
|
145
|
+
const withDist = otelSerializer.serialize(entities({ ...config(), dist: distribution() }));
|
|
146
|
+
expect((withDist as { primary: string }).primary).toBe(otelSerializer.serialize(entities(config())));
|
|
147
|
+
});
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
describe("OTEL128: the config uses only what the Distribution builds (#3374)", () => {
|
|
151
|
+
function run(extra: Record<string, unknown>, dist?: unknown) {
|
|
152
|
+
const otlp = new OtlpReceiver({ protocols: { grpc: { endpoint: "0.0.0.0:4317" } } });
|
|
153
|
+
const all = { otlp, ...extra };
|
|
154
|
+
const yaml = otelSerializer.serialize(entities(all));
|
|
155
|
+
const primary = typeof yaml === "string" ? yaml : yaml.primary;
|
|
156
|
+
return otel128.check(makePostSynthCtx("otel", primary, entities(dist ? { ...all, dist } : all)));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
test("reports a component missing from the Distribution", () => {
|
|
160
|
+
const diags = run({ otlpOut: new OtlpExporter({ endpoint: "tempo:4317" }), spans: new SpanMetricsConnector({}) }, distribution());
|
|
161
|
+
expect(diags.map((d) => [d.checkId, d.severity, d.entity])).toEqual([["OTEL128", "error", "spanmetrics"]]);
|
|
162
|
+
expect(diags[0].message).toContain('connector "spanmetrics" is not in the Distribution');
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("a config the Distribution covers has no finding", () => {
|
|
166
|
+
expect(run({ otlpOut: new OtlpExporter({ endpoint: "tempo:4317" }), batch: new BatchProcessor({}) }, distribution())).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("a project without a Distribution is not checked", () => {
|
|
170
|
+
expect(run({ spans: new SpanMetricsConnector({}) })).toEqual([]);
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
test("a module whose type can't be told makes a missing component a warning that names it", () => {
|
|
174
|
+
const d = distribution({ connectors: [{ gomod: "github.com/acme/otel/joiner v1.0.0" }] });
|
|
175
|
+
const diags = run({ spans: new SpanMetricsConnector({}) }, d);
|
|
176
|
+
expect(diags.map((x) => x.severity)).toEqual(["warning"]);
|
|
177
|
+
expect(diags[0].message).toContain('"github.com/acme/otel/joiner"');
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("is registered and catalogued", () => {
|
|
181
|
+
expect(otelPlugin.postSynthChecks!().map((c) => c.id)).toContain("OTEL128");
|
|
182
|
+
});
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
describe("ocb-build (#3374)", () => {
|
|
186
|
+
function runner(available = true) {
|
|
187
|
+
const calls: Array<{ command: string; cwd?: string }> = [];
|
|
188
|
+
const r: ProcessRunner = {
|
|
189
|
+
async run(command, options) {
|
|
190
|
+
calls.push({ command, cwd: options?.cwd });
|
|
191
|
+
return { stdout: "", stderr: "" };
|
|
192
|
+
},
|
|
193
|
+
async available() {
|
|
194
|
+
return available;
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
return { r, calls };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function manifest(text: string): { dir: string; path: string } {
|
|
201
|
+
const dir = mkdtempSync(join(tmpdir(), "ocb-build-"));
|
|
202
|
+
const path = join(dir, DISTRIBUTION_MANIFEST_FILE);
|
|
203
|
+
writeFileSync(path, text);
|
|
204
|
+
return { dir, path };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
test("runs ocb in the manifest's directory and hands on the output directory", async () => {
|
|
208
|
+
const m = manifest(distributionYaml(distribution()));
|
|
209
|
+
try {
|
|
210
|
+
const { r, calls } = runner();
|
|
211
|
+
const out = await createOcbBuildCapability(r).run({ env: "dev", component: "collector" }, { config: m.path, skipCompilation: true });
|
|
212
|
+
expect(calls).toEqual([{ command: `'ocb' --config '${m.path}' --skip-compilation`, cwd: m.dir }]);
|
|
213
|
+
expect(out.kind).toBe("dir");
|
|
214
|
+
expect(out.dir).toBe(join(m.dir, "_build"));
|
|
215
|
+
expect(out.manifestDigest).toMatch(/^sha256:[0-9a-f]{64}$/);
|
|
216
|
+
} finally {
|
|
217
|
+
rmSync(m.dir, { recursive: true, force: true });
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test("refuses a manifest with no dist.output_path before running ocb", async () => {
|
|
222
|
+
const m = manifest(distributionYaml(new Distribution({ dist: { name: "x" }, receivers: [OtlpReceiver] })));
|
|
223
|
+
try {
|
|
224
|
+
const { r, calls } = runner();
|
|
225
|
+
await expect(createOcbBuildCapability(r).run({ env: "dev", component: "c" }, { config: m.path })).rejects.toThrow(/sets no dist.output_path/);
|
|
226
|
+
expect(calls).toEqual([]);
|
|
227
|
+
} finally {
|
|
228
|
+
rmSync(m.dir, { recursive: true, force: true });
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
test("says when ocb is not installed", async () => {
|
|
233
|
+
const m = manifest(distributionYaml(distribution()));
|
|
234
|
+
try {
|
|
235
|
+
await expect(createOcbBuildCapability(runner(false).r).run({ env: "dev", component: "c" }, { config: m.path })).rejects.toThrow(/"ocb" is not installed/);
|
|
236
|
+
} finally {
|
|
237
|
+
rmSync(m.dir, { recursive: true, force: true });
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("an absolute output_path is kept", () => {
|
|
242
|
+
expect(ocbOutputPath("dist:\n output_path: /opt/build\n", "/work")).toBe("/opt/build");
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("the lexicon contributes it as a build verb", () => {
|
|
246
|
+
expect(otelCapabilityPlugin.capabilities().map((c) => c.kind)).toEqual(["ocb-build"]);
|
|
247
|
+
expect(otelCapabilityPlugin.families!()).toEqual({ build: ["ocb-build"] });
|
|
248
|
+
expect(ocbBuild({ config: "dist/builder-config.yaml" })).toEqual({ kind: "ocb-build", config: "dist/builder-config.yaml" });
|
|
249
|
+
});
|
|
250
|
+
});
|
|
@@ -0,0 +1,305 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Distribution`, the builder manifest `ocb` (the OpenTelemetry Collector
|
|
3
|
+
* Builder) builds a custom collector from (#3374).
|
|
4
|
+
*
|
|
5
|
+
* The fields are the builder's `Config` as read at v0.130.0
|
|
6
|
+
* (`cmd/builder/internal/builder/config.go` in opentelemetry-collector):
|
|
7
|
+
* `dist`, a module list per component kind, `providers`,
|
|
8
|
+
* `converters`, `replaces`, `excludes` and `conf_resolver`. `telemetry`
|
|
9
|
+
* (v0.146.0) is not in it, and `otelcol_version`, removed in v0.113.0, is
|
|
10
|
+
* not either.
|
|
11
|
+
*
|
|
12
|
+
* A component list takes a module (`{ gomod, import?, name?, path? }`) or a
|
|
13
|
+
* component class. A built-in class writes its module at `COLLECTOR_PIN`
|
|
14
|
+
* (`OtlpReceiver` is `go.opentelemetry.io/collector/receiver/otlpreceiver
|
|
15
|
+
* v0.162.0`), and a `defineComponent` class writes its own pin's source and
|
|
16
|
+
* version, so the manifest names the same versions the config was typed
|
|
17
|
+
* against.
|
|
18
|
+
*
|
|
19
|
+
* The serializer writes the manifest as `builder-config.yaml` beside the
|
|
20
|
+
* collector config when a project declares a `Distribution`. OTEL128 then
|
|
21
|
+
* checks that the collector config uses only components the distribution
|
|
22
|
+
* builds. A project without one sees no change.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { createResource } from "@intentius/chant/runtime";
|
|
26
|
+
import type { Declarable } from "@intentius/chant/declarable";
|
|
27
|
+
import { dump } from "js-yaml";
|
|
28
|
+
import { COLLECTOR_PIN, isUsablePin, type ComponentClass, type ComponentDefinition } from "./define";
|
|
29
|
+
import { canonicalComponentType, canonicalTypeOf, COMPONENT_KINDS, SECTION_OF, type CollectorConfig, type ComponentKind } from "./model";
|
|
30
|
+
import type { CollectorIssue } from "./validate-config";
|
|
31
|
+
|
|
32
|
+
export const DISTRIBUTION_TYPE = "OTel::Distribution";
|
|
33
|
+
|
|
34
|
+
/** Where the serializer writes the builder manifest, beside the collector config. */
|
|
35
|
+
export const DISTRIBUTION_MANIFEST_FILE = "builder-config.yaml";
|
|
36
|
+
|
|
37
|
+
/** A Go module the builder adds, as the manifest writes it. */
|
|
38
|
+
export interface DistributionModule {
|
|
39
|
+
/** The module and its version, as in `go.mod`: `github.com/acme/otel/exporter/acmeexporter v1.2.0`. */
|
|
40
|
+
gomod: string;
|
|
41
|
+
/** The Go import path, when it differs from the module path. */
|
|
42
|
+
import?: string;
|
|
43
|
+
/** The package name the generated code imports it as. */
|
|
44
|
+
name?: string;
|
|
45
|
+
/** A local path the module is replaced with. */
|
|
46
|
+
path?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** A component the distribution builds: a component class (built-in or `defineComponent`), or its module. */
|
|
50
|
+
export type DistributionComponent = ComponentClass<ComponentKind, string, any> | DistributionModule;
|
|
51
|
+
|
|
52
|
+
/** The `dist` section. */
|
|
53
|
+
export interface DistributionDist {
|
|
54
|
+
/** The Go module of the generated collector. */
|
|
55
|
+
module?: string;
|
|
56
|
+
/** The binary's name. */
|
|
57
|
+
name?: string;
|
|
58
|
+
/** The `go` binary to build with. */
|
|
59
|
+
go?: string;
|
|
60
|
+
/** The description `--help` prints. */
|
|
61
|
+
description?: string;
|
|
62
|
+
/** Where the generated sources and the binary go. */
|
|
63
|
+
output_path?: string;
|
|
64
|
+
/** The version the binary reports. */
|
|
65
|
+
version?: string;
|
|
66
|
+
/** Go build tags. */
|
|
67
|
+
build_tags?: string;
|
|
68
|
+
/** Build with debug symbols. */
|
|
69
|
+
debug_compilation?: boolean;
|
|
70
|
+
/** Build with cgo enabled. */
|
|
71
|
+
cgo_enabled?: boolean;
|
|
72
|
+
/** Write `replace` paths as absolute paths in the generated `go.mod`. */
|
|
73
|
+
use_absolute_replace_paths?: boolean;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface DistributionProps {
|
|
77
|
+
dist: DistributionDist;
|
|
78
|
+
receivers?: DistributionComponent[];
|
|
79
|
+
processors?: DistributionComponent[];
|
|
80
|
+
exporters?: DistributionComponent[];
|
|
81
|
+
connectors?: DistributionComponent[];
|
|
82
|
+
extensions?: DistributionComponent[];
|
|
83
|
+
/** confmap providers (`env`, `file`, `http`, ...). The builder adds its defaults when none are listed. */
|
|
84
|
+
providers?: DistributionModule[];
|
|
85
|
+
/** confmap converters. */
|
|
86
|
+
converters?: DistributionModule[];
|
|
87
|
+
/** `replace` directives for the generated `go.mod`. */
|
|
88
|
+
replaces?: string[];
|
|
89
|
+
/** `exclude` directives for the generated `go.mod`. */
|
|
90
|
+
excludes?: string[];
|
|
91
|
+
conf_resolver?: { default_uri_scheme?: string };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface DistributionEntity extends Declarable {
|
|
95
|
+
readonly props: DistributionProps;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** A custom collector's builder manifest. Declare at most one per project. */
|
|
99
|
+
export const Distribution = createResource(DISTRIBUTION_TYPE, "otel", {}) as unknown as new (
|
|
100
|
+
props: DistributionProps,
|
|
101
|
+
) => DistributionEntity;
|
|
102
|
+
|
|
103
|
+
export function isDistributionEntity(value: unknown): value is DistributionEntity {
|
|
104
|
+
return typeof value === "object" && value !== null && (value as Declarable).entityType === DISTRIBUTION_TYPE;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const CORE = "go.opentelemetry.io/collector";
|
|
108
|
+
const CONTRIB = "github.com/open-telemetry/opentelemetry-collector-contrib";
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The Go module of every built-in, by `kind:type`. The core collector's
|
|
112
|
+
* components are in opentelemetry-collector, the rest in contrib; both are
|
|
113
|
+
* released at `COLLECTOR_PIN`'s version. A built-in added to `./components`
|
|
114
|
+
* needs a row here (distribution.test.ts checks).
|
|
115
|
+
*/
|
|
116
|
+
export const BUILTIN_MODULES: Readonly<Record<string, string>> = Object.freeze({
|
|
117
|
+
"receiver:otlp": `${CORE}/receiver/otlpreceiver`,
|
|
118
|
+
"receiver:filelog": `${CONTRIB}/receiver/filelogreceiver`,
|
|
119
|
+
"receiver:hostmetrics": `${CONTRIB}/receiver/hostmetricsreceiver`,
|
|
120
|
+
"receiver:prometheus": `${CONTRIB}/receiver/prometheusreceiver`,
|
|
121
|
+
"receiver:k8s_cluster": `${CONTRIB}/receiver/k8sclusterreceiver`,
|
|
122
|
+
"receiver:kubeletstats": `${CONTRIB}/receiver/kubeletstatsreceiver`,
|
|
123
|
+
"processor:batch": `${CORE}/processor/batchprocessor`,
|
|
124
|
+
"processor:memory_limiter": `${CORE}/processor/memorylimiterprocessor`,
|
|
125
|
+
"processor:attributes": `${CONTRIB}/processor/attributesprocessor`,
|
|
126
|
+
"processor:deltatocumulative": `${CONTRIB}/processor/deltatocumulativeprocessor`,
|
|
127
|
+
"processor:k8sattributes": `${CONTRIB}/processor/k8sattributesprocessor`,
|
|
128
|
+
"processor:resourcedetection": `${CONTRIB}/processor/resourcedetectionprocessor`,
|
|
129
|
+
"processor:resource": `${CONTRIB}/processor/resourceprocessor`,
|
|
130
|
+
"processor:filter": `${CONTRIB}/processor/filterprocessor`,
|
|
131
|
+
"processor:redaction": `${CONTRIB}/processor/redactionprocessor`,
|
|
132
|
+
"processor:transform": `${CONTRIB}/processor/transformprocessor`,
|
|
133
|
+
"processor:probabilistic_sampler": `${CONTRIB}/processor/probabilisticsamplerprocessor`,
|
|
134
|
+
"processor:tail_sampling": `${CONTRIB}/processor/tailsamplingprocessor`,
|
|
135
|
+
"exporter:debug": `${CORE}/exporter/debugexporter`,
|
|
136
|
+
"exporter:otlp": `${CORE}/exporter/otlpexporter`,
|
|
137
|
+
"exporter:otlphttp": `${CORE}/exporter/otlphttpexporter`,
|
|
138
|
+
"exporter:googlecloud": `${CONTRIB}/exporter/googlecloudexporter`,
|
|
139
|
+
"exporter:prometheus": `${CONTRIB}/exporter/prometheusexporter`,
|
|
140
|
+
"exporter:loadbalancing": `${CONTRIB}/exporter/loadbalancingexporter`,
|
|
141
|
+
"exporter:clickhouse": `${CONTRIB}/exporter/clickhouseexporter`,
|
|
142
|
+
"connector:forward": `${CORE}/connector/forwardconnector`,
|
|
143
|
+
"connector:count": `${CONTRIB}/connector/countconnector`,
|
|
144
|
+
"connector:routing": `${CONTRIB}/connector/routingconnector`,
|
|
145
|
+
"connector:servicegraph": `${CONTRIB}/connector/servicegraphconnector`,
|
|
146
|
+
"connector:signaltometrics": `${CONTRIB}/connector/signaltometricsconnector`,
|
|
147
|
+
"connector:spanmetrics": `${CONTRIB}/connector/spanmetricsconnector`,
|
|
148
|
+
"connector:sum": `${CONTRIB}/connector/sumconnector`,
|
|
149
|
+
"extension:zpages": `${CORE}/extension/zpagesextension`,
|
|
150
|
+
"extension:health_check": `${CONTRIB}/extension/healthcheckextension`,
|
|
151
|
+
"extension:k8s_leader_elector": `${CONTRIB}/extension/k8sleaderelector`,
|
|
152
|
+
"extension:pprof": `${CONTRIB}/extension/pprofextension`,
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
const KIND_OF_LIST = {
|
|
156
|
+
receivers: "receiver",
|
|
157
|
+
processors: "processor",
|
|
158
|
+
exporters: "exporter",
|
|
159
|
+
connectors: "connector",
|
|
160
|
+
extensions: "extension",
|
|
161
|
+
} as const satisfies Record<string, ComponentKind>;
|
|
162
|
+
|
|
163
|
+
type ComponentList = keyof typeof KIND_OF_LIST;
|
|
164
|
+
|
|
165
|
+
const COMPONENT_LISTS = Object.keys(KIND_OF_LIST) as ComponentList[];
|
|
166
|
+
|
|
167
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
168
|
+
function isComponentClass(v: unknown): v is ComponentClass<ComponentKind, string, any> {
|
|
169
|
+
return typeof v === "function" && typeof (v as { definition?: unknown }).definition === "object";
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/** The module a component class is built from, at the version its config type follows. */
|
|
173
|
+
export function componentModule(def: ComponentDefinition): DistributionModule {
|
|
174
|
+
if (def.builtin) {
|
|
175
|
+
const path = BUILTIN_MODULES[`${def.kind}:${def.type}`];
|
|
176
|
+
if (!path) throw new Error(`otel: no Go module is recorded for the built-in ${def.kind} "${def.type}"`);
|
|
177
|
+
return { gomod: `${path} ${COLLECTOR_PIN.version}` };
|
|
178
|
+
}
|
|
179
|
+
if (!isUsablePin(def.pin)) {
|
|
180
|
+
throw new Error(`otel: the custom ${def.kind} "${def.type}" has no pin, so its Go module is unknown; list it as { gomod } instead`);
|
|
181
|
+
}
|
|
182
|
+
return { gomod: `${def.pin.source} ${def.pin.version}` };
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function copyModule(entry: DistributionModule): DistributionModule {
|
|
186
|
+
const out: DistributionModule = { gomod: entry.gomod };
|
|
187
|
+
if (entry.import !== undefined) out.import = entry.import;
|
|
188
|
+
if (entry.name !== undefined) out.name = entry.name;
|
|
189
|
+
if (entry.path !== undefined) out.path = entry.path;
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function moduleOf(entry: DistributionComponent, list: ComponentList): DistributionModule {
|
|
194
|
+
if (isComponentClass(entry)) {
|
|
195
|
+
const def = entry.definition as ComponentDefinition;
|
|
196
|
+
if (def.kind !== KIND_OF_LIST[list]) {
|
|
197
|
+
throw new Error(`otel: ${def.type} is a ${def.kind}, listed under the distribution's ${list}`);
|
|
198
|
+
}
|
|
199
|
+
return componentModule(def);
|
|
200
|
+
}
|
|
201
|
+
return copyModule(entry);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** The builder manifest a distribution writes, as the object `ocb --config` reads. */
|
|
205
|
+
export function distributionManifest(dist: DistributionEntity | DistributionProps): Record<string, unknown> {
|
|
206
|
+
const p = isDistributionEntity(dist) ? dist.props : dist;
|
|
207
|
+
const out: Record<string, unknown> = {};
|
|
208
|
+
const d: Record<string, unknown> = {};
|
|
209
|
+
for (const [k, v] of Object.entries(p.dist ?? {})) if (v !== undefined) d[k] = v;
|
|
210
|
+
out.dist = d;
|
|
211
|
+
for (const list of COMPONENT_LISTS) {
|
|
212
|
+
const entries = p[list];
|
|
213
|
+
if (entries && entries.length > 0) out[list] = entries.map((e) => moduleOf(e, list));
|
|
214
|
+
}
|
|
215
|
+
if (p.providers && p.providers.length > 0) out.providers = p.providers.map(copyModule);
|
|
216
|
+
if (p.converters && p.converters.length > 0) out.converters = p.converters.map(copyModule);
|
|
217
|
+
if (p.replaces && p.replaces.length > 0) out.replaces = [...p.replaces];
|
|
218
|
+
if (p.excludes && p.excludes.length > 0) out.excludes = [...p.excludes];
|
|
219
|
+
if (p.conf_resolver && p.conf_resolver.default_uri_scheme !== undefined) {
|
|
220
|
+
out.conf_resolver = { default_uri_scheme: p.conf_resolver.default_uri_scheme };
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** The builder manifest as the YAML `ocb --config` reads. */
|
|
226
|
+
export function distributionYaml(dist: DistributionEntity | DistributionProps): string {
|
|
227
|
+
return dump(distributionManifest(dist), { lineWidth: -1, noRefs: true });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** The module path of a `gomod` line: everything before the version. */
|
|
231
|
+
function modulePath(gomod: string): string {
|
|
232
|
+
return gomod.trim().split(/\s+/)[0];
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const BUILTIN_BY_PATH = new Map(Object.entries(BUILTIN_MODULES).map(([key, path]) => [path, key.split(":")[1]]));
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* The component type a module builds, when it can be told: a built-in's
|
|
239
|
+
* module, or a module path ending `<type><kind>` as contrib's do
|
|
240
|
+
* (`.../exporter/datadogexporter` is `datadog`). Undefined otherwise.
|
|
241
|
+
*/
|
|
242
|
+
export function moduleComponentType(kind: ComponentKind, gomod: string): string | undefined {
|
|
243
|
+
const path = modulePath(gomod);
|
|
244
|
+
const builtin = BUILTIN_BY_PATH.get(path);
|
|
245
|
+
if (builtin !== undefined) return builtin;
|
|
246
|
+
const last = path.split("/").pop() ?? "";
|
|
247
|
+
return last.endsWith(kind) && last.length > kind.length ? last.slice(0, -kind.length) : undefined;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** What a distribution builds, per kind: the component types it names, and the modules whose type can't be told. */
|
|
251
|
+
export interface DistributionComponents {
|
|
252
|
+
types: Record<ComponentKind, Set<string>>;
|
|
253
|
+
unknown: Record<ComponentKind, string[]>;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function distributionComponents(dist: DistributionEntity | DistributionProps): DistributionComponents {
|
|
257
|
+
const p = isDistributionEntity(dist) ? dist.props : dist;
|
|
258
|
+
const types = { receiver: new Set<string>(), processor: new Set<string>(), exporter: new Set<string>(), connector: new Set<string>(), extension: new Set<string>() };
|
|
259
|
+
const unknown: Record<ComponentKind, string[]> = { receiver: [], processor: [], exporter: [], connector: [], extension: [] };
|
|
260
|
+
for (const list of COMPONENT_LISTS) {
|
|
261
|
+
const kind = KIND_OF_LIST[list];
|
|
262
|
+
for (const entry of p[list] ?? []) {
|
|
263
|
+
if (isComponentClass(entry)) {
|
|
264
|
+
types[kind].add(canonicalComponentType(kind, (entry.definition as ComponentDefinition).type));
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const type = moduleComponentType(kind, entry.gomod);
|
|
268
|
+
if (type === undefined) unknown[kind].push(entry.gomod);
|
|
269
|
+
else types[kind].add(canonicalComponentType(kind, type));
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return { types, unknown };
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* OTEL128: the components `config` declares that `dist` does not build. A
|
|
277
|
+
* collector built from the distribution refuses such a config at start with
|
|
278
|
+
* "unknown type". When a kind lists a module whose type can't be told from
|
|
279
|
+
* its path, a missing component may be that module, so it is a warning
|
|
280
|
+
* naming the module rather than an error.
|
|
281
|
+
*/
|
|
282
|
+
export function distributionIssues(config: CollectorConfig, dist: DistributionEntity | DistributionProps): CollectorIssue[] {
|
|
283
|
+
const { types, unknown } = distributionComponents(dist);
|
|
284
|
+
const issues: CollectorIssue[] = [];
|
|
285
|
+
for (const kind of COMPONENT_KINDS) {
|
|
286
|
+
const section = (config as Record<string, unknown>)[SECTION_OF[kind]];
|
|
287
|
+
if (typeof section !== "object" || section === null) continue;
|
|
288
|
+
for (const id of Object.keys(section)) {
|
|
289
|
+
const type = canonicalTypeOf(kind, id);
|
|
290
|
+
if (types[kind].has(type)) continue;
|
|
291
|
+
const unsure = unknown[kind];
|
|
292
|
+
issues.push({
|
|
293
|
+
code: "OTEL128",
|
|
294
|
+
severity: unsure.length > 0 ? "warning" : "error",
|
|
295
|
+
component: id,
|
|
296
|
+
message:
|
|
297
|
+
`${kind} "${id}" is not in the Distribution, so a collector built from it does not know the type "${type}"` +
|
|
298
|
+
(unsure.length > 0
|
|
299
|
+
? `, unless it is built by ${unsure.map((m) => `"${modulePath(m)}"`).join(", ")}, whose type can't be read from its path`
|
|
300
|
+
: `. Add it to the Distribution's ${SECTION_OF[kind]}, or remove it from the config`),
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return issues;
|
|
305
|
+
}
|