I wanted a json schema for the opentelemetry-collector's configuration. As far as I know, the only way config is defined is as Go types within the various components (receivers, processors, exporters, connectors, extensions).
My thought went straight to cue, thinking it could automatically extract cue schemas from go types, then maybe validate directly or generate an openapi schema.
Unfortunately,
it turns out that cue get go $go_package
has quite a few issues:
it panics on generics, and it doesn't like unsafe,
I think there was a third panic that i don't quite remember.
This makes it much less useful for automatic schema extraction,
even if the types I want (usually the Config
structs + their fields)
don't use any of generics or unsafe.
1$ cue get go -i go.opentelemetry.io/collector/service
2panic: unsupported type *types.TypeParam [recovered]
3 panic: unsupported type *types.TypeParam
4---
5$ cue get go go.opentelemetry.io/collector/receiver/otlpreceiver
6invalid identifier "unsafe.Pointer"
As for validation directly via cue, I get the feeling the output isn't quite as nice.
1$ cue vet config.yaml config.cue -d '#Config'
2_pipelines.logs.exporters.awss3: conflicting values false and true:
3 ./config.cue:61:3
4 ./config.cue:69:5
5 ./config.cue:70:24
6 ./config.cue:70:31
Config included below for completion
1package otelcolconfig
2
3import (
4 "github.com/open-telemetry/opentelemetry-collector-contrib/exporter/awss3exporter"
5 "github.com/open-telemetry/opentelemetry-collector-contrib/processor/transformprocessor"
6 "github.com/open-telemetry/opentelemetry-collector-contrib/receiver/webhookeventreceiver"
7)
8
9#Config: {
10 receivers: {
11 [=~"^webhookevent(/.+)?$"]: webhookeventreceiver.#Config
12 }
13 processors?: {
14 [=~"^transform(/.+)?$"]: transformprocessor.#Config
15 }
16 exporters: {
17 [=~"^awss3(/.+)$"]: awss3exporter.#Config
18 }
19 connectors?: {}
20 extensions?: {}
21 // can't import service def due to
22 // https://github.com/cue-lang/cue/issues/2217
23 service: close({
24 telemetry?: close({
25 logs?: {
26 level?: "debug" | "info" | "warn" | "error"
27 development?: bool
28 disable_caller?: bool
29 disable_stacktrace?: bool
30 sampling?: {
31 initial?: int
32 thereafter?: int
33 }
34 error_output_paths?: [...string]
35 initial_fields?: [string]: string
36 }
37 metrics?: {
38 level: "none" | "basic" | "normal" | "detailed"
39 address: =~"^.*(:\\d)?"
40 metric_readers?: [...{
41 args?: _
42 type?: string
43 }]
44 }
45 traces?: {
46 propagators?: [...string]
47 }
48 })
49 extensions: [...string]
50 pipelines: [=~"^traces(/.*)?"]: #Pipeline
51 pipelines: [=~"^metrics(/.*)?"]: #Pipeline
52 pipelines: [=~"^logs(/.*)?"]: #Pipeline
53 })
54
55 _extensions: {
56 for e in service.extensions {
57 (e): true & extensions[e] != _|_
58 }
59 }
60 _pipelines: {
61 for pname, pipeline in service.pipelines {
62 (pname): {
63 for r in pipeline.receivers {
64 "receivers": (r): true & receivers[r] != _|_
65 }
66 for p in pipeline.processors {
67 "processors": (p): true & processors[p] != _|_
68 }
69 for e in pipeline.exporters {
70 "exporters": (e): true & exporters[e] != _|_
71 }
72 }
73 }
74 }
75}
76#Pipeline: {
77 receivers: [...string]
78 processors?: [...string]
79 exporters: [...string]
80}