Skip to content

Commit

Permalink
Merge pull request #35 from seipan/refactor/cmd-parseAttack-method
Browse files Browse the repository at this point in the history
refacrtor : cmd ParseandAttack move to lib directory
  • Loading branch information
seipan committed Jun 23, 2024
2 parents cb403d9 + 5b62f6a commit 4c0bfdc
Show file tree
Hide file tree
Showing 9 changed files with 204 additions and 165 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
name: goreleaser

on:
pull_request:
push:
tags:
- "*"
- "v*"

permissions:
contents: write
Expand Down
118 changes: 0 additions & 118 deletions cmd/attack.go

This file was deleted.

3 changes: 2 additions & 1 deletion cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"log"
"os"

"github.com/seipan/bulma/lib"
"github.com/spf13/cobra"
)

Expand Down Expand Up @@ -54,7 +55,7 @@ var rootCmd = &cobra.Command{
if err != nil {
log.Println(err)
}
err = ParseAndAttack(cmd.Context(), ignore, base, path, freq, duration)
err = lib.ParseAndAttack(cmd.Context(), ignore, base, path, freq, duration)
if err != nil {
log.Println(err)
}
Expand Down
96 changes: 82 additions & 14 deletions lib/attack.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,95 @@
package lib

import (
"context"
"encoding/json"
"fmt"
"net/http"
"time"

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

func (atk *Attacker) Attack() vegeta.Metrics {
target := vegeta.Target{
Method: atk.Path.method[atk.MethodIndex].method,
URL: atk.Path.path,
Body: atk.Body,
Header: atk.Header,
func ParseAndAttack(ctx context.Context, ignore []string, beseEndpoint string, path string, freq int, duration time.Duration) error {
oapi := NewOpenAPI(path)
paths, err := oapi.Parse(ctx)
if err != nil {
return fmt.Errorf("failed to parse openapi: %w", err)
}
targeter := vegeta.NewStaticTargeter(target)
rate := vegeta.Rate{Freq: atk.Frequency, Per: time.Second}

attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, atk.Duration, "Vegeta Load Testing") {
metrics.Add(res)
ignores := NewIgnore(ignore)

atks, err := ParthOpenAPItoAttacker(paths, ignores, beseEndpoint, freq, duration)
if err != nil {
return fmt.Errorf("failed to convert openapi to attacker: %w", err)
}
fmt.Println("--------------------------bulma attack start-------------------------------")
for _, atk := range atks {
metrics := atk.Attack()
outputMetrics(metrics, &atk)
}
fmt.Println("--------------------------bulma attack finish-------------------------------")
return nil
}

func ParthOpenAPItoAttacker(pathes []Path, ignores Ignore, beseEndpoint string, freq int, duration time.Duration) ([]Attacker, error) {
var res []Attacker
for i, path := range pathes {
if ignores.IsIgnored(path.Path()) {
continue
}

mtd := path.Method(0)
bodys := mtd.Bodys()
body, err := createBody(bodys)
if err != nil {
return nil, err
}
path.SetPath(beseEndpoint + path.Path())
atk := Attacker{
Path: path,
MethodIndex: i,
Body: body,
Header: http.Header{
"Content-Type": []string{"application/json"},
},
Frequency: freq,
Duration: duration,
}
res = append(res, atk)
}
return res, nil
}

func createBody(bodys []Body) ([]byte, error) {
mp := make(map[string]interface{})
for _, body := range bodys {
mp[body.Name] = body.Shema.Value.Example
}
jsonData, err := json.Marshal(mp)
if err != nil {
return nil, err
}
metrics.Close()
return jsonData, nil
}

func outputMetrics(metrics vegeta.Metrics, atk *Attacker) {
fmt.Printf("--------------------------vegeta attack to %s--------------------------\n", atk.Path.Path())
mtd := atk.Path.Method(atk.MethodIndex)
fmt.Printf("vegeta attack to method: %s\n", mtd.Method())
fmt.Printf("path StatusCode: %v\n", metrics.StatusCodes)

fmt.Println()

fmt.Printf("max percentile: %s\n", metrics.Latencies.Max)
fmt.Printf("mean percentile: %s\n", metrics.Latencies.Mean)
fmt.Printf("total percentile: %s\n", metrics.Latencies.Total)
fmt.Printf("99th percentile: %s\n", metrics.Latencies.P99)

fmt.Println()

fmt.Printf(" earliest: %v\n", metrics.Earliest)
fmt.Printf(" latest: %v\n", metrics.Latest)

return metrics
fmt.Println("-----------------------------------------------------------------------")
}
30 changes: 0 additions & 30 deletions lib/attack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,33 +21,3 @@
// SOFTWARE.

package lib

import (
"fmt"
"testing"
)

func TestAttack(t *testing.T) {
t.Run("Attack", func(t *testing.T) {
atk := Attacker{
Path: Path{
path: "http://localhost:8080/health",
method: []Method{
{
method: "GET",
},
},
},
MethodIndex: 0,
Frequency: 110,
Duration: 2,
}
metrics := atk.Attack()
fmt.Printf("99th percentile: %s\n", metrics.Latencies.P99)
fmt.Printf("max percentile: %s\n", metrics.Latencies.Max)
fmt.Printf("mean percentile: %s\n", metrics.Latencies.Mean)
fmt.Printf("total percentile: %s\n", metrics.Latencies.Total)

fmt.Printf("status code: %v\n", metrics.StatusCodes)
})
}
22 changes: 22 additions & 0 deletions lib/vegeta.go → lib/attacker.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ package lib
import (
"net/http"
"time"

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

type Attacker struct {
Expand All @@ -35,3 +37,23 @@ type Attacker struct {
Frequency int
Duration time.Duration
}

func (atk *Attacker) Attack() vegeta.Metrics {
target := vegeta.Target{
Method: atk.Path.method[atk.MethodIndex].method,
URL: atk.Path.path,
Body: atk.Body,
Header: atk.Header,
}
targeter := vegeta.NewStaticTargeter(target)
rate := vegeta.Rate{Freq: atk.Frequency, Per: time.Second}

attacker := vegeta.NewAttacker()
var metrics vegeta.Metrics
for res := range attacker.Attack(targeter, rate, atk.Duration, "Vegeta Load Testing") {
metrics.Add(res)
}
metrics.Close()

return metrics
}
53 changes: 53 additions & 0 deletions lib/attacker_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// MIT License

// Copyright (c) 2023 Yamasaki Shotaro

// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package lib

import (
"fmt"
"testing"
)

func TestAttack(t *testing.T) {
t.Run("Attack", func(t *testing.T) {
atk := Attacker{
Path: Path{
path: "http://localhost:8080/health",
method: []Method{
{
method: "GET",
},
},
},
MethodIndex: 0,
Frequency: 110,
Duration: 2,
}
metrics := atk.Attack()
fmt.Printf("99th percentile: %s\n", metrics.Latencies.P99)
fmt.Printf("max percentile: %s\n", metrics.Latencies.Max)
fmt.Printf("mean percentile: %s\n", metrics.Latencies.Mean)
fmt.Printf("total percentile: %s\n", metrics.Latencies.Total)

fmt.Printf("status code: %v\n", metrics.StatusCodes)
})
}
Loading

0 comments on commit 4c0bfdc

Please sign in to comment.