diff --git a/.github/workflows/utils/cspell.json b/.github/workflows/utils/cspell.json index dcec12199e6..a681c8f08a3 100644 --- a/.github/workflows/utils/cspell.json +++ b/.github/workflows/utils/cspell.json @@ -313,6 +313,7 @@ "multiclient", "multimod", "mycert", + "mycomponent", "myconnector", "myexporter", "myextension", diff --git a/cmd/mdatagen/README.md b/cmd/mdatagen/README.md index 99469c03d01..7796325c1e1 100644 --- a/cmd/mdatagen/README.md +++ b/cmd/mdatagen/README.md @@ -1,23 +1,25 @@ # Metadata Generator -| Status | | -| ------------- |-----------| -| Stability | [alpha]: metrics | -| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Acmd%2Fmdatagen%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Acmd%2Fmdatagen) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Acmd%2Fmdatagen%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Acmd%2Fmdatagen) | -| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@dmitryax](https://www.github.com/dmitryax) | + +| Status | | +| -------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Stability | [alpha]: metrics | +| Issues | [![Open issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aopen%20label%3Acmd%2Fmdatagen%20&label=open&color=orange&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aopen+is%3Aissue+label%3Acmd%2Fmdatagen) [![Closed issues](https://img.shields.io/github/issues-search/open-telemetry/opentelemetry-collector?query=is%3Aissue%20is%3Aclosed%20label%3Acmd%2Fmdatagen%20&label=closed&color=blue&logo=opentelemetry)](https://github.com/open-telemetry/opentelemetry-collector/issues?q=is%3Aclosed+is%3Aissue+label%3Acmd%2Fmdatagen) | +| [Code Owners](https://github.com/open-telemetry/opentelemetry-collector-contrib/blob/main/CONTRIBUTING.md#becoming-a-code-owner) | [@dmitryax](https://www.github.com/dmitryax) | [alpha]: https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#alpha + Every component's documentation should include a brief description of the component and guidance on how to use it. There is also some information about the component (or metadata) that should be included to help end-users understand the current state of the component and whether it is right for their use case. Examples of this metadata about a component are: -* its stability level -* the distributions containing it -* the types of pipelines it supports -* metrics emitted in the case of a scraping receiver, a scraper, or a connector +- its stability level +- the distributions containing it +- the types of pipelines it supports +- metrics emitted in the case of a scraping receiver, a scraper, or a connector The metadata generator defines a schema for specifying this information to ensure it is complete and well-formed. The metadata generator is then able to ingest the metadata, validate it against the schema and produce documentation in a standardized format. @@ -26,10 +28,12 @@ An example of how this generated documentation looks can be found in [documentat ## Using the Metadata Generator In order for a component to benefit from the metadata generator (`mdatagen`) these requirements need to be met: + 1. A yaml file containing the metadata that needs to be included in the component 2. The component should declare a `go:generate mdatagen` directive which tells `mdatagen` what to generate As an example, here is a minimal `metadata.yaml` for the [OTLP receiver](https://github.com/open-telemetry/opentelemetry-collector/tree/main/receiver/otlpreceiver): + ```yaml type: otlp status: @@ -42,6 +46,7 @@ status: Detailed information about the schema of `metadata.yaml` can be found in [metadata-schema.yaml](./metadata-schema.yaml). The `go:generate mdatagen` directive is usually defined in a `doc.go` file in the same package as the component, for example: + ```go //go:generate mdatagen metadata.yaml @@ -50,11 +55,48 @@ package main Below are some more examples that can be used for reference: -* The ElasticSearch receiver has an extensive [metadata.yaml](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/elasticsearchreceiver/metadata.yaml) -* The host metrics receiver has internal subcomponents, each with their own `metadata.yaml` and `doc.go`. See [cpuscraper](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/hostmetricsreceiver/internal/scraper/cpuscraper) for example. +- The ElasticSearch receiver has an extensive [metadata.yaml](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/elasticsearchreceiver/metadata.yaml) +- The host metrics receiver has internal subcomponents, each with their own `metadata.yaml` and `doc.go`. See [cpuscraper](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/receiver/hostmetricsreceiver/internal/scraper/cpuscraper) for example. You can run `cd cmd/mdatagen && $(GOCMD) install .` to install the `mdatagen` tool in `GOBIN` and then run `mdatagen metadata.yaml` to generate documentation for a specific component or you can run `make generate` to generate documentation for all components. +### Feature Gates Documentation + +The metadata generator supports automatic documentation generation for feature gates used by components. Feature gates are documented by adding a `feature_gates` section to your `metadata.yaml`: + +```yaml +type: mycomponent +status: + class: receiver + stability: + beta: [metrics, traces] + +feature_gates: + - id: mycomponent.newFeature + description: 'Enables new feature functionality that improves performance' + stage: alpha + from_version: 'v0.100.0' + reference_url: 'https://github.com/open-telemetry/opentelemetry-collector/issues/12345' + + - id: mycomponent.stableFeature + description: 'A feature that has reached stability' + stage: stable + from_version: 'v0.90.0' + to_version: 'v0.95.0' + reference_url: 'https://github.com/open-telemetry/opentelemetry-collector/issues/11111' +``` + +This will generate a "Feature Gates" section in the component's `documentation.md` file with a table containing: + +- **Feature Gate**: The gate identifier +- **Stage**: The lifecycle stage (alpha, beta, stable, deprecated) +- **Description**: Brief description of what the gate controls +- **From Version**: Version when the gate was introduced +- **To Version**: Version when stable/deprecated gates will be removed (if applicable) +- **Reference**: Link to additional contextual information + +The feature gate definitions should correspond to actual gates registered in your component code using the [Feature Gates API](../../featuregate/README.md). + ### Generate multiple metadata packages By default, `mdatagen` will generate a package called `metadata` in the `internal` directory. If you want to generate a package with a different name, you can use the `generated_package_name` configuration field to provide an alternate name. diff --git a/cmd/mdatagen/internal/command.go b/cmd/mdatagen/internal/command.go index 3e41a5e77f8..fa1c465a6e3 100644 --- a/cmd/mdatagen/internal/command.go +++ b/cmd/mdatagen/internal/command.go @@ -172,7 +172,7 @@ func run(ymlPath string) error { } } - if len(md.Metrics) != 0 || len(md.Telemetry.Metrics) != 0 || len(md.ResourceAttributes) != 0 || len(md.Events) != 0 { // if there's metrics or internal metrics or events, generate documentation for them + if len(md.Metrics) != 0 || len(md.Telemetry.Metrics) != 0 || len(md.ResourceAttributes) != 0 || len(md.Events) != 0 || len(md.FeatureGates) != 0 { // if there's metrics or internal metrics or events or feature gates, generate documentation for them toGenerate[filepath.Join(tmplDir, "documentation.md.tmpl")] = filepath.Join(ymlDir, "documentation.md") } diff --git a/cmd/mdatagen/internal/command_test.go b/cmd/mdatagen/internal/command_test.go index 2a70c4ef693..2b48c5b5c5a 100644 --- a/cmd/mdatagen/internal/command_test.go +++ b/cmd/mdatagen/internal/command_test.go @@ -47,6 +47,7 @@ func TestRunContents(t *testing.T) { wantGoleakSkip bool wantGoleakSetup bool wantGoleakTeardown bool + wantDocumentationGenerated bool wantErr bool wantOrderErr bool wantAttributes []string @@ -192,6 +193,13 @@ func TestRunContents(t *testing.T) { wantComponentTestGenerated: true, wantLogsGenerated: true, }, + { + yml: "feature_gates.yaml", + wantStatusGenerated: true, + wantReadmeGenerated: true, + wantComponentTestGenerated: true, + wantDocumentationGenerated: true, + }, { yml: "with_conditional_attribute.yaml", wantStatusGenerated: true, @@ -301,7 +309,9 @@ foo require.NoFileExists(t, filepath.Join(tmpdir, generatedPackageDir, "generated_telemetry.go")) } - if !tt.wantMetricsGenerated && !tt.wantTelemetryGenerated && !tt.wantResourceAttributesGenerated && !tt.wantEventsGenerated { + if tt.wantDocumentationGenerated { + require.FileExists(t, filepath.Join(tmpdir, "documentation.md")) + } else if !tt.wantMetricsGenerated && !tt.wantTelemetryGenerated && !tt.wantResourceAttributesGenerated && !tt.wantEventsGenerated { require.NoFileExists(t, filepath.Join(tmpdir, "documentation.md")) } diff --git a/cmd/mdatagen/internal/embedded_templates_test.go b/cmd/mdatagen/internal/embedded_templates_test.go index c3f7c0bcf3c..b52dd4feff3 100644 --- a/cmd/mdatagen/internal/embedded_templates_test.go +++ b/cmd/mdatagen/internal/embedded_templates_test.go @@ -40,6 +40,7 @@ func TestEnsureTemplatesLoaded(t *testing.T) { path.Join(rootDir, "telemetrytest.go.tmpl"): {}, path.Join(rootDir, "telemetrytest_test.go.tmpl"): {}, path.Join(rootDir, "helper.tmpl"): {}, + path.Join(rootDir, "feature_gates.md.tmpl"): {}, } count = 0 ) diff --git a/cmd/mdatagen/internal/metadata.go b/cmd/mdatagen/internal/metadata.go index d5355273e72..bb797f28ecb 100644 --- a/cmd/mdatagen/internal/metadata.go +++ b/cmd/mdatagen/internal/metadata.go @@ -48,6 +48,9 @@ type Metadata struct { Tests Tests `mapstructure:"tests"` // PackageName is the name of the package where the component is defined. PackageName string `mapstructure:"package_name"` + // FeatureGates that are managed by the component. + FeatureGates []FeatureGate `mapstructure:"feature_gates"` + // FeatureGates that are managed by the component. } func (md Metadata) GetCodeCovComponentID() string { @@ -89,6 +92,10 @@ func (md *Metadata) Validate() error { errs = errors.Join(errs, err) } + if err := md.validateFeatureGates(); err != nil { + errs = errors.Join(errs, err) + } + return errs } @@ -270,6 +277,74 @@ func validateEvents(events map[EventName]Event, attributes map[AttributeName]Att return errs } +func (md *Metadata) validateFeatureGates() error { + var errs error + seen := make(map[FeatureGateID]bool) + idRegexp := regexp.MustCompile(`^[0-9a-zA-Z.]*$`) + + // Validate that feature gates are sorted by ID + if !slices.IsSortedFunc(md.FeatureGates, func(a, b FeatureGate) int { + return strings.Compare(string(a.ID), string(b.ID)) + }) { + errs = errors.Join(errs, errors.New("feature gates must be sorted by ID")) + } + + for i, gate := range md.FeatureGates { + // Validate gate ID is not empty + if string(gate.ID) == "" { + errs = errors.Join(errs, fmt.Errorf("feature gate at index %d: ID cannot be empty", i)) + continue + } + + // Validate ID follows the allowed character pattern + if !idRegexp.MatchString(string(gate.ID)) { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": ID contains invalid characters, must match ^[0-9a-zA-Z.]*$`, gate.ID)) + } + + // Check for duplicate IDs + if seen[gate.ID] { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": duplicate ID`, gate.ID)) + continue + } + seen[gate.ID] = true + + // Validate gate has required fields + if gate.Description == "" { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": description is required`, gate.ID)) + } + + // Validate that each feature gate has a reference link + if gate.ReferenceURL == "" { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": reference_url is required`, gate.ID)) + } + + // Validate stage is one of the allowed values + validStages := map[FeatureGateStage]bool{ + FeatureGateStageAlpha: true, + FeatureGateStageBeta: true, + FeatureGateStageStable: true, + FeatureGateStageDeprecated: true, + } + if !validStages[gate.Stage] { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": invalid stage "%v", must be one of: alpha, beta, stable, deprecated`, gate.ID, gate.Stage)) + } + + // Validate version formats if provided + if gate.FromVersion != "" && !strings.HasPrefix(gate.FromVersion, "v") { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": from_version "%v" must start with 'v'`, gate.ID, gate.FromVersion)) + } + if gate.ToVersion != "" && !strings.HasPrefix(gate.ToVersion, "v") { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": to_version "%v" must start with 'v'`, gate.ID, gate.ToVersion)) + } + + // Validate that stable/deprecated gates should have to_version + if (gate.Stage == FeatureGateStageStable || gate.Stage == FeatureGateStageDeprecated) && gate.ToVersion == "" { + errs = errors.Join(errs, fmt.Errorf(`feature gate "%v": to_version is required for %v stage gates`, gate.ID, gate.Stage)) + } + } + return errs +} + type AttributeName string // AttributeRequirementLevel defines the requirement level of an attribute. @@ -521,3 +596,32 @@ type EntityAttributeRef struct { // Ref is the reference to a resource attribute. Ref AttributeName `mapstructure:"ref"` } + +// FeatureGateID represents the identifier for a feature gate. +type FeatureGateID string + +// FeatureGateStage represents the lifecycle stage of a feature gate. +type FeatureGateStage string + +const ( + FeatureGateStageAlpha FeatureGateStage = "alpha" + FeatureGateStageBeta FeatureGateStage = "beta" + FeatureGateStageStable FeatureGateStage = "stable" + FeatureGateStageDeprecated FeatureGateStage = "deprecated" +) + +// FeatureGate represents a feature gate definition in metadata. +type FeatureGate struct { + // ID is the unique identifier for the feature gate. + ID FeatureGateID `mapstructure:"id"` + // Description of the feature gate. + Description string `mapstructure:"description"` + // Stage is the lifecycle stage of the feature gate. + Stage FeatureGateStage `mapstructure:"stage"` + // FromVersion is the version when the feature gate was introduced. + FromVersion string `mapstructure:"from_version"` + // ToVersion is the version when the feature gate reached stable stage. + ToVersion string `mapstructure:"to_version"` + // ReferenceURL is the URL with contextual information about the feature gate. + ReferenceURL string `mapstructure:"reference_url"` +} diff --git a/cmd/mdatagen/internal/metadata_test.go b/cmd/mdatagen/internal/metadata_test.go index fd811c60acb..6f567455edf 100644 --- a/cmd/mdatagen/internal/metadata_test.go +++ b/cmd/mdatagen/internal/metadata_test.go @@ -323,3 +323,185 @@ func TestAttributeRequirementLevelUnmarshalText(t *testing.T) { }) } } + +func TestValidateFeatureGates(t *testing.T) { + tests := []struct { + name string + featureGate FeatureGate + wantErr string + }{ + { + name: "valid alpha gate", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature gate", + Stage: FeatureGateStageAlpha, + FromVersion: "v0.100.0", + ReferenceURL: "https://example.com", + }, + }, + { + name: "valid stable gate with to_version", + featureGate: FeatureGate{ + ID: "component.stable", + Description: "Stable feature gate", + Stage: FeatureGateStageStable, + FromVersion: "v0.90.0", + ToVersion: "v0.95.0", + ReferenceURL: "https://example.com", + }, + }, + { + name: "empty description", + featureGate: FeatureGate{ + ID: "component.feature", + Stage: FeatureGateStageAlpha, + FromVersion: "v0.100.0", + }, + wantErr: `description is required`, + }, + { + name: "invalid stage", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature", + Stage: "invalid", + FromVersion: "v0.100.0", + }, + wantErr: `invalid stage "invalid"`, + }, + { + name: "from_version without v prefix", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature", + Stage: FeatureGateStageAlpha, + FromVersion: "0.100.0", + }, + wantErr: `from_version "0.100.0" must start with 'v'`, + }, + { + name: "to_version without v prefix", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature", + Stage: FeatureGateStageStable, + FromVersion: "v0.90.0", + ToVersion: "0.95.0", + }, + wantErr: `to_version "0.95.0" must start with 'v'`, + }, + { + name: "stable gate missing to_version", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature", + Stage: FeatureGateStageStable, + FromVersion: "v0.90.0", + }, + wantErr: `to_version is required for stable stage gates`, + }, + { + name: "deprecated gate missing to_version", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature", + Stage: FeatureGateStageDeprecated, + FromVersion: "v0.90.0", + }, + wantErr: `to_version is required for deprecated stage gates`, + }, + { + name: "missing reference_url", + featureGate: FeatureGate{ + ID: "component.feature", + Description: "Test feature", + Stage: FeatureGateStageAlpha, + FromVersion: "v0.100.0", + }, + wantErr: `reference_url is required`, + }, + { + name: "invalid characters in ID", + featureGate: FeatureGate{ + ID: "component.feature@invalid", + Description: "Test feature", + Stage: FeatureGateStageAlpha, + FromVersion: "v0.100.0", + ReferenceURL: "https://example.com", + }, + wantErr: `ID contains invalid characters`, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + md := &Metadata{ + FeatureGates: []FeatureGate{tt.featureGate}, + } + err := md.validateFeatureGates() + if tt.wantErr != "" { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErr) + } else { + require.NoError(t, err) + } + }) + } +} + +func TestValidateFeatureGatesEmptyID(t *testing.T) { + md := &Metadata{ + FeatureGates: []FeatureGate{ + { + Description: "Test", + Stage: FeatureGateStageAlpha, + }, + }, + } + err := md.validateFeatureGates() + require.Error(t, err) + assert.ErrorContains(t, err, "ID cannot be empty") +} + +func TestValidateFeatureGatesDuplicateID(t *testing.T) { + md := &Metadata{ + FeatureGates: []FeatureGate{ + { + ID: "component.feature", + Description: "Test feature", + Stage: FeatureGateStageAlpha, + }, + { + ID: "component.feature", + Description: "Duplicate feature", + Stage: FeatureGateStageAlpha, + }, + }, + } + err := md.validateFeatureGates() + require.Error(t, err) + assert.ErrorContains(t, err, "duplicate ID") +} + +func TestValidateFeatureGatesNotSorted(t *testing.T) { + md := &Metadata{ + FeatureGates: []FeatureGate{ + { + ID: "component.zebra", + Description: "Test feature", + Stage: FeatureGateStageAlpha, + ReferenceURL: "https://example.com", + }, + { + ID: "component.alpha", + Description: "Another feature", + Stage: FeatureGateStageAlpha, + ReferenceURL: "https://example.com", + }, + }, + } + err := md.validateFeatureGates() + require.Error(t, err) + assert.ErrorContains(t, err, "feature gates must be sorted by ID") +} diff --git a/cmd/mdatagen/internal/templates/documentation.md.tmpl b/cmd/mdatagen/internal/templates/documentation.md.tmpl index d8b88254404..1102905c738 100644 --- a/cmd/mdatagen/internal/templates/documentation.md.tmpl +++ b/cmd/mdatagen/internal/templates/documentation.md.tmpl @@ -266,3 +266,19 @@ The following telemetry is emitted by this component. {{- end }} {{- end }} + +{{- if .FeatureGates }} + +## Feature Gates + +This component has the following feature gates: + +| Feature Gate | Stage | Description | From Version | To Version | Reference | +| ------------ | ----- | ----------- | ------------ | ---------- | --------- | +{{- range .FeatureGates }} +| `{{ .ID }}` | {{ .Stage }} | {{ .Description }} | {{ if .FromVersion }}{{ .FromVersion }}{{ else }}N/A{{ end }} | {{ if .ToVersion }}{{ .ToVersion }}{{ else }}N/A{{ end }} | {{ if .ReferenceURL }}[Link]({{ .ReferenceURL }}){{ else }}N/A{{ end }} | +{{- end }} + +For more information about feature gates, see the [Feature Gates](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md) documentation. + +{{- end }} diff --git a/cmd/mdatagen/internal/templates/feature_gates.md.tmpl b/cmd/mdatagen/internal/templates/feature_gates.md.tmpl new file mode 100644 index 00000000000..a997a5f42e2 --- /dev/null +++ b/cmd/mdatagen/internal/templates/feature_gates.md.tmpl @@ -0,0 +1,15 @@ +{{- if len .FeatureGates }} + +## Feature Gates + +This component has the following feature gates: + +| Feature Gate | Stage | Description | From Version | To Version | Reference | +| ------------ | ----- | ----------- | ------------ | ---------- | --------- | +{{- range .FeatureGates }} +| `{{ .ID }}` | {{ .Stage }} | {{ .Description }} | {{ if .FromVersion }}{{ .FromVersion }}{{ else }}N/A{{ end }} | {{ if .ToVersion }}{{ .ToVersion }}{{ else }}N/A{{ end }} | {{ if .ReferenceURL }}[Link]({{ .ReferenceURL }}){{ else }}N/A{{ end }} | +{{- end }} + +For more information about feature gates, see the [Feature Gates](https://github.com/open-telemetry/opentelemetry-collector/blob/main/featuregate/README.md) documentation. + +{{- end }} \ No newline at end of file diff --git a/cmd/mdatagen/internal/testdata/feature_gates.yaml b/cmd/mdatagen/internal/testdata/feature_gates.yaml new file mode 100644 index 00000000000..11b6b089439 --- /dev/null +++ b/cmd/mdatagen/internal/testdata/feature_gates.yaml @@ -0,0 +1,19 @@ +type: sample +status: + class: receiver + stability: + beta: [metrics] + +feature_gates: + - id: sample.feature.gate + description: 'This is a sample feature gate for testing purposes' + stage: alpha + from_version: 'v0.100.0' + reference_url: 'https://github.com/open-telemetry/opentelemetry-collector/issues/12345' + + - id: stable.feature.gate + description: 'This is a stable feature gate' + stage: stable + from_version: 'v0.90.0' + to_version: 'v0.95.0' + reference_url: 'https://github.com/open-telemetry/opentelemetry-collector/issues/11111' diff --git a/cmd/mdatagen/internal/testdata/generated_component_test.go b/cmd/mdatagen/internal/testdata/generated_component_test.go index c8a33747a23..e193486d198 100644 --- a/cmd/mdatagen/internal/testdata/generated_component_test.go +++ b/cmd/mdatagen/internal/testdata/generated_component_test.go @@ -7,7 +7,6 @@ import ( "testing" "github.com/stretchr/testify/require" - "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/component/componenttest" "go.opentelemetry.io/collector/confmap/confmaptest" diff --git a/cmd/mdatagen/internal/testdata/generated_package_test.go b/cmd/mdatagen/internal/testdata/generated_package_test.go index baa8ae2580f..ec6e95f6eba 100644 --- a/cmd/mdatagen/internal/testdata/generated_package_test.go +++ b/cmd/mdatagen/internal/testdata/generated_package_test.go @@ -3,9 +3,8 @@ package testdata import ( - "testing" - "go.uber.org/goleak" + "testing" ) func TestMain(m *testing.M) { diff --git a/cmd/mdatagen/internal/testdata/internal/metadata/generated_status.go b/cmd/mdatagen/internal/testdata/internal/metadata/generated_status.go new file mode 100644 index 00000000000..66b7b2c30cf --- /dev/null +++ b/cmd/mdatagen/internal/testdata/internal/metadata/generated_status.go @@ -0,0 +1,16 @@ +// Code generated by mdatagen. DO NOT EDIT. + +package metadata + +import ( + "go.opentelemetry.io/collector/component" +) + +var ( + Type = component.MustNewType("sample") + ScopeName = "go.opentelemetry.io/collector/cmd/mdatagen/internal/testdata" +) + +const ( + MetricsStability = component.StabilityLevelBeta +) diff --git a/cmd/mdatagen/metadata-schema.yaml b/cmd/mdatagen/metadata-schema.yaml index e11880d26b3..cc3e63dd9ac 100644 --- a/cmd/mdatagen/metadata-schema.yaml +++ b/cmd/mdatagen/metadata-schema.yaml @@ -13,7 +13,7 @@ generated_package_name: string # Required for components (Optional for subcomponents): A high-level view of the development status and use of this component status: # Required: The class of the component (For example receiver) - class: + class: # Required: The stability of the component - See https://github.com/open-telemetry/opentelemetry-collector/blob/main/docs/component-stability.md#stability-levels stability: development: [] @@ -145,9 +145,6 @@ metrics: level: # Optional: the version current stability was introduced from: - # Optional: the reference to a semantic convention - semantic_convention: - ref: # Optional: map of event names with the key being the event name and value # being described below. @@ -232,3 +229,18 @@ telemetry: # Optional: array of attributes that were defined in the attributes section that are emitted by this metric. # Note: Only the following attribute types are supported: attributes: [string] + +# Optional: list of feature gate definitions. +feature_gates: + - # Required: unique identifier for the feature gate. + id: + # Required: description of the feature gate. + description: string + # Required: lifecycle stage of the feature gate. + stage: + # Optional: version when the feature gate was introduced. + from_version: string + # Optional: version when the feature gate reached stable stage (for stable/deprecated gates). + to_version: string + # Optional: URL with contextual information about the feature gate. + reference_url: string