wasp

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 18, 2023 License: MIT Imports: 34 Imported by: 3

README

Wasp

Go Tests

A simple protocol-agnostic load testing tool for Go

Goals
  • Easy to reuse any custom client Go code
  • Easy to grasp
  • Have slim codebase (500-1k loc)
  • Have predictable performance footprint when tested with protocol mocks
  • Be able to perform synthetic load testing for request-based protocols in Go with RPS bound load (http, etc.)
  • Be able to perform VU load testing for streaming protocols/scenario tests in Go with МГ bound load (ws, etc.)
  • Scalable in k8s without complicated configuration or vendored UI interfaces
  • Non-opinionated reporting, push any data to Loki

Setup

make install_deps

Run example tests with Grafana + Loki

make start

Insert GRAFANA_TOKEN created in previous command

export LOKI_URL=http://localhost:3030/loki/api/v1/push
export GRAFANA_URL=http://localhost:3000
export GRAFANA_TOKEN=...
export DATA_SOURCE_NAME=Loki
export DASHBOARD_FOLDER=LoadTests
export WASP_LOG_LEVEL=info
make dashboard

Run some tests:

make test_loki

Open your Grafana dashboard

Basic dashboard: dashboard_img

Remove environment:

make stop

Run pyroscope test

make pyro_start
make test_pyro
make pyro_stop

Open pyroscope

You can also use trace.out in the root folder with Go default tracing UI

Tutorial

Check tutorial for more examples

Documentation

Index

Constants

View Source
const (
	DefaultStatTextSize       = 12
	DefaultStatValueSize      = 20
	DefaultAlertEvaluateEvery = "10s"
	DefaultAlertFor           = "10s"
	DefaultDashboardUUID      = "wasp"

	DefaultRequirementLabelKey = "requirement_name"
)
View Source
const (
	AlertTypeQuantile99 = "quantile_99"
	AlertTypeErrors     = "errors"
)
View Source
const (
	DefaultCallTimeout       = 1 * time.Minute
	DefaultStatsPollInterval = 5 * time.Second
	DefaultCallResultBufLen  = 50000
	DefaultGenName           = "Generator"
)
View Source
const (
	RPSScheduleType string = "rps_schedule"
	VUScheduleType  string = "vu_schedule"
)
View Source
const (
	// DefaultStepChangePrecision is default amount of steps in which we split a schedule
	DefaultStepChangePrecision = 10
)
View Source
const (
	LogLevelEnvVar = "WASP_LOG_LEVEL"
)

Variables

View Source
var (
	ErrNoCfg             = errors.New("config is nil")
	ErrNoImpl            = errors.New("either \"gun\" or \"vu\" implementation must provided")
	ErrNoSchedule        = errors.New("no schedule segments were provided")
	ErrWrongScheduleType = errors.New("schedule type must be RPSScheduleType or VUScheduleType, use package constants")
	ErrCallTimeout       = errors.New("generator request call timeout")
	ErrStartFrom         = errors.New("from must be > 0")
	ErrInvalidSteps      = errors.New("both \"Steps\" and \"StepsDuration\" must be defined in a schedule segment")
	ErrNoGun             = errors.New("rps load scheduleSegments selected but gun implementation is nil")
	ErrNoVU              = errors.New("vu load scheduleSegments selected but vu implementation is nil")
)

Functions

func GetLogger

func GetLogger(t *testing.T, componentName string) zerolog.Logger

GetLogger instantiates a logger that takes into account the test context and the log level

func InitDefaultLogging

func InitDefaultLogging()

func LabelsMapToModel

func LabelsMapToModel(m map[string]string) model.LabelSet

LabelsMapToModel create model.LabelSet from map of labels

func LokiAlertParams added in v0.1.1

func LokiAlertParams(queryType, testName, genName string) string

Types

type Alert

