@intentius/chant-lexicon-otel 0.97.0 → 0.98.0

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.
Files changed (37) hide show
  1. package/README.md +5 -0
  2. package/dist/codegen/docs.d.ts.map +1 -1
  3. package/dist/import/generator.d.ts +39 -0
  4. package/dist/import/generator.d.ts.map +1 -0
  5. package/dist/import/parser.d.ts +61 -0
  6. package/dist/import/parser.d.ts.map +1 -0
  7. package/dist/integrity.json +4 -4
  8. package/dist/lint/rules/literal-credential.d.ts.map +1 -1
  9. package/dist/manifest.json +1 -1
  10. package/dist/plugin.d.ts.map +1 -1
  11. package/dist/rules/literal-credential.ts +46 -17
  12. package/dist/skills/chant-otel.md +11 -0
  13. package/package.json +2 -2
  14. package/src/codegen/docs.ts +2 -1
  15. package/src/import/generated-types.e2e.test.ts +75 -0
  16. package/src/import/generator.test.ts +165 -0
  17. package/src/import/generator.ts +509 -0
  18. package/src/import/parser.test.ts +174 -0
  19. package/src/import/parser.ts +236 -0
  20. package/src/import/roundtrip.test.ts +365 -0
  21. package/src/import/testdata/agent-observability-agent.yaml +54 -0
  22. package/src/import/testdata/agent-observability-gateway.yaml +128 -0
  23. package/src/import/testdata/fixtures.ts +274 -0
  24. package/src/import/testdata/gateway.yaml +153 -0
  25. package/src/import/testdata/upstream/couchbase.yaml +150 -0
  26. package/src/import/testdata/upstream/fault-tolerant-logs.yaml +27 -0
  27. package/src/import/testdata/upstream/kubernetes-filelog.yaml +86 -0
  28. package/src/import/testdata/upstream/loadbalancing-agent.yaml +38 -0
  29. package/src/import/testdata/upstream/loadbalancing-backend.yaml +27 -0
  30. package/src/import/testdata/upstream/logline-filter-in.yaml +20 -0
  31. package/src/import/testdata/upstream/logline-filter-out.yaml +20 -0
  32. package/src/import/testdata/upstream/secure-tracing.yaml +26 -0
  33. package/src/import/testdata/upstream/servicegraph-nop.yaml +28 -0
  34. package/src/lint/rules/literal-credential.ts +46 -17
  35. package/src/lint/rules/rules.test.ts +16 -0
  36. package/src/plugin.ts +10 -0
  37. package/src/skills/chant-otel.md +11 -0
