github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/cli/cmd/bench/sysbench.go (about) 1 /* 2 Copyright (C) 2022-2023 ApeCloud Co., Ltd 3 4 This file is part of KubeBlocks project 5 6 This program is free software: you can redistribute it and/or modify 7 it under the terms of the GNU Affero General Public License as published by 8 the Free Software Foundation, either version 3 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU Affero General Public License for more details. 15 16 You should have received a copy of the GNU Affero General Public License 17 along with this program. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 package bench 21 22 import ( 23 "context" 24 "fmt" 25 "strings" 26 27 "github.com/spf13/cobra" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/cli-runtime/pkg/genericiooptions" 32 cmdutil "k8s.io/kubectl/pkg/cmd/util" 33 "k8s.io/kubectl/pkg/util/templates" 34 35 "github.com/apecloud/kubebench/api/v1alpha1" 36 37 "github.com/1aal/kubeblocks/pkg/cli/cluster" 38 "github.com/1aal/kubeblocks/pkg/cli/types" 39 ) 40 41 var ( 42 sysbenchDriverMap = map[string]string{ 43 "mysql": "mysql", 44 "postgresql": "pgsql", 45 } 46 sysbenchSupportedDrivers = []string{"mysql", "pgsql"} 47 ) 48 49 var sysbenchExample = templates.Examples(` 50 # sysbench on a cluster, that will exec for all steps, cleanup, prepare and run 51 kbcli bench sysbench mytest --cluster mycluster --user xxx --password xxx --database mydb 52 53 # sysbench run on a cluster with cleanup, only cleanup by deleting the testdata 54 kbcli bench sysbench cleanup mytest --cluster mycluster --user xxx --password xxx --database mydb 55 56 # sysbench run on a cluster with prepare, just prepare by creating the testdata 57 kbcli bench sysbench prepare mytest --cluster mycluster --user xxx --password xxx --database mydb 58 59 # sysbench run on a cluster with run, just run by running the test 60 kbcli bench sysbench run mytest --cluster mycluster --user xxx --password xxx --database mydb 61 62 # sysbench on a cluster with thread counts 63 kbcli bench sysbench mytest --cluster mycluster --user xxx --password xxx --database mydb --threads 4,8 64 65 # sysbench on a cluster with type 66 kbcli bench sysbench mytest --cluster mycluster --user xxx --password xxx --database mydb --type oltp_read_only,oltp_read_write 67 68 # sysbench on a cluster with specified read/write ratio 69 kbcli bench sysbench mytest --cluster mycluster --user xxx --password xxx --database mydb --type oltp_read_write_pct --read-percent 80 --write-percent 20 70 71 # sysbench on a cluster with specified tables and size 72 kbcli bench sysbench mytest --cluster mycluster --user xxx --password xxx --database mydb --tables 10 --size 25000 73 `) 74 75 type SysBenchOptions struct { 76 Threads []int // the number of threads 77 Tables int // the number of tables 78 Size int // the data size of per table 79 Duration int 80 Type []string 81 ReadPercent int 82 WritePercent int 83 84 BenchBaseOptions 85 } 86 87 func NewSysBenchCmd(f cmdutil.Factory, streams genericiooptions.IOStreams) *cobra.Command { 88 o := &SysBenchOptions{ 89 BenchBaseOptions: BenchBaseOptions{ 90 IOStreams: streams, 91 factory: f, 92 }, 93 } 94 95 cmd := &cobra.Command{ 96 Use: "sysbench [Step] [BenchmarkName]", 97 Short: "run a SysBench benchmark", 98 Example: sysbenchExample, 99 Run: func(cmd *cobra.Command, args []string) { 100 cmdutil.CheckErr(o.Complete(args)) 101 cmdutil.CheckErr(o.Validate()) 102 cmdutil.CheckErr(o.Run()) 103 }, 104 } 105 106 cmd.Flags().StringSliceVar(&o.Type, "type", []string{"oltp_read_write"}, "sysbench type, you can set multiple values") 107 cmd.Flags().IntVar(&o.Size, "size", 25000, "the data size of per table") 108 cmd.Flags().IntVar(&o.Tables, "tables", 10, "the number of tables") 109 cmd.Flags().IntVar(&o.Duration, "duration", 60, "the seconds of running sysbench") 110 cmd.Flags().IntSliceVar(&o.Threads, "threads", []int{4}, "the number of threads, you can set multiple values, like 4,8") 111 cmd.Flags().IntVar(&o.ReadPercent, "read-percent", 0, "the percent of read, only useful when type is oltp_read_write_pct") 112 cmd.Flags().IntVar(&o.WritePercent, "write-percent", 0, "the percent of write, only useful when type is oltp_read_write_pct") 113 o.BenchBaseOptions.AddFlags(cmd) 114 115 return cmd 116 } 117 118 func (o *SysBenchOptions) Complete(args []string) error { 119 var err error 120 var driver string 121 var host string 122 var port int 123 124 if err = o.BenchBaseOptions.BaseComplete(); err != nil { 125 return err 126 } 127 128 o.Step, o.name = parseStepAndName(args, "sysbench") 129 130 o.namespace, _, err = o.factory.ToRawKubeConfigLoader().Namespace() 131 if err != nil { 132 return err 133 } 134 135 if o.dynamic, err = o.factory.DynamicClient(); err != nil { 136 return err 137 } 138 139 if o.client, err = o.factory.KubernetesClientSet(); err != nil { 140 return err 141 } 142 143 if o.ClusterName != "" { 144 clusterGetter := cluster.ObjectsGetter{ 145 Client: o.client, 146 Dynamic: o.dynamic, 147 Name: o.ClusterName, 148 Namespace: o.namespace, 149 GetOptions: cluster.GetOptions{ 150 WithClusterDef: true, 151 WithService: true, 152 WithPod: true, 153 WithEvent: true, 154 WithPVC: true, 155 WithDataProtection: true, 156 }, 157 } 158 if o.ClusterObjects, err = clusterGetter.Get(); err != nil { 159 return err 160 } 161 driver, host, port, err = getDriverAndHostAndPort(o.Cluster, o.Services) 162 if err != nil { 163 return err 164 } 165 } 166 167 if v, ok := sysbenchDriverMap[driver]; ok && o.Driver == "" { 168 o.Driver = v 169 } 170 171 if o.Host == "" && o.Port == 0 { 172 o.Host = host 173 o.Port = port 174 } 175 176 // if user just give readPercent or writePercent, we will calculate the other one 177 if o.ReadPercent != 0 && o.WritePercent == 0 { 178 o.WritePercent = 100 - o.ReadPercent 179 } 180 if o.ReadPercent == 0 && o.WritePercent != 0 { 181 o.ReadPercent = 100 - o.WritePercent 182 } 183 184 return nil 185 } 186 187 func (o *SysBenchOptions) Validate() error { 188 if err := o.BaseValidate(); err != nil { 189 return err 190 } 191 192 var supported bool 193 for _, v := range sysbenchDriverMap { 194 if v == o.Driver { 195 supported = true 196 break 197 } 198 } 199 if !supported { 200 return fmt.Errorf("sysbench now only supports drivers in [%s], current cluster driver is %s", 201 strings.Join(sysbenchSupportedDrivers, ","), o.Driver) 202 } 203 204 if o.User == "" { 205 return fmt.Errorf("user is required") 206 } 207 208 if o.Database == "" { 209 return fmt.Errorf("database is required") 210 } 211 212 if len(o.Type) == 0 { 213 return fmt.Errorf("type is required") 214 } 215 216 if o.Tables <= 0 { 217 return fmt.Errorf("tables must be greater than 0") 218 } 219 220 if o.Duration <= 0 { 221 return fmt.Errorf("duration must be greater than 0") 222 } 223 224 if o.ReadPercent < 0 || o.ReadPercent > 100 { 225 return fmt.Errorf("readPercent must be between 0 and 100") 226 } 227 if o.WritePercent < 0 || o.WritePercent > 100 { 228 return fmt.Errorf("writePercent must be between 0 and 100") 229 } 230 231 return nil 232 } 233 234 func (o *SysBenchOptions) Run() error { 235 if o.ReadPercent > 0 { 236 o.ExtraArgs = append(o.ExtraArgs, fmt.Sprintf("--read-percent=%d", o.ReadPercent)) 237 } 238 if o.WritePercent > 0 { 239 o.ExtraArgs = append(o.ExtraArgs, fmt.Sprintf("--write-percent=%d", o.WritePercent)) 240 } 241 242 sysbench := v1alpha1.Sysbench{ 243 TypeMeta: metav1.TypeMeta{ 244 Kind: "Sysbench", 245 APIVersion: types.SysbenchGVR().GroupVersion().String(), 246 }, 247 ObjectMeta: metav1.ObjectMeta{ 248 Name: o.name, 249 Namespace: o.namespace, 250 }, 251 Spec: v1alpha1.SysbenchSpec{ 252 Tables: o.Tables, 253 Size: o.Size, 254 Threads: o.Threads, 255 Types: o.Type, 256 Duration: o.Duration, 257 BenchCommon: v1alpha1.BenchCommon{ 258 ExtraArgs: o.ExtraArgs, 259 Step: o.Step, 260 Tolerations: o.Tolerations, 261 Target: v1alpha1.Target{ 262 Driver: o.Driver, 263 Host: o.Host, 264 Port: o.Port, 265 User: o.User, 266 Password: o.Password, 267 Database: o.Database, 268 }, 269 }, 270 }, 271 } 272 273 obj := &unstructured.Unstructured{ 274 Object: map[string]interface{}{}, 275 } 276 data, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&sysbench) 277 if err != nil { 278 return err 279 } 280 obj.SetUnstructuredContent(data) 281 282 obj, err = o.dynamic.Resource(types.SysbenchGVR()).Namespace(o.namespace).Create(context.TODO(), obj, metav1.CreateOptions{}) 283 if err != nil { 284 return err 285 } 286 287 fmt.Fprintf(o.Out, "%s %s created\n", obj.GetKind(), obj.GetName()) 288 return nil 289 }