Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/bojand/ghz
Browse files Browse the repository at this point in the history
  • Loading branch information
bojand committed Jan 23, 2019
2 parents 7c480c1 + 3f7e9ee commit aa81533
Show file tree
Hide file tree
Showing 10 changed files with 239 additions and 113 deletions.
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,40 @@ All documentation at [ghz.sh](https://ghz.sh).
Usage: ghz [options...] host
Options:

-config Path to the JSON or TOML config file that specifies all the test run settings.

-proto The Protocol Buffer .proto file.
-protoset The compiled protoset file. Alternative to proto. -proto takes precedence.
-call A fully-qualified method name in 'package/service/method' or 'package.service.method' format.
-cert The file containing the CA root cert file. Ignored if -insecure is specified.
-cname An server name override.
-insecure Specify for non TLS connection.
-config Path to the JSON or TOML config file that specifies all the test settings.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.

-cacert File containing trusted root certificates for verifying the server.
-cert File containing client certificate (public key), to present to the server. Must also provide -key option.
-key File containing client private key, to present to the server. Must also provide -cert option.
-cname Server name override when validating TLS certificate - useful for self signed certs.
-skipTLS Skip TLS client verification of the server's certificate chain and host name.
-insecure Use plaintext and insecure connection.
-authority Value to be used as the :authority pseudo-header. Only works if -insecure is used.
-c Number of requests to run concurrently.
Total number of requests cannot be smaller than the concurrency level. Default is 50.
-n Number of requests to run. Default is 200.
-q Rate limit, in queries per second (QPS). Default is no rate limit.
-t Timeout for each request in seconds. Default is 20, use 0 for infinite.
-z Duration of application to send requests. When duration is reached,
application stops and exits. If duration is specified, n is ignored.
Examples: -z 10s -z 3m.
-z Duration of application to send requests. When duration is reached, application stops and exits.
If duration is specified, n is ignored. Examples: -z 10s -z 3m.
-x Maximum duration of application to send requests with n setting respected.
If duration is reached before n requests are completed, application stops and exits.
Examples: -x 10s -x 3m.
-d The call data as stringified JSON.
If the value is '@' then the request contents are read from stdin.
-D Path for call data JSON file. For example, /home/user/file.json or ./file.json.
-D Path for call data JSON file. Examples: /home/user/file.json or ./file.json.
-b The call data comes as serialized binary message read from stdin.
-B Path for the call data as serialized binary message.
-m Request metadata as stringified JSON.
-M Path for call metadata JSON file. For example, /home/user/metadata.json or ./metadata.json.
-M Path for call metadata JSON file. Examples: /home/user/metadata.json or ./metadata.json.
-o Output path. If none provided stdout is used.
-O Output type. If none provided, a summary is printed.
Expand All @@ -62,16 +69,13 @@ Options:
"influx-summary" outputs the metrics summary as influxdb line protocol.
"influx-details" outputs the metrics details as influxdb line protocol.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.

-T Connection timeout in seconds for the initial connection dial. Default is 10.
-L Keepalive time in seconds. Only used if present and above 0.
-name User specified name for the test.
-tags JSON representation of user-defined string tags.
-cpus Number of used cpu cores. (default for current machine is 8 cores)
-cpus Number of used cpu cores.
-v Print the version.
```
Expand Down
3 changes: 3 additions & 0 deletions cmd/ghz/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ type config struct {
Proto string `json:"proto" toml:"proto" yaml:"proto"`
Protoset string `json:"protoset" toml:"protoset" yaml:"protoset"`
Call string `json:"call" toml:"call" yaml:"call" required:"true"`
RootCert string `json:"cacert" toml:"cacert" yaml:"cacert"`
Cert string `json:"cert" toml:"cert" yaml:"cert"`
Key string `json:"key" toml:"key" yaml:"key"`
SkipTLSVerify bool `json:"skipTLS" toml:"skipTLS" yaml:"skipTLS"`
CName string `json:"cname" toml:"cname" yaml:"cname"`
N uint `json:"n" toml:"n" yaml:"n" default:"200"`
C uint `json:"c" toml:"c" yaml:"c" default:"50"`
Expand Down
82 changes: 47 additions & 35 deletions cmd/ghz/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,74 +20,86 @@ var (
// set by goreleaser with -ldflags="-X main.version=..."
version = "dev"

cPath = flag.String("config", "", "Path to the JSON or TOML config file that specifies all the test run settings.")

proto = flag.String("proto", "", `The Protocol Buffer .proto file.`)
protoset = flag.String("protoset", "", `The .protoset file.`)
call = flag.String("call", "", `A fully-qualified symbol name.`)
cert = flag.String("cert", "", "Client certificate file. If Omitted insecure is used.")
cname = flag.String("cname", "", "Server name override - useful for self signed certs.")
insecure = flag.Bool("insecure", false, "Specify for non TLS connection")
cPath = flag.String("config", "", "Path to the config JSON file.")

c = flag.Uint("c", 50, "Number of requests to run concurrently.")
protoset = flag.String("protoset", "", `The compiled protoset file. Alternative to proto. -proto takes precedence.`)
call = flag.String("call", "", `A fully-qualified method name in 'package/service/method' or 'package.service.method' format.`)
paths = flag.String("i", "", "Comma separated list of proto import paths. The current working directory and the directory of the protocol buffer file are automatically added to the import list.")

cacert = flag.String("cacert", "", "File containing trusted root certificates for verifying the server.")
cert = flag.String("cert", "", "File containing client certificate (public key), to present to the server. Must also provide -key option.")
key = flag.String("key", "", "File containing client private key, to present to the server. Must also provide -cert option.")
cname = flag.String("cname", "", "Server name override when validating TLS certificate - useful for self signed certs.")
skipVerify = flag.Bool("skipTLS", false, "Skip TLS client verification of the server's certificate chain and host name.")
insecure = flag.Bool("insecure", false, "Use plaintext and insecure connection.")
authority = flag.String("authority", "", "Value to be used as the :authority pseudo-header. Only works if -insecure is used.")

c = flag.Uint("c", 50, "Number of requests to run concurrently. Total number of requests cannot be smaller than the concurrency level. Default is 50.")
n = flag.Uint("n", 200, "Number of requests to run. Default is 200.")
q = flag.Uint("q", 0, "Rate limit, in queries per second (QPS). Default is no rate limit.")
t = flag.Uint("t", 20, "Timeout for each request in seconds.")
z = flag.Duration("z", 0, "Duration of application to send requests.")
x = flag.Duration("x", 0, "Maximum duration of application to send requests.")
t = flag.Uint("t", 20, "Timeout for each request in seconds. Default is 20, use 0 for infinite.")
z = flag.Duration("z", 0, "Duration of application to send requests. When duration is reached, application stops and exits. If duration is specified, n is ignored. Examples: -z 10s -z 3m.")
x = flag.Duration("x", 0, "Maximum duration of application to send requests with n setting respected. If duration is reached before n requests are completed, application stops and exits. Examples: -x 10s -x 3m.")

data = flag.String("d", "", "The call data as stringified JSON. If the value is '@' then the request contents are read from stdin.")
dataPath = flag.String("D", "", "Path for call data JSON file.")
binData = flag.Bool("b", false, "The call data as serialized binary message read from stdin.")
binPath = flag.String("B", "", "The call data as serialized binary message read from a file.")
dataPath = flag.String("D", "", "File path for call data JSON file. Examples: /home/user/file.json or ./file.json.")
binData = flag.Bool("b", false, "The call data comes as serialized binary message read from stdin.")
binPath = flag.String("B", "", "File path for the call data as serialized binary message.")
md = flag.String("m", "", "Request metadata as stringified JSON.")
mdPath = flag.String("M", "", "Path for call metadata JSON file.")

paths = flag.String("i", "", "Comma separated list of proto import paths")
mdPath = flag.String("M", "", "File path for call metadata JSON file. Examples: /home/user/metadata.json or ./metadata.json.")

output = flag.String("o", "", "Output path")
format = flag.String("O", "", "Output format")
output = flag.String("o", "", "Output path. If none provided stdout is used.")
format = flag.String("O", "", "Output format. If none provided, a summary is printed.")

ct = flag.Uint("T", 10, "Connection timeout in seconds for the initial connection dial.")
kt = flag.Uint("L", 0, "Keepalive time in seconds.")
ct = flag.Uint("T", 10, "Connection timeout in seconds for the initial connection dial. Default is 10.")
kt = flag.Uint("L", 0, "Keepalive time in seconds. Only used if present and above 0.")

name = flag.String("name", "", "User specified name for the test.")
tags = flag.String("tags", "", "JSON representation of user-defined string tags.")

cpus = flag.Uint("cpus", uint(runtime.GOMAXPROCS(-1)), "")
cpus = flag.Uint("cpus", uint(runtime.GOMAXPROCS(-1)), "Number of used cpu cores.")

v = flag.Bool("v", false, "Print the version.")
)

var usage = `Usage: ghz [options...] host
Options:
-config Path to the JSON or TOML config file that specifies all the test run settings.
-proto The Protocol Buffer .proto file.
-protoset The compiled protoset file. Alternative to proto. -proto takes precedence.
-call A fully-qualified method name in 'package/service/method' or 'package.service.method' format.
-cert The file containing the CA root cert file. Ignored if -insecure is specified.
-cname An server name override.
-insecure Specify for non TLS connection.
-config Path to the JSON or TOML config file that specifies all the test settings.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.
-cacert File containing trusted root certificates for verifying the server.
-cert File containing client certificate (public key), to present to the server. Must also provide -key option.
-key File containing client private key, to present to the server. Must also provide -cert option.
-cname Server name override when validating TLS certificate - useful for self signed certs.
-skipTLS Skip TLS client verification of the server's certificate chain and host name.
-insecure Use plaintext and insecure connection.
-authority Value to be used as the :authority pseudo-header. Only works if -insecure is used.
-c Number of requests to run concurrently.
Total number of requests cannot be smaller than the concurrency level. Default is 50.
-n Number of requests to run. Default is 200.
-q Rate limit, in queries per second (QPS). Default is no rate limit.
-t Timeout for each request in seconds. Default is 20, use 0 for infinite.
-z Duration of application to send requests. When duration is reached,
application stops and exits. If duration is specified, n is ignored.
Examples: -z 10s -z 3m.
-z Duration of application to send requests. When duration is reached, application stops and exits.
If duration is specified, n is ignored. Examples: -z 10s -z 3m.
-x Maximum duration of application to send requests with n setting respected.
If duration is reached before n requests are completed, application stops and exits.
Examples: -x 10s -x 3m.
-d The call data as stringified JSON.
If the value is '@' then the request contents are read from stdin.
-D Path for call data JSON file. For example, /home/user/file.json or ./file.json.
-D Path for call data JSON file. Examples: /home/user/file.json or ./file.json.
-b The call data comes as serialized binary message read from stdin.
-B Path for the call data as serialized binary message.
-m Request metadata as stringified JSON.
-M Path for call metadata JSON file. For example, /home/user/metadata.json or ./metadata.json.
-M Path for call metadata JSON file. Examples: /home/user/metadata.json or ./metadata.json.
-o Output path. If none provided stdout is used.
-O Output type. If none provided, a summary is printed.
Expand All @@ -98,9 +110,6 @@ Options:
"influx-summary" outputs the metrics summary as influxdb line protocol.
"influx-details" outputs the metrics details as influxdb line protocol.
-i Comma separated list of proto import paths. The current working directory and the directory
of the protocol buffer file are automatically added to the import list.
-T Connection timeout in seconds for the initial connection dial. Default is 10.
-L Keepalive time in seconds. Only used if present and above 0.
Expand Down Expand Up @@ -161,7 +170,10 @@ func main() {
options = append(options,
runner.WithProtoFile(cfg.Proto, cfg.ImportPaths),
runner.WithProtoset(cfg.Protoset),
runner.WithCertificate(cfg.Cert, cfg.CName),
runner.WithRootCertificate(cfg.RootCert),
runner.WithCertificate(cfg.Cert, cfg.Key),
runner.WithServerNameOverride(cfg.CName),
runner.WithSkipTLSVerify(cfg.SkipTLSVerify),
runner.WithInsecure(cfg.Insecure),
runner.WithConcurrency(cfg.C),
runner.WithTotalRequests(cfg.N),
Expand Down
112 changes: 106 additions & 6 deletions runner/options.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package runner

import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
Expand All @@ -11,6 +13,7 @@ import (
"time"

"github.com/pkg/errors"
"google.golang.org/grpc/credentials"
)

// RunConfig represents the request Configs
Expand All @@ -22,10 +25,15 @@ type RunConfig struct {
importPaths []string
protoset string

// securit settings
cert string
cname string
insecure bool
// security settings
creds credentials.TransportCredentials
cacert string
cert string
key string
cname string
skipVerify bool
insecure bool
authority string

// test
n int
Expand Down Expand Up @@ -53,18 +61,50 @@ type RunConfig struct {
type Option func(*RunConfig) error

// WithCertificate specifies the certificate options for the run
// WithCertificate("certfile.crt", "")
func WithCertificate(cert string, cname string) Option {
// WithCertificate("client.crt", "client.key")
func WithCertificate(cert, key string) Option {
return func(o *RunConfig) error {
cert = strings.TrimSpace(cert)
key = strings.TrimSpace(key)

o.cert = cert
o.key = key

return nil
}
}

// WithServerNameOverride specifies the certificate options for the run
func WithServerNameOverride(cname string) Option {
return func(o *RunConfig) error {
o.cname = cname

return nil
}
}

// WithAuthority specifies the value to be used as the :authority pseudo-header.
// This only works with WithInsecure option.
func WithAuthority(authority string) Option {
return func(o *RunConfig) error {
o.authority = authority

return nil
}
}

// WithRootCertificate specifies the root certificate options for the run
// WithRootCertificate("ca.crt")
func WithRootCertificate(cert string) Option {
return func(o *RunConfig) error {
cert = strings.TrimSpace(cert)

o.cacert = cert

return nil
}
}

// WithInsecure specifies that this run should be done using insecure mode
// WithInsecure(true)
func WithInsecure(insec bool) Option {
Expand All @@ -75,6 +115,15 @@ func WithInsecure(insec bool) Option {
}
}

// WithSkipTLSVerify skip client side TLS verification of server certificate
func WithSkipTLSVerify(skip bool) Option {
return func(o *RunConfig) error {
o.skipVerify = skip

return nil
}
}

// WithTotalRequests specifies the N (number of total requests) setting
// WithTotalRequests(1000)
func WithTotalRequests(n uint) Option {
Expand Down Expand Up @@ -395,5 +444,56 @@ func newConfig(call, host string, options ...Option) (*RunConfig, error) {
return nil, errors.New("Must provide proto or protoset")
}

creds, err := createClientTransportCredentials(
c.skipVerify,
c.cacert,
c.cert,
c.key,
c.cname,
)

if err != nil {
return nil, err
}

c.creds = creds

return c, nil
}

func createClientTransportCredentials(skipVerify bool, cacertFile, clientCertFile, clientKeyFile, cname string) (credentials.TransportCredentials, error) {
var tlsConf tls.Config

if clientCertFile != "" {
// Load the client certificates from disk
certificate, err := tls.LoadX509KeyPair(clientCertFile, clientKeyFile)
if err != nil {
return nil, fmt.Errorf("could not load client key pair: %v", err)
}
tlsConf.Certificates = []tls.Certificate{certificate}
}

if skipVerify == true {
tlsConf.InsecureSkipVerify = true
} else if cacertFile != "" {
// Create a certificate pool from the certificate authority
certPool := x509.NewCertPool()
ca, err := ioutil.ReadFile(cacertFile)
if err != nil {
return nil, fmt.Errorf("could not read ca certificate: %v", err)
}

// Append the certificates from the CA
if ok := certPool.AppendCertsFromPEM(ca); !ok {
return nil, errors.New("failed to append ca certs")
}

tlsConf.RootCAs = certPool
}

if cname != "" {
tlsConf.ServerName = cname
}

return credentials.NewTLS(&tlsConf), nil
}
Loading

0 comments on commit aa81533

Please sign in to comment.