...

Source file src/github.com/chaos-mesh/chaos-mesh/api/v1alpha1/physical_machine_chaos_webhook.go

Documentation: github.com/chaos-mesh/chaos-mesh/api/v1alpha1

     1  // Copyright 2021 Chaos Mesh Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  
    16  package v1alpha1
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"reflect"
    22  	"strconv"
    23  	"strings"
    24  
    25  	"github.com/alecthomas/units"
    26  	"github.com/google/uuid"
    27  	"github.com/pkg/errors"
    28  	"k8s.io/apimachinery/pkg/util/validation/field"
    29  )
    30  
    31  func (in *PhysicalMachineChaosSpec) Default(root interface{}, field *reflect.StructField) {
    32  	if in == nil {
    33  		return
    34  	}
    35  
    36  	if len(in.UID) == 0 {
    37  		in.UID = uuid.New().String()
    38  	}
    39  
    40  	for i := range in.Address {
    41  		// add http prefix for address
    42  		if !strings.HasPrefix(in.Address[i], "http") {
    43  			in.Address[i] = fmt.Sprintf("http://%s", in.Address[i])
    44  		}
    45  	}
    46  }
    47  
    48  func (in *PhysicalMachineChaosSpec) Validate(root interface{}, path *field.Path) field.ErrorList {
    49  	allErrs := field.ErrorList{}
    50  
    51  	// make sure the configuration corresponding to action is not empty
    52  	var inInterface map[string]interface{}
    53  	inrec, err := json.Marshal(in)
    54  	if err != nil {
    55  		allErrs = append(allErrs,
    56  			field.Invalid(path.Child("spec"), in, err.Error()))
    57  	}
    58  
    59  	err = json.Unmarshal(inrec, &inInterface)
    60  	if err != nil {
    61  		allErrs = append(allErrs,
    62  			field.Invalid(path.Child("spec"), in, err.Error()))
    63  	}
    64  
    65  	skipConfigCheck := false
    66  	if _, ok := inInterface[string(in.Action)]; !ok {
    67  		skipConfigCheck = true
    68  		allErrs = append(allErrs,
    69  			field.Invalid(path.Child("spec"), in,
    70  				"the configuration corresponding to action is empty"))
    71  	}
    72  
    73  	// make sure address is not empty
    74  	if len(in.Address) == 0 {
    75  		allErrs = append(allErrs,
    76  			field.Invalid(path.Child("address"), in.Address, "the address is empty"))
    77  	}
    78  	for _, address := range in.Address {
    79  		if len(address) == 0 {
    80  			allErrs = append(allErrs,
    81  				field.Invalid(path.Child("address"), in.Address, "the address is empty"))
    82  		}
    83  	}
    84  
    85  	if skipConfigCheck {
    86  		return allErrs
    87  	}
    88  
    89  	var validateConfigErr error
    90  	switch in.Action {
    91  	case PMStressCPUAction:
    92  		validateConfigErr = validateStressCPUAction(in.StressCPU)
    93  	case PMStressMemAction:
    94  		validateConfigErr = validateStressMemAction(in.StressMemory)
    95  	case PMDiskWritePayloadAction:
    96  		validateConfigErr = validateDiskPayloadAction(in.DiskWritePayload)
    97  	case PMDiskReadPayloadAction:
    98  		validateConfigErr = validateDiskPayloadAction(in.DiskReadPayload)
    99  	case PMDiskFillAction:
   100  		validateConfigErr = validateDiskFillAction(in.DiskFill)
   101  	case PMNetworkCorruptAction:
   102  		validateConfigErr = validateNetworkCorruptAction(in.NetworkCorrupt)
   103  	case PMNetworkDuplicateAction:
   104  		validateConfigErr = validateNetworkDuplicateAction(in.NetworkDuplicate)
   105  	case PMNetworkLossAction:
   106  		validateConfigErr = validateNetworkLossAction(in.NetworkLoss)
   107  	case PMNetworkDelayAction:
   108  		validateConfigErr = validateNetworkDelayAction(in.NetworkDelay)
   109  	case PMNetworkPartitionAction:
   110  		validateConfigErr = validateNetworkPartitionAction(in.NetworkPartition)
   111  	case PMNetworkBandwidthAction:
   112  		validateConfigErr = validateNetworkBandwidthAction(in.NetworkBandwidth)
   113  	case PMNetworkDNSAction:
   114  		validateConfigErr = validateNetworkDNSAction(in.NetworkDNS)
   115  	case PMProcessAction:
   116  		validateConfigErr = validateProcessAction(in.Process)
   117  	case PMJVMExceptionAction:
   118  		validateConfigErr = validateJVMExceptionAction(in.JVMException)
   119  	case PMJVMGCAction:
   120  		validateConfigErr = validateJVMGCAction(in.JVMGC)
   121  	case PMJVMLatencyAction:
   122  		validateConfigErr = validateJVMLatencyAction(in.JVMLatency)
   123  	case PMJVMReturnAction:
   124  		validateConfigErr = validateJVMReturnAction(in.JVMReturn)
   125  	case PMJVMStressAction:
   126  		validateConfigErr = validateJVMStressAction(in.JVMStress)
   127  	case PMJVMRuleDataAction:
   128  		validateConfigErr = validateJVMRuleDataAction(in.JVMRuleData)
   129  	case PMClockAction:
   130  		validateConfigErr = validateClockAction(in.Clock)
   131  	default:
   132  	}
   133  
   134  	if validateConfigErr != nil {
   135  		allErrs = append(allErrs,
   136  			field.Invalid(path.Child("spec"), in,
   137  				validateConfigErr.Error()))
   138  	}
   139  
   140  	return allErrs
   141  }
   142  
   143  func validateStressCPUAction(spec *StressCPUSpec) error {
   144  	if spec.Load == 0 {
   145  		return errors.New("load can't be 0")
   146  	}
   147  
   148  	if spec.Workers == 0 {
   149  		return errors.New("workers can't be 0")
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  func validateStressMemAction(spec *StressMemorySpec) error {
   156  	if len(spec.Size) == 0 {
   157  		return errors.New("size is required")
   158  	}
   159  
   160  	if _, err := ParseUnit(spec.Size); err != nil {
   161  		return err
   162  	}
   163  
   164  	return nil
   165  }
   166  
   167  func validateDiskPayloadAction(spec *DiskPayloadSpec) error {
   168  	if spec.PayloadProcessNum == 0 {
   169  		return errors.New("payload-process-num can't be 0")
   170  	}
   171  
   172  	if len(spec.Size) == 0 {
   173  		return errors.New("size is required")
   174  	}
   175  
   176  	if _, err := ParseUnit(spec.Size); err != nil {
   177  		return err
   178  	}
   179  
   180  	return nil
   181  }
   182  
   183  func validateDiskFillAction(spec *DiskFillSpec) error {
   184  	if len(spec.Size) == 0 {
   185  		return errors.New("size is required")
   186  	}
   187  
   188  	if _, err := ParseUnit(spec.Size); err != nil {
   189  		return err
   190  	}
   191  
   192  	return nil
   193  }
   194  
   195  func validateNetworkCommon(spec *NetworkCommonSpec) error {
   196  	if !CheckPercent(spec.Correlation, true) {
   197  		return errors.Errorf("correlation %s is invalid", spec.Correlation)
   198  	}
   199  
   200  	if len(spec.Device) == 0 {
   201  		return errors.New("device is required")
   202  	}
   203  
   204  	if len(spec.IPAddress) == 0 && len(spec.Hostname) == 0 {
   205  		return errors.New("one of ip-address and hostname is required")
   206  	}
   207  
   208  	return nil
   209  }
   210  
   211  func validateNetworkCorruptAction(spec *NetworkCorruptSpec) error {
   212  	if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
   213  		return err
   214  	}
   215  
   216  	if !CheckPercent(spec.Percent, false) {
   217  		return errors.New("percent is invalid")
   218  	}
   219  
   220  	return nil
   221  }
   222  
   223  func validateNetworkDuplicateAction(spec *NetworkDuplicateSpec) error {
   224  	if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
   225  		return err
   226  	}
   227  
   228  	if !CheckPercent(spec.Percent, false) {
   229  		return errors.New("percent is invalid")
   230  	}
   231  
   232  	return nil
   233  }
   234  
   235  func validateNetworkLossAction(spec *NetworkLossSpec) error {
   236  	if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
   237  		return err
   238  	}
   239  
   240  	if !CheckPercent(spec.Percent, false) {
   241  		return errors.New("percent is invalid")
   242  	}
   243  
   244  	return nil
   245  }
   246  
   247  func validateNetworkDelayAction(spec *NetworkDelaySpec) error {
   248  	if err := validateNetworkCommon(&spec.NetworkCommonSpec); err != nil {
   249  		return err
   250  	}
   251  
   252  	if len(spec.Latency) == 0 {
   253  		return errors.New("latency is invalid")
   254  	}
   255  
   256  	return nil
   257  }
   258  
   259  func validateNetworkPartitionAction(spec *NetworkPartitionSpec) error {
   260  	if len(spec.Device) == 0 {
   261  		return errors.New("device is required")
   262  	}
   263  
   264  	if len(spec.IPAddress) == 0 && len(spec.Hostname) == 0 {
   265  		return errors.New("one of ip-address and hostname is required")
   266  	}
   267  
   268  	if spec.Direction != "to" && spec.Direction != "from" {
   269  		return errors.New("direction should be one of 'to' and 'from'")
   270  	}
   271  
   272  	if len(spec.AcceptTCPFlags) > 0 && spec.IPProtocol != "tcp" {
   273  		return errors.Errorf("protocol should be 'tcp' when set accept-tcp-flags")
   274  	}
   275  
   276  	return nil
   277  }
   278  
   279  func validateNetworkBandwidthAction(spec *NetworkBandwidthSpec) error {
   280  	if len(spec.Device) == 0 {
   281  		return errors.New("device is required")
   282  	}
   283  
   284  	if len(spec.Rate) == 0 || spec.Limit == 0 || spec.Buffer == 0 {
   285  		return errors.Errorf("rate, limit and buffer both are required when action is bandwidth")
   286  	}
   287  
   288  	return nil
   289  }
   290  
   291  func validateNetworkDNSAction(spec *NetworkDNSSpec) error {
   292  	if (len(spec.DNSDomainName) != 0 && len(spec.DNSIp) == 0) || (len(spec.DNSDomainName) == 0 && len(spec.DNSIp) != 0) {
   293  		return errors.Errorf("DNS host %s must match a DNS ip %s", spec.DNSDomainName, spec.DNSIp)
   294  	}
   295  
   296  	return nil
   297  }
   298  
   299  func validateProcessAction(spec *ProcessSpec) error {
   300  	if len(spec.Process) == 0 {
   301  		return errors.New("process is required")
   302  	}
   303  
   304  	if spec.Signal == 0 {
   305  		return errors.New("signal is required")
   306  	}
   307  
   308  	return nil
   309  }
   310  
   311  func validateJVMClassMethod(spec *JVMClassMethodSpec) error {
   312  	if len(spec.Class) == 0 {
   313  		return errors.New("class is required")
   314  	}
   315  
   316  	if len(spec.Method) == 0 {
   317  		return errors.New("method is required")
   318  	}
   319  
   320  	return nil
   321  }
   322  
   323  func validateJVMExceptionAction(spec *JVMExceptionSpec) error {
   324  	if err := CheckPid(spec.Pid); err != nil {
   325  		return err
   326  	}
   327  
   328  	if err := validateJVMClassMethod(&spec.JVMClassMethodSpec); err != nil {
   329  		return err
   330  	}
   331  
   332  	if len(spec.ThrowException) == 0 {
   333  		return errors.New("exception is required")
   334  	}
   335  
   336  	return nil
   337  }
   338  
   339  func validateJVMGCAction(spec *JVMGCSpec) error {
   340  	return CheckPid(spec.Pid)
   341  }
   342  
   343  func validateJVMLatencyAction(spec *JVMLatencySpec) error {
   344  	if err := CheckPid(spec.Pid); err != nil {
   345  		return err
   346  	}
   347  
   348  	if err := validateJVMClassMethod(&spec.JVMClassMethodSpec); err != nil {
   349  		return err
   350  	}
   351  
   352  	if spec.LatencyDuration == 0 {
   353  		return errors.New("latency is required")
   354  	}
   355  
   356  	return nil
   357  }
   358  
   359  func validateJVMReturnAction(spec *JVMReturnSpec) error {
   360  	if err := CheckPid(spec.Pid); err != nil {
   361  		return err
   362  	}
   363  
   364  	if err := validateJVMClassMethod(&spec.JVMClassMethodSpec); err != nil {
   365  		return err
   366  	}
   367  
   368  	if len(spec.ReturnValue) == 0 {
   369  		return errors.New("value is required")
   370  	}
   371  
   372  	return nil
   373  }
   374  
   375  func validateJVMStressAction(spec *JVMStressSpec) error {
   376  	if err := CheckPid(spec.Pid); err != nil {
   377  		return err
   378  	}
   379  
   380  	if spec.CPUCount == 0 && len(spec.MemoryType) == 0 {
   381  		return errors.New("one of cpu-count and mem-type is required")
   382  	}
   383  
   384  	if spec.CPUCount > 0 && len(spec.MemoryType) > 0 {
   385  		return errors.New("inject stress on both CPU and memory is not support")
   386  	}
   387  
   388  	return nil
   389  }
   390  
   391  func validateJVMRuleDataAction(spec *JVMRuleDataSpec) error {
   392  	if err := CheckPid(spec.Pid); err != nil {
   393  		return err
   394  	}
   395  
   396  	if len(spec.RuleData) == 0 {
   397  		return errors.New("rule-data is required")
   398  	}
   399  
   400  	return nil
   401  }
   402  
   403  func validateClockAction(spec *ClockSpec) error {
   404  	if err := CheckPid(spec.Pid); err != nil {
   405  		return err
   406  	}
   407  
   408  	if len(spec.TimeOffset) == 0 {
   409  		return errors.New("time-offset is required")
   410  	}
   411  
   412  	return nil
   413  }
   414  
   415  func CheckPid(pid int) error {
   416  	if pid == 0 {
   417  		return errors.New("pid is required")
   418  	}
   419  
   420  	if pid < 0 {
   421  		return errors.New("pid is invalid")
   422  	}
   423  
   424  	return nil
   425  }
   426  
   427  func CheckPercent(p string, allowZero bool) bool {
   428  	if len(p) == 0 {
   429  		return allowZero
   430  	}
   431  
   432  	v, err := strconv.ParseFloat(p, 32)
   433  	if err != nil {
   434  		return false
   435  	}
   436  
   437  	if v == 0 && !allowZero {
   438  		return false
   439  	}
   440  
   441  	if v < 0 || v > 100 {
   442  		return false
   443  	}
   444  
   445  	return true
   446  }
   447  
   448  var (
   449  	// See https://en.wikipedia.org/wiki/Binary_prefix
   450  	shortBinaryUnitMap = units.MakeUnitMap("", "c", 1024)
   451  	binaryUnitMap      = units.MakeUnitMap("iB", "c", 1024)
   452  	decimalUnitMap     = units.MakeUnitMap("B", "c", 1000)
   453  )
   454  
   455  // ParseUnit parse a digit with unit such as "K" , "KiB", "KB", "c", "MiB", "MB", "M".
   456  // If input string is a digit without unit ,
   457  // it will be regarded as a digit with unit M(1024*1024 bytes).
   458  func ParseUnit(s string) (uint64, error) {
   459  	if _, err := strconv.Atoi(s); err == nil {
   460  		s += "B"
   461  	}
   462  	if n, err := units.ParseUnit(s, shortBinaryUnitMap); err == nil {
   463  		return uint64(n), nil
   464  	}
   465  
   466  	if n, err := units.ParseUnit(s, binaryUnitMap); err == nil {
   467  		return uint64(n), nil
   468  	}
   469  
   470  	if n, err := units.ParseUnit(s, decimalUnitMap); err == nil {
   471  		return uint64(n), nil
   472  	}
   473  	return 0, fmt.Errorf("units: unknown unit %s", s)
   474  }
   475  
   476  func (in *NetworkBandwidthSpec) Validate(root interface{}, path *field.Path) field.ErrorList {
   477  	allErrs := field.ErrorList{}
   478  
   479  	if len(in.Rate) == 0 {
   480  		allErrs = append(allErrs,
   481  			field.Invalid(path.Child("rate"), in.Rate, "rate is required"))
   482  	}
   483  
   484  	return allErrs
   485  }
   486