From 7daa4440ca2916128a3cdca8b15886a371179dc5 Mon Sep 17 00:00:00 2001 From: Narcis Gemene <7252787+narcis96@users.noreply.github.com> Date: Thu, 24 Oct 2024 09:16:04 +0100 Subject: [PATCH] Update cmd/mdatagen/internal/sampleprocessor/metadata.yaml Co-authored-by: Pablo Baeyens --- cmd/mdatagen/internal/loader.go | 363 ------------------ cmd/mdatagen/internal/loader_test.go | 86 +---- cmd/mdatagen/internal/metadata.go | 77 ++++ cmd/mdatagen/internal/metadata_test.go | 68 ++++ .../internal/sampleprocessor/metadata.yaml | 6 +- .../internal/templates/feature_gates.go.tmpl | 12 +- 6 files changed, 155 insertions(+), 457 deletions(-) diff --git a/cmd/mdatagen/internal/loader.go b/cmd/mdatagen/internal/loader.go index 174e331c333d..028964605482 100644 --- a/cmd/mdatagen/internal/loader.go +++ b/cmd/mdatagen/internal/loader.go @@ -5,327 +5,14 @@ package internal // import "go.opentelemetry.io/collector/cmd/mdatagen/internal" import ( "context" - "errors" - "fmt" "os/exec" "path/filepath" - "regexp" "strings" - "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/config/configtelemetry" - "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/confmap/confmaptest" "go.opentelemetry.io/collector/confmap/provider/fileprovider" - "go.opentelemetry.io/collector/filter" - "go.opentelemetry.io/collector/pdata/pcommon" ) -var ( - // idRegexp is used to validate the ID of a Gate. - // IDs' characters must be alphanumeric or dots. - idRegexp = regexp.MustCompile(`^[0-9a-zA-Z\.]*$`) - versionRegexp = regexp.MustCompile(`^v(\d+)\.(\d+)\.(\d+)$`) - referenceURLRegexp = regexp.MustCompile(`^(https?:\/\/)?([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+)(\/[^\s]*)?$`) - validStages = map[string]bool{ - "StageAlpha": true, - "StageBeta": true, - "StageStable": true, - "StageDeprecated": true, - } -) - -type MetricName string - -func (mn MetricName) Render() (string, error) { - return FormatIdentifier(string(mn), true) -} - -func (mn MetricName) RenderUnexported() (string, error) { - return FormatIdentifier(string(mn), false) -} - -type AttributeName string - -func (mn AttributeName) Render() (string, error) { - return FormatIdentifier(string(mn), true) -} - -func (mn AttributeName) RenderUnexported() (string, error) { - return FormatIdentifier(string(mn), false) -} - -type featureGateName string - -func (mn featureGateName) Render() (string, error) { - return FormatIdentifier(string(mn), true) -} - -func (mn featureGateName) RenderUnexported() (string, error) { - return FormatIdentifier(string(mn), false) -} - -// ValueType defines an attribute value type. -type ValueType struct { - // ValueType is type of the attribute value. - ValueType pcommon.ValueType -} - -// UnmarshalText implements the encoding.TextUnmarshaler interface. -func (mvt *ValueType) UnmarshalText(text []byte) error { - switch vtStr := string(text); vtStr { - case "string": - mvt.ValueType = pcommon.ValueTypeStr - case "int": - mvt.ValueType = pcommon.ValueTypeInt - case "double": - mvt.ValueType = pcommon.ValueTypeDouble - case "bool": - mvt.ValueType = pcommon.ValueTypeBool - case "bytes": - mvt.ValueType = pcommon.ValueTypeBytes - case "slice": - mvt.ValueType = pcommon.ValueTypeSlice - case "map": - mvt.ValueType = pcommon.ValueTypeMap - default: - return fmt.Errorf("invalid type: %q", vtStr) - } - return nil -} - -// String returns capitalized name of the ValueType. -func (mvt ValueType) String() string { - return strings.Title(strings.ToLower(mvt.ValueType.String())) // nolint SA1019 -} - -// Primitive returns name of primitive type for the ValueType. -func (mvt ValueType) Primitive() string { - switch mvt.ValueType { - case pcommon.ValueTypeStr: - return "string" - case pcommon.ValueTypeInt: - return "int64" - case pcommon.ValueTypeDouble: - return "float64" - case pcommon.ValueTypeBool: - return "bool" - case pcommon.ValueTypeBytes: - return "[]byte" - case pcommon.ValueTypeSlice: - return "[]any" - case pcommon.ValueTypeMap: - return "map[string]any" - case pcommon.ValueTypeEmpty: - return "" - default: - return "" - } -} - -type stability struct { - Level string `mapstructure:"level"` - From string `mapstructure:"from"` -} - -func (s stability) String() string { - if len(s.Level) == 0 || strings.EqualFold(s.Level, component.StabilityLevelStable.String()) { - return "" - } - if len(s.From) > 0 { - return fmt.Sprintf(" [%s since %s]", s.Level, s.From) - } - return fmt.Sprintf(" [%s]", s.Level) -} - -type Metric struct { - // Enabled defines whether the metric is enabled by default. - Enabled bool `mapstructure:"enabled"` - - // Warnings that will be shown to user under specified conditions. - Warnings warnings `mapstructure:"warnings"` - - // Description of the metric. - Description string `mapstructure:"description"` - - // The stability level of the metric. - Stability stability `mapstructure:"stability"` - - // ExtendedDocumentation of the metric. If specified, this will - // be appended to the description used in generated documentation. - ExtendedDocumentation string `mapstructure:"extended_documentation"` - - // Optional can be used to specify metrics that may - // or may not be present in all cases, depending on configuration. - Optional bool `mapstructure:"optional"` - - // Unit of the metric. - Unit *string `mapstructure:"unit"` - - // Sum stores metadata for sum metric type - Sum *sum `mapstructure:"sum,omitempty"` - // Gauge stores metadata for gauge metric type - Gauge *gauge `mapstructure:"gauge,omitempty"` - // Histogram stores metadata for histogram metric type - Histogram *histogram `mapstructure:"histogram,omitempty"` - - // Attributes is the list of attributes that the metric emits. - Attributes []AttributeName `mapstructure:"attributes"` - - // Level specifies the minimum `configtelemetry.Level` for which - // the metric will be emitted. This only applies to internal telemetry - // configuration. - Level configtelemetry.Level `mapstructure:"level"` -} - -func (m *Metric) Unmarshal(parser *confmap.Conf) error { - if !parser.IsSet("enabled") { - return errors.New("missing required field: `enabled`") - } - return parser.Unmarshal(m) -} - -func (m Metric) Data() MetricData { - if m.Sum != nil { - return m.Sum - } - if m.Gauge != nil { - return m.Gauge - } - if m.Histogram != nil { - return m.Histogram - } - return nil -} - -type warnings struct { - // A warning that will be displayed if the field is enabled in user config. - IfEnabled string `mapstructure:"if_enabled"` - // A warning that will be displayed if `enabled` field is not set explicitly in user config. - IfEnabledNotSet string `mapstructure:"if_enabled_not_set"` - // A warning that will be displayed if the field is configured by user in any way. - IfConfigured string `mapstructure:"if_configured"` -} - -type Attribute struct { - // Description describes the purpose of the attribute. - Description string `mapstructure:"description"` - // NameOverride can be used to override the attribute name. - NameOverride string `mapstructure:"name_override"` - // Enabled defines whether the attribute is enabled by default. - Enabled bool `mapstructure:"enabled"` - // Include can be used to filter attributes. - Include []filter.Config `mapstructure:"include"` - // Include can be used to filter attributes. - Exclude []filter.Config `mapstructure:"exclude"` - // Enum can optionally describe the set of values to which the attribute can belong. - Enum []string `mapstructure:"enum"` - // Type is an attribute type. - Type ValueType `mapstructure:"type"` - // FullName is the attribute name populated from the map key. - FullName AttributeName `mapstructure:"-"` - // Warnings that will be shown to user under specified conditions. - Warnings warnings `mapstructure:"warnings"` -} - -// Name returns actual name of the attribute that is set on the metric after applying NameOverride. -func (a Attribute) Name() AttributeName { - if a.NameOverride != "" { - return AttributeName(a.NameOverride) - } - return a.FullName -} - -func (a Attribute) TestValue() string { - if a.Enum != nil { - return fmt.Sprintf(`"%s"`, a.Enum[0]) - } - switch a.Type.ValueType { - case pcommon.ValueTypeEmpty: - return "" - case pcommon.ValueTypeStr: - return fmt.Sprintf(`"%s-val"`, a.FullName) - case pcommon.ValueTypeInt: - return fmt.Sprintf("%d", len(a.FullName)) - case pcommon.ValueTypeDouble: - return fmt.Sprintf("%f", 0.1+float64(len(a.FullName))) - case pcommon.ValueTypeBool: - return fmt.Sprintf("%t", len(a.FullName)%2 == 0) - case pcommon.ValueTypeMap: - return fmt.Sprintf(`map[string]any{"key1": "%s-val1", "key2": "%s-val2"}`, a.FullName, a.FullName) - case pcommon.ValueTypeSlice: - return fmt.Sprintf(`[]any{"%s-item1", "%s-item2"}`, a.FullName, a.FullName) - case pcommon.ValueTypeBytes: - return fmt.Sprintf(`[]byte("%s-val")`, a.FullName) - } - return "" -} - -type ignore struct { - Top []string `mapstructure:"top"` - Any []string `mapstructure:"any"` -} - -type goLeak struct { - Skip bool `mapstructure:"skip"` - Ignore ignore `mapstructure:"ignore"` - Setup string `mapstructure:"setup"` - Teardown string `mapstructure:"teardown"` -} - -type tests struct { - Config any `mapstructure:"config"` - SkipLifecycle bool `mapstructure:"skip_lifecycle"` - SkipShutdown bool `mapstructure:"skip_shutdown"` - GoLeak goLeak `mapstructure:"goleak"` - ExpectConsumerError bool `mapstructure:"expect_consumer_error"` - Host string `mapstructure:"host"` -} - -type telemetry struct { - Level configtelemetry.Level `mapstructure:"level"` - Metrics map[MetricName]Metric `mapstructure:"metrics"` -} - -func (t telemetry) Levels() map[string]interface{} { - levels := map[string]interface{}{} - for _, m := range t.Metrics { - levels[m.Level.String()] = nil - } - return levels -} - -type Metadata struct { - // Type of the component. - Type string `mapstructure:"type"` - // Type of the parent component (applicable to subcomponents). - Parent string `mapstructure:"parent"` - // Status information for the component. - Status *Status `mapstructure:"status"` - // The name of the package that will be generated. - GeneratedPackageName string `mapstructure:"generated_package_name"` - // Telemetry information for the component. - Telemetry telemetry `mapstructure:"telemetry"` - // SemConvVersion is a version number of OpenTelemetry semantic conventions applied to the scraped metrics. - SemConvVersion string `mapstructure:"sem_conv_version"` - // ResourceAttributes that can be emitted by the component. - ResourceAttributes map[AttributeName]Attribute `mapstructure:"resource_attributes"` - // Attributes emitted by one or more metrics. - Attributes map[AttributeName]Attribute `mapstructure:"attributes"` - // Metrics that can be emitted by the component. - Metrics map[MetricName]Metric `mapstructure:"metrics"` - // GithubProject is the project where the component README lives in the format of org/repo, defaults to open-telemetry/opentelemetry-collector-contrib - GithubProject string `mapstructure:"github_project"` - // ScopeName of the metrics emitted by the component. - ScopeName string `mapstructure:"scope_name"` - // ShortFolderName is the shortened folder name of the component, removing class if present - ShortFolderName string `mapstructure:"-"` - // Tests is the set of tests generated with the component - Tests tests `mapstructure:"tests"` - // FeatureGates that can be used for the component. - FeatureGates map[featureGateName]featureGate `mapstructure:"feature_gates"` -} - func setAttributesFullName(attrs map[AttributeName]Attribute) { for k, v := range attrs { v.FullName = k @@ -401,53 +88,3 @@ func packageName() (string, error) { } return strings.TrimSpace(string(output)), nil } - -type featureGate struct { - // Required. - ID string `mapstructure:"id"` - // Description describes the purpose of the attribute. - Description string `mapstructure:"description"` - // Stage current stage at which the feature gate is in the development lifecyle - Stage string `mapstructure:"stage"` - // ReferenceURL can optionally give the url of the feature_gate - ReferenceURL string `mapstructure:"reference_url"` - // FromVersion optional field which gives the release version from which the gate has been given the current stage - FromVersion string `mapstructure:"from_version"` - // ToVersion optional field which gives the release version till which the gate the gate had the given lifecycle stage - ToVersion string `mapstructure:"to_version"` - // FeatureGateName name of the feature gate - FeatureGateName featureGateName `mapstructure:"-"` -} - -func (f *featureGate) validate(parser *confmap.Conf) error { - var err []error - if !parser.IsSet("id") { - err = append(err, errors.New("missing required field: `id`")) - } else if !idRegexp.MatchString(fmt.Sprintf("%v", parser.Get("id"))) { - err = append(err, fmt.Errorf("invalid character(s) in ID")) - } - - if !parser.IsSet("stage") { - err = append(err, errors.New("missing required field: `stage`")) - } else if _, ok := validStages[fmt.Sprintf("%v", parser.Get("stage"))]; !ok { - err = append(err, fmt.Errorf("invalid stage")) - } - - if parser.IsSet("from_version") && !versionRegexp.MatchString(fmt.Sprintf("%v", parser.Get("from_version"))) { - err = append(err, fmt.Errorf("invalid character(s) in from_version")) - } - if parser.IsSet("to_version") && !versionRegexp.MatchString(fmt.Sprintf("%v", parser.Get("to_version"))) { - err = append(err, fmt.Errorf("invalid character(s) in to_version")) - } - if parser.IsSet("reference_url") && !referenceURLRegexp.MatchString(fmt.Sprintf("%v", parser.Get("reference_url"))) { - err = append(err, fmt.Errorf("invalid character(s) in reference_url")) - } - return errors.Join(err...) -} - -func (f *featureGate) Unmarshal(parser *confmap.Conf) error { - if err := f.validate(parser); err != nil { - return err - } - return parser.Unmarshal(f) -} diff --git a/cmd/mdatagen/internal/loader_test.go b/cmd/mdatagen/internal/loader_test.go index bda93f536ce3..19f3e118410f 100644 --- a/cmd/mdatagen/internal/loader_test.go +++ b/cmd/mdatagen/internal/loader_test.go @@ -4,13 +4,11 @@ package internal import ( - "errors" "testing" - "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/component" - "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/pmetric" ) @@ -371,88 +369,6 @@ func TestLoadMetadata(t *testing.T) { } } -func TestFeatureGateValidate(t *testing.T) { - tests := []struct { - name string - input map[string]interface{} - expectedErr error - }{ - { - name: "valid input", - input: map[string]interface{}{ - "id": "valid.id", - "stage": "StageAlpha", - "from_version": "v1.2.3", - "to_version": "v1.3.0", - "reference_url": "http://example.com", - }, - expectedErr: nil, - }, - { - name: "missing id", - input: map[string]interface{}{ - "stage": "StageAlpha", - }, - expectedErr: errors.New("missing required field: `id`"), - }, - { - name: "invalid id", - input: map[string]interface{}{ - "id": "invalid@id", - "stage": "StageAlpha", - }, - expectedErr: errors.New("invalid character(s) in ID"), - }, - { - name: "missing stage", - input: map[string]interface{}{ - "id": "valid.id", - }, - expectedErr: errors.New("missing required field: `stage`"), - }, - { - name: "invalid stage", - input: map[string]interface{}{ - "id": "valid.id", - "stage": "InvalidStage", - }, - expectedErr: errors.New("invalid stage"), - }, - { - name: "invalid from_version", - input: map[string]interface{}{ - "id": "valid.id", - "stage": "StageAlpha", - "from_version": "v1.2.a", - }, - expectedErr: errors.New("invalid character(s) in from_version"), - }, - { - name: "invalid reference_url", - input: map[string]interface{}{ - "id": "valid.id", - "stage": "StageAlpha", - "reference_url": "invalid-url", - }, - expectedErr: errors.New("invalid character(s) in reference_url"), - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - parser := confmap.NewFromStringMap(tt.input) - featureGate := &featureGate{} - err := featureGate.validate(parser) - if tt.expectedErr != nil { - require.Error(t, err) - assert.Contains(t, err.Error(), tt.expectedErr.Error()) - } else { - assert.NoError(t, err) - } - }) - } -} - func strPtr(s string) *string { return &s } diff --git a/cmd/mdatagen/internal/metadata.go b/cmd/mdatagen/internal/metadata.go index 8a9fdba0d711..9831c540ac6b 100644 --- a/cmd/mdatagen/internal/metadata.go +++ b/cmd/mdatagen/internal/metadata.go @@ -9,6 +9,7 @@ import ( "regexp" "strings" + "go.opentelemetry.io/collector/confmap" "go.opentelemetry.io/collector/filter" "go.opentelemetry.io/collector/pdata/pcommon" ) @@ -40,6 +41,8 @@ type Metadata struct { ShortFolderName string `mapstructure:"-"` // Tests is the set of tests generated with the component Tests Tests `mapstructure:"tests"` + // FeatureGates that can be used for the component. + FeatureGates map[featureGateName]featureGate `mapstructure:"feature_gates"` } func (md *Metadata) Validate() error { @@ -156,6 +159,70 @@ func validateMetrics(metrics map[MetricName]Metric, attributes map[AttributeName return errs } +var ( + // idRegexp is used to validate the ID of a Gate. + // IDs' characters must be alphanumeric or dots. + idRegexp = regexp.MustCompile(`^[0-9a-zA-Z\.]*$`) + versionRegexp = regexp.MustCompile(`^v(\d+)\.(\d+)\.(\d+)$`) + referenceURLRegexp = regexp.MustCompile(`^(https?:\/\/)?([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+)(\/[^\s]*)?$`) + validStages = map[string]bool{ + "StageAlpha": true, + "StageBeta": true, + "StageStable": true, + "StageDeprecated": true, + } +) + +type featureGate struct { + // Required. + ID string `mapstructure:"id"` + // Description describes the purpose of the attribute. + Description string `mapstructure:"description"` + // Stage current stage at which the feature gate is in the development lifecyle + Stage string `mapstructure:"stage"` + // ReferenceURL can optionally give the url of the feature_gate + ReferenceURL string `mapstructure:"reference_url"` + // FromVersion optional field which gives the release version from which the gate has been given the current stage + FromVersion string `mapstructure:"from_version"` + // ToVersion optional field which gives the release version till which the gate the gate had the given lifecycle stage + ToVersion string `mapstructure:"to_version"` + // FeatureGateName name of the feature gate + FeatureGateName featureGateName `mapstructure:"-"` +} + +func validateFeatureGate(parser *confmap.Conf) error { + var err []error + if !parser.IsSet("id") { + err = append(err, errors.New("missing required field: `id`")) + } else if !idRegexp.MatchString(fmt.Sprintf("%v", parser.Get("id"))) { + err = append(err, fmt.Errorf("invalid character(s) in ID")) + } + + if !parser.IsSet("stage") { + err = append(err, errors.New("missing required field: `stage`")) + } else if _, ok := validStages[fmt.Sprintf("%v", parser.Get("stage"))]; !ok { + err = append(err, fmt.Errorf("invalid stage")) + } + + if parser.IsSet("from_version") && !versionRegexp.MatchString(fmt.Sprintf("%v", parser.Get("from_version"))) { + err = append(err, fmt.Errorf("invalid character(s) in from_version")) + } + if parser.IsSet("to_version") && !versionRegexp.MatchString(fmt.Sprintf("%v", parser.Get("to_version"))) { + err = append(err, fmt.Errorf("invalid character(s) in to_version")) + } + if parser.IsSet("reference_url") && !referenceURLRegexp.MatchString(fmt.Sprintf("%v", parser.Get("reference_url"))) { + err = append(err, fmt.Errorf("invalid character(s) in reference_url")) + } + return errors.Join(err...) +} + +func (f *featureGate) Unmarshal(parser *confmap.Conf) error { + if err := validateFeatureGate(parser); err != nil { + return err + } + return parser.Unmarshal(f) +} + type AttributeName string func (mn AttributeName) Render() (string, error) { @@ -166,6 +233,16 @@ func (mn AttributeName) RenderUnexported() (string, error) { return FormatIdentifier(string(mn), false) } +type featureGateName string + +func (mn featureGateName) Render() (string, error) { + return FormatIdentifier(string(mn), true) +} + +func (mn featureGateName) RenderUnexported() (string, error) { + return FormatIdentifier(string(mn), false) +} + // ValueType defines an attribute value type. type ValueType struct { // ValueType is type of the attribute value. diff --git a/cmd/mdatagen/internal/metadata_test.go b/cmd/mdatagen/internal/metadata_test.go index 83b102ce3153..a6dcac99d700 100644 --- a/cmd/mdatagen/internal/metadata_test.go +++ b/cmd/mdatagen/internal/metadata_test.go @@ -10,6 +10,8 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + + "go.opentelemetry.io/collector/confmap" ) func TestValidate(t *testing.T) { @@ -142,6 +144,72 @@ func TestValidateMetricDuplicates(t *testing.T) { } } +func TestFeatureGateValidate(t *testing.T) { + tests := []struct { + name string + input map[string]interface{} + wantErr string + }{ + { + name: "missing id", + input: map[string]interface{}{ + "stage": "StageAlpha", + }, + wantErr: "missing required field: `id`", + }, + { + name: "invalid id", + input: map[string]interface{}{ + "id": "invalid@id", + "stage": "StageAlpha", + }, + wantErr: "invalid character(s) in ID", + }, + { + name: "missing stage", + input: map[string]interface{}{ + "id": "valid.id", + }, + wantErr: "missing required field: `stage`", + }, + { + name: "invalid stage", + input: map[string]interface{}{ + "id": "valid.id", + "stage": "InvalidStage", + }, + wantErr: "invalid stage", + }, + { + name: "invalid from_version", + input: map[string]interface{}{ + "id": "valid.id", + "stage": "StageAlpha", + "from_version": "v1.2.a", + }, + wantErr: "invalid character(s) in from_version", + }, + { + name: "invalid reference_url", + input: map[string]interface{}{ + "id": "valid.id", + "stage": "StageAlpha", + "reference_url": "invalid-url", + }, + wantErr: "invalid character(s) in reference_url", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + parser := confmap.NewFromStringMap(tt.input) + err := validateFeatureGate(parser) + require.Error(t, err) + require.EqualError(t, err, tt.wantErr) + }) + } +} + func contains(r string, rs []string) bool { for _, s := range rs { if s == r { diff --git a/cmd/mdatagen/internal/sampleprocessor/metadata.yaml b/cmd/mdatagen/internal/sampleprocessor/metadata.yaml index 448cc2c64b43..07f925900e46 100644 --- a/cmd/mdatagen/internal/sampleprocessor/metadata.yaml +++ b/cmd/mdatagen/internal/sampleprocessor/metadata.yaml @@ -72,16 +72,16 @@ feature_gates: id: useOTTLBridgeGate stage: StageAlpha description: When enabled, filterlog will convert filterlog configuration to OTTL and use filterottl evaluation - from_version: 1.0.0 + from_version: v1.0.0 reference_url: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/18642 - to_version: 2.0.0 + to_version: v2.0.0 allowFileDeletion: id: allowFileDeletionGate stage: StageStable description: When enabled, allows usage of the `delete_after_read` setting. - from_version": 1.5.0 + from_version: v1.5.0 reference_url: https://github.com/open-telemetry/opentelemetry-collector-contrib/issues/16314 allowFileCreation: diff --git a/cmd/mdatagen/internal/templates/feature_gates.go.tmpl b/cmd/mdatagen/internal/templates/feature_gates.go.tmpl index e6d871e5c1ce..e059aea77e78 100644 --- a/cmd/mdatagen/internal/templates/feature_gates.go.tmpl +++ b/cmd/mdatagen/internal/templates/feature_gates.go.tmpl @@ -7,15 +7,15 @@ import ( ) func init() { -{{- range .FeatureGates }} +{{- range $name, $fg := .FeatureGates }} { var opts []featuregate.RegisterOption opts = append(opts - {{- if .from_version }}, featuregate.WithRegisterFromVersion("{{.from_version}}") {{- end }} - {{- if .description }}, featuregate.WithRegisterDescription("{{.description}}") {{- end }} - {{- if .reference_url }}, featuregate.WithRegisterReferenceURL("{{.reference_url}}") {{- end }} - {{- if .to_version }}, featuregate.WithRegisterToVersion("{{.to_version}}") {{- end }}) - _ = featuregate.GlobalRegistry().MustRegister("{{.id}}", featuregate.{{.stage}}, opts...) + {{- if $fg.from_version }}, featuregate.WithRegisterFromVersion("{{ $fg.from_version}}") {{- end }} + {{- if $fg.description }}, featuregate.WithRegisterDescription("{{ $fg.description}}") {{- end }} + {{- if $fg.reference_url }}, featuregate.WithRegisterReferenceURL("{{ $fg.reference_url}}") {{- end }} + {{- if $fg.to_version }}, featuregate.WithRegisterToVersion("{{ $fg.to_version}}") {{- end }}) + _ = featuregate.GlobalRegistry().MustRegister("{{ $fg.id}}", featuregate.{{ $fg.stage}}, opts...) } {{- end }}