Skip to content

Commit 26c157e

Browse files
authored
[component] Add MustNewType constructor for component.Type (#9414)
**Description:** - Adds `component.MustNewType` to create a type. This function panics if the type has invalid characters. Add similar functions `component.MustNewID` and `component.MustNewIDWithName`. - Adds `component.Type.String` to recover the string - Use `component.MustNewType`, `component.MustNewID`, `component.MustNewIDWithName` and `component.Type.String` everywhere in this codebase. To do this I changed `component.Type` into an opaque struct and checked for compile-time errors. Some notes: 1. All components currently on core and contrib follow this rule. This is still breaking for other components. 2. A future PR will change this into a struct, to actually validate this (right now you can just do `component.Type("anything")` to bypass validation). I want to do this in two steps to avoid breaking contrib tests: we first introduce this function, and after that we change into a struct. **Link to tracking Issue:** Updates #9208
1 parent 3665732 commit 26c157e

File tree

96 files changed

+1606
-1205
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

96 files changed

+1606
-1205
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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: component
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Validate component.Type at creation and unmarshaling time.
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [9208]
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+
- A component.Type must start with an ASCII alphabetic character and can only contain ASCII alphanumeric characters and '_'.
20+
21+
# Optional: The change log or logs in which this entry should be included.
22+
# e.g. '[user]' or '[user, api]'
23+
# Include 'user' if the change is relevant to end users.
24+
# Include 'api' if there is a change to a library API.
25+
# Default: '[user]'
26+
change_logs: [api]

cmd/mdatagen/internal/metadata/generated_status.go

Lines changed: 4 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/mdatagen/main_test.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,11 @@ import (
365365
"go.opentelemetry.io/otel/trace"
366366
)
367367
368+
var (
369+
Type = component.MustNewType("foo")
370+
)
371+
368372
const (
369-
Type = "foo"
370373
MetricsStability = component.StabilityLevelBeta
371374
)
372375
@@ -399,8 +402,11 @@ import (
399402
"go.opentelemetry.io/otel/trace"
400403
)
401404
405+
var (
406+
Type = component.MustNewType("foo")
407+
)
408+
402409
const (
403-
Type = "foo"
404410
MetricsStability = component.StabilityLevelAlpha
405411
)
406412

cmd/mdatagen/templates/status.go.tmpl

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import (
88
"go.opentelemetry.io/otel/trace"
99
)
1010

11+
var (
12+
Type = component.MustNewType("{{ .Type }}")
13+
)
14+
1115
const (
12-
Type = "{{ .Type }}"
1316
{{- range $stability, $signals := .Status.Stability }}
1417
{{- range $signal := $signals }}
1518
{{ toCamelCase $signal }}Stability = component.StabilityLevel{{ casesTitle $stability }}
@@ -23,4 +26,4 @@ func Meter(settings component.TelemetrySettings) metric.Meter {
2326

2427
func Tracer(settings component.TelemetrySettings) trace.Tracer {
2528
return settings.TracerProvider.Tracer("{{ .ScopeName }}")
26-
}
29+
}

cmd/mdatagen/validate.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package main
66
import (
77
"errors"
88
"fmt"
9+
"regexp"
910

1011
"go.uber.org/multierr"
1112

@@ -29,10 +30,20 @@ func (md *metadata) Validate() error {
2930
return errs
3031
}
3132

33+
// typeRegexp is used to validate the type of a component.
34+
// A type must start with an ASCII alphabetic character and
35+
// can only contain ASCII alphanumeric characters and '_'.
36+
// This must be kept in sync with the regex in component/config.go.
37+
var typeRegexp = regexp.MustCompile(`^[a-zA-Z][0-9a-zA-Z_]*$`)
38+
3239
func (md *metadata) validateType() error {
3340
if md.Type == "" {
3441
return errors.New("missing type")
3542
}
43+
44+
if !typeRegexp.MatchString(md.Type) {
45+
return fmt.Errorf("invalid character(s) in type %q", md.Type)
46+
}
3647
return nil
3748
}
3849

component/componenttest/nop_host_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@ func TestNewNopHost(t *testing.T) {
2121
nh.ReportFatalError(errors.New("TestError"))
2222
assert.Nil(t, nh.GetExporters()) // nolint: staticcheck
2323
assert.Nil(t, nh.GetExtensions())
24-
assert.Nil(t, nh.GetFactory(component.KindReceiver, "test"))
24+
assert.Nil(t, nh.GetFactory(component.KindReceiver, component.MustNewType("test")))
2525
}

component/componenttest/otelprometheuschecker_test.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ func TestPromChecker(t *testing.T) {
3636
pc, err := newStubPromChecker()
3737
require.NoError(t, err)
3838

39-
scraper := component.NewID("fakeScraper")
40-
receiver := component.NewID("fakeReceiver")
41-
processor := component.NewID("fakeProcessor")
42-
exporter := component.NewID("fakeExporter")
39+
scraper := component.MustNewID("fakeScraper")
40+
receiver := component.MustNewID("fakeReceiver")
41+
processor := component.MustNewID("fakeProcessor")
42+
exporter := component.MustNewID("fakeExporter")
4343
transport := "fakeTransport"
4444

4545
assert.NoError(t,

component/config.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
package component // import "go.opentelemetry.io/collector/component"
55

66
import (
7+
"fmt"
78
"reflect"
9+
"regexp"
810

911
"go.uber.org/multierr"
1012

@@ -110,6 +112,45 @@ func callValidateIfPossible(v reflect.Value) error {
110112
// Type is the component type as it is used in the config.
111113
type Type string
112114

115+
// String returns the string representation of the type.
116+
func (t Type) String() string {
117+
return string(t)
118+
}
119+
120+
// typeRegexp is used to validate the type of a component.
121+
// A type must start with an ASCII alphabetic character and
122+
// can only contain ASCII alphanumeric characters and '_'.
123+
// This must be kept in sync with the regex in cmd/mdatagen/validate.go.
124+
var typeRegexp = regexp.MustCompile(`^[a-zA-Z][0-9a-zA-Z_]*$`)
125+
126+
// NewType creates a type. It returns an error if the type is invalid.
127+
// A type must
128+
// - have at least one character,
129+
// - start with an ASCII alphabetic character and
130+
// - can only contain ASCII alphanumeric characters and '_'.
131+
func NewType(ty string) (Type, error) {
132+
if len(ty) == 0 {
133+
return Type(""), fmt.Errorf("id must not be empty")
134+
}
135+
if !typeRegexp.MatchString(ty) {
136+
return Type(""), fmt.Errorf("invalid character(s) in type %q", ty)
137+
}
138+
return Type(ty), nil
139+
}
140+
141+
// MustNewType creates a type. It panics if the type is invalid.
142+
// A type must
143+
// - have at least one character,
144+
// - start with an ASCII alphabetic character and
145+
// - can only contain ASCII alphanumeric characters and '_'.
146+
func MustNewType(strType string) Type {
147+
ty, err := NewType(strType)
148+
if err != nil {
149+
panic(err)
150+
}
151+
return ty
152+
}
153+
113154
// DataType is a special Type that represents the data types supported by the collector. We currently support
114155
// collecting metrics, traces and logs, this can expand in the future.
115156
type DataType = Type

0 commit comments

Comments
 (0)