Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[confmap] log a warning when using $VAR in config (WIP) #9547

Merged
merged 28 commits into from
Mar 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
29844f3
[confmap] log a warning when using $VAR in config
tomasmota Feb 9, 2024
e4af3c0
Merge branch 'main' into dollar-warning
tomasmota Mar 8, 2024
7b2906b
revert bad rebase
tomasmota Mar 8, 2024
62a720b
[chore][Feature Request Template] Comment out header descriptions (#9…
crobert-1 Mar 11, 2024
a3ca427
Remove deprecated obsreport/obsreporttest package (#9724)
dmitryax Mar 11, 2024
30a629d
use generated meter (#9669)
codeboten Mar 11, 2024
31666b3
Update github-actions deps (#9743)
renovate[bot] Mar 12, 2024
fbfe4b2
[exporter/otlp] enable lifecycle test (#9735)
fatsheep9146 Mar 12, 2024
b0fedad
[chore] group build-tools packages (#9742)
codeboten Mar 12, 2024
59f3fc7
[confmap] confmap honors `Unmarshal` methods on config embedded struc…
atoulme Mar 12, 2024
3e13d81
[exporterhelper] Fix persistent queue size backup on reads (#9740)
carsonip Mar 12, 2024
9cb8f44
Give NoOp create settings a unique name (#9637)
dashpole Mar 12, 2024
acd71fb
Update github/codeql-action action to v3.24.7 (#9744)
renovate[bot] Mar 12, 2024
1fe43cf
[exporter/nopexporter] Add the nopexporter (#9448)
evan-bradley Mar 12, 2024
1830f83
[receiver/nopreceiver] Add the nopreceiver (#9446)
evan-bradley Mar 12, 2024
5d5f737
[chore] Run make gotidy to fix the CI (#9747)
dmitryax Mar 12, 2024
8c72ae4
[chore] group golang.org/x packages (#9741)
codeboten Mar 12, 2024
9d2a10c
[chore] Fix an incorrect automatic replace made by a bot in `otel-con…
ababushk Mar 12, 2024
92992c5
[chore] Move resource test to service/internal/resource (#9730)
mx-psi Mar 12, 2024
bae5867
[chore] tidy code to return directly (#9751)
atoulme Mar 13, 2024
8994209
reviewable
tomasmota Mar 14, 2024
63a1bad
Merge branch 'main' into dollar-warning
tomasmota Mar 14, 2024
b610033
add warning message tests
tomasmota Mar 15, 2024
5e5b9f5
add QuoteMeta to regexp
tomasmota Mar 15, 2024
326ec46
Merge branch 'main' into dollar-warning
tomasmota Mar 15, 2024
cb677ff
build
tomasmota Mar 15, 2024
37e0dce
Merge branch 'main' into dollar-warning
tomasmota Mar 19, 2024
169069f
add check for both types together
tomasmota Mar 20, 2024
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
40 changes: 30 additions & 10 deletions confmap/converter/expandconverter/expand.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,51 +5,71 @@ package expandconverter // import "go.opentelemetry.io/collector/confmap/convert

import (
"context"
"fmt"
"os"
"regexp"

"go.uber.org/zap"

"go.opentelemetry.io/collector/confmap"
)

type converter struct{}
type converter struct {
logger *zap.Logger

// Record of which env vars we have logged a warning for
loggedDeprecations map[string]struct{}
}

// New returns a confmap.Converter, that expands all environment variables for a given confmap.Conf.
//
// Notice: This API is experimental.
func New(confmap.ConverterSettings) confmap.Converter {
return converter{}
func New(_ confmap.ConverterSettings) confmap.Converter {
return converter{
loggedDeprecations: make(map[string]struct{}),
logger: zap.NewNop(), // TODO: pass logger in ConverterSettings
}
}

func (converter) Convert(_ context.Context, conf *confmap.Conf) error {
func (c converter) Convert(_ context.Context, conf *confmap.Conf) error {
out := make(map[string]any)
for _, k := range conf.AllKeys() {
out[k] = expandStringValues(conf.Get(k))
out[k] = c.expandStringValues(conf.Get(k))
}
return conf.Merge(confmap.NewFromStringMap(out))
}

func expandStringValues(value any) any {
func (c converter) expandStringValues(value any) any {
switch v := value.(type) {
case string:
return expandEnv(v)
return c.expandEnv(v)
case []any:
nslice := make([]any, 0, len(v))
for _, vint := range v {
nslice = append(nslice, expandStringValues(vint))
nslice = append(nslice, c.expandStringValues(vint))
}
return nslice
case map[string]any:
nmap := map[string]any{}
for mk, mv := range v {
nmap[mk] = expandStringValues(mv)
nmap[mk] = c.expandStringValues(mv)
}
return nmap
default:
return v
}
}

func expandEnv(s string) string {
func (c converter) expandEnv(s string) string {
return os.Expand(s, func(str string) string {
// Matches on $VAR style environment variables
// in order to make sure we don't log a warning for ${VAR}
var regex = regexp.MustCompile(fmt.Sprintf(`\$%s`, regexp.QuoteMeta(str)))
if _, exists := c.loggedDeprecations[str]; !exists && regex.MatchString(s) {
msg := fmt.Sprintf("Variable substitution using $VAR will be deprecated in favor of ${VAR} and ${env:VAR}, please update $%s", str)
c.logger.Warn(msg, zap.String("variable", str))
c.loggedDeprecations[str] = struct{}{}
}
// This allows escaping environment variable substitution via $$, e.g.
// - $FOO will be substituted with env var FOO
// - $$FOO will be replaced with $FOO
Expand Down
93 changes: 93 additions & 0 deletions confmap/converter/expandconverter/expand_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ package expandconverter

import (
"context"
"fmt"
"path/filepath"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"go.uber.org/zap/zaptest/observer"

"go.opentelemetry.io/collector/confmap"
"go.opentelemetry.io/collector/confmap/confmaptest"
Expand Down Expand Up @@ -159,3 +163,92 @@ func TestNewExpandConverterHostPort(t *testing.T) {
})
}
}

func NewTestConverter() (confmap.Converter, *observer.ObservedLogs) {
core, logs := observer.New(zapcore.InfoLevel)
conv := converter{loggedDeprecations: make(map[string]struct{}), logger: zap.New(core)}
return conv, logs
}

func TestDeprecatedWarning(t *testing.T) {
msgTemplate := `Variable substitution using $VAR will be deprecated in favor of ${VAR} and ${env:VAR}, please update $%s`
t.Setenv("HOST", "127.0.0.1")
t.Setenv("PORT", "4317")

t.Setenv("HOST_NAME", "127.0.0.2")
t.Setenv("HOST.NAME", "127.0.0.3")

var testCases = []struct {
name string
input map[string]any
expectedOutput map[string]any
expectedWarnings []string
}{
{
name: "no warning",
input: map[string]any{
"test": "${HOST}:${PORT}",
},
expectedOutput: map[string]any{
"test": "127.0.0.1:4317",
},
expectedWarnings: []string{},
},
{
name: "one deprecated var",
input: map[string]any{
"test": "${HOST}:$PORT",
},
expectedOutput: map[string]any{
"test": "127.0.0.1:4317",
},
expectedWarnings: []string{"PORT"},
},
{
name: "two deprecated vars",
input: map[string]any{
"test": "$HOST:$PORT",
},
expectedOutput: map[string]any{
"test": "127.0.0.1:4317",
},
expectedWarnings: []string{"HOST", "PORT"},
},
{
name: "one depracated serveral times",
input: map[string]any{
"test": "$HOST,$HOST",
"test2": "$HOST",
},
expectedOutput: map[string]any{
"test": "127.0.0.1,127.0.0.1",
"test2": "127.0.0.1",
},
expectedWarnings: []string{"HOST"},
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To test the QuoteMeta change I commented above, we should add a test like $HOSTONAME${HOST.NAME} to check that we are properly escaping characters

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added QuoteMeta because it makes sense, but in practice I'm not really understanding how it is useful or needed, because our regexp will never match ${HOST.NAME}, and if you attempt to have $HOST.NAME you will just end up with 127.0.0.1.NAME according to my testing. Same if you put other special characters after $HOST.

With that in mind, I couldn't think of a good test case, because the $VAR way, which is the only one we match, does not play well with special characters anyways.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right, I mistakenly assumed that . would be a valid part of a naked environment variable name for os.Expand, but it does not seem like that is the case.

I guess a test that has both ${HOST.NAME} and $HOST_NAME should be a valid test, right? Taking a look at the implementation of os.Expand, it also seems like $? would be a valid thing to test, but I doubt anybody is using that in practice 😄

{
name: "one warning",
input: map[string]any{
"test": "$HOST_NAME,${HOST.NAME}",
},
expectedOutput: map[string]any{
"test": "127.0.0.2,127.0.0.3",
},
expectedWarnings: []string{"HOST_NAME"},
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
conf := confmap.NewFromStringMap(tt.input)
conv, logs := NewTestConverter()
require.NoError(t, conv.Convert(context.Background(), conf))

assert.Equal(t, tt.expectedOutput, conf.ToStringMap())
assert.Equal(t, len(tt.expectedWarnings), len(logs.All()))
for i, variable := range tt.expectedWarnings {
errorMsg := fmt.Sprintf(msgTemplate, variable)
assert.Equal(t, errorMsg, logs.All()[i].Message)
}
})
}
}
1 change: 1 addition & 0 deletions confmap/converter/expandconverter/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ require (
github.com/stretchr/testify v1.9.0
go.opentelemetry.io/collector/confmap v0.96.0
go.uber.org/goleak v1.3.0
go.uber.org/zap v1.26.0
)

require (
Expand Down
2 changes: 2 additions & 0 deletions confmap/converter/expandconverter/go.sum

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading