...

Source file src/k8s.io/kubernetes/pkg/apis/core/v1/validation/validation.go

Documentation: k8s.io/kubernetes/pkg/apis/core/v1/validation

     1  /*
     2  Copyright 2014 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 validation
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	"k8s.io/apimachinery/pkg/api/resource"
    25  	"k8s.io/apimachinery/pkg/util/sets"
    26  	"k8s.io/apimachinery/pkg/util/validation/field"
    27  	"k8s.io/kubernetes/pkg/apis/core"
    28  	"k8s.io/kubernetes/pkg/apis/core/helper"
    29  	v1helper "k8s.io/kubernetes/pkg/apis/core/v1/helper"
    30  	apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
    31  )
    32  
    33  const isNegativeErrorMsg string = `must be greater than or equal to 0`
    34  const isNotIntegerErrorMsg string = `must be an integer`
    35  
    36  // ValidateResourceRequirements will check if any of the resource
    37  // Limits/Requests are of a valid value. Any incorrect value will be added to
    38  // the ErrorList.
    39  func ValidateResourceRequirements(requirements *v1.ResourceRequirements, fldPath *field.Path) field.ErrorList {
    40  	allErrs := field.ErrorList{}
    41  	limPath := fldPath.Child("limits")
    42  	reqPath := fldPath.Child("requests")
    43  	for resourceName, quantity := range requirements.Limits {
    44  		fldPath := limPath.Key(string(resourceName))
    45  		// Validate resource name.
    46  		allErrs = append(allErrs, ValidateContainerResourceName(core.ResourceName(resourceName), fldPath)...)
    47  
    48  		// Validate resource quantity.
    49  		allErrs = append(allErrs, ValidateResourceQuantityValue(core.ResourceName(resourceName), quantity, fldPath)...)
    50  
    51  	}
    52  	for resourceName, quantity := range requirements.Requests {
    53  		fldPath := reqPath.Key(string(resourceName))
    54  		// Validate resource name.
    55  		allErrs = append(allErrs, ValidateContainerResourceName(core.ResourceName(resourceName), fldPath)...)
    56  		// Validate resource quantity.
    57  		allErrs = append(allErrs, ValidateResourceQuantityValue(core.ResourceName(resourceName), quantity, fldPath)...)
    58  
    59  		// Check that request <= limit.
    60  		limitQuantity, exists := requirements.Limits[resourceName]
    61  		if exists {
    62  			// For GPUs, not only requests can't exceed limits, they also can't be lower, i.e. must be equal.
    63  			if quantity.Cmp(limitQuantity) != 0 && !v1helper.IsOvercommitAllowed(resourceName) {
    64  				allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be equal to %s limit of %s", resourceName, limitQuantity.String())))
    65  			} else if quantity.Cmp(limitQuantity) > 0 {
    66  				allErrs = append(allErrs, field.Invalid(reqPath, quantity.String(), fmt.Sprintf("must be less than or equal to %s limit of %s", resourceName, limitQuantity.String())))
    67  			}
    68  		}
    69  	}
    70  
    71  	return allErrs
    72  }
    73  
    74  // ValidateContainerResourceName checks the name of resource specified for a container
    75  func ValidateContainerResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
    76  	allErrs := validateResourceName(value, fldPath)
    77  	if len(strings.Split(string(value), "/")) == 1 {
    78  		if !helper.IsStandardContainerResourceName(value) {
    79  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource for containers"))
    80  		}
    81  	} else if !v1helper.IsNativeResource(v1.ResourceName(value)) {
    82  		if !v1helper.IsExtendedResourceName(v1.ResourceName(value)) {
    83  			return append(allErrs, field.Invalid(fldPath, value, "doesn't follow extended resource name standard"))
    84  		}
    85  	}
    86  	return allErrs
    87  }
    88  
    89  // ValidateResourceQuantityValue enforces that specified quantity is valid for specified resource
    90  func ValidateResourceQuantityValue(resource core.ResourceName, value resource.Quantity, fldPath *field.Path) field.ErrorList {
    91  	allErrs := field.ErrorList{}
    92  	allErrs = append(allErrs, ValidateNonnegativeQuantity(value, fldPath)...)
    93  	if helper.IsIntegerResourceName(resource) {
    94  		if value.MilliValue()%int64(1000) != int64(0) {
    95  			allErrs = append(allErrs, field.Invalid(fldPath, value, isNotIntegerErrorMsg))
    96  		}
    97  	}
    98  	return allErrs
    99  }
   100  
   101  // ValidateNonnegativeQuantity checks that a Quantity is not negative.
   102  func ValidateNonnegativeQuantity(value resource.Quantity, fldPath *field.Path) field.ErrorList {
   103  	allErrs := field.ErrorList{}
   104  	if value.Cmp(resource.Quantity{}) < 0 {
   105  		allErrs = append(allErrs, field.Invalid(fldPath, value.String(), isNegativeErrorMsg))
   106  	}
   107  	return allErrs
   108  }
   109  
   110  // Validate compute resource typename.
   111  // Refer to docs/design/resources.md for more details.
   112  func validateResourceName(value core.ResourceName, fldPath *field.Path) field.ErrorList {
   113  	allErrs := apivalidation.ValidateQualifiedName(string(value), fldPath)
   114  	if len(allErrs) != 0 {
   115  		return allErrs
   116  	}
   117  
   118  	if len(strings.Split(string(value), "/")) == 1 {
   119  		if !helper.IsStandardResourceName(value) {
   120  			return append(allErrs, field.Invalid(fldPath, value, "must be a standard resource type or fully qualified"))
   121  		}
   122  	}
   123  
   124  	return allErrs
   125  }
   126  
   127  // ValidatePodLogOptions checks if options that are set are at the correct
   128  // value. Any incorrect value will be returned to the ErrorList.
   129  func ValidatePodLogOptions(opts *v1.PodLogOptions) field.ErrorList {
   130  	allErrs := field.ErrorList{}
   131  	if opts.TailLines != nil && *opts.TailLines < 0 {
   132  		allErrs = append(allErrs, field.Invalid(field.NewPath("tailLines"), *opts.TailLines, isNegativeErrorMsg))
   133  	}
   134  	if opts.LimitBytes != nil && *opts.LimitBytes < 1 {
   135  		allErrs = append(allErrs, field.Invalid(field.NewPath("limitBytes"), *opts.LimitBytes, "must be greater than 0"))
   136  	}
   137  	switch {
   138  	case opts.SinceSeconds != nil && opts.SinceTime != nil:
   139  		allErrs = append(allErrs, field.Forbidden(field.NewPath(""), "at most one of `sinceTime` or `sinceSeconds` may be specified"))
   140  	case opts.SinceSeconds != nil:
   141  		if *opts.SinceSeconds < 1 {
   142  			allErrs = append(allErrs, field.Invalid(field.NewPath("sinceSeconds"), *opts.SinceSeconds, "must be greater than 0"))
   143  		}
   144  	}
   145  	return allErrs
   146  }
   147  
   148  // AccumulateUniqueHostPorts checks all the containers for duplicates ports. Any
   149  // duplicate port will be returned in the ErrorList.
   150  func AccumulateUniqueHostPorts(containers []v1.Container, accumulator *sets.String, fldPath *field.Path) field.ErrorList {
   151  	allErrs := field.ErrorList{}
   152  
   153  	for ci, ctr := range containers {
   154  		idxPath := fldPath.Index(ci)
   155  		portsPath := idxPath.Child("ports")
   156  		for pi := range ctr.Ports {
   157  			idxPath := portsPath.Index(pi)
   158  			port := ctr.Ports[pi].HostPort
   159  			if port == 0 {
   160  				continue
   161  			}
   162  			str := fmt.Sprintf("%d/%s", port, ctr.Ports[pi].Protocol)
   163  			if accumulator.Has(str) {
   164  				allErrs = append(allErrs, field.Duplicate(idxPath.Child("hostPort"), str))
   165  			} else {
   166  				accumulator.Insert(str)
   167  			}
   168  		}
   169  	}
   170  	return allErrs
   171  }
   172  

View as plain text