Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .chloggen/feat_13781.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: "enhancement"

# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
component: "cmd/mdatagen"

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: "Add lint/ordering validation for metadata.yaml"

# One or more tracking issues or pull requests related to the change
issues: [13781]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
2 changes: 1 addition & 1 deletion cmd/mdatagen/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ require (
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.27.0
golang.org/x/text v0.29.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -77,7 +78,6 @@ require (
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/grpc v1.76.0 // indirect
google.golang.org/protobuf v1.36.10 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace go.opentelemetry.io/collector/component => ../../component
Expand Down
69 changes: 69 additions & 0 deletions cmd/mdatagen/internal/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"github.com/spf13/cobra"
"golang.org/x/text/cases"
"golang.org/x/text/language"
"gopkg.in/yaml.v3"
)

const (
Expand Down Expand Up @@ -77,6 +78,15 @@ func run(ymlPath string) error {
ymlDir := filepath.Dir(ymlPath)
packageName := filepath.Base(ymlDir)

raw, readErr := os.ReadFile(ymlPath) //nolint:gosec // G304: abs path is cleaned/validated above; safe to read
if readErr != nil {
return fmt.Errorf("failed reading %v: %w", ymlPath, readErr)
}

if err = validateYAMLKeyOrder(raw); err != nil {
return fmt.Errorf("metadata.yaml ordering check failed: %w", err)
}

md, err := LoadMetadata(ymlPath)
if err != nil {
return fmt.Errorf("failed loading %v: %w", ymlPath, err)
Expand Down Expand Up @@ -404,3 +414,62 @@ func generateFile(tmplFile, outputFile string, md Metadata, goPackage string) er

return formatErr
}

func validateMappingKeysSorted(root *yaml.Node, path ...string) error {
// unwrap doc
n := root
if n.Kind == yaml.DocumentNode && len(n.Content) > 0 {
n = n.Content[0]
}
// follow path
for _, seg := range path {
if n.Kind != yaml.MappingNode {
return nil
}
var next *yaml.Node
for i := 0; i < len(n.Content); i += 2 {
if n.Content[i].Value == seg {
next = n.Content[i+1]
break
}
}
if next == nil {
return nil
}
n = next
}
if n.Kind != yaml.MappingNode {
return nil
}

// collect keys
keys := make([]string, 0, len(n.Content)/2)
for i := 0; i < len(n.Content); i += 2 {
keys = append(keys, n.Content[i].Value)
}

if !slices.IsSorted(keys) {
return fmt.Errorf("%v keys are not sorted: %v", path, keys)
}
return nil
}

// ValidateYAMLKeyOrder checks the sections we care about.
func validateYAMLKeyOrder(raw []byte) error {
var doc yaml.Node
if err := yaml.Unmarshal(raw, &doc); err != nil {
return err
}
for _, p := range [][]string{
{"resource_attributes"},
{"attributes"},
{"metrics"},
{"events"},
{"telemetry", "metrics"},
} {
if err := validateMappingKeysSorted(&doc, p...); err != nil {
return err
}
}
return nil
}
10 changes: 10 additions & 0 deletions cmd/mdatagen/internal/command_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,12 +48,17 @@ func TestRunContents(t *testing.T) {
wantGoleakSetup bool
wantGoleakTeardown bool
wantErr bool
wantOrderErr bool
wantAttributes []string
}{
{
yml: "invalid.yaml",
wantErr: true,
},
{
yml: "unsorted_rattr.yaml",
wantOrderErr: true,
},
{
yml: "basic_connector.yaml",
wantErr: false,
Expand Down Expand Up @@ -234,6 +239,11 @@ foo
require.NoError(t, os.WriteFile(filepath.Join(tmpdir, generatedPackageDir, "generated_component_test.go"), []byte("test"), 0o600))

err = run(metadataFile)
if tt.wantOrderErr {
require.Error(t, err)
require.Contains(t, err.Error(), "metadata.yaml ordering check failed")
return
}
require.NoError(t, err)

var contents []byte
Expand Down
104 changes: 53 additions & 51 deletions cmd/mdatagen/internal/sampleconnector/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,9 @@ status:
- Any additional information that should be brought to the consumer's attention

resource_attributes:
string.resource.attr:
description: Resource attribute with any string value.
type: string
enabled: true

string.enum.resource.attr:
description: Resource attribute with a known set of string values.
type: string
enum: [one, two]
map.resource.attr:
description: Resource attribute with a map value.
type: map
enabled: true

optional.resource.attr:
Expand All @@ -39,9 +33,15 @@ resource_attributes:
type: slice
enabled: true

map.resource.attr:
description: Resource attribute with a map value.
type: map
string.enum.resource.attr:
description: Resource attribute with a known set of string values.
type: string
enum: [one, two]
enabled: true

string.resource.attr:
description: Resource attribute with any string value.
type: string
enabled: true

string.resource.attr_disable_warning:
Expand All @@ -66,20 +66,6 @@ resource_attributes:
if_enabled: This resource_attribute is deprecated and will be removed soon.

attributes:
string_attr:
description: Attribute with any string value.
type: string

overridden_int_attr:
name_override: state
description: Integer attribute with overridden name.
type: int

enum_attr:
description: Attribute with a known set of string values.
type: string
enum: [red, green, blue]

boolean_attr:
description: Attribute with a boolean value.
type: bool
Expand All @@ -90,14 +76,28 @@ attributes:
description: Another attribute with a boolean value.
type: bool

slice_attr:
description: Attribute with a slice value.
type: slice
enum_attr:
description: Attribute with a known set of string values.
type: string
enum: [red, green, blue]

map_attr:
description: Attribute with a map value.
type: map

overridden_int_attr:
name_override: state
description: Integer attribute with overridden name.
type: int

slice_attr:
description: Attribute with a slice value.
type: slice

string_attr:
description: Attribute with any string value.
type: string

metrics:
default.metric:
enabled: true
Expand All @@ -108,30 +108,11 @@ metrics:
value_type: int
monotonic: true
aggregation_temporality: cumulative
attributes: [string_attr, overridden_int_attr, enum_attr, slice_attr, map_attr]
attributes:
[string_attr, overridden_int_attr, enum_attr, slice_attr, map_attr]
warnings:
if_enabled_not_set: This metric will be disabled by default soon.

optional.metric:
enabled: false
description: "[DEPRECATED] Gauge double metric disabled by default."
unit: "1"
gauge:
value_type: double
attributes: [string_attr, boolean_attr, boolean_attr2]
warnings:
if_configured: This metric is deprecated and will be removed soon.

optional.metric.empty_unit:
enabled: false
description: "[DEPRECATED] Gauge double metric disabled by default."
unit: ""
gauge:
value_type: double
attributes: [string_attr, boolean_attr]
warnings:
if_configured: This metric is deprecated and will be removed soon.

default.metric.to_be_removed:
enabled: true
description: "[DEPRECATED] Non-monotonic delta sum double metric enabled by default."
Expand All @@ -153,4 +134,25 @@ metrics:
input_type: string
monotonic: true
aggregation_temporality: cumulative
attributes: [ string_attr, overridden_int_attr, enum_attr, slice_attr, map_attr ]
attributes:
[string_attr, overridden_int_attr, enum_attr, slice_attr, map_attr]

optional.metric:
enabled: false
description: "[DEPRECATED] Gauge double metric disabled by default."
unit: "1"
gauge:
value_type: double
attributes: [string_attr, boolean_attr, boolean_attr2]
warnings:
if_configured: This metric is deprecated and will be removed soon.

optional.metric.empty_unit:
enabled: false
description: "[DEPRECATED] Gauge double metric disabled by default."
unit: ""
gauge:
value_type: double
attributes: [string_attr, boolean_attr]
warnings:
if_configured: This metric is deprecated and will be removed soon.
24 changes: 12 additions & 12 deletions cmd/mdatagen/internal/sampleprocessor/metadata.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,9 @@ status:
- Any additional information that should be brought to the consumer's attention

resource_attributes:
string.resource.attr:
description: Resource attribute with any string value.
type: string
enabled: true

string.enum.resource.attr:
description: Resource attribute with a known set of string values.
type: string
enum: [one, two]
map.resource.attr:
description: Resource attribute with a map value.
type: map
enabled: true

optional.resource.attr:
Expand All @@ -42,9 +36,15 @@ resource_attributes:
type: slice
enabled: true

map.resource.attr:
description: Resource attribute with a map value.
type: map
string.enum.resource.attr:
description: Resource attribute with a known set of string values.
type: string
enum: [one, two]
enabled: true

string.resource.attr:
description: Resource attribute with any string value.
type: string
enabled: true

string.resource.attr_disable_warning:
Expand Down
Loading