Skip to content

Commit

Permalink
Improve config file support and add tests (#192)
Browse files Browse the repository at this point in the history
* wip on config tests

* wip on config tests

* wip on config tests

* add config tests for yaml

* go mod tidy
  • Loading branch information
bojand authored Jun 1, 2020
1 parent 977b41a commit f4b2141
Show file tree
Hide file tree
Showing 28 changed files with 559 additions and 137 deletions.
6 changes: 3 additions & 3 deletions cmd/ghz/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ func createConfigFromArgs(cfg *runner.Config) error {
cfg.DataPath = *dataPath
cfg.BinData = binaryData
cfg.BinDataPath = *binPath
cfg.Metadata = &metadata
cfg.Metadata = metadata
cfg.MetadataPath = *mdPath
cfg.SI = runner.Duration(*si)
cfg.Output = *output
Expand All @@ -383,8 +383,8 @@ func createConfigFromArgs(cfg *runner.Config) error {
cfg.KeepaliveTime = runner.Duration(*kt)
cfg.CPUs = *cpus
cfg.Name = *name
cfg.Tags = &tagsMap
cfg.ReflectMetadata = &rmdMap
cfg.Tags = tagsMap
cfg.ReflectMetadata = rmdMap
cfg.Debug = *debug
cfg.EnableCompression = *enableCompression

Expand Down
4 changes: 4 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/bojand/ghz
go 1.13

require (
cloud.google.com/go v0.46.3 // indirect
github.com/alecthomas/kingpin v1.3.8-0.20191105203113-8c96d1c22481
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751
github.com/bojand/hri v1.1.0
Expand All @@ -25,6 +26,9 @@ require (
go.uber.org/multierr v1.3.0
go.uber.org/zap v1.13.0
golang.org/x/net v0.0.0-20191021144547-ec77196f6094
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc // indirect
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a // indirect
google.golang.org/grpc v1.24.0
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
gopkg.in/yaml.v2 v2.2.4 // indirect
)
72 changes: 72 additions & 0 deletions go.sum

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions protodesc/protodesc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package protodesc

import (
"context"
"fmt"
"testing"

"github.com/bojand/ghz/internal"
Expand Down Expand Up @@ -145,7 +144,6 @@ func TestProtodesc_GetMethodDescFromReflect(t *testing.T) {
refClient := grpcreflect.NewClient(refCtx, reflectpb.NewServerReflectionClient(conn))

mtd, err := GetMethodDescFromReflect("helloworld.Greeter.SayHello", refClient)
fmt.Println(mtd)
assert.NoError(t, err)
assert.NotNil(t, mtd)
assert.Equal(t, "SayHello", mtd.GetName())
Expand Down
216 changes: 97 additions & 119 deletions runner/config.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package runner

import (
"encoding/json"
"errors"
"path"
"strings"
"time"

"github.com/jinzhu/configor"
)

// Duration is our duration with TOML support
Expand All @@ -30,131 +32,67 @@ func (d Duration) String() string {
return time.Duration(d).String()
}

// Config for the run.
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"`
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"`
Authority string `json:"authority" toml:"authority" yaml:"authority"`
Insecure bool `json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty"`
N uint `json:"total" toml:"total" yaml:"total" default:"200"`
C uint `json:"concurrency" toml:"concurrency" yaml:"concurrency" default:"50"`
Connections uint `json:"connections" toml:"connections" yaml:"connections" default:"1"`
QPS uint `json:"qps" toml:"qps" yaml:"qps"`
Z Duration `json:"duration" toml:"duration" yaml:"duration"`
ZStop string `json:"duration-stop" toml:"duration-stop" yaml:"duration-stop" default:"close"`
X Duration `json:"max-duration" toml:"max-duration" yaml:"max-duration"`
Timeout Duration `json:"timeout" toml:"timeout" yaml:"timeout" default:"20s"`
Data interface{} `json:"data,omitempty" toml:"data,omitempty" yaml:"data,omitempty"`
DataPath string `json:"data-file" toml:"data-file" yaml:"data-file"`
BinData []byte `json:"-" toml:"-" yaml:"-"`
BinDataPath string `json:"binary-file" toml:"binary-file" yaml:"binary-file"`
Metadata *map[string]string `json:"metadata,omitempty" toml:"metadata,omitempty" yaml:"metadata,omitempty"`
MetadataPath string `json:"metadata-file" toml:"metadata-file" yaml:"metadata-file"`
SI Duration `json:"stream-interval" toml:"stream-interval" yaml:"stream-interval"`
Output string `json:"output" toml:"output" yaml:"output"`
Format string `json:"format" toml:"format" yaml:"format" default:"summary"`
DialTimeout Duration `json:"connect-timeout" toml:"connect-timeout" yaml:"connect-timeout" default:"10s"`
KeepaliveTime Duration `json:"keepalive" toml:"keepalive" yaml:"keepalive"`
CPUs uint `json:"cpus" toml:"cpus" yaml:"cpus"`
ImportPaths []string `json:"import-paths,omitempty" toml:"import-paths,omitempty" yaml:"import-paths,omitempty"`
Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
Tags *map[string]string `json:"tags,omitempty" toml:"tags,omitempty" yaml:"tags,omitempty"`
ReflectMetadata *map[string]string `json:"reflect-metadata,omitempty" toml:"reflect-metadata,omitempty" yaml:"reflect-metadata,omitempty"`
Debug string `json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty"`
Host string `json:"host" toml:"host" yaml:"host"`
EnableCompression bool `json:"enable-compression,omitempty" toml:"enable-compression,omitempty" yaml:"enable-compression,omitempty"`
}

// UnmarshalJSON is our custom implementation to handle the Duration fields
// and validate data
func (c *Config) UnmarshalJSON(data []byte) error {
type Alias Config
aux := &struct {
Z string `json:"duration"`
X string `json:"max-duration"`
SI string `json:"stream-interval"`
Timeout string `json:"timeout"`
*Alias
}{
Alias: (*Alias)(c),
// UnmarshalJSON is our custom unmarshaller with JSON support
func (d *Duration) UnmarshalJSON(text []byte) error {
// strValue := string(text)
first := text[0]
last := text[len(text)-1]
if first == '"' && last == '"' {
text = text[1 : len(text)-1]
}
if err := json.Unmarshal(data, &aux); err != nil {
dur, err := time.ParseDuration(string(text))
if err != nil {
return err
}

if aux.Data != nil {
err := checkData(aux.Data)
if err != nil {
return err
}
}

if aux.Z != "" {
zd, err := time.ParseDuration(aux.Z)
if err != nil {
return nil
}

c.Z = Duration(zd)
}

aux.ZStop = strings.ToLower(aux.ZStop)
if aux.ZStop != "close" && aux.ZStop != "ignore" && aux.ZStop != "wait" {
aux.ZStop = "close"
}

if aux.X != "" {
xd, err := time.ParseDuration(aux.X)
if err != nil {
return nil
}

c.X = Duration(xd)
}

if aux.SI != "" {
sid, err := time.ParseDuration(aux.SI)
if err != nil {
return nil
}

c.SI = Duration(sid)
}

if aux.Timeout != "" {
td, err := time.ParseDuration(aux.Timeout)
if err != nil {
return nil
}

c.Timeout = Duration(td)
}

*d = Duration(dur)
return nil
}

// MarshalJSON is our custom implementation to handle the Duration fields
func (c Config) MarshalJSON() ([]byte, error) {
type Alias Config
return json.Marshal(&struct {
*Alias
Z string `json:"duration"`
X string `json:"max-duration"`
SI string `json:"stream-interval"`
Timeout string `json:"timeout"`
}{
Alias: (*Alias)(&c),
Z: c.Z.String(),
X: c.X.String(),
SI: c.SI.String(),
Timeout: c.Timeout.String(),
})
// MarshalJSON implements encoding JSONMarshaler
func (d Duration) MarshalJSON() ([]byte, error) {
return []byte(time.Duration(d).String()), nil
}

// Config for the run.
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"`
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"`
Authority string `json:"authority" toml:"authority" yaml:"authority"`
Insecure bool `json:"insecure,omitempty" toml:"insecure,omitempty" yaml:"insecure,omitempty"`
N uint `json:"total" toml:"total" yaml:"total" default:"200"`
C uint `json:"concurrency" toml:"concurrency" yaml:"concurrency" default:"50"`
Connections uint `json:"connections" toml:"connections" yaml:"connections" default:"1"`
QPS uint `json:"qps" toml:"qps" yaml:"qps"`
Z Duration `json:"duration" toml:"duration" yaml:"duration"`
ZStop string `json:"duration-stop" toml:"duration-stop" yaml:"duration-stop" default:"close"`
X Duration `json:"max-duration" toml:"max-duration" yaml:"max-duration"`
Timeout Duration `json:"timeout" toml:"timeout" yaml:"timeout" default:"20s"`
Data interface{} `json:"data,omitempty" toml:"data,omitempty" yaml:"data,omitempty"`
DataPath string `json:"data-file" toml:"data-file" yaml:"data-file"`
BinData []byte `json:"-" toml:"-" yaml:"-"`
BinDataPath string `json:"binary-file" toml:"binary-file" yaml:"binary-file"`
Metadata map[string]string `json:"metadata,omitempty" toml:"metadata,omitempty" yaml:"metadata,omitempty"`
MetadataPath string `json:"metadata-file" toml:"metadata-file" yaml:"metadata-file"`
SI Duration `json:"stream-interval" toml:"stream-interval" yaml:"stream-interval"`
Output string `json:"output" toml:"output" yaml:"output"`
Format string `json:"format" toml:"format" yaml:"format" default:"summary"`
DialTimeout Duration `json:"connect-timeout" toml:"connect-timeout" yaml:"connect-timeout" default:"10s"`
KeepaliveTime Duration `json:"keepalive" toml:"keepalive" yaml:"keepalive"`
CPUs uint `json:"cpus" toml:"cpus" yaml:"cpus"`
ImportPaths []string `json:"import-paths,omitempty" toml:"import-paths,omitempty" yaml:"import-paths,omitempty"`
Name string `json:"name,omitempty" toml:"name,omitempty" yaml:"name,omitempty"`
Tags map[string]string `json:"tags,omitempty" toml:"tags,omitempty" yaml:"tags,omitempty"`
ReflectMetadata map[string]string `json:"reflect-metadata,omitempty" toml:"reflect-metadata,omitempty" yaml:"reflect-metadata,omitempty"`
Debug string `json:"debug,omitempty" toml:"debug,omitempty" yaml:"debug,omitempty"`
Host string `json:"host" toml:"host" yaml:"host"`
EnableCompression bool `json:"enable-compression,omitempty" toml:"enable-compression,omitempty" yaml:"enable-compression,omitempty"`
}

func checkData(data interface{}) error {
Expand All @@ -178,3 +116,43 @@ func checkData(data interface{}) error {

return nil
}

func loadConfig(p string, c *Config) error {
err := configor.Load(c, p)
if err != nil {
return err
}

if c.Data != nil {
ext := path.Ext(p)
if strings.EqualFold(ext, ".yaml") || strings.EqualFold(ext, ".yml") {
objData, isObjData2 := c.Data.(map[interface{}]interface{})
if isObjData2 {
nd := make(map[string]interface{})
for k, v := range objData {
sk, isString := k.(string)
if !isString {
return errors.New("Data key must string")
}
if len(sk) > 0 {
nd[sk] = v
}
}

c.Data = nd
}
}

err := checkData(c.Data)
if err != nil {
return err
}
}

c.ZStop = strings.ToLower(c.ZStop)
if c.ZStop != "close" && c.ZStop != "ignore" && c.ZStop != "wait" {
c.ZStop = "close"
}

return nil
}
Loading

0 comments on commit f4b2141

Please sign in to comment.