diff --git a/CHANGELOG.md b/CHANGELOG.md index 81ceb529f9..d857b94725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,7 @@ Main (unreleased) ### Enhancements - Add `hash_string_id` argument to `foreach` block to hash the string representation of the pipeline id instead of using the string itself. (@wildum) + - Update `async-profiler` binaries for `pyroscope.java` to 4.0-87b7b42 (@github-hamza-bouqal) - (_Experimental_) `prometheus.write.queue` add support for exemplars. (@dehaansa) @@ -30,13 +31,17 @@ Main (unreleased) ### Bugfixes - Fix the `validate` command not understanding the `livedebugging` block. (@dehaansa) + - Fix invalid class names in python profiles obtained with `pyroscope.ebpf`. (@korniltsev) - For CRD-based components (`prometheus.operator.*`), retry initializing informers if the apiserver request fails. This rectifies issues where the apiserver is not reachable immediately after node restart. (@dehaansa) +- Fixed a bug which prevented non-secret optional secrets to be passed in as `number` arguments. (@ptodev) + ### Other changes - Mark `pyroscope.receive_http` and `pyroscope.relabel` components as GA. (@marcsanmi) + - Upgrade `otelcol` components from OpenTelemetry v0.126.0 to v0.127.0 (@korniltsev) v1.9.1 diff --git a/syntax/internal/value/value.go b/syntax/internal/value/value.go index 3ce13a321d..f1e787377d 100644 --- a/syntax/internal/value/value.go +++ b/syntax/internal/value/value.go @@ -541,6 +541,18 @@ func convertValue(val Value, toType Type) (Value, error) { return Uint(parsed), nil } } + + case TypeCapsule: + // Some capsules, such as optional secrects, may be convertible to a string. + // Try to convert them to a string and then rerun convertValue. + into := reflect.New(reflect.TypeOf(string(""))).Elem() + ok, err := tryCapsuleConvert(val, into, TypeString) + if ok && err == nil { + val, err := convertValue(Value{into, TypeString}, toType) + if err == nil { + return val, nil + } + } } return Null, TypeError{Value: val, Expected: toType} diff --git a/syntax/vm/vm_stdlib_test.go b/syntax/vm/vm_stdlib_test.go index 19992ed038..9e33ad5d8e 100644 --- a/syntax/vm/vm_stdlib_test.go +++ b/syntax/vm/vm_stdlib_test.go @@ -203,6 +203,11 @@ func TestVM_Stdlib_Errors(t *testing.T) { func TestStdlibCoalesce(t *testing.T) { t.Setenv("TEST_VAR2", "Hello!") + scope := vm.NewScope(map[string]any{ + "optionalSecretStr": alloytypes.OptionalSecret{Value: "bar"}, + "optionalSecretInt": alloytypes.OptionalSecret{Value: "123", IsSecret: false}, + }) + tt := []struct { name string input string @@ -221,6 +226,10 @@ func TestStdlibCoalesce(t *testing.T) { {"coalesce(object, true) and return true", `coalesce(encoding.from_json("{}"), true)`, true}, {"coalesce(object, false) and return false", `coalesce(encoding.from_json("{}"), false)`, false}, {"coalesce(list, nil)", `coalesce([],null)`, value.Null}, + {"optional secret str first in coalesce", `coalesce(optionalSecretStr, 1)`, string("bar")}, + {"optional secret str second in coalesce", `coalesce("foo", optionalSecretStr)`, string("foo")}, + {"optional secret int first in coalesce", `coalesce(optionalSecretInt, 1)`, int(123)}, + {"optional secret int second in coalesce", `coalesce(1, optionalSecretInt)`, int(1)}, } for _, tc := range tt { @@ -231,7 +240,7 @@ func TestStdlibCoalesce(t *testing.T) { eval := vm.New(expr) rv := reflect.New(reflect.TypeOf(tc.expect)) - require.NoError(t, eval.Evaluate(nil, rv.Interface())) + require.NoError(t, eval.Evaluate(scope, rv.Interface())) require.Equal(t, tc.expect, rv.Elem().Interface()) }) } diff --git a/syntax/vm/vm_test.go b/syntax/vm/vm_test.go index 3f4423fbaa..fdb9a2aa77 100644 --- a/syntax/vm/vm_test.go +++ b/syntax/vm/vm_test.go @@ -6,6 +6,7 @@ import ( "testing" "unicode" + "github.com/grafana/alloy/syntax/alloytypes" "github.com/grafana/alloy/syntax/parser" "github.com/grafana/alloy/syntax/scanner" "github.com/grafana/alloy/syntax/token" @@ -61,6 +62,49 @@ func TestVM_Evaluate_Literals(t *testing.T) { } } +func TestVM_Evaluate_Secrets(t *testing.T) { + scope := vm.NewScope(map[string]any{ + "secretSecret": alloytypes.Secret("foo"), + "optionalSecretStr": alloytypes.OptionalSecret{Value: "bar"}, + "optionalSecretInt": alloytypes.OptionalSecret{Value: "123", IsSecret: false}, + "optionalSecretNegative": alloytypes.OptionalSecret{Value: "-123", IsSecret: false}, + "optionalSecretFloat": alloytypes.OptionalSecret{Value: "23.5", IsSecret: false}, + }) + + tt := map[string]struct { + input string + expect interface{} + errMsg string + }{ + "secret": {`secretSecret`, string("bar"), "secrets may not be converted into strings"}, + "optional secret str": {`optionalSecretStr`, string("bar"), ""}, + "optional secret int": {`optionalSecretInt`, int(123), ""}, + "optional secret negative int": {`optionalSecretNegative`, int(-123), ""}, + "optional secret float": {`optionalSecretFloat`, float64(23.5), ""}, + } + + for name, tc := range tt { + t.Run(name, func(t *testing.T) { + expr, err := parser.ParseExpression(tc.input) + require.NoError(t, err) + + eval := vm.New(expr) + + vPtr := reflect.New(reflect.TypeOf(tc.expect)).Interface() + + err = eval.Evaluate(scope, vPtr) + if tc.errMsg == "" { + require.NoError(t, err) + + actual := reflect.ValueOf(vPtr).Elem().Interface() + require.Equal(t, tc.expect, actual) + } else { + require.ErrorContains(t, err, tc.errMsg) + } + }) + } +} + func TestVM_Evaluate(t *testing.T) { // Shared scope across all tests below scope := vm.NewScope(map[string]interface{}{