SEANK.H.LIAO

failed: otelcol config schema validation in cue

failed experiment 3

cue schema validation

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}