Skip to content

Commit 81d7df6

Browse files
committed
[mdatagen] Replace attributes optional field with requirement_level
metadata.yaml schema change for attributes: boolean `optional` field replaced with enum `requirement_level` field with the following options: - `required`: attribute is always included and cannot be excluded - `conditionally_required`: attribute is included by default when certain conditions are met (replaces `optional: true`) - `recommended`: attribute is included by default but can be disabled via configuration (replaces `optional: false`) - `opt_in`: attribute is not included unless explicitly enabled in user config The `recommended` option is assumed when requirement_level isn't specified explicitly which is the case for the vast majority use cases. metadata.yaml files upgrade guideline: Replace `optional: true` with `requirement_level: conditionally_required` and `optional: false` with `requirement_level: recommended` (or omit for default) in metadata files. 8b0990
1 parent 8b09905 commit 81d7df6

23 files changed

+427
-202
lines changed
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Use this changelog template to create an entry for release notes.
2+
3+
# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
4+
change_type: breaking
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: mdatagen
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: "Replace `optional` field with `requirement_level` field for attributes in metadata schema"
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [13913]
14+
15+
# (Optional) One or more lines of additional information to render under the primary note.
16+
# These lines will be padded with 2 spaces and then inserted directly into the document.
17+
# Use pipe (|) for multiline entries.
18+
subtext: |
19+
The `optional` boolean field for attributes has been replaced with a `requirement_level` field that accepts enum values: `required`, `conditionally_required`, `recommended`, or `opt_in`.
20+
- `required`: attribute is always included and cannot be excluded
21+
- `conditionally_required`: attribute is included by default when certain conditions are met (replaces `optional: true`)
22+
- `recommended`: attribute is included by default but can be disabled via configuration (replaces `optional: false`)
23+
- `opt_in`: attribute is not included unless explicitly enabled in user config
24+
When `requirement_level` is not specified, it defaults to `recommended`.
25+
26+
# Optional: The change log or logs in which this entry should be included.
27+
# e.g. '[user]' or '[user, api]'
28+
# Include 'user' if the change is relevant to end users.
29+
# Include 'api' if there is a change to a library API.
30+
# Default: '[user]'
31+
change_logs: [user]