type Alert struct {
	Annotations struct {
		DashboardUID string `json:"__dashboardUid__"`
		OrgID        string `json:"__orgId__"`
		PanelID      string `json:"__panelId__"`
		Description  string `json:"description"`
		RunbookURL   string `json:"runbook_url"`
		Summary      string `json:"summary"`
	} `json:"annotations"`
	EndsAt      time.Time `json:"endsAt"`
	Fingerprint string    `json:"fingerprint"`
	Receivers   []struct {
		Active       interface{} `json:"active"`
		Integrations interface{} `json:"integrations"`
		Name         string      `json:"name"`
	} `json:"receivers"`
	StartsAt time.Time `json:"startsAt"`
	Status   struct {
		InhibitedBy []interface{} `json:"inhibitedBy"`
		SilencedBy  []interface{} `json:"silencedBy"`
		State       string        `json:"state"`
	} `json:"status"`
	UpdatedAt    time.Time         `json:"updatedAt"`
	GeneratorURL string            `json:"generatorURL"`
	Labels       map[string]string `json:"labels"`
}

type AlertChecker

type AlertChecker struct {
	URL                 string
	APIKey              string
	RequirementLabelKey string
	T                   *testing.T
	// contains filtered or unexported fields
}

AlertChecker is checking alerts according to dashboardUUID and requirements labels

func NewAlertChecker

func NewAlertChecker(t *testing.T) *AlertChecker

func (*AlertChecker) AnyAlerts

func (m *AlertChecker) AnyAlerts(dashboardUUID, requirementLabelValue string) ([]AlertGroupsResponse, error)

AnyAlerts check if any alerts with dashboardUUID have been raised

type AlertGroupsResponse

type AlertGroupsResponse struct {
	Alerts []Alert `json:"alerts"`
	Labels struct {
		Alertname     string `json:"alertname"`
		GrafanaFolder string `json:"grafana_folder"`
	} `json:"labels"`
	Receiver struct {
		Active       interface{} `json:"active"`
		Integrations interface{} `json:"integrations"`
		Name         string      `json:"name"`
	} `json:"receiver"`
}

AlertGroupsResponse is response body for "api/alertmanager/grafana/api/v2/alerts/groups"

type CallResult

type CallResult struct {
	Failed     bool          `json:"failed,omitempty"`
	Timeout    bool          `json:"timeout,omitempty"`
	Duration   time.Duration `json:"duration"`
	StartedAt  *time.Time    `json:"started_at,omitempty"`
	FinishedAt *time.Time    `json:"finished_at,omitempty"`
	Data       interface{}   `json:"data,omitempty"`
	Error      string        `json:"error,omitempty"`
}

CallResult represents basic call result info

type Config

type Config struct {
	T                 *testing.T
	GenName           string
	LoadType          string
	Labels            map[string]string
	LokiConfig        *LokiConfig
	Schedule          []*Segment
	CallResultBufLen  int
	StatsPollInterval time.Duration
	CallTimeout       time.Duration
	Gun               Gun
	VU                VirtualUser
	Logger            zerolog.Logger
	SharedData        interface{}
	// contains filtered or unexported fields
}

Config is for shared load test data and configuration

func (*Config) Validate

func (lgc *Config) Validate() error

type Dashboard added in v0.1.1

type Dashboard struct{}

Dashboard is a Wasp dashboard

func NewDashboard added in v0.1.1

func NewDashboard() *Dashboard

NewDashboard creates new dashboard

func (*Dashboard) Dashboard added in v0.1.1

func (m *Dashboard) Dashboard(datasourceName string, requirements []WaspAlert) (dashboard.Builder, error)

Dashboard creates dashboard instance

func (*Dashboard) Deploy added in v0.1.1

func (m *Dashboard) Deploy(reqs []WaspAlert) (*grabana.Dashboard, error)

Deploy deploys this dashboard to some Grafana folder

type ExtendedLokiClient

type ExtendedLokiClient interface {
	lokiClient.Client
	api.EntryHandler
	HandleStruct(ls model.LabelSet, t time.Time, st interface{}) error
	LastHandleResult() PromtailSendResult
	AllHandleResults() []PromtailSendResult
}

ExtendedLokiClient an extended Loki/Promtail client used for testing last results in batch

func NewMockPromtailClient

func NewMockPromtailClient() ExtendedLokiClient

type Generator

type Generator struct {
	Log zerolog.Logger

	ResponsesWaitGroup *sync.WaitGroup

	ResponsesCtx context.Context

	ResponsesChan chan CallResult
	// contains filtered or unexported fields
}

Generator generates load with some RPS

func NewGenerator

func NewGenerator(cfg *Config) (*Generator, error)

