Giter VIP home page Giter VIP logo

vegeta-server's Introduction

GoDoc Go Report Card Build Status License Coverage Status

Vegeta Server - A RESTful load-testing service

A RESTful API server for vegeta, a load testing tool written in Go.

Vegeta is a versatile HTTP load testing tool built out of a need to drill HTTP services with a constant request rate. The vegeta library is written in Go, which makes it ideal to implement server in Go.

The REST API model enables users to, asynchronously, submit multiple attacks targeting the same (or varied) endpoints, lookup pending or historical reports and update/cancel/re-submit attacks, using a simple RESTful API.

Getting Started

Installing

make all

NOTE: make all resolves all the dependencies, formats the code using gofmt, validates and lints using golangci-lint and golint, builds the vegeta-server binary and drops it in the /bin directory and finally runs tests using go test.

Quick Start

Start the server using the vegeta-server binary generated after the previous step.

Usage: main [<flags>]

Flags:
      --help            Show context-sensitive help (also try --help-long and --help-man).
      --ip="0.0.0.0"  Server IP Address.
      --port="80"     Server Port.
  -v, --version         Version Info
      --debug           Enabled Debug

Example

Serve HTTP traffic at 0.0.0.0:80/api/v1

./bin/vegeta-server --ip=0.0.0.0 --port=80 --debug

Try it out using make run

make run

INFO[0000] creating new dispatcher                       component=dispatcher
INFO[0000] starting dispatcher                           component=dispatcher
INFO[0000] listening                                     component=server ip=0.0.0.0 port=80

Using Docker

Build the docker image using local Dockerfile

docker build .

Run the docker container

docker run -d -p 8000:80 --name vegeta {container id}

You can also build and run a docker container using make

make container_run

NOTE: make container and make container_clean can be used to build the Dockerfile and delete the container and image.

Running tests

make test

Documentation

Contributing

Link to CONTRIBUTING.md

Project Structure

  • /: Extraneous setup and configuration files. No go code exists at this level.
  • /cmd/server: Comprises of package main serving as an entry point to the code.
  • /models: Includes the model definitions used by the DB and the API endpoints.
    • /db.go: Provides the storage interface, which is implemented by the configured database.
  • /internal: Internal only packages used by the server to run attacks and serve reports.
    • /dispatcher: Defines and implements the dispatcher interface, with the primary responsibility to carry out concurrent attacks.
    • /reporter: Defines and implements the reporter interface, with the primary responsibility to generate reports from previously completed attacks, in supported formats (JSON/Text/Binary).
    • /endpoints: Responsible for defining and registering the REST API endpoint handlers.
  • /pkg/vegeta: Vegeta library specific, wrapper methods and definitions. (Keep these isolated from the internals of the server, to support more load-testing tools/libraries in the future.)
  • /scripts: Helper installation scripts.

Roadmap

Link to roadmap

License

Link to LICENSE

Support

Contact Author at [email protected]

vegeta-server's People

Contributors

alleeclark avatar andrysds avatar elsesiy avatar enc avatar isaacnboyd avatar jatinkumarg avatar nitishm avatar prytoegrian avatar rentziass avatar shivam010 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

vegeta-server's Issues

Add unit tests across backend package

Need to add unit tests for the backend server to test all the attack options plus all the additional logic in the internal/vegeta/attack.go, internal/vegeta/report.go and internal/vegeta/store.go among the other files and packages.

Replace go-swagger with gin-gonic for backend server.

Go-swagger, although a great tool, adds too much code bloat and confines all the business logic to the configure_xxx.go file.
Creating new flag options is also much harder with go-swagger.

Switch to gin-gonic as the backend framework. Gin gonic should provide more control over the server logic and allow easy integration.

Move API documentation to new doc directory

Move all the REST API usage examples from the README.md to a new /docs directory, under usage.md.

The README.md is too cluttered at the moment, and needs to be broken into smaller docs that can be linked from this file.

