Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions central/metrics/custom/tracker/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ type Configuration struct {
toDelete []MetricName
period time.Duration
}

func (cfg *Configuration) isEnabled() bool {
return cfg != nil && len(cfg.metrics) > 0 && cfg.period > 0
}
71 changes: 35 additions & 36 deletions central/metrics/custom/tracker/tracker_base.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/stackrox/rox/pkg/set"
"github.com/stackrox/rox/pkg/sync"
"github.com/stackrox/rox/pkg/telemetry/phonehome/telemeter"
"github.com/stackrox/rox/pkg/utils"
)

const inactiveGathererTTL = 2 * 24 * time.Hour
Expand Down Expand Up @@ -72,6 +73,30 @@ type gatherer[F Finding] struct {
config *Configuration
}

// updateMetrics aggregates the fetched findings and updates the gauges.
func (g *gatherer[F]) updateMetrics(generator iter.Seq2[F, error]) error {
g.aggregator.reset()
for finding, err := range generator {
if err != nil {
return err
}
g.aggregator.count(finding)
}
g.registry.Lock()
defer g.registry.Unlock()
for metric, records := range g.aggregator.result {
g.registry.Reset(string(metric))
for _, rec := range records {
g.registry.SetTotal(string(metric), rec.labels, rec.total)
}
}
return nil
}

func (g *gatherer[F]) trySetRunning() bool {
return g.running.CompareAndSwap(false, true)
}

// TrackerBase implements a generic finding tracker.
// Configured with a finding generator and other arguments, it runs a goroutine
// that periodically aggregates gathered values and updates the gauge values.
Expand Down Expand Up @@ -139,7 +164,7 @@ func (tracker *TrackerBase[F]) Reconfigure(cfg *Configuration) {
}
previous := tracker.setConfiguration(cfg)
if previous != nil {
if cfg.period == 0 {
if !cfg.isEnabled() {
log.Debugf("Metrics collection has been disabled for %s", tracker.description)
tracker.unregisterMetrics(slices.Collect(maps.Keys(previous.metrics)))
return
Expand Down Expand Up @@ -235,6 +260,8 @@ func (tracker *TrackerBase[F]) getConfiguration() *Configuration {
return tracker.config
}

// setConfiguration updates the tracker configuration and returns the previous
// one.
func (tracker *TrackerBase[F]) setConfiguration(config *Configuration) *Configuration {
tracker.metricsConfigMux.Lock()
defer tracker.metricsConfigMux.Unlock()
Expand All @@ -243,39 +270,15 @@ func (tracker *TrackerBase[F]) setConfiguration(config *Configuration) *Configur
return previous
}

// track aggregates the fetched findings and updates the gauges.
func (tracker *TrackerBase[F]) track(ctx context.Context, gatherer *gatherer[F], cfg *Configuration) error {
if len(cfg.metrics) == 0 {
return nil
}
aggregator := gatherer.aggregator
registry := gatherer.registry
aggregator.reset()
for finding, err := range tracker.generator(ctx, cfg.metrics) {
if err != nil {
return err
}
aggregator.count(finding)
}
registry.Lock()
defer registry.Unlock()
for metric, records := range aggregator.result {
registry.Reset(string(metric))
for _, rec := range records {
registry.SetTotal(string(metric), rec.labels, rec.total)
}
}
return nil
}

// Gather the data not more often then maxAge.
func (tracker *TrackerBase[F]) Gather(ctx context.Context) {
id, err := authn.IdentityFromContext(ctx)
if err != nil {
cfg := tracker.getConfiguration()
if !cfg.isEnabled() {
return
}
cfg := tracker.getConfiguration()
if cfg == nil {
id, err := authn.IdentityFromContext(ctx)
if err != nil {
utils.Should(err)
return
}
// Pass the cfg so that the same configuration is used there and here.
Expand All @@ -287,11 +290,11 @@ func (tracker *TrackerBase[F]) Gather(ctx context.Context) {
defer tracker.cleanupInactiveGatherers()
defer gatherer.running.Store(false)

if cfg.period == 0 || time.Since(gatherer.lastGather) < cfg.period {
if time.Since(gatherer.lastGather) < cfg.period {
return
}
begin := time.Now()
if err := tracker.track(ctx, gatherer, cfg); err != nil {
if err := gatherer.updateMetrics(tracker.generator(ctx, cfg.metrics)); err != nil {
log.Errorf("Failed to gather %s metrics: %v", tracker.description, err)
}
end := time.Now()
Expand Down Expand Up @@ -364,10 +367,6 @@ func (tracker *TrackerBase[F]) getGatherer(userID string, cfg *Configuration) *g
return gr
}

func (g *gatherer[F]) trySetRunning() bool {
return g.running.CompareAndSwap(false, true)
}

// cleanupInactiveGatherers frees the registries for the userIDs, that haven't
// shown up for inactiveGathererTTL.
func (tracker *TrackerBase[F]) cleanupInactiveGatherers() {
Expand Down
4 changes: 2 additions & 2 deletions central/metrics/custom/tracker/tracker_base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func TestTrackerBase_Track(t *testing.T) {
registry: rf,
aggregator: makeAggregator(tracker.config.metrics, tracker.config.includeFilters, tracker.config.excludeFilters, tracker.getters),
}
assert.NoError(t, tracker.track(context.Background(), testGatherer, tracker.config))
assert.NoError(t, testGatherer.updateMetrics(tracker.generator(context.Background(), tracker.config.metrics)))

if assert.Len(t, result, 2) &&
assert.Contains(t, result, "test_TestTrackerBase_Track_metric1") &&
Expand Down Expand Up @@ -261,7 +261,7 @@ func TestTrackerBase_error(t *testing.T) {
registry: rf,
aggregator: makeAggregator(tracker.config.metrics, tracker.config.includeFilters, tracker.config.excludeFilters, tracker.getters),
}
assert.ErrorIs(t, tracker.track(context.Background(), testGatherer, tracker.config),
assert.ErrorIs(t, testGatherer.updateMetrics(tracker.generator(context.Background(), tracker.config.metrics)),
errox.InvariantViolation)
}

Expand Down
Loading