@@ -0,0 +1,150 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/examples/couchbase/otel-collector-config.yaml
3
+
4
+ receivers:
5
+ prometheus/couchbase:
6
+ config:
7
+ scrape_configs:
8
+ - job_name: 'couchbase'
9
+ scrape_interval: 5s
10
+ static_configs:
11
+ - targets: ['couchbase:8091']
12
+ basic_auth:
13
+ username: 'otelu'
14
+ password: 'otelpassword'
15
+ metric_relabel_configs:
16
+ # Include only a few key metrics
17
+ - source_labels: [ __name__ ]
18
+ regex: "(kv_ops)|\
19
+ (kv_vb_curr_items)|\
20
+ (kv_num_vbuckets)|\
21
+ (kv_ep_cursor_memory_freed_bytes)|\
22
+ (kv_total_memory_used_bytes)|\
23
+ (kv_ep_num_value_ejects)|\
24
+ (kv_ep_mem_high_wat)|\
25
+ (kv_ep_mem_low_wat)|\
26
+ (kv_ep_tmp_oom_errors)|\
27
+ (kv_ep_oom_errors)"
28
+ action: keep
29
+
30
+ processors:
31
+ filter/couchbase:
32
+ # Filter out prometheus scraping meta-metrics.
33
+ metrics:
34
+ exclude:
35
+ match_type: strict
36
+ metric_names:
37
+ - scrape_samples_post_metric_relabeling
38
+ - scrape_series_added
39
+ - scrape_duration_seconds
40
+ - scrape_samples_scraped
41
+ - up
42
+
43
+ metricstransform/couchbase:
44
+ transforms:
45
+ # Rename from prometheus metric name to OTel metric name.
46
+ # We cannot do this with metric_relabel_configs, as the prometheus receiver does not
47
+ # allow metric renames at this time.
48
+ - include: kv_ops
49
+ match_type: strict
50
+ action: update
51
+ new_name: "couchbase.bucket.operation.count"
52
+ - include: kv_vb_curr_items
53
+ match_type: strict
54
+ action: update
55
+ new_name: "couchbase.bucket.item.count"
56
+ - include: kv_num_vbuckets
57
+ match_type: strict
58
+ action: update
59
+ new_name: "couchbase.bucket.vbucket.count"
60
+ - include: kv_ep_cursor_memory_freed_bytes
61
+ match_type: strict
62
+ action: update
63
+ new_name: "couchbase.bucket.memory.usage.free"
64
+ - include: kv_total_memory_used_bytes
65
+ match_type: strict
66
+ action: update
67
+ new_name: "couchbase.bucket.memory.usage.used"
68
+ - include: kv_ep_num_value_ejects
69
+ match_type: strict
70
+ action: update
71
+ new_name: "couchbase.bucket.item.ejection.count"
72
+ - include: kv_ep_mem_high_wat
73
+ match_type: strict
74
+ action: update
75
+ new_name: "couchbase.bucket.memory.high_water_mark.limit"
76
+ - include: kv_ep_mem_low_wat
77
+ match_type: strict
78
+ action: update
79
+ new_name: "couchbase.bucket.memory.low_water_mark.limit"
80
+ - include: kv_ep_tmp_oom_errors
81
+ match_type: strict
82
+ action: update
83
+ new_name: "couchbase.bucket.error.oom.count.recoverable"
84
+ - include: kv_ep_oom_errors
85
+ match_type: strict
86
+ action: update
87
+ new_name: "couchbase.bucket.error.oom.count.unrecoverable"
88
+ # Combine couchbase.bucket.error.oom.count.x and couchbase.bucket.memory.usage.x
89
+ # metrics.
90
+ - include: '^couchbase\.bucket\.error\.oom\.count\.(?P<error_type>unrecoverable|recoverable)$$'
91
+ match_type: regexp
92
+ action: combine
93
+ new_name: "couchbase.bucket.error.oom.count"
94
+ - include: '^couchbase\.bucket\.memory\.usage\.(?P<state>free|used)$$'
95
+ match_type: regexp
96
+ action: combine
97
+ new_name: "couchbase.bucket.memory.usage"
98
+ # Aggregate "result" label on operation count to keep label sets consistent across the metric datapoints
99
+ - include: 'couchbase.bucket.operation.count'
100
+ match_type: strict
101
+ action: update
102
+ operations:
103
+ - action: aggregate_labels
104
+ label_set: ["bucket", "op"]
105
+ aggregation_type: sum
106
+
107
+ transform/couchbase:
108
+ metric_statements:
109
+ - context: datapoint
110
+ statements:
111
+ - convert_gauge_to_sum("cumulative", true) where metric.name == "couchbase.bucket.operation.count"
112
+ - set(metric.description, "Number of operations on the bucket.") where metric.name == "couchbase.bucket.operation.count"
113
+ - set(metric.unit, "{operations}") where metric.name == "couchbase.bucket.operation.count"
114
+
115
+ - convert_gauge_to_sum("cumulative", false) where metric.name == "couchbase.bucket.item.count"
116
+ - set(metric.description, "Number of items that belong to the bucket.") where metric.name == "couchbase.bucket.item.count"
117
+ - set(metric.unit, "{items}") where metric.name == "couchbase.bucket.item.count"
118
+
119
+ - convert_gauge_to_sum("cumulative", false) where metric.name == "couchbase.bucket.vbucket.count"
120
+ - set(metric.description, "Number of non-resident vBuckets.") where metric.name == "couchbase.bucket.vbucket.count"
121
+ - set(metric.unit, "{vbuckets}") where metric.name == "couchbase.bucket.vbucket.count"
122
+
123
+ - convert_gauge_to_sum("cumulative", false) where metric.name == "couchbase.bucket.memory.usage"
124
+ - set(metric.description, "Usage of total memory available to the bucket.") where metric.name == "couchbase.bucket.memory.usage"
125
+ - set(metric.unit, "By") where metric.name == "couchbase.bucket.memory.usage"
126
+
127
+ - convert_gauge_to_sum("cumulative", true) where metric.name == "couchbase.bucket.item.ejection.count"
128
+ - set(metric.description, "Number of item value ejections from memory to disk.") where metric.name == "couchbase.bucket.item.ejection.count"
129
+ - set(metric.unit, "{ejections}") where metric.name == "couchbase.bucket.item.ejection.count"
130
+
131
+ - convert_gauge_to_sum("cumulative", true) where metric.name == "couchbase.bucket.error.oom.count"
132
+ - set(metric.description, "Number of out of memory errors.") where metric.name == "couchbase.bucket.error.oom.count"
133
+ - set(metric.unit, "{errors}") where metric.name == "couchbase.bucket.error.oom.count"
134
+
135
+ - set(metric.description, "The memory usage at which items will be ejected.") where metric.name == "couchbase.bucket.memory.high_water_mark.limit"
136
+ - set(metric.unit, "By") where metric.name == "couchbase.bucket.memory.high_water_mark.limit"
137
+
138
+ - set(metric.description, "The memory usage at which ejections will stop that were previously triggered by a high water mark breach.") where metric.name == "couchbase.bucket.memory.low_water_mark.limit"
139
+ - set(metric.unit, "By") where metric.name == "couchbase.bucket.memory.low_water_mark.limit"
140
+
141
+ exporters:
142
+ prometheus:
143
+ endpoint: "0.0.0.0:9123"
144
+
145
+ service:
146
+ pipelines:
147
+ metrics/couchbase:
148
+ receivers: [prometheus/couchbase]
149
+ processors: [filter/couchbase, metricstransform/couchbase, transform/couchbase]
150
+ exporters: [prometheus]
@@ -0,0 +1,27 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/examples/fault-tolerant-logs-collection/otel-col-config.yaml
3
+
4
+ receivers:
5
+ filelog:
6
+ include: [/var/log/busybox/simple.log]
7
+ storage: file_storage/filelogreceiver
8
+
9
+ extensions:
10
+ file_storage/filelogreceiver:
11
+ directory: /var/lib/otelcol/file_storage/receiver
12
+ file_storage/otlpoutput:
13
+ directory: /var/lib/otelcol/file_storage/output
14
+
15
+ service:
16
+ extensions: [file_storage/filelogreceiver, file_storage/otlpoutput]
17
+ pipelines:
18
+ logs:
19
+ receivers: [filelog]
20
+ exporters: [otlp/custom]
21
+ processors: []
22
+
23
+ exporters:
24
+ otlp/custom:
25
+ endpoint: http://0.0.0.0:4242
26
+ sending_queue:
27
+ storage: file_storage/otlpoutput
@@ -0,0 +1,86 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/examples/kubernetes/otel-collector-config.yml
3
+
4
+ receivers:
5
+ filelog:
6
+ include:
7
+ - /var/log/pods/*/*/*.log
8
+ exclude:
9
+ # Exclude logs from all containers named otel-collector
10
+ - /var/log/pods/*/otel-collector/*.log
11
+ start_at: end
12
+ include_file_path: true
13
+ include_file_name: false
14
+ operators:
15
+ # Find out which format is used by kubernetes
16
+ - type: router
17
+ id: get-format
18
+ routes:
19
+ - output: parser-docker
20
+ expr: 'body matches "^\\{"'
21
+ - output: parser-crio
22
+ expr: 'body matches "^[^ Z]+ "'
23
+ - output: parser-containerd
24
+ expr: 'body matches "^[^ Z]+Z"'
25
+ # Parse CRI-O format
26
+ - type: regex_parser
27
+ id: parser-crio
28
+ regex: '^(?P<time>[^ Z]+) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) ?(?P<log>.*)$'
29
+ output: extract_metadata_from_filepath
30
+ timestamp:
31
+ parse_from: attributes.time
32
+ layout_type: gotime
33
+ layout: '2006-01-02T15:04:05.999999999Z07:00'
34
+ # Parse CRI-Containerd format
35
+ - type: regex_parser
36
+ id: parser-containerd
37
+ regex: '^(?P<time>[^ ^Z]+Z) (?P<stream>stdout|stderr) (?P<logtag>[^ ]*) ?(?P<log>.*)$'
38
+ output: extract_metadata_from_filepath
39
+ timestamp:
40
+ parse_from: attributes.time
41
+ layout: '%Y-%m-%dT%H:%M:%S.%LZ'
42
+ # Parse Docker format
43
+ - type: json_parser
44
+ id: parser-docker
45
+ output: extract_metadata_from_filepath
46
+ timestamp:
47
+ parse_from: attributes.time
48
+ layout: '%Y-%m-%dT%H:%M:%S.%LZ'
49
+ # Extract metadata from file path
50
+ - type: regex_parser
51
+ id: extract_metadata_from_filepath
52
+ regex: '^.*\/(?P<namespace>[^_]+)_(?P<pod_name>[^_]+)_(?P<uid>[a-f0-9\-]{36})\/(?P<container_name>[^\._]+)\/(?P<restart_count>\d+)\.log$'
53
+ parse_from: attributes["log.file.path"]
54
+ cache:
55
+ size: 128 # default maximum amount of Pods per Node is 110
56
+ # Update body field after finishing all parsing
57
+ - type: move
58
+ from: attributes.log
59
+ to: body
60
+ # Rename attributes
61
+ - type: move
62
+ from: attributes.stream
63
+ to: attributes["log.iostream"]
64
+ - type: move
65
+ from: attributes.container_name
66
+ to: resource["k8s.container.name"]
67
+ - type: move
68
+ from: attributes.namespace
69
+ to: resource["k8s.namespace.name"]
70
+ - type: move
71
+ from: attributes.pod_name
72
+ to: resource["k8s.pod.name"]
73
+ - type: move
74
+ from: attributes.restart_count
75
+ to: resource["k8s.container.restart_count"]
76
+ - type: move
77
+ from: attributes.uid
78
+ to: resource["k8s.pod.uid"]
79
+ exporters:
80
+ debug:
81
+ verbosity: detailed
82
+ service:
83
+ pipelines:
84
+ logs:
85
+ receivers: [filelog]
86
+ exporters: [debug]
@@ -0,0 +1,38 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/exporter/loadbalancingexporter/example/otel-agent-config.yaml
3
+
4
+ receivers:
5
+ fluentforward:
6
+ endpoint: 0.0.0.0:24224
7
+
8
+ processors:
9
+ batch:
10
+
11
+ exporters:
12
+ debug:
13
+ verbosity: detailed
14
+ loadbalancing:
15
+ protocol:
16
+ otlp:
17
+ timeout: 1s
18
+ tls:
19
+ insecure: true
20
+ resolver:
21
+ static:
22
+ hostnames:
23
+ - otel-collector-1:4317
24
+ - otel-collector-2:4317
25
+ - otel-collector-3:4317
26
+
27
+ extensions:
28
+ health_check:
29
+ pprof:
30
+ zpages:
31
+
32
+ service:
33
+ extensions: [pprof, zpages, health_check]
34
+ pipelines:
35
+ logs:
36
+ receivers: [fluentforward]
37
+ processors: [batch]
38
+ exporters: [loadbalancing, debug]
@@ -0,0 +1,27 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/exporter/loadbalancingexporter/example/otel-collector-config.yaml
3
+
4
+ receivers:
5
+ otlp:
6
+ protocols:
7
+ grpc:
8
+
9
+ processors:
10
+ batch:
11
+
12
+ exporters:
13
+ debug:
14
+ verbosity: detailed
15
+
16
+ extensions:
17
+ health_check:
18
+ pprof:
19
+ zpages:
20
+
21
+ service:
22
+ extensions: [pprof, zpages, health_check]
23
+ pipelines:
24
+ logs:
25
+ receivers: [otlp]
26
+ processors: [batch]
27
+ exporters: [debug]
@@ -0,0 +1,20 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/examples/logline-filtering/otel-col-config-filter-in-logs.yaml
3
+
4
+ receivers:
5
+ filelog:
6
+ include: [/var/log/busybox/simple.log]
7
+ operators:
8
+ - type: filter
9
+ expr: 'body not matches "^WARN:"'
10
+
11
+ service:
12
+ pipelines:
13
+ logs:
14
+ receivers: [filelog]
15
+ exporters: [otlp/custom]
16
+ processors: []
17
+
18
+ exporters:
19
+ otlp/custom:
20
+ endpoint: http://0.0.0.0:4242
@@ -0,0 +1,20 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/examples/logline-filtering/otel-col-config-filter-out-logs.yaml
3
+
4
+ receivers:
5
+ filelog:
6
+ include: [/var/log/busybox/simple.log]
7
+ operators:
8
+ - type: filter
9
+ expr: 'body matches "^INFO:"'
10
+
11
+ service:
12
+ pipelines:
13
+ logs:
14
+ receivers: [filelog]
15
+ exporters: [otlp/custom]
16
+ processors: []
17
+
18
+ exporters:
19
+ otlp/custom:
20
+ endpoint: http://0.0.0.0:4242
@@ -0,0 +1,26 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/examples/secure-tracing/otel-collector-config.yaml
3
+
4
+ receivers:
5
+ otlp:
6
+ protocols:
7
+ grpc:
8
+ endpoint: 0.0.0.0:55690
9
+ tls:
10
+ cert_file: /etc/otel-collector.crt
11
+ key_file: /etc/otel-collector.key
12
+
13
+ processors:
14
+ batch:
15
+
16
+ exporters:
17
+ debug:
18
+ verbosity: detailed
19
+
20
+ service:
21
+ pipelines:
22
+ traces:
23
+ receivers: [otlp]
24
+ processors: [batch]
25
+ exporters: [debug]
26
+
@@ -0,0 +1,28 @@
1
+ # Vendored from opentelemetry-collector-contrib v0.130.0 (Apache-2.0), unchanged below this header:
2
+ # https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/v0.130.0/connector/servicegraphconnector/testdata/service-graph-connector-config.yaml
3
+
4
+ receivers:
5
+ nop:
6
+
7
+ exporters:
8
+ nop:
9
+
10
+ connectors:
11
+ servicegraph:
12
+ latency_histogram_buckets: [1,2,3,4,5]
13
+ dimensions:
14
+ - dimension-1
15
+ - dimension-2
16
+ store:
17
+ ttl: 1s
18
+ max_items: 10
19
+ database_name_attributes: [db.name]
20
+
21
+ service:
22
+ pipelines:
23
+ traces:
24
+ receivers: [nop]
25
+ exporters: [servicegraph]
26
+ metrics:
27
+ receivers: [servicegraph]
28
+ exporters: [nop]
@@ -25,25 +25,54 @@ export const literalCredentialRule: LintRule = {
25
25
  const diagnostics: LintDiagnostic[] = [];
26
26
  const source = context.sourceFile;
27
27
 
28
+ // `const x = { ... }` (or `[...]`, or a string) anywhere in the file, so a
29
+ // config value lifted out of the constructor into a named const, the way
30
+ // COR001 asks, is scanned where it is used.
31
+ const consts = new Map<string, ts.Expression>();
32
+ const collect = (node: ts.Node) => {
33
+ if (ts.isVariableDeclarationList(node) && node.flags & ts.NodeFlags.Const) {
34
+ for (const d of node.declarations) {
35
+ if (ts.isIdentifier(d.name) && d.initializer) consts.set(d.name.text, d.initializer);
36
+ }
37
+ }
38
+ ts.forEachChild(node, collect);
39
+ };
40
+ collect(source);
41
+
42
+ const flag = (key: string, node: ts.Node) =>
43
+ diagnostics.push({
44
+ ruleId: "OTEL002",
45
+ severity: "error",
46
+ message: `\`${key}\` is a literal credential; write "\${env:NAME}" (or "\${file:/path}") and let the collector read it at start-up`,
47
+ file: context.filePath,
48
+ ...position(source, node),
49
+ });
50
+
51
+ const scanned = new Set<ts.Node>();
52
+ const scanValue = (key: string | undefined, value: ts.Expression) => {
53
+ let init: ts.Expression = value;
54
+ if (ts.isIdentifier(init)) {
55
+ const resolved = consts.get(init.text);
56
+ if (!resolved) return;
57
+ init = resolved;
58
+ }
59
+ if (ts.isObjectLiteralExpression(init) || ts.isArrayLiteralExpression(init)) {
60
+ if (scanned.has(init)) return;
61
+ scanned.add(init);
62
+ if (ts.isObjectLiteralExpression(init)) scan(init);
63
+ else for (const el of init.elements) scanValue(undefined, el as ts.Expression);
64
+ return;
65
+ }
66
+ if (!key || !SECRET_KEY.test(key) || /_file$/i.test(key)) return;
67
+ const text = literalText(init);
68
+ if (text === undefined || text === "" || text.includes("${")) return;
69
+ flag(key, value);
70
+ };
71
+
28
72
  const scan = (obj: ts.ObjectLiteralExpression) => {
29
73
  for (const prop of obj.properties) {
30
- if (!ts.isPropertyAssignment(prop)) continue;
31
- const key = propertyName(prop);
32
- const init = prop.initializer;
33
- if (ts.isObjectLiteralExpression(init)) {
34
- scan(init);
35
- continue;
36
- }
37
- if (!key || !SECRET_KEY.test(key) || /_file$/i.test(key)) continue;
38
- const text = literalText(init);
39
- if (text === undefined || text === "" || text.includes("${")) continue;
40
- diagnostics.push({
41
- ruleId: "OTEL002",
42
- severity: "error",
43
- message: `\`${key}\` is a literal credential; write "\${env:NAME}" (or "\${file:/path}") and let the collector read it at start-up`,
44
- file: context.filePath,
45
- ...position(source, init),
46
- });
74
+ if (ts.isShorthandPropertyAssignment(prop)) scanValue(prop.name.text, prop.name);
75
+ else if (ts.isPropertyAssignment(prop)) scanValue(propertyName(prop), prop.initializer);
47
76
  }
48
77
  };
49
78
 
@@ -67,4 +67,20 @@ describe("OTEL002 literal credential", () => {
67
67
  );
68
68
  expect(diags).toEqual([]);
69
69
  });
70
+
71
+ test("follows config lifted into named consts, and looks inside lists", () => {
72
+ const diags = literalCredentialRule.check(
73
+ ctx(`
74
+ const headers: OtlpExporterConfig["headers"] = { authorization: "Bearer abc123" };
75
+ const safe = { authorization: "Bearer \${env:TOKEN}" };
76
+ const token = "literal";
77
+ const config = { scrape_configs: [{ job_name: "x", basic_auth: { username: "u", password: "p" } }] };
78
+ new OtlpExporter({ endpoint: "x:4317", headers });
79
+ new OtlpExporter({ name: "b", endpoint: "x:4317", headers: safe });
80
+ new SplunkHecExporter({ token, endpoint: "https://hec" });
81
+ new PrometheusReceiver({ config });
82
+ `),
83
+ );
84
+ expect(diags.map((d) => d.message.split("`")[1])).toEqual(["authorization", "token", "password"]);
85
+ });
70
86
  });
package/src/plugin.ts CHANGED
@@ -9,6 +9,8 @@ import { otelAuditCatalog } from "./lint/audit-catalog";
9
9
  import { completions } from "./lsp/completions";
10
10
  import { hover } from "./lsp/hover";
11
11
  import { detectTemplate } from "./detect";
12
+ import { OtelCollectorParser } from "./import/parser";
13
+ import { OtelCollectorGenerator } from "./import/generator";
12
14
  import { otelSkills } from "./skill-defs";
13
15
  import { BUILTIN_CATALOG } from "./catalog";
14
16
  import { COLLECTOR_PIN } from "./define";
@@ -96,6 +98,14 @@ export const otelPlugin: LexiconPlugin = {
96
98
  return detectTemplate(data);
97
99
  },
98
100
 
101
+ templateParser() {
102
+ return new OtelCollectorParser();
103
+ },
104
+
105
+ templateGenerator() {
106
+ return new OtelCollectorGenerator();
107
+ },
108
+
99
109
  completionProvider(ctx: CompletionContext) {
100
110
  return completions(ctx);
101
111
  },
@@ -78,6 +78,17 @@ It deletes prompt, completion, system-instruction and tool-call content from spa
78
78
  - Every pipeline needs at least one receiver and one exporter (OTEL102). A declared component no pipeline uses is a warning (OTEL103).
79
79
  - Declared extensions are enabled in declaration order unless a `Service` lists `extensions` itself.
80
80
 
81
+ ## Starting from an existing config
82
+
83
+ When the user already has a collector config file, import it rather than retyping it:
84
+
85
+ ```bash
86
+ chant import otel-collector-config.yaml --output src
87
+ chant build src --lexicon otel -o collector.yaml # the same config back
88
+ ```
89
+
90
+ The importer writes `receivers.ts`, `processors.ts`, `exporters.ts`, `connectors.ts`, `extensions.ts`, `pipelines.ts`, and `service.ts` when extensions or telemetry need a `Service`. A component type with no built-in class lands in `custom-components.ts` as a `defineComponent` whose config is untyped data; offer to give it a config interface. Settings outside a built-in's config type still import and build, but `tsc` flags them. Read the warnings `chant import` prints: they name anything the lexicon has no place for.
91
+
81
92
  ## Reading the result
82
93
 
83
94
  `collectorTopologyOf(entities)` returns the pipelines, each component's endpoints and schema pin, for each exporter the signals it carries, the connector `edges` between pipelines, and under `semconv` the semantic-conventions version (`GENAI_SEMCONV_PIN`) the config's `gen_ai.` keys follow, as plain data.