cmd/mdatagen/internal/command.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,14 @@ func templatize(tmplFile string, md Metadata) *template.Template {
231231
"attributeInfo": func(an AttributeName) Attribute {
232232
return md.Attributes[an]
233233
},
234-
"getEventOptionalAttributes": func(attrs map[AttributeName]Attribute) []AttributeName {
234+
"getEventConditionalAttributes": func(attrs map[AttributeName]Attribute) []AttributeName {
235235
seen := make(map[AttributeName]bool)
236236
used := make([]AttributeName, 0)
237237

238238
for _, event := range md.Events {
239239
for _, attribute := range event.Attributes {
240240
v, exists := attrs[attribute]
241-
if exists && v.Optional && !seen[attribute] {
241+
if exists && v.IsConditional() && !seen[attribute] {
242242
used = append(used, attribute)
243243
seen[attribute] = true
244244
}
@@ -248,14 +248,14 @@ func templatize(tmplFile string, md Metadata) *template.Template {
248248

249249
return used
250250
},
251-
"getMetricOptionalAttributes": func(attrs map[AttributeName]Attribute) []AttributeName {
251+
"getMetricConditionalAttributes": func(attrs map[AttributeName]Attribute) []AttributeName {
252252
seen := make(map[AttributeName]bool)
253253
used := make([]AttributeName, 0)
254254

255255
for _, event := range md.Metrics {
256256
for _, attribute := range event.Attributes {
257257
v, exists := attrs[attribute]
258-
if exists && v.Optional && !seen[attribute] {
258+
if exists && v.IsConditional() && !seen[attribute] {
259259
used = append(used, attribute)
260260
seen[attribute] = true
261261
}

cmd/mdatagen/internal/command_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ func TestRunContents(t *testing.T) {
193193
wantLogsGenerated: true,
194194
},
195195
{
196-
yml: "with_optional_attribute.yaml",
196+
yml: "with_conditional_attribute.yaml",
197197
wantStatusGenerated: true,
198198
wantReadmeGenerated: true,
199199
wantMetricsGenerated: true,

cmd/mdatagen/internal/loader.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,12 @@ import (
1515
"go.opentelemetry.io/collector/confmap/provider/fileprovider"
1616
)
1717

18-
func setAttributesFullName(attrs map[AttributeName]Attribute) {
18+
func setAttributeDefaultFields(attrs map[AttributeName]Attribute) {
1919
for k, v := range attrs {
2020
v.FullName = k
21+
if v.RequirementLevel == "" {
22+
v.RequirementLevel = AttributeRequirementLevelRecommended
23+
}
2124
attrs[k] = v
2225
}
2326
}
@@ -61,8 +64,8 @@ func LoadMetadata(filePath string) (Metadata, error) {
6164
return md, err
6265
}
6366

64-
setAttributesFullName(md.Attributes)
65-
setAttributesFullName(md.ResourceAttributes)
67+
setAttributeDefaultFields(md.Attributes)
68+
setAttributeDefaultFields(md.ResourceAttributes)
6669

6770
return md, nil
6871
}

cmd/mdatagen/internal/loader_test.go

Lines changed: 50 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ func TestLoadMetadata(t *testing.T) {
7878
Type: ValueType{
7979
ValueType: pcommon.ValueTypeStr,
8080
},
81-
FullName: "string.resource.attr",
81+
FullName: "string.resource.attr",
82+
RequirementLevel: AttributeRequirementLevelRecommended,
8283
},
8384
"string.enum.resource.attr": {
8485
Description: "Resource attribute with a known set of string values.",
@@ -87,31 +88,35 @@ func TestLoadMetadata(t *testing.T) {
8788
Type: ValueType{
8889
ValueType: pcommon.ValueTypeStr,
8990
},
90-
FullName: "string.enum.resource.attr",
91+
FullName: "string.enum.resource.attr",
92+
RequirementLevel: AttributeRequirementLevelRecommended,
9193
},
9294
"optional.resource.attr": {
9395
Description: "Explicitly disabled ResourceAttribute.",
9496
EnabledPtr: boolPtr(false),
9597
Type: ValueType{
9698
ValueType: pcommon.ValueTypeStr,
9799
},
98-
FullName: "optional.resource.attr",
100+
FullName: "optional.resource.attr",
101+
RequirementLevel: AttributeRequirementLevelRecommended,
99102
},
100103
"slice.resource.attr": {
101104
Description: "Resource attribute with a slice value.",
102105
EnabledPtr: boolPtr(true),
103106
Type: ValueType{
104107
ValueType: pcommon.ValueTypeSlice,
105108
},
106-
FullName: "slice.resource.attr",
109+
FullName: "slice.resource.attr",
110+
RequirementLevel: AttributeRequirementLevelRecommended,
107111
},
108112
"map.resource.attr": {
109113
Description: "Resource attribute with a map value.",
110114
EnabledPtr: boolPtr(true),
111115
Type: ValueType{
112116
ValueType: pcommon.ValueTypeMap,
113117
},
114-
FullName: "map.resource.attr",
118+
FullName: "map.resource.attr",
119+
RequirementLevel: AttributeRequirementLevelRecommended,
115120
},
116121
"string.resource.attr_disable_warning": {
117122
Description: "Resource attribute with any string value.",
@@ -122,7 +127,8 @@ func TestLoadMetadata(t *testing.T) {
122127
Type: ValueType{
123128
ValueType: pcommon.ValueTypeStr,
124129
},
125-
FullName: "string.resource.attr_disable_warning",
130+
FullName: "string.resource.attr_disable_warning",
131+
RequirementLevel: AttributeRequirementLevelRecommended,
126132
},
127133
"string.resource.attr_remove_warning": {
128134
Description: "Resource attribute with any string value.",
@@ -133,7 +139,8 @@ func TestLoadMetadata(t *testing.T) {
133139
Type: ValueType{
134140
ValueType: pcommon.ValueTypeStr,
135141
},
136-
FullName: "string.resource.attr_remove_warning",
142+
FullName: "string.resource.attr_remove_warning",
143+
RequirementLevel: AttributeRequirementLevelRecommended,
137144
},
138145
"string.resource.attr_to_be_removed": {
139146
Description: "Resource attribute with any string value.",
@@ -144,7 +151,8 @@ func TestLoadMetadata(t *testing.T) {
144151
Type: ValueType{
145152
ValueType: pcommon.ValueTypeStr,
146153
},
147-
FullName: "string.resource.attr_to_be_removed",
154+
FullName: "string.resource.attr_to_be_removed",
155+
RequirementLevel: AttributeRequirementLevelRecommended,
148156
},
149157
},
150158

@@ -156,67 +164,82 @@ func TestLoadMetadata(t *testing.T) {
156164
Type: ValueType{
157165
ValueType: pcommon.ValueTypeStr,
158166
},
159-
FullName: "enum_attr",
167+
FullName: "enum_attr",
168+
RequirementLevel: AttributeRequirementLevelRecommended,
160169
},
161170
"string_attr": {
162171
Description: "Attribute with any string value.",
163172
NameOverride: "",
164173
Type: ValueType{
165174
ValueType: pcommon.ValueTypeStr,
166175
},
167-
FullName: "string_attr",
176+
FullName: "string_attr",
177+
RequirementLevel: AttributeRequirementLevelRecommended,
168178
},
169179
"overridden_int_attr": {
170180
Description: "Integer attribute with overridden name.",
171181
NameOverride: "state",
172182
Type: ValueType{
173183
ValueType: pcommon.ValueTypeInt,
174184
},
175-
FullName: "overridden_int_attr",
185+
FullName: "overridden_int_attr",
186+
RequirementLevel: AttributeRequirementLevelRecommended,
176187
},
177188
"boolean_attr": {
178189
Description: "Attribute with a boolean value.",
179190
Type: ValueType{
180191
ValueType: pcommon.ValueTypeBool,
181192
},
182-
FullName: "boolean_attr",
193+
FullName: "boolean_attr",
194+
RequirementLevel: AttributeRequirementLevelRecommended,
183195
},
184196
"boolean_attr2": {
185197
Description: "Another attribute with a boolean value.",
186198
Type: ValueType{
187199
ValueType: pcommon.ValueTypeBool,
188200
},
189-
FullName: "boolean_attr2",
201+
FullName: "boolean_attr2",
202+
RequirementLevel: AttributeRequirementLevelRecommended,
190203
},
191204
"slice_attr": {
192205
Description: "Attribute with a slice value.",
193206
Type: ValueType{
194207
ValueType: pcommon.ValueTypeSlice,
195208
},
196-
FullName: "slice_attr",
209+
FullName: "slice_attr",
210+
RequirementLevel: AttributeRequirementLevelRecommended,
197211
},
198212
"map_attr": {
199213
Description: "Attribute with a map value.",
200214
Type: ValueType{
201215
ValueType: pcommon.ValueTypeMap,
202216
},
203-
FullName: "map_attr",
217+
FullName: "map_attr",
218+
RequirementLevel: AttributeRequirementLevelRecommended,
204219
},
205-
"optional_int_attr": {
206-
Description: "An optional attribute with an integer value",
220+
"conditional_int_attr": {
221+
Description: "A conditional attribute with an integer value",
207222
Type: ValueType{
208223
ValueType: pcommon.ValueTypeInt,
209224
},
210-
FullName: "optional_int_attr",
211-
Optional: true,
225+
FullName: "conditional_int_attr",
226+
RequirementLevel: AttributeRequirementLevelConditionallyRequired,
212227
},
213-
"optional_string_attr": {
214-
Description: "An optional attribute with any string value",
228+
"conditional_string_attr": {
229+
Description: "A conditional attribute with any string value",
215230
Type: ValueType{
216231
ValueType: pcommon.ValueTypeStr,
217232
},
218-
FullName: "optional_string_attr",
219-
Optional: true,
233+
FullName: "conditional_string_attr",
234+
RequirementLevel: AttributeRequirementLevelConditionallyRequired,
235+
},
236+
"opt_in_bool_attr": {
237+
Description: "An opt-in attribute with a boolean value",
238+
Type: ValueType{
239+
ValueType: pcommon.ValueTypeBool,
240+
},
241+
FullName: "opt_in_bool_attr",
242+
RequirementLevel: AttributeRequirementLevelOptIn,
220243
},
221244
},
222245
Metrics: map[MetricName]Metric{
@@ -228,7 +251,7 @@ func TestLoadMetadata(t *testing.T) {
228251
Warnings: Warnings{
229252
IfEnabledNotSet: "This metric will be disabled by default soon.",
230253
},
231-
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "optional_int_attr", "optional_string_attr"},
254+
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "conditional_int_attr", "conditional_string_attr", "opt_in_bool_attr"},
232255
},
233256
Unit: strPtr("s"),
234257
Sum: &Sum{
@@ -244,7 +267,7 @@ func TestLoadMetadata(t *testing.T) {
244267
Warnings: Warnings{
245268
IfConfigured: "This metric is deprecated and will be removed soon.",
246269
},
247-
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "optional_string_attr"},
270+
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "conditional_string_attr"},
248271
},
249272
Unit: strPtr("1"),
250273
Gauge: &Gauge{
@@ -305,7 +328,7 @@ func TestLoadMetadata(t *testing.T) {
305328
Warnings: Warnings{
306329
IfEnabledNotSet: "This event will be disabled by default soon.",
307330
},
308-
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "optional_int_attr", "optional_string_attr"},
331+
Attributes: []AttributeName{"string_attr", "overridden_int_attr", "enum_attr", "slice_attr", "map_attr", "conditional_int_attr", "conditional_string_attr", "opt_in_bool_attr"},
309332
},
310333
},
311334
"default.event.to_be_renamed": {
@@ -316,7 +339,7 @@ func TestLoadMetadata(t *testing.T) {
316339
Warnings: Warnings{
317340
IfConfigured: "This event is deprecated and will be renamed soon.",
318341
},
319-
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "optional_string_attr"},
342+
Attributes: []AttributeName{"string_attr", "boolean_attr", "boolean_attr2", "conditional_string_attr"},
320343
},
321344
},
322345
"default.event.to_be_removed": {

cmd/mdatagen/internal/metadata.go

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,35 @@ func validateEvents(events map[EventName]Event, attributes map[AttributeName]Att
217217

218218
type AttributeName string
219219

220+
// AttributeRequirementLevel defines the requirement level of an attribute.
221+
type AttributeRequirementLevel string
222+
223+
const (
224+
// AttributeRequirementLevelRequired means the attribute is always included and cannot be excluded.
225+
AttributeRequirementLevelRequired AttributeRequirementLevel = "required"
226+
// AttributeRequirementLevelConditionallyRequired means the attribute is included by default when certain conditions are met.
227+
AttributeRequirementLevelConditionallyRequired AttributeRequirementLevel = "conditionally_required"
228+
// AttributeRequirementLevelRecommended means the attribute is included by default but can be disabled via configuration.
229+
AttributeRequirementLevelRecommended AttributeRequirementLevel = "recommended"
230+
// AttributeRequirementLevelOptIn means the attribute is not included unless explicitly enabled in user config.
231+
AttributeRequirementLevelOptIn AttributeRequirementLevel = "opt_in"
232+
)
233+
234+
// String returns capitalized display name of the requirement level for documentation.
235+
func (rl AttributeRequirementLevel) String() string {
236+
switch rl {
237+
case AttributeRequirementLevelRequired:
238+
return "Required"
239+
case AttributeRequirementLevelConditionallyRequired:
240+
return "Conditionally Required"
241+
case AttributeRequirementLevelRecommended:
242+
return "Recommended"
243+
case AttributeRequirementLevelOptIn:
244+
return "Opt-In"
245+
}
246+
return ""
247+
}
248+
220249
func (mn AttributeName) Render() (string, error) {
221250
return FormatIdentifier(string(mn), true)
222251
}
@@ -311,8 +340,32 @@ type Attribute struct {
311340
FullName AttributeName `mapstructure:"-"`
312341
// Warnings that will be shown to user under specified conditions.
313342
Warnings Warnings `mapstructure:"warnings"`
314-
// Optional defines whether the attribute is required.
315-
Optional bool `mapstructure:"optional"`
343+
// RequirementLevel defines the requirement level of the attribute.
344+
RequirementLevel AttributeRequirementLevel `mapstructure:"requirement_level"`
345+
}
346+
347+
// IsConditional returns true if the attribute is conditionally required.
348+
func (a Attribute) IsConditional() bool {
349+
return a.RequirementLevel == AttributeRequirementLevelConditionallyRequired
350+
}
351+
352+
// UnmarshalText implements the encoding.TextUnmarshaler interface.
353+
func (rl *AttributeRequirementLevel) UnmarshalText(text []byte) error {
354+
switch string(text) {
355+
case "required":
356+
*rl = AttributeRequirementLevelRequired
357+
case "conditionally_required":
358+
*rl = AttributeRequirementLevelConditionallyRequired
359+
case "recommended":
360+
*rl = AttributeRequirementLevelRecommended
361+
case "opt_in":
362+
*rl = AttributeRequirementLevelOptIn
363+
case "":
364+
*rl = AttributeRequirementLevelRecommended
365+
default:
366+
return fmt.Errorf("invalid requirement_level %q", string(text))
367+
}
368+
return nil
316369
}
317370

318371
// Enabled returns the boolean value of EnabledPtr.
@@ -380,9 +433,9 @@ type Signal struct {
380433
Attributes []AttributeName `mapstructure:"attributes"`
381434
}
382435

383-
func (s Signal) HasOptionalAttribute(attrs map[AttributeName]Attribute) bool {
436+
func (s Signal) HasConditionalAttributes(attrs map[AttributeName]Attribute) bool {
384437
for _, attr := range s.Attributes {
385-
if v, exists := attrs[attr]; exists && v.Optional {
438+
if v, exists := attrs[attr]; exists && v.IsConditional() {
386439
return true
387440
}
388441
}

0 commit comments

Comments
 (0)