k8s.io/perf-tests/clusterloader2@v0.0.0-20240304094227-64bdb12da87e/pkg/modifier/modifier.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package modifier
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"k8s.io/klog/v2"
    26  	"k8s.io/perf-tests/clusterloader2/api"
    27  	"k8s.io/perf-tests/clusterloader2/pkg/config"
    28  	"k8s.io/perf-tests/clusterloader2/pkg/flags"
    29  )
    30  
    31  // Modifier mutates provided test
    32  type Modifier interface {
    33  	ChangeTest(*api.Config) error
    34  }
    35  
    36  // InitFlags allows setting configuration with flags
    37  func InitFlags(m *config.ModifierConfig) {
    38  	flags.StringArrayVar(&m.OverwriteTestConfig, "overwrite-test-config", []string{}, "Overwrite test config with specific value, used as parameter1.parameter2=value, for example 'Namespace.Prefix=custom-prefix'")
    39  	flags.StringArrayVar(&m.SkipSteps, "skip-steps", []string{}, "Name of steps to skip in test")
    40  }
    41  
    42  // NewModifier creates new Modifier according to provided configuration
    43  func NewModifier(m *config.ModifierConfig) Modifier {
    44  	return &simpleModifier{overwriteTestConfig: m.OverwriteTestConfig, skipSteps: m.SkipSteps}
    45  }
    46  
    47  type simpleModifier struct {
    48  	overwriteTestConfig []string
    49  	skipSteps           []string
    50  }
    51  
    52  // Ensuring that simpleModifier implements Modifier interface
    53  var _ Modifier = &simpleModifier{}
    54  
    55  func (m *simpleModifier) ChangeTest(c *api.Config) error {
    56  	m.modifySkipSteps(c)
    57  	return m.modifyOverwrite(c)
    58  }
    59  
    60  func (m *simpleModifier) modifySkipSteps(c *api.Config) {
    61  	steps := c.Steps
    62  	c.Steps = []*api.Step{}
    63  	for _, s := range steps {
    64  		ignored := false
    65  		for _, i := range m.skipSteps {
    66  			if i == s.Name {
    67  				ignored = true
    68  				klog.V(3).Infof("Ignoring step %s", s.Name)
    69  				break
    70  			}
    71  		}
    72  		if !ignored {
    73  			c.Steps = append(c.Steps, s)
    74  		}
    75  	}
    76  }
    77  
    78  func (m *simpleModifier) modifyOverwrite(c *api.Config) error {
    79  	for _, o := range m.overwriteTestConfig {
    80  		kv := strings.Split(o, "=")
    81  		if len(kv) != 2 {
    82  			return fmt.Errorf("not a key=value pair: '%s'", o)
    83  		}
    84  		k, v := kv[0], kv[1]
    85  
    86  		parameterPath := strings.Split(k, ".")
    87  		curValue := reflect.ValueOf(c).Elem()
    88  		for _, p := range parameterPath {
    89  			curValue = curValue.FieldByName(p)
    90  			if !curValue.IsValid() {
    91  				return fmt.Errorf("cannot overwrite config for key '%s'. Path does not exist", k)
    92  			}
    93  			// We want to dereference pointers if any happen along the way
    94  			if curValue.Kind() == reflect.Ptr {
    95  				// If path came across ptr to nil, we need to create zero value before dereferencing
    96  				if curValue.IsNil() {
    97  					expectedType := curValue.Type().Elem()
    98  					pointerToZeroValue := reflect.New(expectedType)
    99  					curValue.Set(pointerToZeroValue)
   100  				}
   101  				curValue = curValue.Elem()
   102  			}
   103  		}
   104  		err := m.overwriteValue(curValue, v, k)
   105  		if err != nil {
   106  			return err
   107  		}
   108  	}
   109  	return nil
   110  }
   111  
   112  func (m *simpleModifier) overwriteValue(node reflect.Value, v, k string) error {
   113  	switch node.Kind() {
   114  	case reflect.Bool:
   115  		boolV, err := strconv.ParseBool(v)
   116  		if err != nil {
   117  			return fmt.Errorf("test config overwrite error: Cannot parse '%s' for key '%s' to bool: %v", v, k, err)
   118  		}
   119  		klog.V(2).Infof("Setting bool value '%t' for key '%s'", boolV, k)
   120  		node.SetBool(boolV)
   121  	case reflect.String:
   122  		klog.V(2).Infof("Setting string value '%s' for key '%s'", v, k)
   123  		node.SetString(v)
   124  	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
   125  		intV, err := strconv.ParseInt(v, 10, node.Type().Bits())
   126  		if err != nil {
   127  			return fmt.Errorf("test config overwrite error: Cannot parse '%s' for key '%s' to int: %v", v, k, err)
   128  		}
   129  		klog.V(2).Infof("Setting int value '%d' for key '%s'", intV, k)
   130  		node.SetInt(intV)
   131  	default:
   132  		return fmt.Errorf("unsupported kind: %v for key %s", node.Kind(), k)
   133  	}
   134  	return nil
   135  }