Add CreatedAt and UpdatedAt fields to models.AttackDetails

Add the CreatedAt and UpdatedAt fields to models.AttackDetails, that get stored in the DB, to track the task submission time and the status update time.
The CreatedAt is set in db.Add() and the UpdatedAt is updated every time db.Update() is invoked with the AttackDetails.

Vegeta attacker does not stop attacking on Cancel

Describe the bug
When /api/v1/attack/<attackID>/cancel is invoked on an attack in Running state, the go routine exits and marks the attack as Canceled but the vegeta attack still continues in the background.

INFO[0011] canceling attack                              ID=86727dd6-7ec3-4031-85dc-4fcadd39373b ToCancel=true component=dispatcher
[GIN] 2019/02/18 - 17:48:58 | 200 |     143.206µs |             ::1 | POST     /api/v1/attack/86727dd6-7ec3-4031-85dc-4fcadd39373b/cancel
WARN[0011] task was canceled86727dd6-7ec3-4031-85dc-4fcadd39373b  ID=86727dd6-7ec3-4031-85dc-4fcadd39373b Status=canceled component=task
[GIN] 2019/02/18 - 17:48:58 | 200 |      94.847µs |             ::1 | GET      /api/v1/attack
[GIN] 2019/02/18 - 17:48:59 | 200 |      72.283µs |             ::1 | GET      /api/v1/attack
[GIN] 2019/02/18 - 17:49:00 | 200 |      89.406µs |             ::1 | GET      /api/v1/attack
[GIN] 2019/02/18 - 17:49:01 | 200 |     103.727µs |             ::1 | GET      /api/v1/attack
[GIN] 2019/02/18 - 17:49:02 | 200 |      63.409µs |             ::1 | GET      /api/v1/attack

To Reproduce
Submit attack with following body

{
	"rate": 10,
	"duration": "50s",
	"target": {
		"method": "GET",
		"URL": "http://localhost:80/api/v1/report",
		"scheme": "http"
	}
}

and invoke cancel.

Expected behavior
The vegeta attack stops as well.

Race condition when testing dispatcher.Cancel

Describe the bug
RACE WARNING when testing dispatcher.Cancel().

=== RUN   Test_dispatcher_Cancel_Error_completed
INFO[0000] creating new dispatcher                       component=dispatcher
INFO[0000] starting dispatcher                           component=dispatcher
INFO[0000] dispatching new attack                        ID=d63a79ac-6f51-486e-845d-077c8c76168a Status=scheduled component=dispatcher
==================
WARNING: DATA RACE
Read at 0x00c0000f8d68 by goroutine 8:
  vegeta-server/internal/dispatcher.(*task).Complete()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:116 +0x61
  vegeta-server/internal/dispatcher.run()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:213 +0x17a

Previous write at 0x00c0000f8d68 by goroutine 7:
  vegeta-server/internal/dispatcher.(*task).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:107 +0x12a
  vegeta-server/internal/dispatcher.(*dispatcher).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/dispatcher.go:109 +0xb5f

Goroutine 8 (running) created at:
  vegeta-server/internal/dispatcher.(*task).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:105 +0x11c
  vegeta-server/internal/dispatcher.(*dispatcher).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/dispatcher.go:109 +0xb5f

Goroutine 7 (running) created at:
  vegeta-server/internal/dispatcher.Test_dispatcher_Cancel_Error_completed()
      /Users/nitishm/vegeta-server/internal/dispatcher/dispatcher_test.go:249 +0x545
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
==================
==================
WARNING: DATA RACE
Write at 0x00c0000f8d98 by goroutine 8:
  vegeta-server/internal/dispatcher.(*task).SendUpdate()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:164 +0x70
  vegeta-server/internal/dispatcher.(*task).Complete()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:128 +0x20e
  vegeta-server/internal/dispatcher.run()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:213 +0x17a

