Skip to content

Commit

Permalink
[cmd/telemetrygen] ensure validate is called (#35745)
Browse files Browse the repository at this point in the history
Prior to this change, the Validate func wasn't being called resulting in
wonky error messages like the following:

```
2024-10-10T15:39:40.139-0700    INFO    logs/worker.go:49       stopping the exporter   {"worker": 0}
panic: runtime error: cannot convert slice with length 8 to array or pointer to array with length 16

goroutine 6 [running]:
github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/logs.worker.simulateLogs({0x140002404c4, 0x1, {0x100dd3152, 0xb}, 0x9, {0x100dccf6b, 0x4}, 0x0, 0x7fefffffffffffff, 0x140002404e0, ...}, ...)
        github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/logs/worker.go:70 +0xa34
created by github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/logs.Run in goroutine 1
```

With the validation in place, we now get a useful error message:

```
Error: TraceID must be a 32 character hex string, like: 'ae87dadd90e9935a4bc9660628efd569'
```

---------

Signed-off-by: Alex Boten <223565+codeboten@users.noreply.github.com>
  • Loading branch information
codeboten authored Oct 11, 2024
1 parent b6a28a3 commit 1e1a945
Show file tree
Hide file tree
Showing 10 changed files with 197 additions and 8 deletions.
27 changes: 27 additions & 0 deletions .chloggen/codeboten_validate-telemetrygen.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: bug_fix

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: telemetrygen

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: ensure validate is called

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [35745]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# If your change doesn't affect end users or the exported elements of any package,
# you should instead start your pull request title with [chore] or use the "Skip Changelog" label.
# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: []
6 changes: 6 additions & 0 deletions cmd/telemetrygen/internal/logs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package logs

import (
"fmt"

"github.com/spf13/pflag"

"github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common"
Expand Down Expand Up @@ -36,6 +38,10 @@ func (c *Config) Flags(fs *pflag.FlagSet) {

// Validate validates the test scenario parameters.
func (c *Config) Validate() error {
if c.TotalDuration <= 0 && c.NumLogs <= 0 {
return fmt.Errorf("either `logs` or `duration` must be greater than 0")
}

if c.TraceID != "" {
if err := common.ValidateTraceID(c.TraceID); err != nil {
return err
Expand Down
7 changes: 4 additions & 3 deletions cmd/telemetrygen/internal/logs/logs.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ func Start(cfg *Config) error {
}

if err = Run(cfg, expFunc, logger); err != nil {
logger.Error("failed to stop the exporter", zap.Error(err))
return err
}

Expand All @@ -69,10 +68,12 @@ func Start(cfg *Config) error {

// Run executes the test scenario.
func Run(c *Config, exp func() (sdklog.Exporter, error), logger *zap.Logger) error {
if err := c.Validate(); err != nil {
return err
}

if c.TotalDuration > 0 {
c.NumLogs = 0
} else if c.NumLogs <= 0 {
return fmt.Errorf("either `logs` or `duration` must be greater than 0")
}

limit := rate.Limit(c.Rate)
Expand Down
52 changes: 52 additions & 0 deletions cmd/telemetrygen/internal/logs/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,58 @@ func TestLogsWithTraceIDAndSpanID(t *testing.T) {
}
}

func TestValidate(t *testing.T) {
tests := []struct {
name string
cfg *Config
wantErrMessage string
}{
{
name: "No duration or NumLogs",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
TraceID: "123",
},
wantErrMessage: "either `logs` or `duration` must be greater than 0",
},
{
name: "TraceID invalid",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
NumLogs: 5,
TraceID: "123",
},
wantErrMessage: "TraceID must be a 32 character hex string, like: 'ae87dadd90e9935a4bc9660628efd569'",
},
{
name: "SpanID invalid",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
NumLogs: 5,
TraceID: "ae87dadd90e9935a4bc9660628efd569",
SpanID: "123",
},
wantErrMessage: "SpanID must be a 16 character hex string, like: '5828fa4960140870'",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &mockExporter{}
expFunc := func() (sdklog.Exporter, error) {
return m, nil
}
logger, _ := zap.NewDevelopment()
require.EqualError(t, Run(tt.cfg, expFunc, logger), tt.wantErrMessage)
})
}
}

func configWithNoAttributes(qty int, body string) *Config {
return &Config{
Body: body,
Expand Down
6 changes: 6 additions & 0 deletions cmd/telemetrygen/internal/metrics/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
package metrics

import (
"fmt"

"github.com/spf13/pflag"

"github.com/open-telemetry/opentelemetry-collector-contrib/cmd/telemetrygen/internal/common"
Expand Down Expand Up @@ -39,6 +41,10 @@ func (c *Config) Flags(fs *pflag.FlagSet) {

// Validate validates the test scenario parameters.
func (c *Config) Validate() error {
if c.TotalDuration <= 0 && c.NumMetrics <= 0 {
return fmt.Errorf("either `metrics` or `duration` must be greater than 0")
}

if c.TraceID != "" {
if err := common.ValidateTraceID(c.TraceID); err != nil {
return err
Expand Down
7 changes: 4 additions & 3 deletions cmd/telemetrygen/internal/metrics/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,6 @@ func Start(cfg *Config) error {
}

if err = Run(cfg, expFunc, logger); err != nil {
logger.Error("failed to stop the exporter", zap.Error(err))
return err
}

Expand All @@ -71,10 +70,12 @@ func Start(cfg *Config) error {

// Run executes the test scenario.
func Run(c *Config, exp func() (sdkmetric.Exporter, error), logger *zap.Logger) error {
if err := c.Validate(); err != nil {
return err
}

if c.TotalDuration > 0 {
c.NumMetrics = 0
} else if c.NumMetrics <= 0 {
return fmt.Errorf("either `metrics` or `duration` must be greater than 0")
}

limit := rate.Limit(c.Rate)
Expand Down
55 changes: 55 additions & 0 deletions cmd/telemetrygen/internal/metrics/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,61 @@ func TestGaugeMultipleTelemetryAttr(t *testing.T) {
}
}

func TestValidate(t *testing.T) {
tests := []struct {
name string
cfg *Config
wantErrMessage string
}{
{
name: "No duration or NumMetrics",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
MetricType: metricTypeSum,
TraceID: "123",
},
wantErrMessage: "either `metrics` or `duration` must be greater than 0",
},
{
name: "TraceID invalid",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
NumMetrics: 5,
MetricType: metricTypeSum,
TraceID: "123",
},
wantErrMessage: "TraceID must be a 32 character hex string, like: 'ae87dadd90e9935a4bc9660628efd569'",
},
{
name: "SpanID invalid",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
NumMetrics: 5,
MetricType: metricTypeSum,
TraceID: "ae87dadd90e9935a4bc9660628efd569",
SpanID: "123",
},
wantErrMessage: "SpanID must be a 16 character hex string, like: '5828fa4960140870'",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
m := &mockExporter{}
expFunc := func() (sdkmetric.Exporter, error) {
return m, nil
}
logger, _ := zap.NewDevelopment()
require.EqualError(t, Run(tt.cfg, expFunc, logger), tt.wantErrMessage)
})
}
}

func configWithNoAttributes(metric metricType, qty int) *Config {
return &Config{
Config: common.Config{
Expand Down
9 changes: 9 additions & 0 deletions cmd/telemetrygen/internal/traces/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
package traces

import (
"fmt"
"time"

"github.com/spf13/pflag"
Expand Down Expand Up @@ -40,3 +41,11 @@ func (c *Config) Flags(fs *pflag.FlagSet) {
fs.IntVar(&c.LoadSize, "size", 0, "Desired minimum size in MB of string data for each trace generated. This can be used to test traces with large payloads, i.e. when testing the OTLP receiver endpoint max receive size.")
fs.DurationVar(&c.SpanDuration, "span-duration", 123*time.Microsecond, "The duration of each generated span.")
}

// Validate validates the test scenario parameters.
func (c *Config) Validate() error {
if c.TotalDuration <= 0 && c.NumTraces <= 0 {
return fmt.Errorf("either `traces` or `duration` must be greater than 0")
}
return nil
}
6 changes: 4 additions & 2 deletions cmd/telemetrygen/internal/traces/traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,12 @@ func Start(cfg *Config) error {

// Run executes the test scenario.
func Run(c *Config, logger *zap.Logger) error {
if err := c.Validate(); err != nil {
return err
}

if c.TotalDuration > 0 {
c.NumTraces = 0
} else if c.NumTraces <= 0 {
return fmt.Errorf("either `traces` or `duration` must be greater than 0")
}

limit := rate.Limit(c.Rate)
Expand Down
30 changes: 30 additions & 0 deletions cmd/telemetrygen/internal/traces/worker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,36 @@ func TestSpansWithMultipleAttrs(t *testing.T) {
}
}

func TestValidate(t *testing.T) {
tests := []struct {
name string
cfg *Config
wantErrMessage string
}{
{
name: "No duration or NumTraces",
cfg: &Config{
Config: common.Config{
WorkerCount: 1,
},
},
wantErrMessage: "either `traces` or `duration` must be greater than 0",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
syncer := &mockSyncer{}

tracerProvider := sdktrace.NewTracerProvider()
sp := sdktrace.NewSimpleSpanProcessor(syncer)
tracerProvider.RegisterSpanProcessor(sp)
otel.SetTracerProvider(tracerProvider)
logger, _ := zap.NewDevelopment()
require.EqualError(t, Run(tt.cfg, logger), tt.wantErrMessage)
})
}
}

var _ sdktrace.SpanExporter = (*mockSyncer)(nil)

type mockSyncer struct {
Expand Down

0 comments on commit 1e1a945

Please sign in to comment.