Skip to content

Commit

Permalink
Add docs and fix for test component
Browse files Browse the repository at this point in the history
  • Loading branch information
mattdurham committed Dec 13, 2023
1 parent be99d21 commit 9d4f828
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 23 deletions.
1 change: 1 addition & 0 deletions component/all/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ import (
_ "github.com/grafana/agent/component/prometheus/relabel" // Import prometheus.relabel
_ "github.com/grafana/agent/component/prometheus/remotewrite" // Import prometheus.remote_write
_ "github.com/grafana/agent/component/prometheus/scrape" // Import prometheus.scrape
_ "github.com/grafana/agent/component/prometheus/test/metrics" // Import prometheus.test.metrics
_ "github.com/grafana/agent/component/pyroscope/ebpf" // Import pyroscope.ebpf
_ "github.com/grafana/agent/component/pyroscope/scrape" // Import pyroscope.scrape
_ "github.com/grafana/agent/component/pyroscope/write" // Import pyroscope.write
Expand Down
65 changes: 51 additions & 14 deletions component/prometheus/test/metrics/component.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import (
"sync"
"time"

"github.com/golang/snappy"
"github.com/gorilla/mux"

"github.com/grafana/agent/component"
Expand All @@ -23,8 +22,6 @@ func init() {
Name: "prometheus.test.metrics",
Args: Arguments{},
Exports: Exports{},

NeedsServices: []string{http.ServiceName},
Build: func(opts component.Options, args component.Arguments) (component.Component, error) {
return NewComponent(opts, args.(Arguments))
},
Expand All @@ -34,8 +31,9 @@ func init() {
type Component struct {
mut sync.Mutex
args Arguments
o component.Options
instances []*instance
argsUpdate chan struct{}
argsUpdate chan Arguments
path string
handler http.Data
}
Expand Down Expand Up @@ -66,7 +64,6 @@ func (c *Component) serveMetrics(w httpgo.ResponseWriter, r *httpgo.Request) {
w.WriteHeader(httpgo.StatusNotFound)
return
}
w.Header().Set("Content-Encoding", "snappy")
_, _ = w.Write(c.instances[id].buffer())
}

Expand All @@ -78,16 +75,18 @@ func NewComponent(o component.Options, c Arguments) (*Component, error) {
httpData := data.(http.Data)
fullpath := httpData.HTTPPathForComponent(o.ID)
return &Component{
o: o,
args: c,
path: fullpath,
argsUpdate: make(chan struct{}),
argsUpdate: make(chan Arguments),
instances: make([]*instance, 0),
handler: httpData,
}, nil
}

func (c *Component) Run(ctx context.Context) error {
c.generateNewSet(true)
forceNewSet := true
c.generateNewSet(forceNewSet)
for {
c.mut.Lock()
t := time.NewTicker(c.args.MetricsRefresh)
Expand All @@ -97,11 +96,14 @@ func (c *Component) Run(ctx context.Context) error {
return nil
case <-t.C:
{
c.generateNewSet(false)
c.generateNewSet(forceNewSet)
forceNewSet = false
}
case <-c.argsUpdate:
case a := <-c.argsUpdate:
{
// Mainly to update the args.
// If instance count changed we need to force a new update
forceNewSet = a.NumberOfInstances != c.args.NumberOfInstances
c.args = a
}
}
}
Expand All @@ -111,11 +113,22 @@ func (c *Component) Update(args component.Arguments) error {
c.mut.Lock()
defer c.mut.Unlock()

c.args = args.(Arguments)
c.argsUpdate <- struct{}{}
c.argsUpdate <- args.(Arguments)
return nil
}

func (c *Component) generateDiscovery() []byte {
c.mut.Lock()
defer c.mut.Unlock()

instances := make([]target, len(c.instances))
for x := range c.instances {
instances[x] = createTarget(c.handler.HTTPListenAddr, c.path+fmt.Sprintf("instance/%d/metrics", x))
}
marshalledBytes, _ := json.Marshal(instances)
return marshalledBytes
}

type Arguments struct {
NumberOfInstances int `river:"number_of_instances,attr"`
NumberOfMetrics int `river:"number_of_metrics,attr"`
Expand All @@ -124,11 +137,26 @@ type Arguments struct {
ChurnPercent float32 `river:"churn_percent,attr"`
}

func getDefault() Arguments {
return Arguments{
NumberOfInstances: 1,
NumberOfMetrics: 1,
NumberOfSeries: 1,
MetricsRefresh: 1 * time.Minute,
ChurnPercent: 0,
}
}

// SetToDefault implements river.Defaulter.
func (a *Arguments) SetToDefault() {
*a = getDefault()
}

type Exports struct {
Targets []map[string]string `river:"targets,attr,optional"`
}

// generateNewSet creates the buffers of data. forceNewInstances instatiates all new buffers.
// generateNewSet creates the buffers of data. forceNewInstances instantiates all new buffers.
func (c *Component) generateNewSet(forceNewInstances bool) {
c.mut.Lock()
defer c.mut.Unlock()
Expand All @@ -150,6 +178,15 @@ func (c *Component) generateNewSet(forceNewInstances bool) {
for _, i := range c.instances {
i.generateData(c.args.NumberOfSeries)
}
if forceNewInstances {
targets := make([]map[string]string, len(c.instances))
for x, _ := range c.instances {
targets[x] = make(map[string]string)
targets[x]["__address__"] = c.handler.HTTPListenAddr
targets[x]["__metrics_path__"] = c.path + fmt.Sprintf("instance/%d/metrics", x)
}
c.o.OnStateChange(Exports{Targets: targets})
}
}

func (c *Component) data() [][]byte {
Expand Down Expand Up @@ -211,7 +248,7 @@ func (i *instance) generateData(seriesCount int) {
buf.WriteString(lblstring)
buf.WriteString("} 1\n")
}
i.buf = snappy.Encode(nil, buf.Bytes())
i.buf = buf.Bytes()
}

func createTarget(host, path string) target {
Expand Down
19 changes: 10 additions & 9 deletions component/prometheus/test/metrics/component_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ package metrics
import (
"context"
"fmt"
"github.com/golang/snappy"
"net"
"strings"
"testing"
"time"

"github.com/grafana/agent/component"
"github.com/grafana/agent/pkg/util"
http_service "github.com/grafana/agent/service/http"
"github.com/grafana/agent/service/labelstore"
prometheus_client "github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"net"
"strings"
"testing"
"time"
)

func TestMetrcsGeneration(t *testing.T) {
func TestMetricsGeneration(t *testing.T) {
opts := component.Options{
Logger: util.TestFlowLogger(t),
Registerer: prometheus_client.NewRegistry(),
Expand All @@ -35,6 +35,9 @@ func TestMetrcsGeneration(t *testing.T) {
return nil, fmt.Errorf("service %q does not exist", name)
}
},
OnStateChange: func(e component.Exports) {

},
}

s, err := NewComponent(opts, Arguments{
Expand All @@ -55,9 +58,7 @@ func TestMetrcsGeneration(t *testing.T) {
return len(bb) > 0 && len(bb[0]) > 2
}, 10*time.Second, 100*time.Millisecond)
require.Len(t, bb, 1)
out, err := snappy.Decode(nil, bb[0])
require.NoError(t, err)
metrics := string(out)
metrics := string(bb[0])
require.True(t, strings.Contains(metrics, "counter"))
require.True(t, strings.Contains(metrics, "agent_metric_test_0"))

Expand Down
83 changes: 83 additions & 0 deletions docs/sources/flow/reference/components/prometheus.test.metrics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
---
aliases:
- /docs/grafana-cloud/agent/flow/reference/components/prometheus.test.metrics/
canonical: https://grafana.com/docs/agent/latest/flow/reference/components/prometheus.test.metrics/
description: Learn about prometheus.test.metrics
labels:
stage: experimental
title: prometheus.test.metrics
---

# prometheus.test.metrics


`prometheus.test.metrics` configures a locally hosted service discovery that then exposes endpoints
for collecting generated metrics. It is best used for testing perfomance and will always be considered
experimental.

Multiple `prometheus.test.metrics` components can be specified by giving them
different labels.

## Usage

```
prometheus.test.metrics "LABEL" {
}
```

## Arguments

The following arguments are supported:

Name | Type | Description | Default | Required
---- | ---- | ----------- | ------- | --------
`number_of_instances` | `int` | Number of instances to provide. | `1` | no
`number_of_metrics` | `int` | Number of metrics to provide. | `1` | no
`number_of_labels` | `int` | Number of labels to provide per metric. | `0` | no
`metrics_refresh` | `duration` | How often to refresh metrics and labels. | `1m` | no
`churn_percent` | `float` | What percentage of metrics to churn during a refresh, must be between 0 and 1. | `0` | no

## Exported fields

The following fields are exported and can be referenced by other components:

Name | Type | Description
--------- | ------------------- | -----------
`targets` | `list(map(string))` | The set of targets discovered from the component.


## Component health

`prometheus.test.metrics` is only reported as unhealthy if given an invalid
configuration.

## Example

```river
prometheus.test.metrics "single" {
number_of_instances = 1
number_of_metrics = 1000
number_of_labels = 5
metrics_refresh = "10m"
churn_percent = 0.05
}
prometheus.scrape "default" {
targets = concat(prometheus.test.metrics.single.targets)
forward_to = [prometheus.remote_write.default.receiver]
}
prometheus.remote_write "default" {
endpoint {
url = "http://mimir:9009/api/v1/push"
basic_auth {
username = "example-user"
password = "example-password"
}
}
}
```


0 comments on commit 9d4f828

Please sign in to comment.