Previous write at 0x00c0000f8d98 by goroutine 7:
  vegeta-server/internal/dispatcher.(*task).SendUpdate()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:164 +0x70
  vegeta-server/internal/dispatcher.(*task).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:109 +0x15d
  vegeta-server/internal/dispatcher.(*dispatcher).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/dispatcher.go:109 +0xb5f

Goroutine 8 (running) created at:
  vegeta-server/internal/dispatcher.(*task).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/task.go:105 +0x11c
  vegeta-server/internal/dispatcher.(*dispatcher).Run()
      /Users/nitishm/vegeta-server/internal/dispatcher/dispatcher.go:109 +0xb5f

Goroutine 7 (running) created at:
  vegeta-server/internal/dispatcher.Test_dispatcher_Cancel_Error_completed()
      /Users/nitishm/vegeta-server/internal/dispatcher/dispatcher_test.go:249 +0x545
  testing.tRunner()
      /usr/local/go/src/testing/testing.go:827 +0x162
==================
INFO[0002] canceling attack                              ID=d63a79ac-6f51-486e-845d-077c8c76168a ToCancel=true component=dispatcher
ERRO[0002] failed to cancel task                         ID=d63a79ac-6f51-486e-845d-077c8c76168a ToCancel=true component=dispatcher error="cannot cancel task d63a79ac-6f51-486e-845d-077c8c76168a with status completed"
WARN[0002] gracefully shutting down the dispatcher       component=dispatcher
--- FAIL: Test_dispatcher_Cancel_Error_completed (2.01s)
    testing.go:771: race detected during execution of test

To Reproduce
Remove the // +build !race from dispatcher_test.go and run go test -v -race --run Test_dispatcher_Cancel from the dispatcher directory.

Create a road-map to keep track of tasks

Create a road-map to keep track of tasks, and add a few more ideas to the project

  • Create a UI dashboard to kick-off and monitor/plot/report attacks. This can make use of the plot options built into vegeta itself, among other server related items

Remove dependency on vegeta lib from the internal package

Remove any dependence on the vegeta lib from /internal package. We want internal to be free of upstream dependencies to make it generic. The idea is to extend this project to support more tools (other than vegeta) in the future. Since the API is, mostly, a generic set of parameters, used across load-testing tools, it should not be too hard to bind them to other load-testing libraries and tools.

Dependency removal comment:
TODO - https://github.com/nitishm/vegeta-server/blob/master/internal/dispatcher/task.go#L164

Add support for binary encoded format for GET /api/v1/report endpoint

The report endpoint returns a JSON report by default.

This task is to provide (query string based) support for the binary encoded format for the GET /api/v1/report?format=binary.

This allows it to be saved in a result.bin file to be used directly with the vegeta CLI tool.

This should be as easy as returning the Result member from the AttackDetails struct, which stores the gob encoded binary attack results.

Add more query params to `GET /api/v1/attack`

Add more query params to GET /api/v1/attack endpoint, to filter attacks based on different attributes, like status=completed or status=failed to list successful vs failed attacks.

Another query-able param would be to list attacks within a certain date/time range using the createdAt and modifiedAt fields.

The Target as a collective should also be query-able based on the endpoint like GET http://xyz.com

There should be more params that would make sense, and I am open to suggestions, but we need to track all that work in this ticket.

Report in format TEXT does not display the attack ID

Describe the bug
Report in format text (/api/v1/report/ae901be0-8a60-49ec-ab9e-4d2c08598a62?format=text) does not show the AttackID.

Requests      [total, rate]            200, 100.38
Duration      [total, attack, wait]    1.992482s, 1.992482s, 0s
Latencies     [mean, 50, 95, 99, max]  0s, 0s, 0s, 0s, 0s
Bytes In      [total, mean]            0, 0.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  0.00%
Status Codes  [code:count]             0:200  

To Reproduce
Shown above

Expected behavior

