Skip to content

Commit

Permalink
*: init runner sub-command.
Browse files Browse the repository at this point in the history
Signed-off-by: Wei Fu <weifu@microsoft.com>
  • Loading branch information
fuweid committed Dec 7, 2023
1 parent 14a255d commit 6bbdc59
Show file tree
Hide file tree
Showing 3 changed files with 275 additions and 28 deletions.
142 changes: 115 additions & 27 deletions cmd/kperf/commands/runner/runner.go
Original file line number Diff line number Diff line change
@@ -1,40 +1,128 @@
package runner

import (
"context"
"fmt"
"os"
"sort"

"github.com/Azure/kperf/api/types"
"github.com/Azure/kperf/request"

"github.com/urfave/cli"
"gopkg.in/yaml.v2"
)

// Command represents runner sub-command.
//
// Subcommand runner is to create request load to apiserver.
//
// NOTE: It can work with subcommand multirunners. The multirunners subcommand
// will deploy subcommand runner in pod. Details in ../multirunners.
//
// Command line interface:
//
// kperf runner --help
//
// Options:
//
// --kubeconfig PATH (default: empty_string, use token if it's empty)
// --load-config PATH (default: empty_string, required, the config defined in api/types/load_traffic.go)
// --conns INT (default: 1, Total number of connections. It can override corresponding value defined by --load-config)
// --rate INT (default: 0, Maximum requests per second. It can override corresponding value defined by --load-config)
// --total INT (default: 1000, Total number of request. It can override corresponding value defined by --load-config)
// Command represents runner subcommand.
var Command = cli.Command{
Name: "runner",
Usage: "run a load test to kube-apiserver",
Flags: []cli.Flag{},
Usage: "Setup benchmark to kube-apiserver from one endpoint",
Subcommands: []cli.Command{
runCommand,
},
}

var runCommand = cli.Command{
Name: "run",
Usage: "run a benchmark test to kube-apiserver",
Flags: []cli.Flag{
cli.StringFlag{
Name: "kubeconfig",
Usage: "Path to the kubeconfig file",
},
cli.StringFlag{
Name: "config",
Usage: "Path to the configuration file",
Required: true,
},
cli.IntFlag{
Name: "conns",
Usage: "Total number of connections. It can override corresponding value defined by --config",
Value: 1,
},
cli.IntFlag{
Name: "rate",
Usage: "Maximum requests per second (Zero means no limitation). It can override corresponding value defined by --config",
},
cli.IntFlag{
Name: "total",
Usage: "Total number of requests. It can override corresponding value defined by --config",
Value: 1000,
},
cli.StringFlag{
Name: "user-agent",
Usage: "User Agent",
},
},
Action: func(cliCtx *cli.Context) error {
// 1. Parse options
// 2. Setup producer-consumer goroutines
// 2.1 Use go limter to generate request
// 2.2 Use client-go's client to file requests
// 3. Build progress tracker to track failure number and P99/P95/P90 latencies.
// 4. Export summary in stdout.
return fmt.Errorf("runner - not implemented")
profileCfg, err := loadConfig(cliCtx)
if err != nil {
return err
}

kubeCfgPath := cliCtx.String("kubeconfig")
userAgent := cliCtx.String("user-agent")

conns := profileCfg.Spec.Conns
rate := profileCfg.Spec.Rate
restClis, err := request.NewClients(kubeCfgPath, conns, userAgent, rate)
if err != nil {
return err
}

stats, err := request.Schedule(context.TODO(), &profileCfg.Spec, restClis)
if err != nil {
return err
}
printResponseStats(stats)
return nil
},
}

// loadConfig loads and validates the config.
func loadConfig(cliCtx *cli.Context) (*types.LoadProfile, error) {
var profileCfg types.LoadProfile

cfgPath := cliCtx.String("config")

cfgInRaw, err := os.ReadFile(cfgPath)
if err != nil {
return nil, fmt.Errorf("failed to read file %s: %w", cfgPath, err)
}

if err := yaml.Unmarshal(cfgInRaw, &profileCfg); err != nil {
return nil, fmt.Errorf("failed to unmarshal %s from yaml format: %w", cfgPath, err)
}

// override value by flags
//
// TODO(weifu): do not override if flag is not set
profileCfg.Spec.Rate = cliCtx.Int("rate")
profileCfg.Spec.Conns = cliCtx.Int("conns")
profileCfg.Spec.Total = cliCtx.Int("total")

if err := profileCfg.Validate(); err != nil {
return nil, err
}
return &profileCfg, nil
}

// printResponseStats prints ResponseStats into stdout.
func printResponseStats(stats *types.ResponseStats) {
fmt.Println("Response stat:")
fmt.Printf(" Total: %v\n", stats.Total)
fmt.Printf(" Failures: %v\n", stats.Failures)
fmt.Printf(" Duration: %v\n", stats.Duration)
fmt.Printf(" Requests/sec: %.2f\n", float64(stats.Total)/stats.Duration.Seconds())

fmt.Println(" Latency Distribution:")
keys := make([]float64, 0, len(stats.Latencies))
for q := range stats.Latencies {
keys = append(keys, q)
}
sort.Float64s(keys)

for _, q := range keys {
fmt.Printf(" [%.2f] %.3fs\n", q, stats.Latencies[q])
}
}
34 changes: 34 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,49 @@ module github.com/Azure/kperf
go 1.20

require (
github.com/prometheus/client_golang v1.17.0
github.com/stretchr/testify v1.8.4
github.com/urfave/cli v1.22.14
golang.org/x/time v0.3.0
gopkg.in/yaml.v2 v2.4.0
k8s.io/apimachinery v0.28.4
k8s.io/client-go v0.28.4
k8s.io/kubectl v0.28.4
)

require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/imdario/mergo v0.3.6 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16 // indirect
github.com/prometheus/common v0.44.0 // indirect
github.com/prometheus/procfs v0.11.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/oauth2 v0.8.0 // indirect
golang.org/x/sys v0.13.0 // indirect
golang.org/x/term v0.13.0 // indirect
golang.org/x/text v0.13.0 // indirect
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.31.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.28.4 // indirect
k8s.io/klog/v2 v2.100.1 // indirect
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
Loading

0 comments on commit 6bbdc59

Please sign in to comment.