Skip to content

Commit

Permalink
making changes related to PR tsenart#534 review
Browse files Browse the repository at this point in the history
  • Loading branch information
flaviostutz committed Oct 9, 2020
1 parent e5b4a0c commit c0cae43
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 59 deletions.
1 change: 0 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ RUN go mod download
# now build source code
ADD / /vegeta

RUN go test -v ./...
RUN make vegeta

FROM alpine:3.12.0
Expand Down
10 changes: 3 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,8 @@ attack command:
Attack name
-output string
Output file (default "stdout")
-prom-bind
Host to bind Prometheus service to. Defaults to 0.0.0.0
-prometheus-enable
Enable Prometheus metrics endpoint so that requests can be monitored by Prometheus with default bind (0.0.0.0:8880). Defaults to false.
-prometheus-url
Prometheus metrics bind. Defaults to 0.0.0.0:8880
Prometheus metrics bind url for enabling Prometheus metrics exporter. Ex.: "http://0.0.0.0:8880"
-proxy-header value
Proxy CONNECT header
-rate value
Expand Down Expand Up @@ -815,7 +811,7 @@ Just pass a new number as the argument to change it.

Vegeta has a built-in Prometheus Exporter that may be enabled during "attacks" so that you can point any Prometheus instance to Vegeta instances and get some metrics about http requests performance and about Vegeta process itself.

To enable Prometheus Exporter on command line, see flags with "prom-" prefix.
To enable the Prometheus Exporter on the command line, use the "prometheus-url" flag.
To enable Prometheus Exporter on lib usage, add Option "PrometheusEnable" and/or "PrometheusSettings".