NewGenerator creates a new generator, shoots for scheduled RPS until timeout, test logic is defined through Gun or VirtualUser

func (*Generator) Errors

func (g *Generator) Errors() []string

Errors get all calls errors

func (*Generator) GetData

func (g *Generator) GetData() *ResponseData

GetData get all calls data

func (*Generator) InputSharedData

func (g *Generator) InputSharedData() interface{}

InputSharedData returns the SharedData passed in Generator config

func (*Generator) Run

func (g *Generator) Run(wait bool) (interface{}, bool)

Run runs load loop until timeout or stop

func (*Generator) Stats

func (g *Generator) Stats() *Stats

Stats get all load stats

func (*Generator) StatsJSON

func (g *Generator) StatsJSON() map[string]interface{}

StatsJSON get all load stats for export

func (*Generator) Stop

func (g *Generator) Stop() (interface{}, bool)

Stop stops load generator, waiting for all calls for either finish or timeout

func (*Generator) Wait

func (g *Generator) Wait() (interface{}, bool)

Wait waits until test ends

type Gun

type Gun interface {
	Call(l *Generator) CallResult
}

Gun is basic interface to some synthetic load test Call one request with some RPS schedule

type HTTPMockServer

type HTTPMockServer struct {
	Sleep time.Duration
	// contains filtered or unexported fields
}

func NewHTTPMockServer

func NewHTTPMockServer(cfg *HTTPMockServerConfig) *HTTPMockServer

func (*HTTPMockServer) Run

func (s *HTTPMockServer) Run()

func (*HTTPMockServer) URL

func (s *HTTPMockServer) URL() string

type HTTPMockServerConfig added in v0.1.1

type HTTPMockServerConfig struct {
	FirstAPILatency   time.Duration
	FirstAPIHTTPCode  int
	SecondAPILatency  time.Duration
	SecondAPIHTTPCode int
}

type LocalLogger

type LocalLogger struct{}

func (*LocalLogger) Log

func (m *LocalLogger) Log(kvars ...interface{}) error

type LokiClient

type LokiClient struct {
	lokiClient.Client
}

LokiClient is a Loki/Promtail client wrapper

func NewLokiClient

func NewLokiClient(extCfg *LokiConfig) (*LokiClient, error)

NewLokiClient creates a new Loki/Promtail client

func (*LokiClient) AllHandleResults

func (m *LokiClient) AllHandleResults() []PromtailSendResult

func (*LokiClient) Handle

func (m *LokiClient) Handle(ls model.LabelSet, t time.Time, s string) error

Handle handles adding a new label set and a message to the batch

func (*LokiClient) HandleStruct

func (m *LokiClient) HandleStruct(ls model.LabelSet, t time.Time, st interface{}) error

HandleStruct handles adding a new label set and a message to the batch, marshalling JSON from struct

func (*LokiClient) LastHandleResult

func (m *LokiClient) LastHandleResult() PromtailSendResult

func (*LokiClient) Stop

func (m *LokiClient) Stop()

Stop stops the client goroutine

type LokiConfig

type LokiConfig struct {
	// URL url to Loki endpoint
	URL string `yaml:"url"`
	// Token is Loki authorization token
	Token string `yaml:"token"`
	// BatchWait max time to wait until sending a new batch
	BatchWait time.Duration `yaml:"batch_wait"`
	// BatchSize size of a messages batch
	BatchSize int `yaml:"batch_size"`
	// Timeout is a batch send timeout
	Timeout time.Duration `yaml:"timeout"`
}

LokiConfig Loki/Promtail client configuration

func NewDefaultLokiConfig

func NewDefaultLokiConfig(url string, token string) *LokiConfig

func NewEnvLokiConfig

func NewEnvLokiConfig() *LokiConfig

type MockGun

type MockGun struct {
	Data []string
	// contains filtered or unexported fields
}

MockGun is a mock gun

func NewMockGun

func NewMockGun(cfg *MockGunConfig) *MockGun

NewMockGun create a mock gun

func (*MockGun) Call

func (m *MockGun) Call(l *Generator) CallResult

Call implements example gun call, assertions on response bodies should be done here

type MockGunConfig

type MockGunConfig struct {
	// FailRatio in percentage, 0-100
	FailRatio int
	// TimeoutRatio in percentage, 0-100
	TimeoutRatio int
	// CallSleep time spent waiting inside a call
	CallSleep time.Duration
}

