From 812d8cb543b31ca258712e6ef0414e947d342866 Mon Sep 17 00:00:00 2001 From: Liyu Ma Date: Tue, 3 Mar 2026 17:33:07 +1000 Subject: [PATCH 1/4] Allow configurable qps/burst for data configmap --- .../commands/data/configmaps/configmap.go | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go index e4c70f30..08b50c8d 100644 --- a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go +++ b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go @@ -44,6 +44,16 @@ var Command = cli.Command{ Usage: "Namespace to use with commands. If the namespace does not exist, it will be created.", Value: "default", }, + cli.Float64Flag{ + Name: "qps", + Usage: "QPS for the Kubernetes client rate limiter to control configmap operations", + Value: 30, + }, + cli.IntFlag{ + Name: "burst", + Usage: "Burst for the Kubernetes client rate limiter to control configmap operations", + Value: 10, + }, }, Subcommands: []cli.Command{ configmapAddCommand, @@ -94,12 +104,15 @@ var configmapAddCommand = cli.Command{ } namespace := cliCtx.GlobalString("namespace") - err = prepareNamespace(kubeCfgPath, namespace) + qps := float32(cliCtx.GlobalFloat64("qps")) + burst := cliCtx.GlobalInt("burst") + + clientset, err := newClientsetWithRateLimiter(kubeCfgPath, qps, burst) if err != nil { return err } - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, 30, 10) + err = prepareNamespace(clientset, namespace) if err != nil { return err } @@ -129,9 +142,11 @@ var configmapDelCommand = cli.Command{ namespace := cliCtx.GlobalString("namespace") kubeCfgPath := cliCtx.GlobalString("kubeconfig") + qps := float32(cliCtx.GlobalFloat64("qps")) + burst := cliCtx.GlobalInt("burst") labelSelector := fmt.Sprintf("app=%s,cmName=%s", appLebel, cmName) - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, 30, 10) + clientset, err := newClientsetWithRateLimiter(kubeCfgPath, qps, burst) if err != nil { return err } @@ -155,7 +170,9 @@ var configmapListCommand = cli.Command{ Action: func(cliCtx *cli.Context) error { namespace := cliCtx.GlobalString("namespace") kubeCfgPath := cliCtx.GlobalString("kubeconfig") - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, 30, 10) + qps := float32(cliCtx.GlobalFloat64("qps")) + burst := cliCtx.GlobalInt("burst") + clientset, err := newClientsetWithRateLimiter(kubeCfgPath, qps, burst) if err != nil { return err } @@ -201,7 +218,7 @@ var configmapListCommand = cli.Command{ }, } -func prepareNamespace(kubeCfgPath string, namespace string) error { +func prepareNamespace(clientset *kubernetes.Clientset, namespace string) error { if namespace == "" { return fmt.Errorf("namespace cannot be empty") } @@ -210,12 +227,7 @@ func prepareNamespace(kubeCfgPath string, namespace string) error { return nil } - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, 30, 10) - if err != nil { - return err - } - - _, err = clientset.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ + _, err := clientset.CoreV1().Namespaces().Create(context.TODO(), &corev1.Namespace{ ObjectMeta: metav1.ObjectMeta{ Name: namespace, }, From 38e1a73c2509c96386c4153ba4ff40aa8c8f6616 Mon Sep 17 00:00:00 2001 From: Liyu Ma Date: Wed, 4 Mar 2026 10:27:35 +1000 Subject: [PATCH 2/4] Update default qps/burst for data configmap --- contrib/cmd/runkperf/commands/data/configmaps/configmap.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go index 08b50c8d..c45c9de8 100644 --- a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go +++ b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go @@ -47,12 +47,12 @@ var Command = cli.Command{ cli.Float64Flag{ Name: "qps", Usage: "QPS for the Kubernetes client rate limiter to control configmap operations", - Value: 30, + Value: 500, }, cli.IntFlag{ Name: "burst", Usage: "Burst for the Kubernetes client rate limiter to control configmap operations", - Value: 10, + Value: 1000, }, }, Subcommands: []cli.Command{ From d740246349e2320960da9ca0af0a29fd001af00c Mon Sep 17 00:00:00 2001 From: Liyu Ma Date: Wed, 4 Mar 2026 11:31:32 +1000 Subject: [PATCH 3/4] Use default qps/burst for delete/list --- contrib/cmd/runkperf/commands/data/client.go | 31 +++++++++++++++++++ .../commands/data/configmaps/configmap.go | 28 +++-------------- contrib/cmd/runkperf/commands/data/root.go | 14 ++++----- contrib/cmd/runkperf/commands/root.go | 9 ++++++ 4 files changed, 51 insertions(+), 31 deletions(-) create mode 100644 contrib/cmd/runkperf/commands/data/client.go diff --git a/contrib/cmd/runkperf/commands/data/client.go b/contrib/cmd/runkperf/commands/data/client.go new file mode 100644 index 00000000..47e6df2b --- /dev/null +++ b/contrib/cmd/runkperf/commands/data/client.go @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +package data + +import ( + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/flowcontrol" +) + +// NewClientset creates a Kubernetes clientset with default rate limiting. +func NewClientset(kubeCfgPath string) (*kubernetes.Clientset, error) { + config, err := clientcmd.BuildConfigFromFlags("", kubeCfgPath) + if err != nil { + return nil, err + } + return kubernetes.NewForConfig(config) +} + +// NewClientsetWithRateLimiter creates a Kubernetes clientset with custom QPS and burst rate limiting. +func NewClientsetWithRateLimiter(kubeCfgPath string, qps float32, burst int) (*kubernetes.Clientset, error) { + config, err := clientcmd.BuildConfigFromFlags("", kubeCfgPath) + if err != nil { + return nil, err + } + + config.QPS = qps + config.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst) + return kubernetes.NewForConfig(config) +} diff --git a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go index c45c9de8..ac65c358 100644 --- a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go +++ b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go @@ -16,8 +16,7 @@ import ( "golang.org/x/sync/errgroup" "github.com/Azure/kperf/cmd/kperf/commands/utils" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/flowcontrol" + "github.com/Azure/kperf/contrib/cmd/runkperf/commands/data" "github.com/urfave/cli" @@ -107,7 +106,7 @@ var configmapAddCommand = cli.Command{ qps := float32(cliCtx.GlobalFloat64("qps")) burst := cliCtx.GlobalInt("burst") - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, qps, burst) + clientset, err := data.NewClientsetWithRateLimiter(kubeCfgPath, qps, burst) if err != nil { return err } @@ -142,11 +141,9 @@ var configmapDelCommand = cli.Command{ namespace := cliCtx.GlobalString("namespace") kubeCfgPath := cliCtx.GlobalString("kubeconfig") - qps := float32(cliCtx.GlobalFloat64("qps")) - burst := cliCtx.GlobalInt("burst") labelSelector := fmt.Sprintf("app=%s,cmName=%s", appLebel, cmName) - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, qps, burst) + clientset, err := data.NewClientset(kubeCfgPath) if err != nil { return err } @@ -170,9 +167,7 @@ var configmapListCommand = cli.Command{ Action: func(cliCtx *cli.Context) error { namespace := cliCtx.GlobalString("namespace") kubeCfgPath := cliCtx.GlobalString("kubeconfig") - qps := float32(cliCtx.GlobalFloat64("qps")) - burst := cliCtx.GlobalInt("burst") - clientset, err := newClientsetWithRateLimiter(kubeCfgPath, qps, burst) + clientset, err := data.NewClientset(kubeCfgPath) if err != nil { return err } @@ -258,21 +253,6 @@ func checkConfigmapParams(size int, groupSize int, total int) error { return nil } -func newClientsetWithRateLimiter(kubeCfgPath string, qps float32, burst int) (*kubernetes.Clientset, error) { - config, err := clientcmd.BuildConfigFromFlags("", kubeCfgPath) - if err != nil { - return nil, err - } - - config.QPS = qps - config.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(qps, burst) - clientset, err := kubernetes.NewForConfig(config) - if err != nil { - return nil, err - } - return clientset, nil -} - var letterRunes = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") func randString(n int) (string, error) { diff --git a/contrib/cmd/runkperf/commands/data/root.go b/contrib/cmd/runkperf/commands/data/root.go index d1cbc5a1..b24c893d 100644 --- a/contrib/cmd/runkperf/commands/data/root.go +++ b/contrib/cmd/runkperf/commands/data/root.go @@ -4,17 +4,17 @@ package data import ( - "github.com/Azure/kperf/contrib/cmd/runkperf/commands/data/configmaps" - "github.com/Azure/kperf/contrib/cmd/runkperf/commands/data/daemonsets" - "github.com/urfave/cli" ) var Command = cli.Command{ Name: "data", Usage: "Create data for runkperf", - Subcommands: []cli.Command{ - configmaps.Command, - daemonsets.Command, - }, +} + +// RegisterSubcommands adds subcommands to the data command. +// This is called from the parent package to avoid import cycles, +// allowing sub-packages to import the data package for shared utilities. +func RegisterSubcommands(cmds ...cli.Command) { + Command.Subcommands = append(Command.Subcommands, cmds...) } diff --git a/contrib/cmd/runkperf/commands/root.go b/contrib/cmd/runkperf/commands/root.go index cf311215..50796d1f 100644 --- a/contrib/cmd/runkperf/commands/root.go +++ b/contrib/cmd/runkperf/commands/root.go @@ -11,12 +11,21 @@ import ( "github.com/Azure/kperf/contrib/cmd/runkperf/commands/bench" "github.com/Azure/kperf/contrib/cmd/runkperf/commands/data" + "github.com/Azure/kperf/contrib/cmd/runkperf/commands/data/configmaps" + "github.com/Azure/kperf/contrib/cmd/runkperf/commands/data/daemonsets" "github.com/Azure/kperf/contrib/cmd/runkperf/commands/warmup" "github.com/urfave/cli" "k8s.io/klog/v2" ) +func init() { + data.RegisterSubcommands( + configmaps.Command, + daemonsets.Command, + ) +} + // App returns kperf application. func App() *cli.App { return &cli.App{ From 24bfbefa2e2aad6c313577ec46c1e3c91d3237cf Mon Sep 17 00:00:00 2001 From: Liyu Ma Date: Mon, 16 Mar 2026 16:15:27 +1000 Subject: [PATCH 4/4] Update default values for qps/burst --- .../commands/data/configmaps/configmap.go | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go index ac65c358..6ce936a7 100644 --- a/contrib/cmd/runkperf/commands/data/configmaps/configmap.go +++ b/contrib/cmd/runkperf/commands/data/configmaps/configmap.go @@ -43,16 +43,6 @@ var Command = cli.Command{ Usage: "Namespace to use with commands. If the namespace does not exist, it will be created.", Value: "default", }, - cli.Float64Flag{ - Name: "qps", - Usage: "QPS for the Kubernetes client rate limiter to control configmap operations", - Value: 500, - }, - cli.IntFlag{ - Name: "burst", - Usage: "Burst for the Kubernetes client rate limiter to control configmap operations", - Value: 1000, - }, }, Subcommands: []cli.Command{ configmapAddCommand, @@ -81,6 +71,16 @@ var configmapAddCommand = cli.Command{ Usage: "Total amount of configmaps", Value: 10, }, + cli.Float64Flag{ + Name: "qps", + Usage: "QPS for the Kubernetes client rate limiter to control configmap operations", + Value: 30, + }, + cli.IntFlag{ + Name: "burst", + Usage: "Burst for the Kubernetes client rate limiter to control configmap operations", + Value: 10, + }, }, Action: func(cliCtx *cli.Context) error { if cliCtx.NArg() != 1 { @@ -103,8 +103,8 @@ var configmapAddCommand = cli.Command{ } namespace := cliCtx.GlobalString("namespace") - qps := float32(cliCtx.GlobalFloat64("qps")) - burst := cliCtx.GlobalInt("burst") + qps := float32(cliCtx.Float64("qps")) + burst := cliCtx.Int("burst") clientset, err := data.NewClientsetWithRateLimiter(kubeCfgPath, qps, burst) if err != nil {