From 2654585f068498c3d5c0453639c26ea3726a930f Mon Sep 17 00:00:00 2001 From: Torubarov Timur Date: Fri, 12 Apr 2019 19:12:04 +0300 Subject: [PATCH 1/3] websocket gun sample added to docs --- docs/custom.rst | 235 +++++++++++++++++++++----- examples/custom_pandora/websockets.go | 129 ++++++++++++++ 2 files changed, 321 insertions(+), 43 deletions(-) create mode 100644 examples/custom_pandora/websockets.go diff --git a/docs/custom.rst b/docs/custom.rst index 2ff435e98..325881565 100644 --- a/docs/custom.rst +++ b/docs/custom.rst @@ -1,15 +1,62 @@ Custom guns =========== + +Basic tutorial +-------------- You can create you own Golang-based gun with `pandora`. There is an example of custom gun shooting via gRPC. We create a new gun and define ```shoot``` method for it w/ our test logic. +You can find examples of custom guns' code below: + - `gRPC <#gRPC>`_ + - `Websockets <#Websockets>`_ -.. code-block:: go +Now it's time to compile our gun. Install deps and compile your custom gun file (```go build my_custom_gun.go```). + +After that step you'll get ```my_custom_gun``` binary file, it is compiled pandora with your custom gun inside. + +Now its time to create ```load.yaml```: + +.. code-block:: yaml + pools: + - id: HTTP pool + gun: + type: my_custom_gun_name # custom gun name (specified at `register.Gun("my_custom_gun_name", ...`) + target: "your_grpc_host:your_grpc_port" + ammo: + type: custom_provider + source: + type: file + path: ./json.ammo + result: + type: phout + destination: ./phout.log + rps: {duration: 30s, type: line, from: 1, to: 2} + startup: + type: once + times: 10 + log: + level: error + +And create ammofile ```./json.ammo```: + +.. code-block:: yaml + + {"tag": "/MyCase1", "Param1": "146837693,146837692,146837691"} + {"tag": "/MyCase2", "Param2": "555", "Param1": "500002"} + + +We are ready to shoot. Try it. + + +gRPC +---- + +.. code-block:: go // create a package package main @@ -18,22 +65,24 @@ We create a new gun and define ```shoot``` method for it w/ our test logic. // and protobuf contracts for your grpc service import ( + "log" + "context" + "strconv" + "strings" + "time" + "github.com/golang/protobuf/ptypes/timestamp" "github.com/satori/go.uuid" "github.com/spf13/afero" + "google.golang.org/grpc" + pb "my_package/my_protobuf_contracts" + "github.com/yandex/pandora/cli" "github.com/yandex/pandora/components/phttp/import" "github.com/yandex/pandora/core" "github.com/yandex/pandora/core/aggregator/netsample" "github.com/yandex/pandora/core/import" "github.com/yandex/pandora/core/register" - "google.golang.org/grpc" - "log" - "context" - "strconv" - "strings" - "time" - pb "my_package/my_contracts" ) type Ammo struct { @@ -191,7 +240,7 @@ We create a new gun and define ```shoot``` method for it w/ our test logic. // Custom imports. Integrate your custom types into configuration system. coreimport.RegisterCustomJSONProvider("custom_provider", func() core.Ammo { return &Ammo{} }) - register.Gun("My_custom_gun_name", NewGun, func() GunConfig { + register.Gun("my_custom_gun_name", NewGun, func() GunConfig { return GunConfig{ Target: "default target", } @@ -200,39 +249,139 @@ We create a new gun and define ```shoot``` method for it w/ our test logic. cli.Run() } -Now it's time to compile our gun. Install deps and compile your custom gun file (```go build my_custom_gun.go```). -After that step you'll get ```my_custom_gun``` binary file, it is compiled pandora with your custom gun inside. - -Now its time to create ```load.yaml```: - -.. code-block:: yaml - - pools: - - id: HTTP pool - gun: - type: My_custom_gun_name # custom gun name specified - target: "your_grpc_host:your_grpc_port" - ammo: - type: custom_provider - source: - type: file - path: ./json.ammo - result: - type: phout - destination: ./phout.log - rps: {duration: 30s, type: line, from: 1, to: 2} - startup: - type: once - times: 10 - log: - level: error - -And create ammofile ```./json.ammo```: -.. code-block:: yaml +Websockets +---------- - {"tag": "/MyCase1", "Param1": "146837693,146837692,146837691"} - {"tag": "/MyCase2", "Param2": "555", "Param1": "500002"} - - -We are ready to shoot. Try it. +.. code-block:: go + package main + + import ( + "bytes" + "encoding/json" + "io/ioutil" + "log" + "math/rand" + "mime/multipart" + "net/http" + "net/url" + "strconv" + "time" + + "github.com/gorilla/websocket" + "github.com/spf13/afero" + + "github.com/yandex/pandora/cli" + "github.com/yandex/pandora/components/phttp/import" + "github.com/yandex/pandora/core" + "github.com/yandex/pandora/core/aggregator/netsample" + "github.com/yandex/pandora/core/import" + "github.com/yandex/pandora/core/register" + ) + + type Ammo struct { + Tag string + } + + type Sample struct { + URL string + ShootTimeSeconds float64 + } + + type GunConfig struct { + Target string `validate:"required"` + Handler string `validate:"required"`// Configuration will fail, without target defined + } + + type Gun struct { + // Configured on construction. + client websocket.Conn + conf GunConfig + // Configured on Bind, before shooting + aggr core.Aggregator // May be your custom Aggregator. + core.GunDeps + } + + func NewGun(conf GunConfig) *Gun { + return &Gun{conf: conf} + } + + func (g *Gun) Bind(aggr core.Aggregator, deps core.GunDeps) error { + targetPath := url.URL{Scheme: "ws", Host: g.conf.Target, Path: g.conf.Handler} + sample := netsample.Acquire("connection") + code := 0 + rand.Seed(time.Now().Unix()) + conn, _, err := websocket.DefaultDialer.Dial( + targetPath.String(), + nil, + ) + if err != nil { + log.Fatalf("dial err FATAL %s:", err) + code = 500 + } else { + code = 200 + } + g.client = *conn + g.aggr = aggr + g.GunDeps = deps + defer func() { + sample.SetProtoCode(code) + g.aggr.Report(sample) + }() + + go func() { + for { + _, message, err := conn.ReadMessage() + if err != nil { + log.Println("read:", err) + code = 400 + return + } + log.Printf("recv: %s", message) + } + }() + + err = conn.WriteMessage(websocket.TextMessage, []byte("some websocket connection initialization text, e.g. token")) + if err != nil { + log.Println("write:", err) + } + return nil + } + + func (g *Gun) Shoot(ammo core.Ammo) { + sample := netsample.Acquire("message") + code := 0 + conn := g.client + err := conn.WriteMessage(websocket.TextMessage, []byte("test_message")) + if err != nil { + log.Println("connection closed", err) + code = 600 + } else { + code = 200 + } + defer func() { + sample.SetProtoCode(code) + g.aggr.Report(sample) + }() + + } + + func main() { + //debug.SetGCPercent(-1) + // Standard imports. + fs := afero.NewOsFs() + coreimport.Import(fs) + // May not be imported, if you don't need http guns and etc. + phttp.Import(fs) + + // Custom imports. Integrate your custom types into configuration system. + coreimport.RegisterCustomJSONProvider("ammo_provider", func() core.Ammo { return &Ammo{} }) + + register.Gun("my_custom_gun_name", NewGun, func() GunConfig { + return GunConfig{ + Target: "default target", + } + }) + + cli.Run() + } diff --git a/examples/custom_pandora/websockets.go b/examples/custom_pandora/websockets.go new file mode 100644 index 000000000..cb1c64fd9 --- /dev/null +++ b/examples/custom_pandora/websockets.go @@ -0,0 +1,129 @@ +package main + +import ( + "bytes" + "encoding/json" + "github.com/gorilla/websocket" + "github.com/spf13/afero" + "github.com/yandex/pandora/cli" + "github.com/yandex/pandora/components/phttp/import" + "github.com/yandex/pandora/core" + "github.com/yandex/pandora/core/aggregator/netsample" + "github.com/yandex/pandora/core/import" + "github.com/yandex/pandora/core/register" + "io/ioutil" + "log" + "math/rand" + "mime/multipart" + "net/http" + "net/url" + "strconv" + "time" +) + +type Ammo struct { + Tag string +} + +type Sample struct { + URL string + ShootTimeSeconds float64 +} + +type GunConfig struct { + Target string `validate:"required"` + Handler string `validate:"required"`// Configuration will fail, without target defined +} + +type Gun struct { + // Configured on construction. + client websocket.Conn + conf GunConfig + // Configured on Bind, before shooting + aggr core.Aggregator // May be your custom Aggregator. + core.GunDeps +} + +func NewGun(conf GunConfig) *Gun { + return &Gun{conf: conf} +} + +func (g *Gun) Bind(aggr core.Aggregator, deps core.GunDeps) error { + targetPath := url.URL{Scheme: "ws", Host: g.conf.Target, Path: g.conf.Handler} + sample := netsample.Acquire("connection") + code := 0 + rand.Seed(time.Now().Unix()) + conn, _, err := websocket.DefaultDialer.Dial( + targetPath.String(), + nil, + ) + if err != nil { + log.Fatalf("dial err FATAL %s:", err) + code = 500 + } else { + code = 200 + } + g.client = *conn + g.aggr = aggr + g.GunDeps = deps + defer func() { + sample.SetProtoCode(code) + g.aggr.Report(sample) + }() + + go func() { + for { + _, message, err := conn.ReadMessage() + if err != nil { + log.Println("read:", err) + code = 400 + return + } + log.Printf("recv: %s", message) + } + }() + + err = conn.WriteMessage(websocket.TextMessage, []byte("some websocket connection initialization text, e.g. token")) + if err != nil { + log.Println("write:", err) + } + return nil +} + +func (g *Gun) Shoot(ammo core.Ammo) { + sample := netsample.Acquire("message") + code := 0 + conn := g.client + err := conn.WriteMessage(websocket.TextMessage, []byte("test_message")) + if err != nil { + log.Println("connection closed", err) + code = 600 + } else { + code = 200 + } + defer func() { + sample.SetProtoCode(code) + g.aggr.Report(sample) + }() + +} + +func main() { + //debug.SetGCPercent(-1) + // Standard imports. + fs := afero.NewOsFs() + coreimport.Import(fs) + // May not be imported, if you don't need http guns and etc. + phttp.Import(fs) + + // Custom imports. Integrate your custom types into configuration system. + coreimport.RegisterCustomJSONProvider("ammo_provider", func() core.Ammo { return &Ammo{} }) + + register.Gun("websocketGun", NewGun, func() GunConfig { + return GunConfig{ + Target: "default target", + } + }) + + cli.Run() +} \ No newline at end of file From 8f245755db8350a511ec9ff1aef8d719332674fa Mon Sep 17 00:00:00 2001 From: Torubarov Timur Date: Fri, 12 Apr 2019 19:12:54 +0300 Subject: [PATCH 2/3] cleanup --- examples/custom_pandora/websockets.go | 129 -------------------------- 1 file changed, 129 deletions(-) delete mode 100644 examples/custom_pandora/websockets.go diff --git a/examples/custom_pandora/websockets.go b/examples/custom_pandora/websockets.go deleted file mode 100644 index cb1c64fd9..000000000 --- a/examples/custom_pandora/websockets.go +++ /dev/null @@ -1,129 +0,0 @@ -package main - -import ( - "bytes" - "encoding/json" - "github.com/gorilla/websocket" - "github.com/spf13/afero" - "github.com/yandex/pandora/cli" - "github.com/yandex/pandora/components/phttp/import" - "github.com/yandex/pandora/core" - "github.com/yandex/pandora/core/aggregator/netsample" - "github.com/yandex/pandora/core/import" - "github.com/yandex/pandora/core/register" - "io/ioutil" - "log" - "math/rand" - "mime/multipart" - "net/http" - "net/url" - "strconv" - "time" -) - -type Ammo struct { - Tag string -} - -type Sample struct { - URL string - ShootTimeSeconds float64 -} - -type GunConfig struct { - Target string `validate:"required"` - Handler string `validate:"required"`// Configuration will fail, without target defined -} - -type Gun struct { - // Configured on construction. - client websocket.Conn - conf GunConfig - // Configured on Bind, before shooting - aggr core.Aggregator // May be your custom Aggregator. - core.GunDeps -} - -func NewGun(conf GunConfig) *Gun { - return &Gun{conf: conf} -} - -func (g *Gun) Bind(aggr core.Aggregator, deps core.GunDeps) error { - targetPath := url.URL{Scheme: "ws", Host: g.conf.Target, Path: g.conf.Handler} - sample := netsample.Acquire("connection") - code := 0 - rand.Seed(time.Now().Unix()) - conn, _, err := websocket.DefaultDialer.Dial( - targetPath.String(), - nil, - ) - if err != nil { - log.Fatalf("dial err FATAL %s:", err) - code = 500 - } else { - code = 200 - } - g.client = *conn - g.aggr = aggr - g.GunDeps = deps - defer func() { - sample.SetProtoCode(code) - g.aggr.Report(sample) - }() - - go func() { - for { - _, message, err := conn.ReadMessage() - if err != nil { - log.Println("read:", err) - code = 400 - return - } - log.Printf("recv: %s", message) - } - }() - - err = conn.WriteMessage(websocket.TextMessage, []byte("some websocket connection initialization text, e.g. token")) - if err != nil { - log.Println("write:", err) - } - return nil -} - -func (g *Gun) Shoot(ammo core.Ammo) { - sample := netsample.Acquire("message") - code := 0 - conn := g.client - err := conn.WriteMessage(websocket.TextMessage, []byte("test_message")) - if err != nil { - log.Println("connection closed", err) - code = 600 - } else { - code = 200 - } - defer func() { - sample.SetProtoCode(code) - g.aggr.Report(sample) - }() - -} - -func main() { - //debug.SetGCPercent(-1) - // Standard imports. - fs := afero.NewOsFs() - coreimport.Import(fs) - // May not be imported, if you don't need http guns and etc. - phttp.Import(fs) - - // Custom imports. Integrate your custom types into configuration system. - coreimport.RegisterCustomJSONProvider("ammo_provider", func() core.Ammo { return &Ammo{} }) - - register.Gun("websocketGun", NewGun, func() GunConfig { - return GunConfig{ - Target: "default target", - } - }) - - cli.Run() -} \ No newline at end of file From 4ad1e32ceb6fc6ddf90747cf3cba91012cbf9daa Mon Sep 17 00:00:00 2001 From: Torubarov Timur Date: Fri, 12 Apr 2019 19:13:48 +0300 Subject: [PATCH 3/3] docs codeblocks for custom guns bugfixes --- docs/custom.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/custom.rst b/docs/custom.rst index 325881565..c72c1a537 100644 --- a/docs/custom.rst +++ b/docs/custom.rst @@ -57,6 +57,7 @@ gRPC ---- .. code-block:: go + // create a package package main @@ -254,6 +255,7 @@ Websockets ---------- .. code-block:: go + package main import (