Prometheus HTTP endpoint will be available only during the lifespan of an "attack" and will be closed right after the attack is finished.
Expand All @@ -839,7 +835,7 @@ services:
image: tsenart/vegeta
ports:
- 8880:8880
command: sh -c 'echo "GET https://www.yahoo.com" | vegeta attack -duration=30s -rate=5 -prometheus-enable=true'
command: sh -c 'echo "GET https://www.yahoo.com" | vegeta attack -duration=30s -rate=5 -prometheus-url=http://0.0.0.0:8880'
prometheus:
image: flaviostutz/prometheus:2.19.2.0
Expand Down
13 changes: 3 additions & 10 deletions attack.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ func attackCmd() command {
laddr: localAddr{&vegeta.DefaultLocalAddr},
rate: vegeta.Rate{Freq: 50, Per: time.Second},
maxBody: vegeta.DefaultMaxBody,
promEnable: false,
promURL: "0.0.0.0:8880",
}
fs.StringVar(&opts.name, "name", "", "Attack name")
Expand Down Expand Up @@ -59,7 +58,6 @@ func attackCmd() command {
fs.Var(&opts.laddr, "laddr", "Local IP address")
fs.BoolVar(&opts.keepalive, "keepalive", true, "Use persistent connections")
fs.StringVar(&opts.unixSocket, "unix-socket", "", "Connect over a unix socket. This overrides the host address in target URLs")
fs.BoolVar(&opts.promEnable, "prometheus-enable", false, "Enable Prometheus metrics endpoint so that requests can be monitored by Prometheus using default parameters(bind to 0.0.0.0:8880). Defaults to false.")
fs.StringVar(&opts.promURL, "prometheus-url", "", "Enable Prometheus metrics with specific bind parameters in format [bind ip]:[bind port]. Example: 0.0.0.0:8880")
systemSpecificFlags(fs, opts)

Expand Down Expand Up @@ -104,7 +102,6 @@ type attackOpts struct {
keepalive bool
resolvers csl
unixSocket string
promEnable bool
promURL string
}

Expand Down Expand Up @@ -180,12 +177,8 @@ func attack(opts *attackOpts) (err error) {
}

var promMetrics *prom.PrometheusMetrics
if opts.promEnable || opts.promURL != "" {
purl := opts.promURL
if purl == "" {
purl = "0.0.0.0:8880"
}
promMetrics, err = prom.NewPrometheusMetricsWithParams(purl)
if opts.promURL != "" {
promMetrics, err = prom.NewPrometheusMetricsWithParams(opts.promURL)
if err != nil {
return err
}
Expand Down Expand Up @@ -223,7 +216,7 @@ func attack(opts *attackOpts) (err error) {
if !ok {
return nil
}
if opts.promEnable {
if opts.promURL != "" {
promMetrics.Observe(r)
}
if err = enc.Encode(r); err != nil {
Expand Down
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ require (
github.com/miekg/dns v1.1.17
github.com/prometheus/client_golang v1.7.1
github.com/streadway/quantile v0.0.0-20150917103942-b0c588724d25
github.com/stretchr/testify v1.4.0
github.com/tsenart/go-tsz v0.0.0-20180814232043-cdeb9e1e981e
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297
pgregory.net/rapid v0.3.3
Expand Down
31 changes: 12 additions & 19 deletions lib/prom/prom.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,16 @@ package prom
import (
"context"
"fmt"
"net"
"net/http"
"regexp"
"net/url"
"strconv"
"time"

"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promauto"
"github.com/prometheus/client_golang/prometheus/promhttp"

vegeta "github.com/tsenart/vegeta/v12/lib"
)

Expand All @@ -26,32 +28,23 @@ type PrometheusMetrics struct {

//NewPrometheusMetrics same as NewPrometheusMetricsWithParams with default params:
func NewPrometheusMetrics() (*PrometheusMetrics, error) {
return NewPrometheusMetricsWithParams("0.0.0.0:8880")
return NewPrometheusMetricsWithParams("http://0.0.0.0:8880")
}

// NewPrometheusMetricsWithParams creates a new Prometheus Metrics to Observe attack results and expose metrics
// For example, after using NewPrometheusMetricsWithParams("0.0.0.0:8880"),
// For example, after using NewPrometheusMetricsWithParams("http://0.0.0.0:8880"),
// during an "attack" you can call "curl http://127.0.0.0:8880" to see current metrics.
// This endpoint can be configured in scrapper section of your Prometheus server.
func NewPrometheusMetricsWithParams(bindURL string) (*PrometheusMetrics, error) {

//parse bind url elements
re := regexp.MustCompile("(.+):([0-9]+)")
rr := re.FindAllStringSubmatch(bindURL, 3)
bindHost := ""
bindPort := 0
var err error
if len(rr) == 1 {
if len(rr[0]) == 3 {
bindHost = rr[0][1]
bindPort, err = strconv.Atoi(rr[0][2])
if err != nil {
return nil, err
}
}
p, err := url.Parse(bindURL)
if err != nil {
return nil, fmt.Errorf("Invalid bindURL %s. Must be in format 'http://0.0.0.0:8880'. err=%s", bindURL, err)
}
if bindHost == "" {
return nil, fmt.Errorf("Invalid bindURL %s. Must be in format '0.0.0.0:8880'", bindURL)
bindHost, bindPort, err := net.SplitHostPort(p.Host)
if err != nil {
return nil, fmt.Errorf("Invalid bindURL %s. Must be in format 'http://0.0.0.0:8880'. err=%s", bindURL, err)
}

pm := &PrometheusMetrics{
Expand Down Expand Up @@ -101,7 +94,7 @@ func NewPrometheusMetricsWithParams(bindURL string) (*PrometheusMetrics, error)

//setup prometheus metrics http server
pm.srv = http.Server{
Addr: fmt.Sprintf("%s:%d", bindHost, bindPort),
Addr: fmt.Sprintf("%s:%s", bindHost, bindPort),
Handler: promhttp.HandlerFor(pm.registry, promhttp.HandlerOpts{}),
}

Expand Down
86 changes: 65 additions & 21 deletions lib/prom/prom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package prom
import (
"io/ioutil"
"net/http"
"strings"
"testing"
"time"

Expand All @@ -12,26 +13,43 @@ import (

func TestPromServerBasic1(t *testing.T) {
pm, err := NewPrometheusMetrics()
assert.Nil(t, err, "Error launching Prometheus http server. err=%s", err)
if err != nil {
t.Errorf("Error launching Prometheus http server. err=%s", err)
}

err = pm.Close()
assert.Nil(t, err, "Error stopping Prometheus http server. err=%s", err)
if err != nil {
t.Errorf("Error stopping Prometheus http server. err=%s", err)
}
}

func TestPromServerBasic2(t *testing.T) {
pm, err := NewPrometheusMetrics()
assert.Nil(t, err, "Error launching Prometheus metrics. err=%s", err)
if err != nil {
t.Errorf("Error launching Prometheus metrics. err=%s", err)
}
err = pm.Close()
assert.Nil(t, err, "Error stopping Prometheus http server. err=%s", err)
if err != nil {
t.Errorf("Error stopping Prometheus http server. err=%s", err)
}

pm, err = NewPrometheusMetrics()
assert.Nil(t, err, "Error launching Prometheus metrics. err=%s", err)
if err != nil {
t.Errorf("Error launching Prometheus metrics. err=%s", err)
}
err = pm.Close()
assert.Nil(t, err, "Error stopping Prometheus http server. err=%s", err)
if err != nil {
t.Errorf("Error stopping Prometheus http server. err=%s", err)
}

pm, err = NewPrometheusMetrics()
assert.Nil(t, err, "Error launching Prometheus metrics. err=%s", err)
if err != nil {
t.Errorf("Error launching Prometheus metrics. err=%s", err)
}
err = pm.Close()
assert.Nil(t, err, "Error stopping Prometheus http server. err=%s", err)
if err != nil {
t.Errorf("Error stopping Prometheus http server. err=%s", err)
}
}

func TestPromServerObserve(t *testing.T) {
Expand All @@ -54,32 +72,58 @@ func TestPromServerObserve(t *testing.T) {

time.Sleep(3 * time.Second)
resp, err := http.Get("http://localhost:8880")
assert.Nil(t, err, "Error calling prometheus metrics. err=%s", err)
assert.Equal(t, 200, resp.StatusCode, "Status code should be 200")
if err != nil {
t.Errorf("Error calling prometheus metrics. err=%s", err)
}
if resp.StatusCode != 200 {
t.Errorf("Status code should be 200")
}

data, err := ioutil.ReadAll(resp.Body)
assert.Nil(t, err, "Error calling prometheus metrics. err=%s", err)
if err != nil {
t.Errorf("Error calling prometheus metrics. err=%s", err)
}
str := string(data)
assert.NotEqual(t, 0, len(str), "Body not empty")
assert.Contains(t, str, "request_seconds", "Metrics should contain request_seconds")
assert.Contains(t, str, "request_bytes_in", "Metrics should contain request_bytes_in")
assert.Contains(t, str, "request_bytes_out", "Metrics should contain request_bytes_out")
assert.NotContains(t, str, "request_fail_count", "Metrics should contain request_fail_count")
if len(str) == 0 {
t.Errorf("Body not empty. body=%s", str)
}
if !strings.Contains(str, "request_seconds") {
t.Error("Metrics should contain request_seconds")
}
if !strings.Contains(str, "request_bytes_in") {
t.Error("Metrics should contain request_bytes_in")
}
if !strings.Contains(str, "request_bytes_out") {
t.Error("Metrics should contain request_bytes_out")
}
if strings.Contains(str, "request_fail_count") {
t.Error("Metrics should contain request_fail_count")
}

r.Code = 500
r.Error = "REQUEST FAILED"
pm.Observe(r)

resp, err = http.Get("http://localhost:8880")
assert.Nil(t, err, "Error calling prometheus metrics. err=%s", err)
assert.Equal(t, 200, resp.StatusCode, "Status code should be 200")
if err != nil {
t.Errorf("Error calling prometheus metrics. err=%s", err)
}
if resp.StatusCode != 200 {
t.Errorf("Status code should be 200")
}

data, err = ioutil.ReadAll(resp.Body)
assert.Nil(t, err, "Error calling prometheus metrics. err=%s", err)
if err != nil {
t.Errorf("Error calling prometheus metrics. err=%s", err)
}
str = string(data)

assert.Contains(t, str, "request_fail_count", "Metrics should contain request_fail_count")
if !strings.Contains(str, "request_fail_count") {
t.Error("Metrics should contain request_fail_count")
}

err = pm.Close()
assert.Nil(t, err, "Error stopping Prometheus http server. err=%s", err)
if err != nil {
t.Errorf("Error stopping Prometheus http server. err=%s", err)
}
}

0 comments on commit c0cae43

Please sign in to comment.