From 3ce884393915b6d2b6367c4ba88a79d0fc2c13f7 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Sat, 9 Nov 2024 02:38:46 +0200 Subject: [PATCH 1/6] metrics: rework metrics collector registry. Rework our metrics collector registry to take care of most of the necessary bits for metrics registration, collection and gathering. Use the prometheus-provided namespacing and subsystems to put all generated metrics under a prefix and provide additional grouping. Signed-off-by: Krisztian Litkey --- .../developers-guide/architecture.md | 10 +- pkg/metrics/doc.go | 67 ++ pkg/metrics/metrics.go | 702 +++++++++++++++++- pkg/metrics/metrics_test.go | 428 +++++++++++ pkg/metrics/register/register_metrics.go | 6 - 5 files changed, 1174 insertions(+), 39 deletions(-) create mode 100644 pkg/metrics/doc.go create mode 100644 pkg/metrics/metrics_test.go delete mode 100644 pkg/metrics/register/register_metrics.go diff --git a/docs/resource-policy/developers-guide/architecture.md b/docs/resource-policy/developers-guide/architecture.md index 0b466eb0c..e58477873 100644 --- a/docs/resource-policy/developers-guide/architecture.md +++ b/docs/resource-policy/developers-guide/architecture.md @@ -162,11 +162,11 @@ for post-policy enforcement of decisions. ### [Metrics Collector](tree:/pkg/metrics/) -The metrics collector gathers a set of runtime metrics about the containers -running on the node. NRI-RP can be configured to periodically evaluate this -collected data to determine how optimal the current assignment of container -resources is and to attempt a rebalancing/reallocation if it is deemed -both possible and necessary. +The metrics collector gathers a set of runtime metrics about system resources, +containers running on the node, and policy-specific resource assignments and +expose these as Prometheus metrics. This data can be externally evaluated and +used to trigger rebalancing of resources if the NRI-RP implementation provides +a (policy-specific) external interface for this. ### [Policy Implementations](tree:/cmd/plugins) diff --git a/pkg/metrics/doc.go b/pkg/metrics/doc.go new file mode 100644 index 000000000..dcec801ed --- /dev/null +++ b/pkg/metrics/doc.go @@ -0,0 +1,67 @@ +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +// The metrics package provides a simple framework for collecting and +// exporting metrics. It is implemented as a set of simple wrappers around +// prometheus types. These help enforce metrics namespacing, allow metrics +// grouping, provide dynamic runtime configurability, and allow for periodic +// collection of computationally expensive metrics which would be too costly +// to calculate each time they are externally requested. +// +// Simple Usage +// +//package main +// +//import ( +// "log" +// "net/http" +// "os" +// +// "github.com/containers/nri-plugins/pkg/metrics" +// "github.com/prometheus/client_golang/prometheus/collectors" +// "github.com/prometheus/client_golang/prometheus/promhttp" +//) +// +//func main() { +// metrics.MustRegister( +// "build", +// collectors.NewBuildInfoCollector(), +// metrics.WithGroup("group1"), +// ) +// metrics.MustRegister( +// "golang", +// collectors.NewGoCollector(), +// metrics.WithGroup("group1"), +// ) +// metrics.MustRegister( +// "process", +// collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}), +// metrics.WithGroup("group2"), +// ) +// +// enabled = []string{"*"} +// if len(os.Args) > 1 { +// enabled = os.Args[1:] +// } +// +// g, err := metrics.NewGatherer(metrics.WithMetrics(enabled, nil)) +// if err != nil { +// log.Fatal(err) +// } +// +// http.Handle("/metrics", promhttp.HandlerFor(g, promhttp.HandlerOpts{})) +// log.Fatal(http.ListenAndServe(":8891", nil)) +//} diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index 1f15b85ef..24bcdd7ca 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -1,57 +1,703 @@ +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package metrics import ( "fmt" + "path" + "strings" + "sync" + "time" + logger "github.com/containers/nri-plugins/pkg/log" "github.com/prometheus/client_golang/prometheus" + model "github.com/prometheus/client_model/go" ) var ( - builtInCollectors = make(map[string]InitCollector) - registeredCollectors = []prometheus.Collector{} - initializedCollectors = make(map[string]struct{}) - log = logger.NewLogger("collectors") + log = logger.Get("metrics") + clog = logger.Get("collector") +) + +type ( + // State represents the configuration of a collector or a group of collectors. + State int + + // Collector is a registered prometheus.Collector. + Collector struct { + collector prometheus.Collector + name string + group string + State + lastpoll []prometheus.Metric + } + + // CollectorOption is an option for a Collector. + CollectorOption func(*Collector) +) + +const ( + // Enabled marks a collector as enabled. + Enabled State = (1 << iota) + // Polled marks a collector as polled. Polled collectors return cached metrics + // collected during the last polling cycle. This is useful for computationally + // expensive metrics that should not be collected during normal collection. + Polled + // NamespacePrefix causes a collector's metrics to be prefixed with a common + // namespace. + NamespacePrefix + // SubsystemPrefix causes a collecor's metrics to be prefixed with the name + // of the group the collector belongs to. + SubsystemPrefix + + // DefaultName is the name of the default group. An alias for "". + DefaultName = "default" +) + +// WithoutNamespace is an option to disable namespace prefixing for a collector. +func WithoutNamespace() CollectorOption { + return func(c *Collector) { + c.State &^= NamespacePrefix + } +} + +// WithoutSubsystem is an option to disable group prefixing for a collector. +func WithoutSubsystem() CollectorOption { + return func(c *Collector) { + c.State &^= SubsystemPrefix + } +} + +// WithPolled is an option to mark a collector polled. +func WithPolled() CollectorOption { + return func(c *Collector) { + c.State |= Polled + } +} + +// IsEnabled returns true if the collector is enabled. +func (s State) IsEnabled() bool { + return s&Enabled != 0 +} + +// IsPolled returns true if the collector is polled. +func (s State) IsPolled() bool { + return s&Polled != 0 +} + +// NeedsNamespace returns true if the collector needs a namespace prefix. +func (s State) NeedsNamespace() bool { + return s&NamespacePrefix != 0 +} + +// NeedsSubsystem returns true if the collector needs a group prefix. +func (s State) NeedsSubsystem() bool { + return s&SubsystemPrefix != 0 +} + +// String returns a string representation of the collector state. +func (s State) String() string { + var ( + str = "" + sep = "" + ) + + if s.IsEnabled() { + str += sep + "enabled" + sep = "," + } else { + str += sep + "disabled" + sep = "," + } + if s.IsPolled() { + str += sep + "polled" + sep = "," + } + if s.NeedsNamespace() { + str += sep + "namespace-prefixed" + sep = "," + } + if s.NeedsSubsystem() { + str += sep + "subsystem-prefixed" + } + + return str +} + +// NewCollector creates a new collector with the given name and collector. +func NewCollector(name string, collector prometheus.Collector, options ...CollectorOption) *Collector { + c := &Collector{ + name: name, + collector: collector, + State: Enabled | NamespacePrefix | SubsystemPrefix, + } + + for _, o := range options { + o(c) + } + + return c +} + +// Name returns the name of the collector. +func (c *Collector) Name() string { + return c.group + "/" + c.name +} + +// Matches returns true if the collector matches the given glob pattern. +func (c *Collector) Matches(glob string) bool { + if glob == c.group || glob == c.name || glob == c.Name() { + return true + } + + ok, err := path.Match(glob, c.group) + if ok { + return true + } + + ok, err = path.Match(glob, c.name) + if ok { + return true + } + + ok, err = path.Match(glob, c.Name()) + if ok { + return true + } + + if err != nil { + log.Error("invalid glob pattern %q (name %s): %v", glob, c.Name(), err) + } + + return false +} + +// Describe implements the prometheus.Collector interface. +func (c *Collector) Describe(ch chan<- *prometheus.Desc) { + c.collector.Describe(ch) +} + +// Collect implements the prometheus.Collector interface. +func (c *Collector) Collect(ch chan<- prometheus.Metric) { + switch { + case !c.IsEnabled(): + return + + case !c.IsPolled(): + clog.Debug("collecting %q", c.Name()) + c.collector.Collect(ch) + + default: // c.IsEnabled() && c.IsPolled(): + clog.Debug("collecting (polled) %q", c.Name()) + for _, m := range c.lastpoll { + ch <- m + } + } +} + +// Poll collects metrics from the collector if it is polled. +func (c *Collector) Poll() { + if !c.IsEnabled() || !c.IsPolled() { + return + } + + clog.Debug("polling %q", c.Name()) + + ch := make(chan prometheus.Metric, 32) + go func() { + c.collector.Collect(ch) + close(ch) + }() + + polled := make([]prometheus.Metric, 0, 16) + for m := range ch { + polled = append(polled, m) + } + + c.lastpoll = polled[:] +} + +// Enable enables or disables the collector. +func (c *Collector) Enable(state bool) { + if state { + c.State |= Enabled + } else { + c.State &^= Enabled + } +} + +// Polled marks the collector polled or non-polled. +func (c *Collector) Polled(state bool) { + if state { + c.State |= Polled + } else { + c.State &^= Polled + } +} + +func (c *Collector) state() State { + return c.State +} + +type ( + // Group is a collection of collectors. + Group struct { + name string + collectors []*Collector + } ) -// InitCollector is the type for functions that initialize collectors. -type InitCollector func() (prometheus.Collector, error) +func newGroup(name string) *Group { + return &Group{name: name} +} + +// Describe implements the prometheus.Collector interface. +func (g *Group) Describe(ch chan<- *prometheus.Desc) { + for _, c := range g.collectors { + c.Describe(ch) + } +} + +// Collect implements the prometheus.Collector interface. +func (g *Group) Collect(ch chan<- prometheus.Metric) { + clog.Debug("collecting group %s", g.name) + for _, c := range g.collectors { + c.Collect(ch) + } +} + +func (g *Group) poll() { + if !g.state().IsPolled() { + return + } -// RegisterCollector registers the named prometheus.Collector for metrics collection. -func RegisterCollector(name string, init InitCollector) error { - log.Info("registering collector %s...", name) + clog.Debug("polling group %s", g.name) + wg := sync.WaitGroup{} + for _, c := range g.collectors { + wg.Add(1) + go func() { + defer wg.Done() + c.Poll() + }() + } + wg.Wait() +} - if _, found := builtInCollectors[name]; found { - return metricsError("Collector %s already registered", name) +func (g *Group) state() State { + var state State + for _, c := range g.collectors { + state |= c.state() } + return state +} + +func (g *Group) add(c *Collector) { + c.group = g.name + g.collectors = append(g.collectors, c) + log.Info("registered collector %q", c.Name()) +} + +func (g *Group) register(plain, ns prometheus.Registerer) error { + var ( + plainGrp = prefixedRegisterer(g.name, plain) + nsGrp = prefixedRegisterer(g.name, ns) + ) + + for _, c := range g.collectors { + var reg prometheus.Registerer - builtInCollectors[name] = init + if c.NeedsNamespace() { + if c.NeedsSubsystem() { + reg = nsGrp + } else { + reg = ns + } + } else { + if c.NeedsSubsystem() { + reg = plainGrp + } else { + reg = plain + } + } + + if err := reg.Register(c); err != nil { + return err + } + } return nil } -// NewMetricGatherer creates a new prometheus.Gatherer with all registered collectors. -func NewMetricGatherer() (prometheus.Gatherer, error) { - reg := prometheus.NewPedanticRegistry() +func (g *Group) configure(enabled, polled []string, match map[string]struct{}) State { + for _, c := range g.collectors { + c.Enable(false) + } - for name, cb := range builtInCollectors { - if _, ok := initializedCollectors[name]; ok { - continue + state := State(0) + for _, c := range g.collectors { + for _, glob := range enabled { + if c.Matches(glob) { + match[glob] = struct{}{} + c.Enable(true) + log.Info("collector %q now %s", c.Name(), c.state()) + } + state |= c.state() + } + for _, glob := range polled { + if c.Matches(glob) { + match[glob] = struct{}{} + c.Enable(true) + // TODO(klihub): Note that this is currently a one-way street. + // Once we force a collector to be polled we never reset it to + // be normally collected. So let's give a warning about it... + if !c.IsPolled() { + log.Warn("permanently forcing collector %q to be polled", c.Name()) + } + c.Polled(true) + log.Info("collector %q now %s", c.Name(), c.state()) + } + state |= c.state() } + } + + log.Info("group %q now %s", g.name, state) + + return state +} + +type ( + // Registry is a collection of groups. + Registry struct { + groups map[string]*Group + state State + } + + // RegisterOptions are options for registering collectors. + RegisterOptions struct { + group string + copts []CollectorOption + } - c, err := cb() - if err != nil { - log.Error("Failed to initialize collector '%s': %v. Skipping it.", name, err) - continue + // RegisterOption is an option for registering collectors. + RegisterOption func(*RegisterOptions) +) + +// WithGroup is an option to register a collector in a specific group. +func WithGroup(name string) RegisterOption { + return func(o *RegisterOptions) { + if name == "" { + name = DefaultName + } + o.group = name + } +} + +// WithCollectorOptions is an option to register a collector with options. +func WithCollectorOptions(opts ...CollectorOption) RegisterOption { + return func(o *RegisterOptions) { + o.copts = append(o.copts, opts...) + } +} + +// NewRegistry creates a new registry. +func NewRegistry() *Registry { + return &Registry{ + groups: make(map[string]*Group), + } +} + +// Register registers a collector with the registry. +func (r *Registry) Register(name string, collector prometheus.Collector, opts ...RegisterOption) error { + options := &RegisterOptions{group: DefaultName} + for _, o := range opts { + o(options) + } + + grp, ok := r.groups[options.group] + if !ok { + grp = newGroup(options.group) + r.groups[grp.name] = grp + } + + grp.add(NewCollector(name, collector, options.copts...)) + r.state = 0 + + return nil +} + +// Configure enables the collectors matching any of the given globs. Any +// collector matching any glob in polled is forced to polled mode. +func (r *Registry) Configure(enabled []string, polled []string) (State, error) { + log.Info("configuring registry with collectors enabled=[%s], polled=[%s]", + strings.Join(enabled, ","), strings.Join(polled, ",")) + + match := make(map[string]struct{}) + r.state = 0 + for _, g := range r.groups { + r.state |= g.configure(enabled, polled, match) + } + + unmatched := []string{} + for _, glob := range enabled { + if _, ok := match[glob]; !ok { + unmatched = append(unmatched, glob) + } + } + for _, glob := range polled { + if _, ok := match[glob]; !ok { + unmatched = append(unmatched, glob) + } + } + + if len(unmatched) > 0 { + return r.state, fmt.Errorf("no collectors match globs %s", strings.Join(unmatched, ", ")) + } + + return r.state, nil +} + +// Poll all collectors with are enabled and in polled mode. +func (r *Registry) Poll() { + wg := sync.WaitGroup{} + for _, g := range r.groups { + wg.Add(1) + go func() { + defer wg.Done() + g.poll() + }() + } + wg.Wait() +} + +// State returns the collective state of all collectors in the registry. +func (r *Registry) State() State { + if r.state == 0 { + for _, g := range r.groups { + r.state |= g.state() + } + } + return r.state +} + +// Getherer returns a gatherer for the registry, configured with the given options. +func (r *Registry) Gatherer(opts ...GathererOption) (*Gatherer, error) { + return r.NewGatherer(opts...) +} + +func prefixedRegisterer(prefix string, reg prometheus.Registerer) prometheus.Registerer { + if prefix != "" { + return prometheus.WrapRegistererWithPrefix(prefix+"_", reg) + } + return reg +} + +type ( + // Gatherer is a prometheus gatherer for our registry. + Gatherer struct { + *prometheus.Registry + r *Registry + namespace string + ticker *time.Ticker + pollInterval time.Duration + stopCh chan chan struct{} + lock sync.Mutex + enabled []string + polled []string + } + + // GathererOption is an option for the gatherer. + GathererOption func(*Gatherer) +) + +const ( + // MinPollInterval is the most frequent allowed polling interval. + MinPollInterval = 5 * time.Second + // DefaultPollInterval is the default interval for polling collectors. + DefaultPollInterval = 30 * time.Second +) + +// WithNamespace defines the common namespace prefix for gathered collectors. +func WithNamespace(namespace string) GathererOption { + return func(g *Gatherer) { + g.namespace = namespace + } +} + +// WithPollInterval defines the polling interval for the gatherer. +func WithPollInterval(interval time.Duration) GathererOption { + return func(g *Gatherer) { + if interval < MinPollInterval { + g.pollInterval = MinPollInterval + } else { + g.pollInterval = interval } - registeredCollectors = append(registeredCollectors, c) - initializedCollectors[name] = struct{}{} } +} - reg.MustRegister(registeredCollectors[:]...) +// WithoutPolling disables internally triggered polling for the gatherer. +func WithoutPolling() GathererOption { + return func(g *Gatherer) { + g.pollInterval = 0 + } +} - return reg, nil +// WithMetrics defines which groups or collectors will be enabled, and +// and polled if any. +func WithMetrics(enabled, polled []string) GathererOption { + return func(g *Gatherer) { + g.enabled = enabled + g.polled = polled + } +} + +// NewGatherer creates a new gatherer for the registry, with the given options. +func (r *Registry) NewGatherer(opts ...GathererOption) (*Gatherer, error) { + g := &Gatherer{ + r: r, + Registry: prometheus.NewPedanticRegistry(), + pollInterval: DefaultPollInterval, + } + + for _, o := range opts { + o(g) + } + + if _, err := r.Configure(g.enabled, g.polled); err != nil { + return nil, err + } + + nsg := prefixedRegisterer(g.namespace, g.Registry) + + for _, grp := range r.groups { + if err := grp.register(g.Registry, nsg); err != nil { + return nil, err + } + } + + g.start() + + return g, nil +} + +// Gather implements the prometheus.Gatherer interface. +func (g *Gatherer) Gather() ([]*model.MetricFamily, error) { + g.Block() + defer g.Unblock() + + mfs, err := g.Registry.Gather() + if err != nil { + return nil, err + } + + return mfs, nil +} + +// Block the gatherer from polling collectors. +func (g *Gatherer) Block() { + g.lock.Lock() +} + +// Allow the gatherer to poll collectors. +func (g *Gatherer) Unblock() { + g.lock.Unlock() +} + +// Poll all enabled collectors in poll mode in the registry. +func (g *Gatherer) Poll() { + g.Block() + g.r.Poll() + g.Unblock() +} + +func (g *Gatherer) start() { + g.Block() + defer g.Unblock() + + if !g.r.State().IsPolled() { + log.Info("no polling (no collectors in polled mode)") + return + } + + if g.pollInterval == 0 { + log.Info("no polling (internally triggered polling disabled)") + return + } + + log.Info("will do periodic polling (some collectors in polled mode)") + + g.stopCh = make(chan chan struct{}) + g.ticker = time.NewTicker(g.pollInterval) + + g.r.Poll() + go g.poller() +} + +func (g *Gatherer) poller() { + for { + select { + case doneCh := <-g.stopCh: + g.ticker.Stop() + g.ticker = nil + close(doneCh) + return + case _ = <-g.ticker.C: + g.Poll() + } + } +} + +func (g *Gatherer) Stop() { + g.Block() + defer g.Unblock() + + if g.stopCh == nil { + return + } + + doneCh := make(chan struct{}) + g.stopCh <- doneCh + _ = <-doneCh + + g.stopCh = nil +} + +var ( + defaultRegistry *Registry +) + +// Default returns the default registry. +func Default() *Registry { + if defaultRegistry == nil { + defaultRegistry = NewRegistry() + } + return defaultRegistry +} + +// Register registers a collector with the default registry. +func Register(name string, collector prometheus.Collector, opts ...RegisterOption) error { + return Default().Register(name, collector, opts...) +} + +// MustRegister registers a collector with the default registry, panicking on error. +func MustRegister(name string, collector prometheus.Collector, opts ...RegisterOption) { + if err := Register(name, collector, opts...); err != nil { + panic(err) + } } -func metricsError(format string, args ...interface{}) error { - return fmt.Errorf("metrics: "+format, args...) +// NewGatherer creates a new gatherer for the default registry, with the given options. +func NewGatherer(opts ...GathererOption) (*Gatherer, error) { + return Default().Gatherer(opts...) } diff --git a/pkg/metrics/metrics_test.go b/pkg/metrics/metrics_test.go new file mode 100644 index 000000000..cc39fdc0b --- /dev/null +++ b/pkg/metrics/metrics_test.go @@ -0,0 +1,428 @@ +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics_test + +import ( + "bufio" + "context" + "net/http" + "strings" + "testing" + "time" + + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/stretchr/testify/require" + + logger "github.com/containers/nri-plugins/pkg/log" + "github.com/containers/nri-plugins/pkg/metrics" +) + +func TestMetricsDescriptors(t *testing.T) { + r := metrics.NewRegistry() + require.NotNil(t, r, "non-nil registry") + + newTestGauge(t, r, "test1", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test2", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test3", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test4", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + + var ( + enabled = []string{"*"} + none []string + ) + + srv := newTestServer(t, r, enabled, none, 0) + defer srv.stop() + + descriptors, _ := srv.collect(t) + require.True(t, descriptors.HasEntry("test1", "gauge")) + require.True(t, descriptors.HasEntry("test2", "gauge")) + require.True(t, descriptors.HasEntry("test3", "gauge")) + require.True(t, descriptors.HasEntry("test4", "gauge")) +} + +func TestUnprefixedDefaultCollection(t *testing.T) { + r := metrics.NewRegistry() + require.NotNil(t, r, "non-nil registry") + + newTestGauge(t, r, "test1", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test2", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test3", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test4", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + + var ( + enabled = []string{"*"} + none []string + ) + + srv := newTestServer(t, r, enabled, none, 0) + defer srv.stop() + + _, metrics := srv.collect(t) + require.Equal(t, "0", metrics.GetValue("test1")) + require.Equal(t, "0", metrics.GetValue("test2")) + require.Equal(t, "0", metrics.GetValue("test3")) + require.Equal(t, "0", metrics.GetValue("test4")) +} + +func TestPrefixedDefaultCollection(t *testing.T) { + r := metrics.NewRegistry() + require.NotNil(t, r, "non-nil registry") + + newTestGauge(t, r, "test1") + newTestGauge(t, r, "test2") + newTestGauge(t, r, "test3") + newTestGauge(t, r, "test4") + + var ( + enabled = []string{"*"} + none []string + ) + + srv := newTestServer(t, r, enabled, none, 0) + defer srv.stop() + + _, metrics := srv.collect(t) + require.Equal(t, "0", metrics.GetValue("default_test1")) + require.Equal(t, "0", metrics.GetValue("default_test2")) + require.Equal(t, "0", metrics.GetValue("default_test3")) + require.Equal(t, "0", metrics.GetValue("default_test4")) +} + +func TestUpdatedMetricsCollection(t *testing.T) { + r := metrics.NewRegistry() + require.NotNil(t, r, "non-nil registry") + + g1 := newTestGauge(t, r, "test1", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + g2 := newTestGauge(t, r, "test2", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + g3 := newTestGauge(t, r, "test3", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + g4 := newTestGauge(t, r, "test4", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + + var ( + enabled = []string{"*"} + none []string + ) + + srv := newTestServer(t, r, enabled, none, 0) + defer srv.stop() + + _, metrics := srv.collect(t) + require.Equal(t, "0", metrics.GetValue("test1")) + require.Equal(t, "0", metrics.GetValue("test2")) + require.Equal(t, "0", metrics.GetValue("test3")) + require.Equal(t, "0", metrics.GetValue("test4")) + + g1.gauge.Inc() + g2.gauge.Set(5) + g3.gauge.Inc() + g4.gauge.Set(3) + + _, metrics = srv.collect(t) + require.Equal(t, "1", metrics.GetValue("test1")) + require.Equal(t, "5", metrics.GetValue("test2")) + require.Equal(t, "1", metrics.GetValue("test3")) + require.Equal(t, "3", metrics.GetValue("test4")) + + g1.gauge.Set(4) + g2.gauge.Inc() + g3.gauge.Set(7) + g4.gauge.Dec() + + _, metrics = srv.collect(t) + require.Equal(t, "4", metrics.GetValue("test1")) + require.Equal(t, "6", metrics.GetValue("test2")) + require.Equal(t, "7", metrics.GetValue("test3")) + require.Equal(t, "2", metrics.GetValue("test4")) +} + +func TestMetricsConfiguration(t *testing.T) { + r := metrics.NewRegistry() + require.NotNil(t, r) + + newTestGauge(t, r, "test1", metrics.WithGroup("group1")) + newTestGauge(t, r, "test2", metrics.WithGroup("group1"), + metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test3", metrics.WithGroup("group2"), + metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + newTestGauge(t, r, "test4", metrics.WithGroup("group2")) + + var ( + enabled = []string{"test1", "group2"} + none []string + ) + + srv := newTestServer(t, r, enabled, none, 0) + defer srv.stop() + + described, metrics := srv.collect(t) + require.True(t, described.HasEntry("group1_test1", "gauge")) + require.True(t, described.HasEntry("test3", "gauge")) + require.True(t, described.HasEntry("group2_test4", "gauge")) + + require.True(t, metrics.HasEntry("group1_test1"), "group1_test1 collected") + require.False(t, metrics.HasEntry("test2"), "test2 not collected") + require.True(t, metrics.HasEntry("test3"), "test3 collected") + require.True(t, metrics.HasEntry("group2_test4"), "group2_test4 collected") +} + +func TestMetricsPolling(t *testing.T) { + r := metrics.NewRegistry() + require.NotNil(t, r, "non-nil registry") + + g0 := newTestPolled(t, r, "test1", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + g1 := newTestPolled(t, r, "test2", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + g2 := newTestPolled(t, r, "test3", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + g3 := newTestPolled(t, r, "test4", metrics.WithCollectorOptions(metrics.WithoutSubsystem())) + + var ( + enabled []string + polled = []string{"*"} + interval = metrics.MinPollInterval + ) + + srv := newTestServer(t, r, enabled, polled, interval) + defer srv.stop() + + _, metrics := srv.collect(t) + require.Equal(t, "0", metrics.GetValue("test1")) + require.Equal(t, "0", metrics.GetValue("test2")) + require.Equal(t, "0", metrics.GetValue("test3")) + require.Equal(t, "0", metrics.GetValue("test4")) + + g0.Set(1) + g1.Set(2) + g2.Set(3) + g3.Set(4) + + _, metrics = srv.collect(t) + require.Equal(t, "0", metrics.GetValue("test1")) + require.Equal(t, "0", metrics.GetValue("test2")) + require.Equal(t, "0", metrics.GetValue("test3")) + require.Equal(t, "0", metrics.GetValue("test4")) + + g0.Inc() + g1.Inc() + g2.Set(7) + g3.Set(9) + + _, metrics = srv.collect(t) + require.Equal(t, "0", metrics.GetValue("test1")) + require.Equal(t, "0", metrics.GetValue("test2")) + require.Equal(t, "0", metrics.GetValue("test3")) + require.Equal(t, "0", metrics.GetValue("test4")) + + t.Logf("waiting for metrics poll interval (%s)", interval) + time.Sleep(interval) + + _, metrics = srv.collect(t) + require.Equal(t, "2", metrics.GetValue("test1")) + require.Equal(t, "3", metrics.GetValue("test2")) + require.Equal(t, "7", metrics.GetValue("test3")) + require.Equal(t, "9", metrics.GetValue("test4")) +} + +type testGauge struct { + name string + gauge prometheus.Gauge +} + +func newTestGauge(t *testing.T, r *metrics.Registry, name string, options ...metrics.RegisterOption) *testGauge { + g := &testGauge{ + name: name, + } + g.gauge = prometheus.NewGauge( + prometheus.GaugeOpts{ + Name: name, + Help: "Test gauge " + name, + }, + ) + + require.NoError(t, r.Register(g.name, g.gauge, options...)) + + return g +} + +type testPolled struct { + desc *prometheus.Desc + value int +} + +func newTestPolled(t *testing.T, r *metrics.Registry, name string, options ...metrics.RegisterOption) *testPolled { + p := &testPolled{ + desc: prometheus.NewDesc(name, "Help for metric "+name, nil, nil), + } + require.NoError(t, r.Register(name, p, options...)) + return p +} + +func (p *testPolled) Describe(ch chan<- *prometheus.Desc) { + ch <- p.desc +} + +func (p *testPolled) Collect(ch chan<- prometheus.Metric) { + m, err := prometheus.NewConstMetric(p.desc, prometheus.GaugeValue, float64(p.value)) + if err != nil { + return + } + ch <- m +} + +func (p *testPolled) Set(v int) { + p.value = v +} + +func (p *testPolled) Inc() { + p.value++ +} + +type described []string + +func (d described) HasEntry(name, kind string) bool { + for _, e := range d { + split := strings.Split(e, " ") + if len(split) >= 2 && split[0] == name && split[1] == kind { + return true + } + } + + return false +} + +type collected []string + +func (c collected) HasEntry(name string) bool { + for _, e := range c { + if !strings.HasPrefix(e, "#") { + split := strings.SplitN(e, " ", 2) + if len(split) > 0 && split[0] == name { + return true + } + } + } + + return false +} + +func (c collected) HasValue(name, value string) bool { + for _, e := range c { + if strings.HasPrefix(e, "#") { + continue + } + split := strings.SplitN(e, " ", 2) + if len(split) == 2 && split[0] == name { + if split[0] == name && split[1] == value { + return true + } + } + } + + return false +} + +func (c collected) GetValue(name string) string { + for _, e := range c { + if strings.HasPrefix(e, "#") { + continue + } + split := strings.SplitN(e, " ", 2) + if len(split) == 2 && split[0] == name { + if split[0] == name { + return split[1] + } + } + } + + return "" +} + +type testServer struct { + srv *http.Server + mux *http.ServeMux + r *metrics.Registry + g *metrics.Gatherer +} + +func newTestServer(t *testing.T, r *metrics.Registry, enabled, polled []string, poll time.Duration) *testServer { + g, err := r.NewGatherer( + metrics.WithMetrics(enabled, polled), + metrics.WithPollInterval(poll), + ) + require.NoError(t, err) + require.NotNil(t, g) + + handlerOpts := promhttp.HandlerOpts{ + ErrorLog: logger.Get("metrics-test"), + ErrorHandling: promhttp.PanicOnError, + } + + mux := http.NewServeMux() + mux.Handle("/metrics", promhttp.HandlerFor(g, handlerOpts)) + + srv := &http.Server{ + Addr: ":4321", + Handler: mux, + } + + go func() { + require.Equal(t, http.ErrServerClosed, srv.ListenAndServe()) + }() + + return &testServer{ + srv: srv, + r: r, + g: g, + } +} + +func (srv *testServer) stop() { + if srv.srv != nil { + srv.srv.Shutdown(context.Background()) + } + if srv.g != nil { + srv.g.Stop() + } +} + +func (srv *testServer) collect(t *testing.T) (described, collected) { + resp, err := http.Get("http://localhost" + srv.srv.Addr + "/metrics") + require.NoError(t, err) + + defer resp.Body.Close() + + var ( + types []string + metrics []string + scanner = bufio.NewScanner(resp.Body) + ) + + for scanner.Scan() { + e := scanner.Text() + + switch { + case strings.HasPrefix(e, "# HELP"): + case strings.HasPrefix(e, "# TYPE "): + types = append(types, strings.TrimPrefix(e, "# TYPE ")) + default: + metrics = append(metrics, e) + } + } + + return described(types), collected(metrics) +} + +func (srv *testServer) poll() { + srv.r.Poll() +} diff --git a/pkg/metrics/register/register_metrics.go b/pkg/metrics/register/register_metrics.go deleted file mode 100644 index be22722e8..000000000 --- a/pkg/metrics/register/register_metrics.go +++ /dev/null @@ -1,6 +0,0 @@ -package register - -import ( - // Pull in cgroup-based metric collector. - _ "github.com/containers/nri-plugins/pkg/cgroupstats" -) From 71a13bfe35f6d33d126f1d7004a205b8d425982c Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Mon, 11 Nov 2024 01:00:30 +0200 Subject: [PATCH 2/6] metrics: update cgroupstats collector. Update cgroupstats collector for the reworked metrics registry. Split out automatic registration to a register subpackage. Signed-off-by: Krisztian Litkey --- pkg/cgroupstats/collector.go | 22 ++++++--------- pkg/cgroupstats/register/collector.go | 39 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 14 deletions(-) create mode 100644 pkg/cgroupstats/register/collector.go diff --git a/pkg/cgroupstats/collector.go b/pkg/cgroupstats/collector.go index 301837eb6..b0db8c72a 100644 --- a/pkg/cgroupstats/collector.go +++ b/pkg/cgroupstats/collector.go @@ -25,7 +25,6 @@ import ( "github.com/containers/nri-plugins/pkg/cgroups" logger "github.com/containers/nri-plugins/pkg/log" - "github.com/containers/nri-plugins/pkg/metrics" "github.com/prometheus/client_golang/prometheus" ) @@ -42,7 +41,7 @@ const ( var descriptors = [numDescriptors]*prometheus.Desc{ numaStatsDesc: prometheus.NewDesc( - "cgroup_numa_stats", + "numa_stats", "NUMA statistics for a given container and pod.", []string{ // cgroup path @@ -54,7 +53,7 @@ var descriptors = [numDescriptors]*prometheus.Desc{ }, nil, ), memoryUsageDesc: prometheus.NewDesc( - "cgroup_memory_usage", + "memory_usage", "Memory usage statistics for a given container and pod.", []string{ "container_id", @@ -62,14 +61,14 @@ var descriptors = [numDescriptors]*prometheus.Desc{ }, nil, ), memoryMigrateDesc: prometheus.NewDesc( - "cgroup_memory_migrate", + "memory_migrate", "Memory migrate status for a given container and pod.", []string{ "container_id", }, nil, ), cpuAcctUsageDesc: prometheus.NewDesc( - "cgroup_cpu_acct", + "cpu_acct", "CPU accounting for a given container and pod.", []string{ "container_id", @@ -79,7 +78,7 @@ var descriptors = [numDescriptors]*prometheus.Desc{ }, nil, ), hugeTlbUsageDesc: prometheus.NewDesc( - "cgroup_hugetlb_usage", + "hugetlb_usage", "Hugepages usage for a given container and pod.", []string{ "container_id", @@ -88,7 +87,7 @@ var descriptors = [numDescriptors]*prometheus.Desc{ }, nil, ), blkioDeviceUsageDesc: prometheus.NewDesc( - "cgroup_blkio_device_usage", + "blkio_device_usage", "Blkio Device bytes usage for a given container and pod.", []string{ "container_id", @@ -114,8 +113,8 @@ type collector struct { } // NewCollector creates new Prometheus collector -func NewCollector() (prometheus.Collector, error) { - return &collector{}, nil +func NewCollector() prometheus.Collector { + return &collector{} } // Describe implements prometheus.Collector interface @@ -405,9 +404,4 @@ func (c collector) Collect(ch chan<- prometheus.Metric) { func init() { flag.StringVar(&cgroupRoot, "cgroup-path", cgroupRoot, "Path to cgroup filesystem mountpoint") - - err := metrics.RegisterCollector("cgroupstats", NewCollector) - if err != nil { - log.Error("failed register cgroupstats collector: %v", err) - } } diff --git a/pkg/cgroupstats/register/collector.go b/pkg/cgroupstats/register/collector.go new file mode 100644 index 000000000..5fda778a6 --- /dev/null +++ b/pkg/cgroupstats/register/collector.go @@ -0,0 +1,39 @@ +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package collectors + +import ( + "github.com/containers/nri-plugins/pkg/cgroupstats" + logger "github.com/containers/nri-plugins/pkg/log" + "github.com/containers/nri-plugins/pkg/metrics" +) + +var ( + log = logger.Get("collector") +) + +func init() { + err := metrics.Register( + "stats", + cgroupstats.NewCollector(), + metrics.WithGroup("cgroup"), + metrics.WithCollectorOptions( + metrics.WithoutNamespace(), + ), + ) + if err != nil { + log.Error("failed to register cgroup/stats collector: %v", err) + } +} From 90670c4f42743a615e1e24715fc9e57732327be7 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Sat, 9 Nov 2024 23:42:59 +0200 Subject: [PATCH 3/6] resmgr: remove old scattered bits of metrics polling. Remove the old resmgr-triggered polling of policy metrics and the old resmgr-level polling policy metrics collector. Implement policy metrics collection in the policy package itself. Signed-off-by: Krisztian Litkey --- pkg/resmgr/events.go | 22 ---- pkg/resmgr/metrics/metrics.go | 149 ------------------------ pkg/resmgr/metrics/prometheus.go | 34 ------ pkg/resmgr/nri.go | 9 ++ pkg/resmgr/policy/metrics.go | 55 +++++++++ pkg/resmgr/policy/policy.go | 56 ++++----- pkg/resmgr/policycollector/collector.go | 62 ---------- pkg/resmgr/resource-manager.go | 22 +--- 8 files changed, 88 insertions(+), 321 deletions(-) delete mode 100644 pkg/resmgr/metrics/metrics.go delete mode 100644 pkg/resmgr/metrics/prometheus.go create mode 100644 pkg/resmgr/policy/metrics.go delete mode 100644 pkg/resmgr/policycollector/collector.go diff --git a/pkg/resmgr/events.go b/pkg/resmgr/events.go index 6565e61cf..2ad5265de 100644 --- a/pkg/resmgr/events.go +++ b/pkg/resmgr/events.go @@ -17,7 +17,6 @@ package resmgr import ( logger "github.com/containers/nri-plugins/pkg/log" "github.com/containers/nri-plugins/pkg/resmgr/cache" - "github.com/containers/nri-plugins/pkg/resmgr/metrics" ) // Our logger instance for events. @@ -25,34 +24,14 @@ var evtlog = logger.NewLogger("events") // setupEventProcessing sets up event and metrics processing. func (m *resmgr) setupEventProcessing() error { - var err error - m.events = make(chan interface{}, 8) m.stop = make(chan interface{}) - options := metrics.Options{ - PollInterval: opt.MetricsTimer, - } - if m.metrics, err = metrics.NewMetrics(options); err != nil { - return resmgrError("failed to create metrics (pre)processor: %v", err) - } - - return nil -} - -func (m *resmgr) startMetricsProcessing() error { - if err := m.metrics.Start(); err != nil { - return resmgrError("failed to start metrics (pre)processor: %v", err) - } return nil } // startEventProcessing starts event and metrics processing. func (m *resmgr) startEventProcessing() error { - if err := m.startMetricsProcessing(); err != nil { - return resmgrError("failed to start metrics (pre)processor: %v", err) - } - stop := m.stop go func() { for { @@ -73,7 +52,6 @@ func (m *resmgr) startEventProcessing() error { func (m *resmgr) stopEventProcessing() { if m.stop != nil { close(m.stop) - m.metrics.Stop() m.stop = nil } } diff --git a/pkg/resmgr/metrics/metrics.go b/pkg/resmgr/metrics/metrics.go deleted file mode 100644 index 119613d48..000000000 --- a/pkg/resmgr/metrics/metrics.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2020 Intel Corporation. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - "bytes" - "fmt" - "strings" - "sync" - "time" - - "github.com/prometheus/client_golang/prometheus" - model "github.com/prometheus/client_model/go" - "github.com/prometheus/common/expfmt" - - logger "github.com/containers/nri-plugins/pkg/log" - - "github.com/containers/nri-plugins/pkg/instrumentation" - "github.com/containers/nri-plugins/pkg/metrics" - - // pull in all metrics collectors - _ "github.com/containers/nri-plugins/pkg/metrics/register" -) - -// Options describes options for metrics collection and processing. -type Options struct { - // PollInterval is the interval for polling raw metrics. - PollInterval time.Duration -} - -// Metrics implements collecting, caching and processing of raw metrics. -type Metrics struct { - sync.RWMutex - opts Options // metrics collecting options - g prometheus.Gatherer // prometheus/raw metrics gatherer - stop chan interface{} // channel to stop polling goroutine - raw []*model.MetricFamily // latest set of raw metrics - pend []*model.MetricFamily // pending metrics for forwarding -} - -// Our logger instance. -var log = logger.NewLogger("metrics") - -// NewMetrics creates a new instance for metrics collecting and processing. -func NewMetrics(opts Options) (*Metrics, error) { - g, err := metrics.NewMetricGatherer() - if err != nil { - return nil, metricsError("failed to create raw metrics gatherer: %v", err) - } - - m := &Metrics{ - opts: opts, - raw: make([]*model.MetricFamily, 0), - g: g, - } - - m.poll() - instrumentation.RegisterGatherer(m) - - return m, nil -} - -// Start starts metrics collection and processing. -func (m *Metrics) Start() error { - if m.stop != nil { - return nil - } - - stop := make(chan interface{}) - go func() { - var pollTimer *time.Ticker - var pollChan <-chan time.Time - - if m.opts.PollInterval > 0 { - pollTimer = time.NewTicker(m.opts.PollInterval) - pollChan = pollTimer.C - } else { - log.Info("periodic collection of metrics is disabled") - } - - for { - select { - case _ = <-stop: - if pollTimer != nil { - pollTimer.Stop() - } - return - case _ = <-pollChan: - if err := m.poll(); err != nil { - log.Error("failed to poll raw metrics: %v", err) - } - } - } - }() - m.stop = stop - - return nil -} - -// Stop stops metrics collection and processing. -func (m *Metrics) Stop() { - if m.stop != nil { - close(m.stop) - m.stop = nil - } -} - -// poll does a single round of raw metrics collection. -func (m *Metrics) poll() error { - m.Lock() - defer m.Unlock() - - f, err := m.g.Gather() - if err != nil { - return metricsError("failed to poll raw metrics: %v", err) - } - m.raw = f - m.pend = f - return nil -} - -// dump debug-dumps the given MetricFamily data -func dump(prefix string, f *model.MetricFamily) { - if !log.DebugEnabled() { - return - } - buf := &bytes.Buffer{} - if _, err := expfmt.MetricFamilyToText(buf, f); err != nil { - return - } - log.DebugBlock(" <"+prefix+"> ", "%s", strings.TrimSpace(buf.String())) -} - -// metricsError returns a new formatted error specific to metrics-processing. -func metricsError(format string, args ...interface{}) error { - return fmt.Errorf("metrics: "+format, args...) -} diff --git a/pkg/resmgr/metrics/prometheus.go b/pkg/resmgr/metrics/prometheus.go deleted file mode 100644 index 2ade86961..000000000 --- a/pkg/resmgr/metrics/prometheus.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2020 Intel Corporation. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - model "github.com/prometheus/client_model/go" -) - -// Gather is our prometheus.Gatherer interface for proxying metrics. -func (m *Metrics) Gather() ([]*model.MetricFamily, error) { - m.Lock() - pend := m.pend - m.Unlock() - - if pend == nil { - log.Debug("no data to proxy to prometheus...") - } else { - log.Debug("proxying data to prometheus...") - } - - return pend, nil -} diff --git a/pkg/resmgr/nri.go b/pkg/resmgr/nri.go index f83edc881..83adba5ce 100644 --- a/pkg/resmgr/nri.go +++ b/pkg/resmgr/nri.go @@ -313,6 +313,10 @@ func (p *nriPlugin) StopPodSandbox(ctx context.Context, podSandbox *api.PodSandb m := p.resmgr + // TODO(klihub): shouldn't we m.Lock()/defer m.Unlock() here? + metrics.Block() + defer metrics.Unblock() + released := []cache.Container{} pod, _ := m.cache.LookupPod(podSandbox.GetId()) @@ -391,6 +395,8 @@ func (p *nriPlugin) CreateContainer(ctx context.Context, podSandbox *api.PodSand m := p.resmgr m.Lock() defer m.Unlock() + metrics.Block() + defer metrics.Unblock() c, err := m.cache.InsertContainer(container) if err != nil { @@ -612,6 +618,9 @@ func (p *nriPlugin) RemoveContainer(ctx context.Context, pod *api.PodSandbox, co func (p *nriPlugin) updateContainers() (retErr error) { // Notes: must be called with p.resmgr lock held. + metrics.Block() + defer metrics.Unblock() + updates := p.getPendingUpdates(nil) event := UpdateContainers diff --git a/pkg/resmgr/policy/metrics.go b/pkg/resmgr/policy/metrics.go new file mode 100644 index 000000000..19eb09169 --- /dev/null +++ b/pkg/resmgr/policy/metrics.go @@ -0,0 +1,55 @@ +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package policy + +import ( + "github.com/prometheus/client_golang/prometheus" + + "github.com/containers/nri-plugins/pkg/metrics" +) + +type PolicyCollector struct { + policy *policy +} + +func (p *policy) newPolicyCollector() *PolicyCollector { + return &PolicyCollector{ + policy: p, + } +} + +func (c *PolicyCollector) register() error { + return metrics.Register(c.policy.ActivePolicy(), c, metrics.WithGroup("policy")) +} + +func (c *PolicyCollector) Describe(ch chan<- *prometheus.Desc) { + for _, d := range c.policy.active.DescribeMetrics() { + ch <- d + } +} + +func (c *PolicyCollector) Collect(ch chan<- prometheus.Metric) { + polled := c.policy.active.PollMetrics() + + collected, err := c.policy.active.CollectMetrics(polled) + if err != nil { + log.Error("failed to collect metrics: %v", err) + return + } + + for _, m := range collected { + ch <- m + } +} diff --git a/pkg/resmgr/policy/policy.go b/pkg/resmgr/policy/policy.go index f8ec5963d..2ecdcb29a 100644 --- a/pkg/resmgr/policy/policy.go +++ b/pkg/resmgr/policy/policy.go @@ -147,12 +147,6 @@ type Policy interface { HandleEvent(*events.Policy) (bool, error) // ExportResourceData exports/updates resource data for the container. ExportResourceData(cache.Container) - // DescribeMetrics generates policy-specific prometheus metrics data descriptors. - DescribeMetrics() []*prometheus.Desc - // PollMetrics provides policy metrics for monitoring. - PollMetrics() Metrics - // CollectMetrics generates prometheus metrics from cached/polled policy-specific metrics data. - CollectMetrics(Metrics) ([]prometheus.Metric, error) // GetTopologyZones returns the policy/pool data for 'topology zone' CRDs. GetTopologyZones() []*TopologyZone } @@ -204,11 +198,12 @@ type ZoneAttribute struct { // Policy instance/state. type policy struct { - options Options // policy options - cache cache.Cache // system state cache - active Backend // our active backend - system system.System // system/HW/topology info - sendEvent SendEventFn // function to send event up to the resource manager + options Options // policy options + cache cache.Cache // system state cache + active Backend // our active backend + system system.System // system/HW/topology info + sendEvent SendEventFn // function to send event up to the resource manager + pcollect *PolicyCollector // policy metrics collector } // backend is a registered Backend. @@ -225,11 +220,25 @@ var log logger.Logger = logger.NewLogger("policy") func NewPolicy(backend Backend, cache cache.Cache, o *Options) (Policy, error) { log.Info("creating '%s' policy...", backend.Name()) - return &policy{ + p := &policy{ cache: cache, options: *o, active: backend, - }, nil + } + + sys, err := system.DiscoverSystem() + if err != nil { + return nil, policyError("failed to discover system topology: %v", err) + } + p.system = sys + + pcollect := p.newPolicyCollector() + if err := pcollect.register(); err != nil { + return nil, policyError("failed to register policy collector: %v", err) + } + p.pcollect = pcollect + + return p, nil } func (p *policy) ActivePolicy() string { @@ -241,12 +250,6 @@ func (p *policy) ActivePolicy() string { // Start starts up policy, preparing it for serving requests. func (p *policy) Start(cfg interface{}) error { - sys, err := system.DiscoverSystem() - if err != nil { - return policyError("failed to discover system topology: %v", err) - } - p.system = sys - log.Info("activating '%s' policy...", p.active.Name()) if err := p.active.Setup(&BackendOptions{ @@ -315,21 +318,6 @@ func (p *policy) ExportResourceData(c cache.Container) { p.cache.WriteFile(c.GetID(), ExportedResources, 0644, buf.Bytes()) } -// PollMetrics provides policy metrics for monitoring. -func (p *policy) PollMetrics() Metrics { - return p.active.PollMetrics() -} - -// DescribeMetrics generates policy-specific prometheus metrics data descriptors. -func (p *policy) DescribeMetrics() []*prometheus.Desc { - return p.active.DescribeMetrics() -} - -// CollectMetrics generates prometheus metrics from cached/polled policy-specific metrics data. -func (p *policy) CollectMetrics(m Metrics) ([]prometheus.Metric, error) { - return p.active.CollectMetrics(m) -} - // GetTopologyZones returns the policy/pool data for 'topology zone' CRDs. func (p *policy) GetTopologyZones() []*TopologyZone { return p.active.GetTopologyZones() diff --git a/pkg/resmgr/policycollector/collector.go b/pkg/resmgr/policycollector/collector.go deleted file mode 100644 index 6946d2095..000000000 --- a/pkg/resmgr/policycollector/collector.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2019 Intel Corporation. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package policycollector - -import ( - "github.com/containers/nri-plugins/pkg/metrics" - "github.com/containers/nri-plugins/pkg/resmgr/policy" - "github.com/prometheus/client_golang/prometheus" -) - -type PolicyCollector struct { - policy policy.Policy -} - -func (c *PolicyCollector) SetPolicy(policy policy.Policy) { - c.policy = policy -} - -// HasPolicySpecificMetrics judges whether the policy defines the policy-specific metrics -func (c *PolicyCollector) HasPolicySpecificMetrics() bool { - if c.policy.DescribeMetrics() == nil { - return false - } - return true -} - -// Describe implements prometheus.Collector interface -func (c *PolicyCollector) Describe(ch chan<- *prometheus.Desc) { - for _, d := range c.policy.DescribeMetrics() { - ch <- d - } -} - -// Collect implements prometheus.Collector interface -func (c *PolicyCollector) Collect(ch chan<- prometheus.Metric) { - prometheusMetrics, err := c.policy.CollectMetrics(c.policy.PollMetrics()) - if err != nil { - return - } - for _, m := range prometheusMetrics { - ch <- m - } -} - -// RegisterPolicyMetricsCollector registers policy-specific collector -func (c *PolicyCollector) RegisterPolicyMetricsCollector() error { - return metrics.RegisterCollector("policyMetrics", func() (prometheus.Collector, error) { - return c, nil - }) -} diff --git a/pkg/resmgr/resource-manager.go b/pkg/resmgr/resource-manager.go index 93b11a4b2..948ced007 100644 --- a/pkg/resmgr/resource-manager.go +++ b/pkg/resmgr/resource-manager.go @@ -27,7 +27,6 @@ import ( "github.com/containers/nri-plugins/pkg/pidfile" "github.com/containers/nri-plugins/pkg/resmgr/cache" "github.com/containers/nri-plugins/pkg/resmgr/control" - "github.com/containers/nri-plugins/pkg/resmgr/metrics" "github.com/containers/nri-plugins/pkg/resmgr/policy" "github.com/containers/nri-plugins/pkg/sysfs" "github.com/containers/nri-plugins/pkg/topology" @@ -35,8 +34,6 @@ import ( "sigs.k8s.io/yaml" cfgapi "github.com/containers/nri-plugins/pkg/apis/config/v1alpha1" - - policyCollector "github.com/containers/nri-plugins/pkg/resmgr/policycollector" ) // ResourceManager is the interface we expose for controlling the CRI resource manager. @@ -59,7 +56,6 @@ type resmgr struct { cache cache.Cache // cached state policy policy.Policy // resource manager policy control control.Control // policy controllers/enforcement - metrics *metrics.Metrics // metrics collector/pre-processor events chan interface{} // channel for delivering events stop chan interface{} // channel for signalling shutdown to goroutines nri *nriPlugin // NRI plugins, if we're running as such @@ -103,10 +99,6 @@ func NewResourceManager(backend policy.Backend, agt *agent.Agent) (ResourceManag return nil, err } - if err := m.registerPolicyMetricsCollector(); err != nil { - return nil, err - } - if err := m.setupEventProcessing(); err != nil { return nil, err } @@ -174,12 +166,13 @@ func (m *resmgr) start(cfg cfgapi.ResmgrConfig) error { mCfg := cfg.CommonConfig() logger.Configure(&mCfg.Log) - instrumentation.Reconfigure(&mCfg.Instrumentation) if err := m.policy.Start(m.cfg.PolicyConfig()); err != nil { return err } + instrumentation.Reconfigure(&mCfg.Instrumentation) + if err := m.nri.start(); err != nil { return err } @@ -280,17 +273,6 @@ func (m *resmgr) updateTopologyZones() { } } -// registerPolicyMetricsCollector registers policy metrics collector· -func (m *resmgr) registerPolicyMetricsCollector() error { - pc := &policyCollector.PolicyCollector{} - pc.SetPolicy(m.policy) - if pc.HasPolicySpecificMetrics() { - return pc.RegisterPolicyMetricsCollector() - } - log.Info("%s policy has no policy-specific metrics.", m.policy.ActivePolicy()) - return nil -} - func (m *resmgr) reconfigure(cfg cfgapi.ResmgrConfig) error { apply := func(cfg cfgapi.ResmgrConfig) error { mCfg := cfg.CommonConfig() From 331eb0933c651f22d6bbd2ffe05fd7be913ea2b7 Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Sat, 9 Nov 2024 23:54:59 +0200 Subject: [PATCH 4/6] instrumentation: remove opencensus metrics exporter. Remove the old opencensus-based prometheus exporter. Rework prometheus exporting using our update metrics registry and a promhttp /metrics-handler. Signed-off-by: Krisztian Litkey --- go.mod | 8 +- go.sum | 90 ---------- pkg/instrumentation/instrumentation.go | 15 +- pkg/instrumentation/metrics/metrics.go | 135 +++++++++++++- .../metrics/opencensus/metrics.go | 164 ------------------ pkg/log/log.go | 7 + pkg/resmgr/nri.go | 28 ++- pkg/resmgr/resource-manager.go | 8 +- 8 files changed, 169 insertions(+), 286 deletions(-) delete mode 100644 pkg/instrumentation/metrics/opencensus/metrics.go diff --git a/go.mod b/go.mod index 7ef116a22..75e71efbc 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,6 @@ module github.com/containers/nri-plugins go 1.22 require ( - contrib.go.opencensus.io/exporter/prometheus v0.4.2 github.com/containerd/nri v0.6.0 github.com/containerd/otelttrpc v0.0.0-20240305015340-ea5083fda723 github.com/containerd/ttrpc v1.2.3-0.20231030150553-baadfd8e7956 @@ -17,10 +16,8 @@ require ( github.com/pelletier/go-toml/v2 v2.1.0 github.com/prometheus/client_golang v1.17.0 github.com/prometheus/client_model v0.5.0 - github.com/prometheus/common v0.44.0 github.com/sirupsen/logrus v1.9.3 github.com/stretchr/testify v1.8.4 - go.opencensus.io v0.24.0 go.opentelemetry.io/otel v1.19.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 @@ -44,8 +41,6 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.9.0 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/go-kit/log v0.2.1 // indirect - github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect @@ -54,7 +49,6 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/godbus/dbus/v5 v5.0.4 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.3 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -73,8 +67,8 @@ require ( github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.12.0 // indirect - github.com/prometheus/statsd_exporter v0.24.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect go.opentelemetry.io/otel/metric v1.19.0 // indirect diff --git a/go.sum b/go.sum index 3f5ae9a7a..f848575ca 100644 --- a/go.sum +++ b/go.sum @@ -592,8 +592,6 @@ cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoIS cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcPALq2CxzdePw= -contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg= -contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= @@ -605,19 +603,11 @@ github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0= github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI= github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= @@ -629,7 +619,6 @@ github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -696,20 +685,8 @@ github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmn github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= -github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= @@ -725,13 +702,11 @@ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/goccy/go-json v0.9.11/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= @@ -742,8 +717,6 @@ github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -858,16 +831,10 @@ github.com/intel/goresctrl v0.8.0 h1:N3shVbS3kA1Hk2AmcbHv8805Hjbv+zqsCIZCGktxx50 github.com/intel/goresctrl v0.8.0/go.mod h1:T3ZZnuHSNouwELB5wvOoUJaB7l/4Rm23rJy/wuWJlr0= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/k8stopologyawareschedwg/noderesourcetopology-api v0.1.2 h1:uAwqOtyrFYggq3pVf3hs1XKkBxrQ8dkgjWz3LCLJsiY= @@ -878,10 +845,7 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE= github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= @@ -899,7 +863,6 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8/go.mod h1:mC1jAcsrzbxHt8iiaC+zU4b1ylILSosueou12R++wfY= @@ -907,14 +870,10 @@ github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3/go.mod h1:RagcQ7I8Ie github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/onsi/ginkgo/v2 v2.16.0 h1:7q1w9frJDzninhXxjZd+Y/x54XNjG/UlRLIYPZafsPM= github.com/onsi/ginkgo/v2 v2.16.0/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= @@ -928,7 +887,6 @@ github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -936,41 +894,18 @@ github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZ github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI= -github.com/prometheus/statsd_exporter v0.24.0 h1:aZmN6CzS2H1Non1JKZdjkQlAkDtGoQBYIESk2SlU1OI= -github.com/prometheus/statsd_exporter v0.24.0/go.mod h1:+dQiRTqn9DnPmN5mI5Xond+k8nuRKzdgh1omxh9OgFY= github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -980,9 +915,6 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -993,7 +925,6 @@ github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcD github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -1008,7 +939,6 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -1025,7 +955,6 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= @@ -1048,7 +977,6 @@ go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lI go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -1119,7 +1047,6 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -1127,7 +1054,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1155,7 +1081,6 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= @@ -1227,12 +1152,9 @@ golang.org/x/sync v0.0.0-20220819030929-7fc1605a5dde/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1242,7 +1164,6 @@ golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1256,8 +1177,6 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1277,7 +1196,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1291,7 +1209,6 @@ golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1303,7 +1220,6 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -1707,22 +1623,16 @@ google.golang.org/protobuf v1.29.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/instrumentation/instrumentation.go b/pkg/instrumentation/instrumentation.go index 0cc4024ec..25a54f29a 100644 --- a/pkg/instrumentation/instrumentation.go +++ b/pkg/instrumentation/instrumentation.go @@ -18,8 +18,6 @@ import ( "fmt" "sync" - "github.com/prometheus/client_golang/prometheus" - cfgapi "github.com/containers/nri-plugins/pkg/apis/config/v1alpha1/instrumentation" "github.com/containers/nri-plugins/pkg/http" "github.com/containers/nri-plugins/pkg/instrumentation/metrics" @@ -52,11 +50,6 @@ var ( Attribute = tracing.Attribute ) -// RegisterGatherer registers a prometheus metrics gatherer. -func RegisterGatherer(g prometheus.Gatherer) { - metrics.RegisterGatherer(g) -} - // HTTPServer returns our HTTP server. func HTTPServer() *http.Server { return srv @@ -94,7 +87,7 @@ func Restart() error { err := start() if err != nil { - log.Error("failed to start tracing: %v", err) + log.Error("failed to start instrumentation: %v", err) } return err @@ -122,9 +115,11 @@ func start() error { if err := metrics.Start( srv.GetMux(), + metrics.WithNamespace("nri"), metrics.WithExporterDisabled(!cfg.PrometheusExport), - metrics.WithServiceName(ServiceName), - metrics.WithPeriod(cfg.ReportPeriod.Duration), + metrics.WithReportPeriod(cfg.ReportPeriod.Duration), + // TODO(klihub): make this configurable via apis/config/.../instrumentation.Config + metrics.WithMetrics([]string{"misc/buildinfo"}, []string{"policy"}), ); err != nil { return fmt.Errorf("failed to start metrics: %v", err) } diff --git a/pkg/instrumentation/metrics/metrics.go b/pkg/instrumentation/metrics/metrics.go index f32b57317..0f23076c9 100644 --- a/pkg/instrumentation/metrics/metrics.go +++ b/pkg/instrumentation/metrics/metrics.go @@ -15,14 +15,135 @@ package metrics import ( - oc "github.com/containers/nri-plugins/pkg/instrumentation/metrics/opencensus" + "fmt" + "slices" + "time" + + "github.com/prometheus/client_golang/prometheus/promhttp" + + "github.com/containers/nri-plugins/pkg/http" + logger "github.com/containers/nri-plugins/pkg/log" + "github.com/containers/nri-plugins/pkg/metrics" +) + +type ( + Option func() error ) var ( - RegisterGatherer = oc.RegisterGatherer - WithExporterDisabled = oc.WithExporterDisabled - WithPeriod = oc.WithPeriod - WithServiceName = oc.WithServiceName - Start = oc.Start - Stop = oc.Stop + disabled bool + namespace string + enabled []string + polled []string + reportPeriod time.Duration + mux *http.ServeMux + gatherer *metrics.Gatherer + log = logger.Get("metrics") ) + +func WithExporterDisabled(v bool) Option { + return func() error { + disabled = v + return nil + } +} + +func WithNamespace(v string) Option { + return func() error { + namespace = v + return nil + } +} + +func WithReportPeriod(v time.Duration) Option { + return func() error { + reportPeriod = v + return nil + } +} + +func WithMetrics(enable []string, poll []string) Option { + return func() error { + enabled = slices.Clone(enable) + polled = slices.Clone(poll) + return nil + } +} + +func Start(m *http.ServeMux, options ...Option) error { + Stop() + + for _, opt := range options { + if err := opt(); err != nil { + return err + } + } + + if m == nil { + log.Info("no mux provided, metrics exporter disabled") + return nil + } + + if disabled { + log.Info("metrics exporter disabled") + return nil + } + + log.Info("starting metrics exporter...") + + g, err := metrics.NewGatherer( + metrics.WithNamespace("nri"), + metrics.WithPollInterval(reportPeriod), + metrics.WithMetrics(enabled, polled), + ) + if err != nil { + return fmt.Errorf("failed to create metrics gatherer: %v", err) + } + + gatherer = g + + handlerOpts := promhttp.HandlerOpts{ + ErrorLog: log, + ErrorHandling: promhttp.ContinueOnError, + } + m.Handle("/metrics", promhttp.HandlerFor(g, handlerOpts)) + + mux = m + + return nil +} + +func Stop() { + if mux == nil { + return + } + + mux.Unregister("/metrics") + mux = nil + gatherer.Stop() + gatherer = nil +} + +func Block() *MetricsBlock { + return newMetricsBlock(gatherer) +} + +type MetricsBlock struct { + g *metrics.Gatherer +} + +func newMetricsBlock(g *metrics.Gatherer) *MetricsBlock { + if g == nil { + return nil + } + g.Block() + return &MetricsBlock{g: g} +} + +func (b *MetricsBlock) Done() { + if b == nil || b.g == nil { + return + } + b.g.Unblock() + b.g = nil +} diff --git a/pkg/instrumentation/metrics/opencensus/metrics.go b/pkg/instrumentation/metrics/opencensus/metrics.go deleted file mode 100644 index 1073a0fb1..000000000 --- a/pkg/instrumentation/metrics/opencensus/metrics.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright The NRI Plugins Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package metrics - -import ( - "fmt" - "os" - "path/filepath" - "strings" - "sync" - "time" - - "contrib.go.opencensus.io/exporter/prometheus" - promcli "github.com/prometheus/client_golang/prometheus" - model "github.com/prometheus/client_model/go" - "go.opencensus.io/stats/view" - - "github.com/containers/nri-plugins/pkg/http" - logger "github.com/containers/nri-plugins/pkg/log" -) - -// Option represents an option which can be applied to metrics. -type Option func(*metrics) error - -type metrics struct { - disabled bool - service string - period time.Duration - exporter *prometheus.Exporter - mux *http.ServeMux -} - -var ( - log = logger.Get("metrics") - mtr = &metrics{ - service: filepath.Base(os.Args[0]), - } -) - -const ( - // HTTP path our mux serves metrics on. - httpMetricsPath = "/metrics" -) - -// WithExporterDisabled can be used to disable the metrics exporter. -func WithExporterDisabled(disabled bool) Option { - return func(m *metrics) error { - m.disabled = disabled - return nil - } -} - -// WithPeriod sets the internal metrics collection period. -func WithPeriod(period time.Duration) Option { - return func(m *metrics) error { - m.period = period - return nil - } -} - -// WithServiceName sets the service name reported for metrics. -func WithServiceName(name string) Option { - return func(m *metrics) error { - m.service = name - return nil - } -} - -// Start metrics exporter. -func Start(mux *http.ServeMux, options ...Option) error { - return mtr.start(mux, options...) -} - -// Stop metrics exporter. -func Stop() { - mtr.shutdown() -} - -func (m *metrics) start(mux *http.ServeMux, options ...Option) error { - m.shutdown() - - for _, opt := range options { - if err := opt(m); err != nil { - return fmt.Errorf("failed to set metrics option: %w", err) - } - } - - if m.disabled { - log.Info("metrics exporter disabled") - return nil - } - - log.Info("starting metrics exporter...") - - exporter, err := prometheus.NewExporter( - prometheus.Options{ - Namespace: strings.ReplaceAll(strings.ToLower(m.service), "-", "_"), - Gatherer: promcli.Gatherers{registeredGatherers}, - OnError: func(err error) { log.Error("prometheus export error: %v", err) }, - }, - ) - if err != nil { - return fmt.Errorf("failed to create prometheus exporter: %w", err) - } - - m.mux = mux - m.exporter = exporter - - m.mux.Handle(httpMetricsPath, m.exporter) - view.RegisterExporter(m.exporter) - view.SetReportingPeriod(m.period) - - return nil -} - -func (m *metrics) shutdown() { - if m.exporter == nil { - return - } - - view.UnregisterExporter(m.exporter) - m.mux.Unregister(httpMetricsPath) - - m.exporter = nil - m.mux = nil -} - -// Our registered prometheus gatherers. -var ( - registeredGatherers = &gatherers{gatherers: promcli.Gatherers{}} -) - -type gatherers struct { - sync.RWMutex - gatherers promcli.Gatherers -} - -func (g *gatherers) register(gatherer promcli.Gatherer) { - g.Lock() - defer g.Unlock() - g.gatherers = append(g.gatherers, gatherer) -} - -func (g *gatherers) Gather() ([]*model.MetricFamily, error) { - g.RLock() - defer g.RUnlock() - return g.gatherers.Gather() -} - -func RegisterGatherer(g promcli.Gatherer) { - registeredGatherers.register(g) -} diff --git a/pkg/log/log.go b/pkg/log/log.go index 6047543f2..3f44e9698 100644 --- a/pkg/log/log.go +++ b/pkg/log/log.go @@ -77,6 +77,9 @@ type Logger interface { // Fatal formats and emits an error message and os.Exit()'s with status 1. Fatal(format string, args ...interface{}) + // Println to mimic minimal stdlin log.Logger interface. + Println(v ...any) + // DebugBlock formats and emits a multiline debug message. DebugBlock(prefix string, format string, args ...interface{}) // InfoBlock formats and emits a multiline information message. @@ -410,6 +413,10 @@ func (l logger) Panic(format string, args ...interface{}) { panic(msg) } +func (l logger) Println(a ...any) { + l.Info("%s", fmt.Sprintln(a...)) +} + func (l logger) DebugBlock(prefix string, format string, args ...interface{}) { if l.DebugEnabled() { l.block(LevelDebug, prefix, format, args...) diff --git a/pkg/resmgr/nri.go b/pkg/resmgr/nri.go index 83adba5ce..379b992db 100644 --- a/pkg/resmgr/nri.go +++ b/pkg/resmgr/nri.go @@ -19,6 +19,7 @@ import ( "fmt" "os" + "github.com/containers/nri-plugins/pkg/instrumentation/metrics" "github.com/containers/nri-plugins/pkg/instrumentation/tracing" logger "github.com/containers/nri-plugins/pkg/log" "github.com/containers/nri-plugins/pkg/resmgr/cache" @@ -256,6 +257,9 @@ func (p *nriPlugin) Synchronize(ctx context.Context, pods []*api.PodSandbox, con p.dump(out, event, updates, retErr) }() + b := metrics.Block() + defer b.Done() + m := p.resmgr allocated, released, err := p.syncWithNRI(pods, containers) @@ -294,6 +298,8 @@ func (p *nriPlugin) RunPodSandbox(ctx context.Context, pod *api.PodSandbox) (ret m := p.resmgr m.Lock() defer m.Unlock() + b := metrics.Block() + defer b.Done() m.cache.InsertPod(pod) return nil @@ -314,8 +320,8 @@ func (p *nriPlugin) StopPodSandbox(ctx context.Context, podSandbox *api.PodSandb m := p.resmgr // TODO(klihub): shouldn't we m.Lock()/defer m.Unlock() here? - metrics.Block() - defer metrics.Unblock() + b := metrics.Block() + defer b.Done() released := []cache.Container{} pod, _ := m.cache.LookupPod(podSandbox.GetId()) @@ -370,6 +376,8 @@ func (p *nriPlugin) RemovePodSandbox(ctx context.Context, podSandbox *api.PodSan m.Lock() defer m.Unlock() + b := metrics.Block() + defer b.Done() m.cache.DeletePod(podSandbox.GetId()) return nil @@ -395,8 +403,8 @@ func (p *nriPlugin) CreateContainer(ctx context.Context, podSandbox *api.PodSand m := p.resmgr m.Lock() defer m.Unlock() - metrics.Block() - defer metrics.Unblock() + b := metrics.Block() + defer b.Done() c, err := m.cache.InsertContainer(container) if err != nil { @@ -464,6 +472,8 @@ func (p *nriPlugin) StartContainer(ctx context.Context, pod *api.PodSandbox, con m := p.resmgr m.Lock() defer m.Unlock() + b := metrics.Block() + defer b.Done() c, ok := m.cache.LookupContainer(container.Id) if !ok { @@ -510,6 +520,8 @@ func (p *nriPlugin) UpdateContainer(ctx context.Context, pod *api.PodSandbox, co m := p.resmgr m.Lock() defer m.Unlock() + b := metrics.Block() + defer b.Done() c, ok := m.cache.LookupContainer(container.Id) if !ok { @@ -572,6 +584,8 @@ func (p *nriPlugin) StopContainer(ctx context.Context, pod *api.PodSandbox, cont m := p.resmgr m.Lock() defer m.Unlock() + b := metrics.Block() + defer b.Done() c, ok := m.cache.LookupContainer(container.Id) if !ok { @@ -610,6 +624,8 @@ func (p *nriPlugin) RemoveContainer(ctx context.Context, pod *api.PodSandbox, co m := p.resmgr m.Lock() defer m.Unlock() + b := metrics.Block() + defer b.Done() m.cache.DeleteContainer(container.Id) return nil @@ -618,8 +634,8 @@ func (p *nriPlugin) RemoveContainer(ctx context.Context, pod *api.PodSandbox, co func (p *nriPlugin) updateContainers() (retErr error) { // Notes: must be called with p.resmgr lock held. - metrics.Block() - defer metrics.Unblock() + b := metrics.Block() + defer b.Done() updates := p.getPendingUpdates(nil) diff --git a/pkg/resmgr/resource-manager.go b/pkg/resmgr/resource-manager.go index 948ced007..e8ed5b6fa 100644 --- a/pkg/resmgr/resource-manager.go +++ b/pkg/resmgr/resource-manager.go @@ -171,7 +171,9 @@ func (m *resmgr) start(cfg cfgapi.ResmgrConfig) error { return err } - instrumentation.Reconfigure(&mCfg.Instrumentation) + if err := instrumentation.Reconfigure(&mCfg.Instrumentation); err != nil { + return err + } if err := m.nri.start(); err != nil { return err @@ -278,7 +280,9 @@ func (m *resmgr) reconfigure(cfg cfgapi.ResmgrConfig) error { mCfg := cfg.CommonConfig() logger.Configure(&mCfg.Log) - instrumentation.Reconfigure(&mCfg.Instrumentation) + if err := instrumentation.Reconfigure(&mCfg.Instrumentation); err != nil { + return err + } m.control.StartStopControllers(&mCfg.Control) err := m.policy.Reconfigure(cfg.PolicyConfig()) From 4cb857281036744529836c18605fd1685e65894c Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Sun, 10 Nov 2024 20:26:45 +0200 Subject: [PATCH 5/6] config: expose metrics configuration. Add configuration bits for controlling which metrics are collected. Enable collection of policy metrics by default. Signed-off-by: Krisztian Litkey --- .../bases/config.nri_balloonspolicies.yaml | 26 ++++++++++- .../bases/config.nri_templatepolicies.yaml | 26 ++++++++++- .../config.nri_topologyawarepolicies.yaml | 26 ++++++++++- .../crds/config.nri_balloonspolicies.yaml | 26 ++++++++++- .../crds/config.nri_templatepolicies.yaml | 26 ++++++++++- .../config.nri_topologyawarepolicies.yaml | 26 ++++++++++- docs/resource-policy/policy/balloons.md | 9 ++-- .../config/v1alpha1/instrumentation/config.go | 8 +++- .../instrumentation/zz_generated.deepcopy.go | 44 ++++++++++++++++++ pkg/apis/config/v1alpha1/metrics/config.go | 28 +++++++++++ .../v1alpha1/metrics/zz_generated.deepcopy.go | 46 +++++++++++++++++++ .../config/v1alpha1/zz_generated.deepcopy.go | 8 ++-- pkg/instrumentation/instrumentation.go | 3 +- pkg/instrumentation/metrics/metrics.go | 13 ++++-- 14 files changed, 296 insertions(+), 19 deletions(-) create mode 100644 pkg/apis/config/v1alpha1/instrumentation/zz_generated.deepcopy.go create mode 100644 pkg/apis/config/v1alpha1/metrics/config.go create mode 100644 pkg/apis/config/v1alpha1/metrics/zz_generated.deepcopy.go diff --git a/config/crd/bases/config.nri_balloonspolicies.yaml b/config/crd/bases/config.nri_balloonspolicies.yaml index 84cf113a1..8ac1e48f8 100644 --- a/config/crd/bases/config.nri_balloonspolicies.yaml +++ b/config/crd/bases/config.nri_balloonspolicies.yaml @@ -320,11 +320,35 @@ spec: to expose Prometheus metrics among other things. example: :8891 type: string + metrics: + default: + enabled: + - policy + description: Metrics defines which metrics to collect. + properties: + enabled: + description: Enabled enables collection for metrics matched + by glob patterns. + example: + - '*' + items: + type: string + type: array + polled: + description: Polled forces polled collection for metrics matched + by glob patterns. + example: + - computationally-expensive-metrics + items: + type: string + type: array + type: object prometheusExport: description: PrometheusExport enables exporting /metrics for Prometheus. type: boolean reportPeriod: - description: ReportPeriod is the interval between reporting aggregated + default: 30s + description: ReportPeriod is the interval between collecting polled metrics. format: duration type: string diff --git a/config/crd/bases/config.nri_templatepolicies.yaml b/config/crd/bases/config.nri_templatepolicies.yaml index 7aa462501..e5a5656ca 100644 --- a/config/crd/bases/config.nri_templatepolicies.yaml +++ b/config/crd/bases/config.nri_templatepolicies.yaml @@ -92,11 +92,35 @@ spec: to expose Prometheus metrics among other things. example: :8891 type: string + metrics: + default: + enabled: + - policy + description: Metrics defines which metrics to collect. + properties: + enabled: + description: Enabled enables collection for metrics matched + by glob patterns. + example: + - '*' + items: + type: string + type: array + polled: + description: Polled forces polled collection for metrics matched + by glob patterns. + example: + - computationally-expensive-metrics + items: + type: string + type: array + type: object prometheusExport: description: PrometheusExport enables exporting /metrics for Prometheus. type: boolean reportPeriod: - description: ReportPeriod is the interval between reporting aggregated + default: 30s + description: ReportPeriod is the interval between collecting polled metrics. format: duration type: string diff --git a/config/crd/bases/config.nri_topologyawarepolicies.yaml b/config/crd/bases/config.nri_topologyawarepolicies.yaml index df4944647..353bfa434 100644 --- a/config/crd/bases/config.nri_topologyawarepolicies.yaml +++ b/config/crd/bases/config.nri_topologyawarepolicies.yaml @@ -119,11 +119,35 @@ spec: to expose Prometheus metrics among other things. example: :8891 type: string + metrics: + default: + enabled: + - policy + description: Metrics defines which metrics to collect. + properties: + enabled: + description: Enabled enables collection for metrics matched + by glob patterns. + example: + - '*' + items: + type: string + type: array + polled: + description: Polled forces polled collection for metrics matched + by glob patterns. + example: + - computationally-expensive-metrics + items: + type: string + type: array + type: object prometheusExport: description: PrometheusExport enables exporting /metrics for Prometheus. type: boolean reportPeriod: - description: ReportPeriod is the interval between reporting aggregated + default: 30s + description: ReportPeriod is the interval between collecting polled metrics. format: duration type: string diff --git a/deployment/helm/balloons/crds/config.nri_balloonspolicies.yaml b/deployment/helm/balloons/crds/config.nri_balloonspolicies.yaml index 84cf113a1..8ac1e48f8 100644 --- a/deployment/helm/balloons/crds/config.nri_balloonspolicies.yaml +++ b/deployment/helm/balloons/crds/config.nri_balloonspolicies.yaml @@ -320,11 +320,35 @@ spec: to expose Prometheus metrics among other things. example: :8891 type: string + metrics: + default: + enabled: + - policy + description: Metrics defines which metrics to collect. + properties: + enabled: + description: Enabled enables collection for metrics matched + by glob patterns. + example: + - '*' + items: + type: string + type: array + polled: + description: Polled forces polled collection for metrics matched + by glob patterns. + example: + - computationally-expensive-metrics + items: + type: string + type: array + type: object prometheusExport: description: PrometheusExport enables exporting /metrics for Prometheus. type: boolean reportPeriod: - description: ReportPeriod is the interval between reporting aggregated + default: 30s + description: ReportPeriod is the interval between collecting polled metrics. format: duration type: string diff --git a/deployment/helm/template/crds/config.nri_templatepolicies.yaml b/deployment/helm/template/crds/config.nri_templatepolicies.yaml index 7aa462501..e5a5656ca 100644 --- a/deployment/helm/template/crds/config.nri_templatepolicies.yaml +++ b/deployment/helm/template/crds/config.nri_templatepolicies.yaml @@ -92,11 +92,35 @@ spec: to expose Prometheus metrics among other things. example: :8891 type: string + metrics: + default: + enabled: + - policy + description: Metrics defines which metrics to collect. + properties: + enabled: + description: Enabled enables collection for metrics matched + by glob patterns. + example: + - '*' + items: + type: string + type: array + polled: + description: Polled forces polled collection for metrics matched + by glob patterns. + example: + - computationally-expensive-metrics + items: + type: string + type: array + type: object prometheusExport: description: PrometheusExport enables exporting /metrics for Prometheus. type: boolean reportPeriod: - description: ReportPeriod is the interval between reporting aggregated + default: 30s + description: ReportPeriod is the interval between collecting polled metrics. format: duration type: string diff --git a/deployment/helm/topology-aware/crds/config.nri_topologyawarepolicies.yaml b/deployment/helm/topology-aware/crds/config.nri_topologyawarepolicies.yaml index df4944647..353bfa434 100644 --- a/deployment/helm/topology-aware/crds/config.nri_topologyawarepolicies.yaml +++ b/deployment/helm/topology-aware/crds/config.nri_topologyawarepolicies.yaml @@ -119,11 +119,35 @@ spec: to expose Prometheus metrics among other things. example: :8891 type: string + metrics: + default: + enabled: + - policy + description: Metrics defines which metrics to collect. + properties: + enabled: + description: Enabled enables collection for metrics matched + by glob patterns. + example: + - '*' + items: + type: string + type: array + polled: + description: Polled forces polled collection for metrics matched + by glob patterns. + example: + - computationally-expensive-metrics + items: + type: string + type: array + type: object prometheusExport: description: PrometheusExport enables exporting /metrics for Prometheus. type: boolean reportPeriod: - description: ReportPeriod is the interval between reporting aggregated + default: 30s + description: ReportPeriod is the interval between collecting polled metrics. format: duration type: string diff --git a/docs/resource-policy/policy/balloons.md b/docs/resource-policy/policy/balloons.md index 8d550d036..b531a54cb 100644 --- a/docs/resource-policy/policy/balloons.md +++ b/docs/resource-policy/policy/balloons.md @@ -264,13 +264,13 @@ Balloons policy parameters: - `prometheusExport`: if set to True, balloons with their CPUs and assigned containers are readable through `/metrics` from the httpEndpoint. - - `reportPeriod`: `/metrics` aggregation interval. + - `reportPeriod`: `/metrics` aggregation interval for polled metrics. ### Example Example configuration that runs all pods in balloons of 1-4 CPUs. Instrumentation enables reading CPUs and containers in balloons -from `http://localhost:8891/metrics`. +from `http://$localhost_or_pod_IP:8891/metrics`. ```yaml apiVersion: config.nri/v1alpha1 @@ -413,9 +413,12 @@ nri-resource-policy global config: instrumentation: # The balloons policy exports containers running in each balloon, # and cpusets of balloons. Accessible in command line: - # curl --silent http://localhost:8891/metrics + # curl --silent http://$localhost_or_pod_IP:8891/metrics HTTPEndpoint: :8891 PrometheusExport: true + metrics: + enabled: # use '*' instead for all available metrics + - policy logger: Debug: policy ``` diff --git a/pkg/apis/config/v1alpha1/instrumentation/config.go b/pkg/apis/config/v1alpha1/instrumentation/config.go index 97e96251f..79b2e83f7 100644 --- a/pkg/apis/config/v1alpha1/instrumentation/config.go +++ b/pkg/apis/config/v1alpha1/instrumentation/config.go @@ -15,10 +15,12 @@ package instrumentation import ( + "github.com/containers/nri-plugins/pkg/apis/config/v1alpha1/metrics" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) // Config provides runtime configuration for instrumentation. +// +k8s:deepcopy-gen=true type Config struct { // SamplingRatePerMillion is the number of samples to collect per million spans. // +optional @@ -33,9 +35,10 @@ type Config struct { // +optional // +kubebuilder:example="otlp-http://localhost:4318" TracingCollector string `json:"tracingCollector,omitempty"` - // ReportPeriod is the interval between reporting aggregated metrics. + // ReportPeriod is the interval between collecting polled metrics. // +optional // +kubebuilder:validation:Format="duration" + // +kubebuilder:default="30s" ReportPeriod metav1.Duration `json:"reportPeriod,omitempty"` // HTTPEndpoint is the address our HTTP server listens on. This endpoint is used // to expose Prometheus metrics among other things. @@ -45,4 +48,7 @@ type Config struct { // PrometheusExport enables exporting /metrics for Prometheus. // +optional PrometheusExport bool `json:"prometheusExport,omitempty"` + // Metrics defines which metrics to collect. + // +kubebuilder:default={"enabled": {"policy"}} + Metrics *metrics.Config `json:"metrics,omitempty"` } diff --git a/pkg/apis/config/v1alpha1/instrumentation/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/instrumentation/zz_generated.deepcopy.go new file mode 100644 index 000000000..d9887b1a2 --- /dev/null +++ b/pkg/apis/config/v1alpha1/instrumentation/zz_generated.deepcopy.go @@ -0,0 +1,44 @@ +//go:build !ignore_autogenerated + +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by controller-gen. DO NOT EDIT. + +package instrumentation + +import ( + "github.com/containers/nri-plugins/pkg/apis/config/v1alpha1/metrics" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + out.ReportPeriod = in.ReportPeriod + if in.Metrics != nil { + in, out := &in.Metrics, &out.Metrics + *out = new(metrics.Config) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/config/v1alpha1/metrics/config.go b/pkg/apis/config/v1alpha1/metrics/config.go new file mode 100644 index 000000000..e9159c4d1 --- /dev/null +++ b/pkg/apis/config/v1alpha1/metrics/config.go @@ -0,0 +1,28 @@ +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package metrics + +// Config provides runtime configuration for metrics collection. +// +k8s:deepcopy-gen=true +type Config struct { + // Enabled enables collection for metrics matched by glob patterns. + // +optional + // +kubebuilder:example={"*"} + Enabled []string `json:"enabled,omitempty"` + // Polled forces polled collection for metrics matched by glob patterns. + // +optional + // +kubebuilder:example={"computationally-expensive-metrics"} + Polled []string `json:"polled,omitempty"` +} diff --git a/pkg/apis/config/v1alpha1/metrics/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/metrics/zz_generated.deepcopy.go new file mode 100644 index 000000000..c9c49723a --- /dev/null +++ b/pkg/apis/config/v1alpha1/metrics/zz_generated.deepcopy.go @@ -0,0 +1,46 @@ +//go:build !ignore_autogenerated + +// Copyright The NRI Plugins Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Code generated by controller-gen. DO NOT EDIT. + +package metrics + +import () + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Config) DeepCopyInto(out *Config) { + *out = *in + if in.Enabled != nil { + in, out := &in.Enabled, &out.Enabled + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Polled != nil { + in, out := &in.Polled, &out.Polled + *out = make([]string, len(*in)) + copy(*out, *in) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Config. +func (in *Config) DeepCopy() *Config { + if in == nil { + return nil + } + out := new(Config) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go index 608c046b7..2ca084794 100644 --- a/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/config/v1alpha1/zz_generated.deepcopy.go @@ -87,7 +87,7 @@ func (in *BalloonsPolicySpec) DeepCopyInto(out *BalloonsPolicySpec) { in.Config.DeepCopyInto(&out.Config) in.Control.DeepCopyInto(&out.Control) in.Log.DeepCopyInto(&out.Log) - out.Instrumentation = in.Instrumentation + in.Instrumentation.DeepCopyInto(&out.Instrumentation) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BalloonsPolicySpec. @@ -105,7 +105,7 @@ func (in *CommonConfig) DeepCopyInto(out *CommonConfig) { *out = *in in.Control.DeepCopyInto(&out.Control) in.Log.DeepCopyInto(&out.Log) - out.Instrumentation = in.Instrumentation + in.Instrumentation.DeepCopyInto(&out.Instrumentation) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonConfig. @@ -226,7 +226,7 @@ func (in *TemplatePolicySpec) DeepCopyInto(out *TemplatePolicySpec) { in.Config.DeepCopyInto(&out.Config) in.Control.DeepCopyInto(&out.Control) in.Log.DeepCopyInto(&out.Log) - out.Instrumentation = in.Instrumentation + in.Instrumentation.DeepCopyInto(&out.Instrumentation) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TemplatePolicySpec. @@ -304,7 +304,7 @@ func (in *TopologyAwarePolicySpec) DeepCopyInto(out *TopologyAwarePolicySpec) { in.Config.DeepCopyInto(&out.Config) in.Control.DeepCopyInto(&out.Control) in.Log.DeepCopyInto(&out.Log) - out.Instrumentation = in.Instrumentation + in.Instrumentation.DeepCopyInto(&out.Instrumentation) } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TopologyAwarePolicySpec. diff --git a/pkg/instrumentation/instrumentation.go b/pkg/instrumentation/instrumentation.go index 25a54f29a..b931f33e5 100644 --- a/pkg/instrumentation/instrumentation.go +++ b/pkg/instrumentation/instrumentation.go @@ -118,8 +118,7 @@ func start() error { metrics.WithNamespace("nri"), metrics.WithExporterDisabled(!cfg.PrometheusExport), metrics.WithReportPeriod(cfg.ReportPeriod.Duration), - // TODO(klihub): make this configurable via apis/config/.../instrumentation.Config - metrics.WithMetrics([]string{"misc/buildinfo"}, []string{"policy"}), + metrics.WithMetrics(cfg.Metrics), ); err != nil { return fmt.Errorf("failed to start metrics: %v", err) } diff --git a/pkg/instrumentation/metrics/metrics.go b/pkg/instrumentation/metrics/metrics.go index 0f23076c9..a036156ca 100644 --- a/pkg/instrumentation/metrics/metrics.go +++ b/pkg/instrumentation/metrics/metrics.go @@ -24,6 +24,8 @@ import ( "github.com/containers/nri-plugins/pkg/http" logger "github.com/containers/nri-plugins/pkg/log" "github.com/containers/nri-plugins/pkg/metrics" + + config "github.com/containers/nri-plugins/pkg/apis/config/v1alpha1/metrics" ) type ( @@ -62,10 +64,15 @@ func WithReportPeriod(v time.Duration) Option { } } -func WithMetrics(enable []string, poll []string) Option { +func WithMetrics(cfg *config.Config) Option { return func() error { - enabled = slices.Clone(enable) - polled = slices.Clone(poll) + if cfg != nil { + enabled = slices.Clone(cfg.Enabled) + polled = slices.Clone(cfg.Polled) + } else { + enabled = nil + polled = nil + } return nil } } From bea44681b783ae14dc670cfde4d908c3354f034d Mon Sep 17 00:00:00 2001 From: Krisztian Litkey Date: Mon, 11 Nov 2024 22:15:50 +0200 Subject: [PATCH 6/6] resmgr: warn about obsolete command line argument. Remove obsolete and unused option entries. Give a warning about using the now-obsolete '-metrics-interval' argument. It's used unconditionally by our existing Helm charts, so we'll phase it out a bit more gently. Signed-off-by: Krisztian Litkey --- pkg/resmgr/flags.go | 22 +++++++++------------- pkg/resmgr/resource-manager.go | 6 ++++++ 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/pkg/resmgr/flags.go b/pkg/resmgr/flags.go index 88f7bdf08..9270700a9 100644 --- a/pkg/resmgr/flags.go +++ b/pkg/resmgr/flags.go @@ -29,18 +29,13 @@ const ( // Options captures our command line parameters. type options struct { - HostRoot string - StateDir string - PidFile string - ResctrlPath string - FallbackConfig string - ForceConfig string - ForceConfigSignal string - MetricsTimer time.Duration - RebalanceTimer time.Duration - NriPluginName string - NriPluginIdx string - NriSocket string + HostRoot string + StateDir string + PidFile string + MetricsTimer time.Duration + NriPluginName string + NriPluginIdx string + NriSocket string } // ResourceManager command line options. @@ -60,7 +55,8 @@ func init() { flag.StringVar(&opt.PidFile, "pid-file", pidfile.GetPath(), "PID file to write daemon PID to") flag.DurationVar(&opt.MetricsTimer, "metrics-interval", 0, - "Interval for polling/gathering runtime metrics data. Use 'disable' for disabling.") + "Obsolete way to set interval for polling/gathering runtime metrics data.\n"+ + "Use the instrumentation section of the CR-based configuration interface instead.") flag.StringVar(&opt.StateDir, "state-dir", "/var/lib/nri-resource-policy", "Permanent storage directory path for the resource manager to store its state in.") } diff --git a/pkg/resmgr/resource-manager.go b/pkg/resmgr/resource-manager.go index e8ed5b6fa..cff9c7cfb 100644 --- a/pkg/resmgr/resource-manager.go +++ b/pkg/resmgr/resource-manager.go @@ -80,6 +80,12 @@ func NewResourceManager(backend policy.Backend, agt *agent.Agent) (ResourceManag goresctrlpath.SetPrefix(opt.HostRoot) } + if opt.MetricsTimer != 0 { + log.Warn("WARNING: obsolete metrics-interval flag given, ignoring...") + log.Warn("WARNING: use the CR-based configuration interface instead") + log.Warn("WARNING: this flag will be removed in a future release") + } + m := &resmgr{ agent: agt, }