diff --git a/CHANGELOG.md b/CHANGELOG.md index f88cb18593..f6295c459f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,13 @@ v1.9.2 - Send profiles concurrently from `pyroscope.ebpf`. (@korniltsev) +- Fix the `validate` command not understanding the `livedebugging` block. (@dehaansa) + +- Fix invalid class names in python profiles obtained with `pyroscope.ebpf`. (@korniltsev) + +- Fixed a bug which prevented non-secret optional secrets to be passed in as `number` arguments. (@ptodev) + +- 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) v1.9.1 ----------------- diff --git a/go.mod b/go.mod index cd85b6eb23..dd5c8dbc00 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.24.3 require ( cloud.google.com/go/pubsub v1.45.3 - connectrpc.com/connect v1.16.2 + connectrpc.com/connect v1.18.1 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.9.0 github.com/Azure/go-autorest/autorest v0.11.29 @@ -52,7 +52,7 @@ require ( github.com/google/cadvisor v0.47.0 github.com/google/dnsmasq_exporter v0.2.1-0.20230620100026-44b14480804a github.com/google/go-cmp v0.7.0 - github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db + github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad github.com/google/renameio/v2 v2.0.0 github.com/google/uuid v1.6.0 github.com/gorilla/mux v1.8.1 @@ -71,7 +71,7 @@ require ( github.com/grafana/loki/v3 v3.0.0-20250218135905-f078e0e3f9b6 // k217-alloy-v1.7-fork branch github.com/grafana/pyroscope-go/godeltaprof v0.1.8 github.com/grafana/pyroscope/api v1.2.0 - github.com/grafana/pyroscope/ebpf v0.4.9 + github.com/grafana/pyroscope/ebpf v0.4.10 github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc github.com/grafana/snowflake-prometheus-exporter v0.0.0-20250507154309-83bcbaac6b04 github.com/grafana/tail v0.0.0-20230510142333-77b18831edf0 @@ -187,7 +187,7 @@ require ( github.com/prometheus/mysqld_exporter v0.17.2 github.com/prometheus/node_exporter v1.6.0 github.com/prometheus/procfs v0.16.1 - github.com/prometheus/prometheus v0.300.1 // replaced by a fork of v2.54.1 further down this file + github.com/prometheus/prometheus v0.302.1 // replaced by a fork of v2.54.1 further down this file github.com/prometheus/snmp_exporter v0.29.0 // if you update the snmp_exporter version, make sure to update the SNMP_VERSION in _index github.com/prometheus/statsd_exporter v0.28.0 github.com/richardartoul/molecule v1.0.1-0.20240531184615-7ca0df43c0b3 diff --git a/go.sum b/go.sum index 5adc228770..20b3c4bdfe 100644 --- a/go.sum +++ b/go.sum @@ -48,8 +48,8 @@ cloud.google.com/go/trace v1.11.3 h1:c+I4YFjxRQjvAhRmSsmjpASUKq88chOX854ied0K/pE cloud.google.com/go/trace v1.11.3/go.mod h1:pt7zCYiDSQjC9Y2oqCsh9jF4GStB/hmjrYLsxRR27q8= code.cloudfoundry.org/clock v1.0.0/go.mod h1:QD9Lzhd/ux6eNQVUDVRJX/RKTigpewimNYBi7ivZKY8= collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= -connectrpc.com/connect v1.16.2 h1:ybd6y+ls7GOlb7Bh5C8+ghA6SvCBajHwxssO2CGFjqE= -connectrpc.com/connect v1.16.2/go.mod h1:n2kgwskMHXC+lVqb18wngEpF95ldBHXjZYJussz5FRc= +connectrpc.com/connect v1.18.1 h1:PAg7CjSAGvscaf6YZKUefjoih5Z/qYkyaTrBW8xvYPw= +connectrpc.com/connect v1.18.1/go.mod h1:0292hj1rnx8oFrStN7cB4jjVBeqs+Yx5yDIC2prWDO8= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= @@ -1318,8 +1318,8 @@ github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= -github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg= +github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg= github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4= @@ -1423,8 +1423,8 @@ github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKt github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/pyroscope/api v1.2.0 h1:SfHDZcEZ4Vbj/Jj3bTOSpm4IDB33wLA2xBYxROhiL4U= github.com/grafana/pyroscope/api v1.2.0/go.mod h1:CCWrMnwvTB5O+VBZfT+jO2RAvgm0GxdG2//kAWuMDhA= -github.com/grafana/pyroscope/ebpf v0.4.9 h1:sClXWakmkfFzvSzuxt/Fr6xb22C4oiKfdcCFzvAeRd0= -github.com/grafana/pyroscope/ebpf v0.4.9/go.mod h1:e5QegCu0a5BJ1bvZS+0WNkUouS8wVYLkD/5FWTV30eY= +github.com/grafana/pyroscope/ebpf v0.4.10 h1:EnO8mcZ1uiYdi5HR8kiG0gQYFHLP5nIHQ03sNT6cquw= +github.com/grafana/pyroscope/ebpf v0.4.10/go.mod h1:QxUq39zUwWG2MSzmReS81U3Jynf6mYy2nRJpsfvCc9A= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc h1:GN2Lv3MGO7AS6PrRoT6yV5+wkrOpcszoIsO4+4ds248= github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc/go.mod h1:+JKpmjMGhpgPL+rXZ5nsZieVzvarn86asRlBg4uNGnk= github.com/grafana/smimesign v0.2.1-0.20220408144937-2a5adf3481d3 h1:UPkAxuhlAcRmJT3/qd34OMTl+ZU7BLLfOO2+NXBlJpY= diff --git a/internal/alloycli/cmd_validate.go b/internal/alloycli/cmd_validate.go index 9cdcdec978..9eb97e34b4 100644 --- a/internal/alloycli/cmd_validate.go +++ b/internal/alloycli/cmd_validate.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/alloy/internal/service/cluster" "github.com/grafana/alloy/internal/service/http" "github.com/grafana/alloy/internal/service/labelstore" + "github.com/grafana/alloy/internal/service/livedebugging" "github.com/grafana/alloy/internal/service/otel" "github.com/grafana/alloy/internal/service/remotecfg" "github.com/grafana/alloy/internal/service/ui" @@ -70,6 +71,7 @@ func (v *alloyValidate) Run(configFile string) error { &cluster.Service{}, &http.Service{}, &labelstore.Service{}, + &livedebugging.Service{}, &otel.Service{}, &remotecfg.Service{}, &ui.Service{}, diff --git a/internal/component/prometheus/operator/common/crdmanager.go b/internal/component/prometheus/operator/common/crdmanager.go index 09875e0ada..62252f16c2 100644 --- a/internal/component/prometheus/operator/common/crdmanager.go +++ b/internal/component/prometheus/operator/common/crdmanager.go @@ -13,6 +13,7 @@ import ( "github.com/go-kit/log" "github.com/grafana/ckit/shard" + "github.com/grafana/dskit/backoff" promopv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" promopv1alpha1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1alpha1" "github.com/prometheus/common/model" @@ -162,7 +163,7 @@ func (c *crdManager) Run(ctx context.Context) error { if err := c.runInformers(restConfig, ctx); err != nil { return err } - level.Info(c.logger).Log("msg", "informers started") + level.Info(c.logger).Log("msg", "informers started") var cachedTargets map[string][]*targetgroup.Group // Start the target discovery loop to update the scrape manager with new targets. @@ -323,6 +324,23 @@ func (c *crdManager) runInformers(restConfig *rest.Config, ctx context.Context) return nil } +func getInformer(ctx context.Context, informers cache.Informers, prototype client.Object, timeout time.Duration) (cache.Informer, error) { + informerCtx, cancel := context.WithTimeout(ctx, timeout) + defer cancel() + + informer, err := informers.GetInformer(informerCtx, prototype) + if err != nil { + if errors.Is(informerCtx.Err(), context.DeadlineExceeded) { // Check the context to prevent GetInformer returning a fake timeout + return nil, fmt.Errorf("timeout exceeded while configuring informers. Check the connection"+ + " to the Kubernetes API is stable and that Alloy has appropriate RBAC permissions for %T", prototype) + } + + return nil, err + } + + return informer, err +} + // configureInformers configures the informers for the CRDManager to watch for crd changes. func (c *crdManager) configureInformers(ctx context.Context, informers cache.Informers) error { var prototype client.Object @@ -339,18 +357,32 @@ func (c *crdManager) configureInformers(ctx context.Context, informers cache.Inf return fmt.Errorf("unknown kind to configure Informers: %s", c.kind) } - informerCtx, cancel := context.WithTimeout(ctx, c.args.InformerSyncTimeout) - defer cancel() + // On node restart, the API server is not always immediately available. + // Retry with backoff to give time for the network to initialize. + var informer cache.Informer + var err error - informer, err := informers.GetInformer(informerCtx, prototype) - if err != nil { - if errors.Is(informerCtx.Err(), context.DeadlineExceeded) { // Check the context to prevent GetInformer returning a fake timeout - return fmt.Errorf("timeout exceeded while configuring informers. Check the connection"+ - " to the Kubernetes API is stable and that Alloy has appropriate RBAC permissions for %v", prototype) + backoff := backoff.New( + ctx, + backoff.Config{ + MinBackoff: 1 * time.Second, + MaxBackoff: 10 * time.Second, + MaxRetries: 3, // retry up to 3 times + }, + ) + for backoff.Ongoing() { + // Retry to get the informer in case of a timeout. + informer, err = getInformer(ctx, informers, prototype, c.args.InformerSyncTimeout) + if err == nil { + break } - + level.Warn(c.logger).Log("msg", "failed to get informer, retrying", "next backoff", backoff.NextDelay(), "err", err) + backoff.Wait() + } + if err != nil { return err } + const resync = 5 * time.Minute switch c.kind { case KindPodMonitor: diff --git a/internal/validator/testdata/default/valid.txtar b/internal/validator/testdata/default/valid.txtar index 8ac39ad176..51ee25d546 100644 --- a/internal/validator/testdata/default/valid.txtar +++ b/internal/validator/testdata/default/valid.txtar @@ -12,6 +12,10 @@ logging { format = "logfmt" } +livedebugging { + enabled = true +} + tracing { sampling_fraction = 0.1 diff --git a/internal/validator/validate_test.go b/internal/validator/validate_test.go index 84c6f96859..fd0fa54443 100644 --- a/internal/validator/validate_test.go +++ b/internal/validator/validate_test.go @@ -19,6 +19,7 @@ import ( "github.com/grafana/alloy/internal/service/cluster" "github.com/grafana/alloy/internal/service/http" "github.com/grafana/alloy/internal/service/labelstore" + "github.com/grafana/alloy/internal/service/livedebugging" "github.com/grafana/alloy/internal/service/otel" "github.com/grafana/alloy/internal/service/remotecfg" "github.com/grafana/alloy/internal/service/ui" @@ -64,6 +65,7 @@ func testDirectory(t *testing.T, dir string, minStability featuregate.Stability, &cluster.Service{}, &http.Service{}, &labelstore.Service{}, + &livedebugging.Service{}, &otel.Service{}, &remotecfg.Service{}, &ui.Service{}, 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 9a2014889c..49489bb1e2 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{}{