Skip to content

Commit e7ce1d5

Browse files
authored
[otelcol] Add hint about confmap.strictlyTypedInput feature gate (#10553)
<!--Ex. Fixing a bug - Describe the bug and how this fixes the issue. Ex. Adding a feature - Explain what this achieves.--> #### Description <!-- Issue number if applicable --> Adds coda to errors related to `confmap.strictlyTypedInput` that direct users to #10552 and explain the feature gate. #### Link to tracking issue Updates #9532 <!--Describe what testing was performed and which tests were added.--> #### Testing Tests that the coda is present when there are weakly typed errors and not present with non weakly-typed errors. <!--Describe the documentation added.-->
1 parent 9d990b9 commit e7ce1d5

12 files changed

+436
-1
lines changed
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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: enhancement
5+
6+
# The name of the component, or a single word describing the area of concern, (e.g. otlpreceiver)
7+
component: confmap
8+
9+
# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
10+
note: Add explanation to errors related to `confmap.strictlyTypedInput` feature gate.
11+
12+
# One or more tracking issues or pull requests related to the change
13+
issues: [9532]
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+
20+
# Optional: The change log or logs in which this entry should be included.
21+
# e.g. '[user]' or '[user, api]'
22+
# Include 'user' if the change is relevant to end users.
23+
# Include 'api' if there is a change to a library API.
24+
# Default: '[user]'
25+
change_logs: []

otelcol/configprovider.go

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,16 @@ package otelcol // import "go.opentelemetry.io/collector/otelcol"
66
import (
77
"context"
88
"fmt"
9+
"strings"
910

1011
"go.opentelemetry.io/collector/confmap"
12+
"go.opentelemetry.io/collector/internal/featuregates"
13+
)
14+
15+
var (
16+
strictlyTypedMessageCoda = `Hint: Temporarily restore the previous behavior by disabling
17+
the ` + fmt.Sprintf("`%s`", featuregates.StrictlyTypedInputID) + ` feature gate. More details at:
18+
https://github.com/open-telemetry/opentelemetry-collector/issues/10552`
1119
)
1220

1321
// ConfigProvider provides the service configuration.
@@ -95,7 +103,26 @@ func (cm *configProvider) Get(ctx context.Context, factories Factories) (*Config
95103

96104
var cfg *configSettings
97105
if cfg, err = unmarshal(conf, factories); err != nil {
98-
return nil, fmt.Errorf("cannot unmarshal the configuration: %w", err)
106+
err = fmt.Errorf("cannot unmarshal the configuration: %w", err)
107+
108+
if featuregates.StrictlyTypedInputGate.IsEnabled() {
109+
var shouldAddCoda bool
110+
for _, errorStr := range []string{
111+
"got unconvertible type", // https://github.com/mitchellh/mapstructure/blob/8508981/mapstructure.go#L610
112+
"source data must be", // https://github.com/mitchellh/mapstructure/blob/8508981/mapstructure.go#L1114
113+
"expected a map, got 'slice'", // https://github.com/mitchellh/mapstructure/blob/8508981/mapstructure.go#L831
114+
} {
115+
shouldAddCoda = strings.Contains(err.Error(), errorStr)
116+
if shouldAddCoda {
117+
break
118+
}
119+
}
120+
if shouldAddCoda {
121+
err = fmt.Errorf("%w\n\n%s", err, strictlyTypedMessageCoda)
122+
}
123+
}
124+
125+
return nil, err
99126
}
100127

101128
return &Config{

otelcol/configprovider_test.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import (
1414
"gopkg.in/yaml.v3"
1515

1616
"go.opentelemetry.io/collector/confmap"
17+
"go.opentelemetry.io/collector/featuregate"
18+
"go.opentelemetry.io/collector/internal/featuregates"
1719
)
1820

1921
func newConfig(yamlBytes []byte, factories Factories) (*Config, error) {
@@ -137,3 +139,75 @@ func TestGetConfmap(t *testing.T) {
137139

138140
assert.EqualValues(t, yamlMap, cmap.ToStringMap())
139141
}
142+
143+
func TestStrictlyTypedCoda(t *testing.T) {
144+
tests := []struct {
145+
basename string
146+
// isErrFromStrictTypes indicates whether the test should expect an error when the feature gate is
147+
// disabled. If so, we check that it errs both with and without the feature gate and that the coda is never
148+
// present.
149+
isErrFromStrictTypes bool
150+
}{
151+
{basename: "weak-implicit-bool-to-string.yaml"},
152+
{basename: "weak-implicit-int-to-string.yaml"},
153+
{basename: "weak-implicit-bool-to-int.yaml"},
154+
{basename: "weak-implicit-string-to-int.yaml"},
155+
{basename: "weak-implicit-int-to-bool.yaml"},
156+
{basename: "weak-implicit-string-to-bool.yaml"},
157+
{basename: "weak-empty-map-to-empty-array.yaml"},
158+
{basename: "weak-slice-of-maps-to-map.yaml"},
159+
{basename: "weak-single-element-to-slice.yaml"},
160+
{
161+
basename: "otelcol-invalid-components.yaml",
162+
isErrFromStrictTypes: true,
163+
},
164+
}
165+
166+
for _, tt := range tests {
167+
t.Run(tt.basename, func(t *testing.T) {
168+
filename := filepath.Join("testdata", tt.basename)
169+
uriLocation := "file:" + filename
170+
fileProvider := newFakeProvider("file", func(_ context.Context, _ string, _ confmap.WatcherFunc) (*confmap.Retrieved, error) {
171+
return confmap.NewRetrieved(newConfFromFile(t, filename))
172+
})
173+
cp, err := NewConfigProvider(ConfigProviderSettings{
174+
ResolverSettings: confmap.ResolverSettings{
175+
URIs: []string{uriLocation},
176+
ProviderFactories: []confmap.ProviderFactory{fileProvider},
177+
},
178+
})
179+
require.NoError(t, err)
180+
factories, err := nopFactories()
181+
require.NoError(t, err)
182+
183+
// Save the previous value of the feature gate and restore it after the test.
184+
prev := featuregates.StrictlyTypedInputGate.IsEnabled()
185+
defer func() {
186+
require.NoError(t, featuregate.GlobalRegistry().Set(featuregates.StrictlyTypedInputID, prev))
187+
}()
188+
189+
// Ensure the error does not appear with the feature gate disabled.
190+
require.NoError(t, featuregate.GlobalRegistry().Set(featuregates.StrictlyTypedInputID, false))
191+
_, errWeakTypes := cp.Get(context.Background(), factories)
192+
if tt.isErrFromStrictTypes {
193+
require.Error(t, errWeakTypes)
194+
// Ensure coda is **NOT** present.
195+
assert.NotContains(t, errWeakTypes.Error(), strictlyTypedMessageCoda)
196+
} else {
197+
require.NoError(t, errWeakTypes)
198+
}
199+
200+
// Test with the feature gate enabled.
201+
require.NoError(t, featuregate.GlobalRegistry().Set(featuregates.StrictlyTypedInputID, true))
202+
_, errStrictTypes := cp.Get(context.Background(), factories)
203+
require.Error(t, errStrictTypes)
204+
if tt.isErrFromStrictTypes {
205+
// Ensure coda is **NOT** present.
206+
assert.NotContains(t, errStrictTypes.Error(), strictlyTypedMessageCoda)
207+
} else {
208+
// Ensure coda is present.
209+
assert.ErrorContains(t, errStrictTypes, strictlyTypedMessageCoda)
210+
}
211+
})
212+
}
213+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
receivers:
2+
nop:
3+
4+
processors:
5+
nop:
6+
7+
exporters:
8+
nop:
9+
10+
extensions:
11+
nop:
12+
13+
connectors:
14+
nop/con:
15+
16+
service:
17+
telemetry:
18+
metrics:
19+
address: localhost:8888
20+
extensions: [nop]
21+
pipelines:
22+
traces:
23+
receivers: [nop]
24+
processors: {} # <-- Empty map casted to empty array
25+
exporters: [nop, nop/con]
26+
metrics:
27+
receivers: [nop]
28+
processors: [nop]
29+
exporters: [nop]
30+
logs:
31+
receivers: [nop, nop/con]
32+
processors: [nop]
33+
exporters: [nop]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
receivers:
2+
nop:
3+
4+
processors:
5+
nop:
6+
7+
exporters:
8+
nop:
9+
10+
extensions:
11+
nop:
12+
13+
connectors:
14+
nop/con:
15+
16+
service:
17+
telemetry:
18+
metrics:
19+
address: localhost:8888
20+
logs:
21+
sampling:
22+
initial: true # <-- Implicit cast bool to int
23+
extensions: [nop]
24+
pipelines:
25+
traces:
26+
receivers: [nop]
27+
processors: [nop]
28+
exporters: [nop, nop/con]
29+
metrics:
30+
receivers: [nop]
31+
processors: [nop]
32+
exporters: [nop]
33+
logs:
34+
receivers: [nop, nop/con]
35+
processors: [nop]
36+
exporters: [nop]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
receivers:
2+
nop:
3+
4+
processors:
5+
nop:
6+
7+
exporters:
8+
nop:
9+
10+
extensions:
11+
nop:
12+
13+
connectors:
14+
nop/con:
15+
16+
service:
17+
telemetry:
18+
metrics:
19+
address: true # <-- Implicit cast bool to string
20+
extensions: [nop]
21+
pipelines:
22+
traces:
23+
receivers: [nop]
24+
processors: [nop]
25+
exporters: [nop, nop/con]
26+
metrics:
27+
receivers: [nop]
28+
processors: [nop]
29+
exporters: [nop]
30+
logs:
31+
receivers: [nop, nop/con]
32+
processors: [nop]
33+
exporters: [nop]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
receivers:
2+
nop:
3+
4+
processors:
5+
nop:
6+
7+
exporters:
8+
nop:
9+
10+
extensions:
11+
nop:
12+
13+
connectors:
14+
nop/con:
15+
16+
service:
17+
telemetry:
18+
metrics:
19+
address: localhost:8888
20+
logs:
21+
sampling:
22+
enabled: 1 # <-- Implicit cast int to bool
23+
extensions: [nop]
24+
pipelines:
25+
traces:
26+
receivers: [nop]
27+
processors: [nop]
28+
exporters: [nop, nop/con]
29+
metrics:
30+
receivers: [nop]
31+
processors: [nop]
32+
exporters: [nop]
33+
logs:
34+
receivers: [nop, nop/con]
35+
processors: [nop]
36+
exporters: [nop]
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
receivers:
2+
nop:
3+
4+
processors:
5+
nop:
6+
7+
exporters:
8+
nop:
9+
10+
extensions:
11+
nop:
12+
13+
connectors:
14+
nop/con:
15+
16+
service:
17+
telemetry:
18+
metrics:
19+
address: 0xdeadbeef # <-- Implicit cast int to string
20+
extensions: [nop]
21+
pipelines:
22+
traces:
23+
receivers: [nop]
24+
processors: [nop]
25+
exporters: [nop, nop/con]
26+
metrics:
27+
receivers: [nop]
28+
processors: [nop]
29+
exporters: [nop]
30+
logs:
31+
receivers: [nop, nop/con]
32+
processors: [nop]
33+
exporters: [nop]
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
receivers:
2+
nop:
3+
4+
processors:
5+
nop:
6+
7+
exporters:
8+
nop:
9+
10+
extensions:
11+
nop:
12+
13+
connectors:
14+
nop/con:
15+
16+
service:
17+
telemetry:
18+
metrics:
19+
address: localhost:8888
20+
logs:
21+
sampling:
22+
enabled: t # <-- Implicit cast string to bool
23+
extensions: [nop]
24+
pipelines:
25+
traces:
26+
receivers: [nop]
27+
processors: [nop]
28+
exporters: [nop, nop/con]
29+
metrics:
30+
receivers: [nop]
31+
processors: [nop]
32+
exporters: [nop]
33+
logs:
34+
receivers: [nop, nop/con]
35+
processors: [nop]
36+
exporters: [nop]

0 commit comments

Comments
 (0)