MockGunConfig configures a mock gun

type MockHTTPGun

type MockHTTPGun struct {
	Data []string
	// contains filtered or unexported fields
}

MockHTTPGun is a mock gun

func NewHTTPMockGun

func NewHTTPMockGun(cfg *MockHTTPGunConfig) *MockHTTPGun

NewHTTPMockGun create an HTTP mock gun

func (*MockHTTPGun) Call

func (m *MockHTTPGun) Call(l *Generator) CallResult

Call implements example gun call, assertions on response bodies should be done here

type MockHTTPGunConfig

type MockHTTPGunConfig struct {
	TargetURL string
}

MockHTTPGunConfig configures a mock HTTP gun

type MockPromtailClient

type MockPromtailClient struct {
	Results       []PromtailSendResult
	OnHandleEntry api.EntryHandlerFunc
}

func (*MockPromtailClient) AllHandleResults

func (c *MockPromtailClient) AllHandleResults() []PromtailSendResult

func (*MockPromtailClient) Handle

func (c *MockPromtailClient) Handle(labels model.LabelSet, time time.Time, entry string) error

Handle implements client.Client

func (*MockPromtailClient) HandleStruct

func (c *MockPromtailClient) HandleStruct(ls model.LabelSet, t time.Time, st interface{}) error

func (*MockPromtailClient) LastHandleResult

func (c *MockPromtailClient) LastHandleResult() PromtailSendResult

func (*MockPromtailClient) Stop

func (c *MockPromtailClient) Stop()

Stop implements client.Client

type MockVirtualUser added in v0.1.2

type MockVirtualUser struct {
	Data []string
	// contains filtered or unexported fields
}

MockVirtualUser is a mock virtual user

func NewMockVU added in v0.1.2

NewMockVU create a mock virtual user

func (MockVirtualUser) Call added in v0.1.2

func (m MockVirtualUser) Call(l *Generator)

func (MockVirtualUser) Clone added in v0.1.2

func (MockVirtualUser) Setup added in v0.1.2

func (m MockVirtualUser) Setup(_ *Generator) error

func (MockVirtualUser) Stop added in v0.1.2

func (m MockVirtualUser) Stop(_ *Generator)

func (MockVirtualUser) StopChan added in v0.1.2

func (m MockVirtualUser) StopChan() chan struct{}

func (MockVirtualUser) Teardown added in v0.1.2

func (m MockVirtualUser) Teardown(_ *Generator) error

type MockVirtualUserConfig added in v0.1.2

type MockVirtualUserConfig struct {
	// FailRatio in percentage, 0-100
	FailRatio int
	// TimeoutRatio in percentage, 0-100
	TimeoutRatio int
	// CallSleep time spent waiting inside a call
	CallSleep time.Duration
}

MockVirtualUserConfig configures a mock virtual user

type MockWSServer

type MockWSServer struct {
	// Logf controls where logs are sent.
	Logf  func(f string, v ...interface{})
	Sleep time.Duration
}

func (MockWSServer) ServeHTTP

func (s MockWSServer) ServeHTTP(w http.ResponseWriter, r *http.Request)

type Profile

type Profile struct {
	Generators []*Generator
}

Profile is a set of concurrent generators forming some workload profile

func NewRPSProfile

func NewRPSProfile(t *testing.T, labels map[string]string, pp []*ProfileGunPart) (*Profile, error)

NewRPSProfile creates new RPSProfile from parts

func NewVUProfile added in v0.1.2

func NewVUProfile(t *testing.T, labels map[string]string, pp []*ProfileVUPart) (*Profile, error)

NewVUProfile creates new virtual user profile from parts

func (*Profile) Run

func (m *Profile) Run(wait bool) error

Run runs all generators and wait until they finish

func (*Profile) Wait

func (m *Profile) Wait()

Wait waits until all generators have finished the workload

type ProfileGunPart

type ProfileGunPart struct {
	Name     string
	Schedule []*Segment
	Gun      Gun
}

type ProfileVUPart added in v0.1.2

type ProfileVUPart struct {
	Name     string
	Schedule []*Segment
	VU       VirtualUser
}

type PromtailSendResult

