Skip to content

Commit c9f489a

Browse files
[chore][cmd/mdatagen] Align types for mdatagen configs (#15659)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description Replace JSON Schema vocabulary with Go-centric types in mdatagen config schema: `int/bool/slice/map` instead of `integer/boolean/array/object`, and merge `items/additionalProperties` into a single `values` field. <!--Describe what testing was performed and which tests were added.--> #### Testing Unit tests and sample component aligned. <!--Describe the documentation added.--> #### Documentation 1. Added list of supported types to README.md file 2. Metadata.yaml schema aligned <!--Authorship attestation. See AGENTS.md for details. AI agents must not check this box on behalf of the user; the human author must check it themselves before the PR is ready for review.--> #### Authorship - [x] I, a human, wrote this pull request description myself. <!--Please delete paragraphs that you did not use before submitting.-->
1 parent 84a98cb commit c9f489a

26 files changed

Lines changed: 651 additions & 584 deletions

cmd/mdatagen/README.md

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -81,50 +81,90 @@ config:
8181
description: The endpoint to listen on
8282
default: "localhost:4317"
8383
timeout:
84-
type: string
85-
format: duration
84+
type: duration
8685
description: Request timeout duration
8786
default: "30s"
8887
tls:
8988
$ref: go.opentelemetry.io/collector/config/configtls.server_config
9089
required: [endpoint]
9190
```
9291

93-
The `config` section is based on [JSON Schema standard](https://json-schema.org/) (draft 2020-12) and supports:
92+
The `config` section uses Go-centric types directly in the `type` field, and also supports:
9493

95-
- **Standard JSON Schema types**: string, number, integer, boolean, object, array, null
9694
- **Validation constraints**: minLength, maxLength, pattern, minimum, maximum, enum, etc.
9795
- **References**: Internal (`$ref: definition_name`), external (`$ref: package.path.type`), or relative (`$ref: ./internal/config.type`)
9896
- **Reusable definitions**: Define common schemas in `$defs` and reference them with `$ref`
9997
- **Schema composition**: Use `allOf` for complex configurations
100-
- **Extended type aliases**: first-class aliases that expand to the correct JSON Schema shape and Go type automatically (see table below)
101-
102-
#### Extended type aliases
103-
104-
Instead of combining `type`+`format` or `type`+`x-customType` by hand, you can use an alias directly as the `type` value:
105-
106-
| Alias | Go type | JSON Schema representation |
107-
|---|---|-----------------------------------|
108-
| `rune` | `rune` | `integer` |
109-
| `byte` | `byte` | `integer` |
110-
| `uint` | `uint` | `integer` |
111-
| `int8` | `int8` | `integer` |
112-
| `uint8` | `uint8` | `integer` |
113-
| `int16` | `int16` | `integer` |
114-
| `uint16` | `uint16` | `integer` |
115-
| `int32` | `int32` | `integer` |
116-
| `uint32` | `uint32` | `integer` |
117-
| `int64` | `int64` | `integer` |
118-
| `uint64` | `uint64` | `integer` |
119-
| `float32` | `float32` | `number` |
120-
| `float64` | `float64` | `number` |
121-
| `duration` | `time.Duration` | `string` with Go duration pattern |
122-
| `time` | `time.Time` | `string` with `format: date-time` |
123-
| `opaque_string` | `configopaque.String` | `string` |
124-
| `opaque_map` | `configopaque.MapList` | `object` of name/value pairs |
125-
| `id` | `component.ID` | `string` |
126-
127-
Example:
98+
99+
#### Supported types
100+
101+
##### Primitive types
102+
103+
Use Go type names directly in the `type` field:
104+
105+
| Type | Go type | JSON Schema type |
106+
|------|---------|-----------------|
107+
| `string` | `string` | `string` |
108+
| `bool` | `bool` | `boolean` |
109+
| `int` | `int` | `integer` |
110+
| `int8` | `int8` | `integer` |
111+
| `int16` | `int16` | `integer` |
112+
| `int32` | `int32` | `integer` |
113+
| `int64` | `int64` | `integer` |
114+
| `uint` | `uint` | `integer` |
115+
| `uint8` | `uint8` | `integer` |
116+
| `uint16` | `uint16` | `integer` |
117+
| `uint32` | `uint32` | `integer` |
118+
| `uint64` | `uint64` | `integer` |
119+
| `byte` | `byte` | `integer` |
120+
| `rune` | `rune` | `integer` |
121+
| `float32` | `float32` | `number` |
122+
| `float64` | `float64` | `number` |
123+
| `any` | `any` | *(no type constraint)* |
124+
125+
##### Container types
126+
127+
| Type | Go type | JSON Schema type | Notes |
128+
|------|---------|-----------------|-------|
129+
| `object` | struct | `object` | Requires `properties:` |
130+
| `slice` | `[]T` | `array` | Requires `values:` for the element type |
131+
| `map` | `map[string]T` | `object` | Requires `values:` for the value type |
132+
133+
Example using container types:
134+
135+
```yaml
136+
config:
137+
type: object
138+
properties:
139+
endpoints:
140+
type: slice
141+
values:
142+
type: string
143+
description: List of endpoints to connect to.
144+
headers:
145+
type: map
146+
values:
147+
type: string
148+
description: Extra HTTP headers to attach to each request.
149+
tls:
150+
$ref: go.opentelemetry.io/collector/config/configtls.server_config
151+
```
152+
153+
##### Alias types
154+
155+
Shorthand types that expand to a primitive or container type with additional annotations:
156+
157+
| Alias | Go type | JSON Schema representation | Notes |
158+
|-------|---------|---------------------------|-------|
159+
| `float` | `float32` | `number` | Shorthand for `float32` |
160+
| `double` | `float64` | `number` | Shorthand for `float64` |
161+
| `duration` | `time.Duration` | `string` with Go duration pattern | e.g. `"30s"`, `"1h30m"` |
162+
| `time` | `time.Time` | `string` with `format: date-time` | RFC 3339 format |
163+
| `opaque_string` | `configopaque.String` | `string` | Masked in logs |
164+
| `component_id` | `component.ID` | `string` | Collector component ID |
165+
| `opaque_map` | `configopaque.MapList` | `object` of name/value pairs | Values masked in logs |
166+
167+
Example using alias types:
128168

129169
```yaml
130170
config:
@@ -140,8 +180,6 @@ config:
140180
type: opaque_string
141181
```
142182

143-
Aliases are additive: existing uses of standard JSON Schema types, `format`, and `x-customType` remain supported without migration.
144-
145183
### Metrics Builder Configuration
146184

147185
For receivers, scrapers, and other components that emit metrics, `mdatagen` can generate metrics builder
@@ -172,7 +210,7 @@ metrics:
172210
description: Number of received requests.
173211
unit: "{request}"
174212
sum:
175-
value_type: int
213+
values: int
176214
monotonic: true
177215
aggregation_temporality: cumulative
178216
attributes: [status_code]

cmd/mdatagen/internal/cfggen/docgen.go

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ func CfgDocType(cfg *ConfigMetadata) string {
9696
return "object"
9797
}
9898
switch cfg.Type {
99-
case "string":
99+
case StringType:
100100
if cfg.GoType == "time.Duration" || cfg.Format == "duration" {
101101
return "duration"
102102
}
@@ -111,24 +111,18 @@ func CfgDocType(cfg *ConfigMetadata) string {
111111
return "string (one of: " + strings.Join(vals, ", ") + ")"
112112
}
113113
return "string"
114-
case "integer":
115-
return "int"
116-
case "number":
117-
return "float"
118-
case "boolean":
119-
return "bool"
120-
case "array":
121-
if cfg.Items != nil {
122-
return "[]" + CfgDocType(cfg.Items)
114+
case SliceType:
115+
if cfg.Values != nil {
116+
return "[]" + CfgDocType(cfg.Values)
123117
}
124118
return "[]any"
125-
case "object":
126-
if cfg.AdditionalProperties != nil {
127-
return "map[string]" + CfgDocType(cfg.AdditionalProperties)
119+
case MapType:
120+
if cfg.Values != nil {
121+
return "map[string]" + CfgDocType(cfg.Values)
128122
}
129-
return "object"
123+
return "map[string]any"
130124
default:
131-
return "any"
125+
return string(cfg.Type)
132126
}
133127
}
134128

cmd/mdatagen/internal/cfggen/docgen_test.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -107,10 +107,10 @@ func TestCfgIsObject(t *testing.T) {
107107
}{
108108
{"nil", nil, false},
109109
{"primitive string", &ConfigMetadata{Type: "string"}, false},
110-
{"array of strings", &ConfigMetadata{Type: "array", Items: &ConfigMetadata{Type: "string"}}, false},
110+
{"slice of strings", &ConfigMetadata{Type: "slice", Values: &ConfigMetadata{Type: "string"}}, false},
111111
{"inline object", &ConfigMetadata{Type: "object", Properties: map[string]*ConfigMetadata{"x": {Type: "string"}}}, true},
112112
{"ref-resolved object", &ConfigMetadata{Type: "object", Ref: "confighttp.ServerConfig", Properties: map[string]*ConfigMetadata{"port": {Type: "integer"}}}, true},
113-
{"map without properties", &ConfigMetadata{Type: "object", AdditionalProperties: &ConfigMetadata{Type: "string"}}, false},
113+
{"map without properties", &ConfigMetadata{Type: "map", Values: &ConfigMetadata{Type: "string"}}, false},
114114
}
115115
for _, tc := range cases {
116116
t.Run(tc.name, func(t *testing.T) {
@@ -140,15 +140,15 @@ func TestCfgDocType(t *testing.T) {
140140
{"datetime via GoType", &ConfigMetadata{Type: "string", GoType: "time.Time"}, "datetime"},
141141
{"datetime via Format", &ConfigMetadata{Type: "string", Format: "date-time"}, "datetime"},
142142
{"enum string", &ConfigMetadata{Type: "string", Enum: []any{"a", "b"}}, "string (one of: a, b)"},
143-
{"integer", &ConfigMetadata{Type: "integer"}, "int"},
144-
{"number", &ConfigMetadata{Type: "number"}, "float"},
145-
{"boolean", &ConfigMetadata{Type: "boolean"}, "bool"},
146-
{"array of string", &ConfigMetadata{Type: "array", Items: &ConfigMetadata{Type: "string"}}, "[]string"},
147-
{"array of any", &ConfigMetadata{Type: "array"}, "[]any"},
148-
{"map of string", &ConfigMetadata{Type: "object", AdditionalProperties: &ConfigMetadata{Type: "string"}}, "map[string]string"},
143+
{"int", &ConfigMetadata{Type: "int"}, "int"},
144+
{"float64", &ConfigMetadata{Type: "float64"}, "float64"},
145+
{"bool", &ConfigMetadata{Type: "bool"}, "bool"},
146+
{"array of string", &ConfigMetadata{Type: "slice", Values: &ConfigMetadata{Type: "string"}}, "[]string"},
147+
{"array of any", &ConfigMetadata{Type: "slice"}, "[]any"},
148+
{"map of string", &ConfigMetadata{Type: "map", Values: &ConfigMetadata{Type: "string"}}, "map[string]string"},
149149
{"inline object", &ConfigMetadata{Type: "object", Properties: map[string]*ConfigMetadata{"x": {Type: "string"}}}, "object"},
150150
{"plain object no props", &ConfigMetadata{Type: "object"}, "object"},
151-
{"unknown type", &ConfigMetadata{Type: "unknown"}, "any"},
151+
{"unknown type", &ConfigMetadata{Type: "unknown"}, "unknown"},
152152
}
153153
for _, tc := range cases {
154154
t.Run(tc.name, func(t *testing.T) {

0 commit comments

Comments
 (0)