ID ae901be0-8a60-49ec-ab9e-4d2c08598a62
Requests      [total, rate]            200, 100.38
Duration      [total, attack, wait]    1.992482s, 1.992482s, 0s
Latencies     [mean, 50, 95, 99, max]  0s, 0s, 0s, 0s, 0s
Bytes In      [total, mean]            0, 0.00
Bytes Out     [total, mean]            0, 0.00
Success       [ratio]                  0.00%
Status Codes  [code:count]             0:200  
Error Set:

**Steps to resolve**
Prepend the ID the the report string before returning the response.

Wrap all upstream errors with errors.Wrap

All upstream package errors should be wrapped with errors.Wrap() with an error message describing the error clause.

These errors should be propagated to caller, and eventually to the main, where they should be handled.

For example, take this block of code from dispatcher.go

if cancel {
		err := t.Cancel()
		if err != nil {
			d.log(fields).WithError(err).Error("failed to cancel task")
			return err
		}
}

and replace with,

if cancel {
		err := t.Cancel()
		if err != nil {
			d.log(fields).WithError(err).Error("failed to cancel task")
			return errors.Wrap(err, "failed to cancel task")
		}
}

This needs to be done across the package.

Remove context from task struct as it violates the pattern

Describe the bug
Context should not be stored in structs, but instead be passed explicitly to the function. The current implementation violates the pattern and needs to be resolved.

To Reproduce
N/A

Expected behavior
N/A

Screenshots
N/A

Additional context
https://golang.org/pkg/context/ specifications explicitly state, not storing the context in the struct. In task.go we store the context and cancelFn within the struct. This was needed to provide a way to cancel running tasks, when the task.Cancel() function is invoked by the caller (i.e. the dispatcher, on handling a POST /v1/api/attack/<attackID/cancel request.

Potential Solution
The dispatcher is responsible for creating a new context for every task, prior to calling the tasks Run() method. The ctx should be passed to the Run() method explicitly, which can then be passed around among the task methods. The dispatcher instead can keep a reference to the context and invoke the cancel function by passing the stored context to the task's Cancel() method.

Returns the status code in the result

The /report command doesn't return the status_code field which is available in the output from the vegeta report CLI command.

The problem is that the status_code block returns the HTTP status code (key) mapped to the code cound (value). Swagger OpenAPI 2.0 doesn't support dynamic keys.

One possible way is to extract the status code into a map[string]string type and then adapt it to the spec, where the spec could be of type

      statusCodes:
        type: object
        properties:
          code:
            type: string
          count:
            type: integer

Unable to build the project using docker

Describe the bug
Unable to build the project using docker due to a certificate validation error.

To Reproduce
Build the docker using docker build .

Expected behavior
A vegeta-server docker image built.

Additional context
Error message:
go: gopkg.in/[email protected]: git fetch -f origin refs/heads/:refs/heads/ refs/tags/:refs/tags/ in /go/pkg/mod/cache/vcs/7e5fa1eab4705eb80c9746632736cea906708d060702d529df6241d1c8c2c9f9: exit status 128:
fatal: unable to access 'https://gopkg.in/check.v1/': server certificate verification failed. CAfile: none CRLfile: none
go: error loading module requirements
make: *** [Makefile:20: deps] Error 1

Secure server with HTTPS option via swagger

The present server spec only specifies HTTP as the allowed scheme to talk to the server.

Need to add HTTPs to the schemes and also look into authentication options for the server. Swagger OpenAPI 2.0 allows this to be specified using the spec and go-swagger can handle generating the code as well.

It should be straightforward to follow the go-swagger and swagger tutorials to add this option and generate the code.

Add make container_stop target to makefile

We now have,

  • make container which builds the container,
  • make container_run that depends on make container, and then runs the container, with name "vegeta".
  • make container_clean that, currently removes the container and, removes the docker image.

Add a make container_stop target to the Makefile that would only do a docker rm -f vegeta.

Also update make container_clean to depend on make container_stop and remove the docker rm -vegeta step.
make container_run should also, first depend on make container followed by make container_stop .

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.