type PromtailSendResult struct {
	Labels model.LabelSet
	Time   time.Time
	Entry  string
}

type ResponseData

type ResponseData struct {
	OKData *SliceBuffer[any]

	OKResponses *SliceBuffer[CallResult]

	FailResponses *SliceBuffer[CallResult]
	// contains filtered or unexported fields
}

ResponseData includes any request/response data that a gun might store ok* slices usually contains successful responses and their verifications if their done async fail* slices contains CallResult with response data and an error

type Segment

type Segment struct {
	From         int64
	Increase     int64
	Steps        int64
	StepDuration time.Duration
	// contains filtered or unexported fields
}

Segment load test schedule segment

func Combine

func Combine(segs ...[]*Segment) []*Segment

func CombineAndRepeat

func CombineAndRepeat(times int, segs ...[]*Segment) []*Segment

func Line

func Line(from, to int64, duration time.Duration) []*Segment

func Plain

func Plain(from int64, duration time.Duration) []*Segment

func (*Segment) Validate

func (ls *Segment) Validate() error

type SliceBuffer

type SliceBuffer[T any] struct {
	Idx      int
	Capacity int
	Data     []T
}

SliceBuffer keeps Capacity of type T, after len => cap overrides old data

func NewSliceBuffer

func NewSliceBuffer[T any](cap int) *SliceBuffer[T]

NewSliceBuffer creates new limited capacity slice

func (*SliceBuffer[T]) Append

func (m *SliceBuffer[T]) Append(s T)

Append appends T if len <= cap, overrides old data otherwise

type Stats

type Stats struct {
	CurrentRPS     atomic.Int64 `json:"currentRPS"`
	CurrentVUs     atomic.Int64 `json:"currentVUs"`
	LastSegment    atomic.Int64 `json:"last_segment"`
	CurrentSegment atomic.Int64 `json:"current_schedule_segment"`
	CurrentStep    atomic.Int64 `json:"current_schedule_step"`
	RunStopped     atomic.Bool  `json:"runStopped"`
	RunFailed      atomic.Bool  `json:"runFailed"`
	Success        atomic.Int64 `json:"success"`
	Failed         atomic.Int64 `json:"failed"`
	CallTimeout    atomic.Int64 `json:"callTimeout"`
}

Stats basic generator load stats

type VirtualUser added in v0.1.2

type VirtualUser interface {
	Call(l *Generator)
	Stop(l *Generator)
	Clone(l *Generator) VirtualUser
	Setup(l *Generator) error
	Teardown(l *Generator) error
	StopChan() chan struct{}
}

VirtualUser is basic interface to run virtual users load you should use it if: - your protocol is stateful, ex.: ws, grpc - you'd like to have some VirtualUser modelling

type WSMockVU added in v0.1.2

type WSMockVU struct {
	Data []string
	// contains filtered or unexported fields
}

WSMockVU ws mock virtual user

func NewWSMockVU added in v0.1.2

func NewWSMockVU(cfg WSMockVUConfig) WSMockVU

NewWSMockVU create a ws mock virtual user

func (WSMockVU) Call added in v0.1.2

func (m WSMockVU) Call(l *Generator)

Call create a virtual user firing read requests against mock ws server

func (WSMockVU) Clone added in v0.1.2

func (m WSMockVU) Clone(_ *Generator) VirtualUser

func (WSMockVU) Setup added in v0.1.2

func (m WSMockVU) Setup(l *Generator) error

func (WSMockVU) Stop added in v0.1.2

func (m WSMockVU) Stop(_ *Generator)

func (WSMockVU) StopChan added in v0.1.2

func (m WSMockVU) StopChan() chan struct{}

func (WSMockVU) Teardown added in v0.1.2

func (m WSMockVU) Teardown(_ *Generator) error

type WSMockVUConfig added in v0.1.2

type WSMockVUConfig struct {
	TargetURl string
}

WSMockVUConfig ws mock config

type WaspAlert added in v0.1.1

type WaspAlert struct {
	Name                 string
	AlertType            string
	TestName             string
	GenName              string
	RequirementGroupName string
	AlertIf              alert.ConditionEvaluator
	CustomAlert          timeseries.Option
}

Directories

Path Synopsis
examples
alerts command
go_test command
profiles command
scenario command
simple_rps command
simple_vu command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL