Merge pull request #24802 from thockin/validation_pt8-10
Automatic merge from submit-queue Make validators return error strings Part of the larger validation PR, broken out for easier review and merge. Builds on previous PRs in the series. This is a group of smaller commits, so I don't waster precious PR numbers.
This commit is contained in:
		@@ -19,7 +19,6 @@ package validation
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"encoding/json"
 | 
						"encoding/json"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"math"
 | 
					 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
@@ -51,17 +50,9 @@ var RepairMalformedUpdates bool = true
 | 
				
			|||||||
const isNegativeErrorMsg string = `must be greater than or equal to 0`
 | 
					const isNegativeErrorMsg string = `must be greater than or equal to 0`
 | 
				
			||||||
const isInvalidQuotaResource string = `must be a standard resource for quota`
 | 
					const isInvalidQuotaResource string = `must be a standard resource for quota`
 | 
				
			||||||
const fieldImmutableErrorMsg string = `field is immutable`
 | 
					const fieldImmutableErrorMsg string = `field is immutable`
 | 
				
			||||||
const cIdentifierErrorMsg string = `must be a C identifier (matching regex ` + validation.CIdentifierFmt + `): e.g. "my_name" or "MyName"`
 | 
					 | 
				
			||||||
const isNotIntegerErrorMsg string = `must be an integer`
 | 
					const isNotIntegerErrorMsg string = `must be an integer`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func InclusiveRangeErrorMsg(lo, hi int) string {
 | 
					var pdPartitionErrorMsg string = validation.InclusiveRangeError(1, 255)
 | 
				
			||||||
	return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var pdPartitionErrorMsg string = InclusiveRangeErrorMsg(1, 255)
 | 
					 | 
				
			||||||
var PortRangeErrorMsg string = InclusiveRangeErrorMsg(1, 65535)
 | 
					 | 
				
			||||||
var IdRangeErrorMsg string = InclusiveRangeErrorMsg(0, math.MaxInt32)
 | 
					 | 
				
			||||||
var PortNameErrorMsg string = fmt.Sprintf(`must be an IANA_SVC_NAME (at most 15 characters, matching regex %s, it must contain at least one letter [a-z], and hyphens cannot be adjacent to other hyphens): e.g. "http"`, validation.IdentifierNoHyphensBeginEndFmt)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
 | 
					const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -610,7 +601,7 @@ func validateISCSIVolumeSource(iscsi *api.ISCSIVolumeSource, fldPath *field.Path
 | 
				
			|||||||
		allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
 | 
							allErrs = append(allErrs, field.Required(fldPath.Child("iqn"), ""))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if iscsi.Lun < 0 || iscsi.Lun > 255 {
 | 
						if iscsi.Lun < 0 || iscsi.Lun > 255 {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, InclusiveRangeErrorMsg(0, 255)))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), iscsi.Lun, validation.InclusiveRangeError(0, 255)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -625,7 +616,7 @@ func validateFCVolumeSource(fc *api.FCVolumeSource, fldPath *field.Path) field.E
 | 
				
			|||||||
		allErrs = append(allErrs, field.Required(fldPath.Child("lun"), ""))
 | 
							allErrs = append(allErrs, field.Required(fldPath.Child("lun"), ""))
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		if *fc.Lun < 0 || *fc.Lun > 255 {
 | 
							if *fc.Lun < 0 || *fc.Lun > 255 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), fc.Lun, InclusiveRangeErrorMsg(0, 255)))
 | 
								allErrs = append(allErrs, field.Invalid(fldPath.Child("lun"), fc.Lun, validation.InclusiveRangeError(0, 255)))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
@@ -1055,8 +1046,10 @@ func validateContainerPorts(ports []api.ContainerPort, fldPath *field.Path) fiel
 | 
				
			|||||||
	for i, port := range ports {
 | 
						for i, port := range ports {
 | 
				
			||||||
		idxPath := fldPath.Index(i)
 | 
							idxPath := fldPath.Index(i)
 | 
				
			||||||
		if len(port.Name) > 0 {
 | 
							if len(port.Name) > 0 {
 | 
				
			||||||
			if !validation.IsValidPortName(port.Name) {
 | 
								if msgs := validation.IsValidPortName(port.Name); len(msgs) != 0 {
 | 
				
			||||||
				allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), port.Name, PortNameErrorMsg))
 | 
									for i = range msgs {
 | 
				
			||||||
 | 
										allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), port.Name, msgs[i]))
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
			} else if allNames.Has(port.Name) {
 | 
								} else if allNames.Has(port.Name) {
 | 
				
			||||||
				allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), port.Name))
 | 
									allErrs = append(allErrs, field.Duplicate(idxPath.Child("name"), port.Name))
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
@@ -1064,12 +1057,16 @@ func validateContainerPorts(ports []api.ContainerPort, fldPath *field.Path) fiel
 | 
				
			|||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if port.ContainerPort == 0 {
 | 
							if port.ContainerPort == 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, PortRangeErrorMsg))
 | 
								allErrs = append(allErrs, field.Required(idxPath.Child("containerPort"), ""))
 | 
				
			||||||
		} else if !validation.IsValidPortNum(int(port.ContainerPort)) {
 | 
							} else {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, PortRangeErrorMsg))
 | 
								for _, msg := range validation.IsValidPortNum(int(port.ContainerPort)) {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, msg))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if port.HostPort != 0 && !validation.IsValidPortNum(int(port.HostPort)) {
 | 
							if port.HostPort != 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(idxPath.Child("hostPort"), port.HostPort, PortRangeErrorMsg))
 | 
								for _, msg := range validation.IsValidPortNum(int(port.HostPort)) {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(idxPath.Child("hostPort"), port.HostPort, msg))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if len(port.Protocol) == 0 {
 | 
							if len(port.Protocol) == 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Required(idxPath.Child("protocol"), ""))
 | 
								allErrs = append(allErrs, field.Required(idxPath.Child("protocol"), ""))
 | 
				
			||||||
@@ -1087,8 +1084,10 @@ func validateEnv(vars []api.EnvVar, fldPath *field.Path) field.ErrorList {
 | 
				
			|||||||
		idxPath := fldPath.Index(i)
 | 
							idxPath := fldPath.Index(i)
 | 
				
			||||||
		if len(ev.Name) == 0 {
 | 
							if len(ev.Name) == 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
 | 
								allErrs = append(allErrs, field.Required(idxPath.Child("name"), ""))
 | 
				
			||||||
		} else if !validation.IsCIdentifier(ev.Name) {
 | 
							} else {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, cIdentifierErrorMsg))
 | 
								for _, msg := range validation.IsCIdentifier(ev.Name) {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(idxPath.Child("name"), ev.Name, msg))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"))...)
 | 
							allErrs = append(allErrs, validateEnvVarValueFrom(ev, idxPath.Child("valueFrom"))...)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -1303,36 +1302,43 @@ func validateExecAction(exec *api.ExecAction, fldPath *field.Path) field.ErrorLi
 | 
				
			|||||||
	return allErrors
 | 
						return allErrors
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var supportedHTTPSchemes = sets.NewString(string(api.URISchemeHTTP), string(api.URISchemeHTTPS))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validateHTTPGetAction(http *api.HTTPGetAction, fldPath *field.Path) field.ErrorList {
 | 
					func validateHTTPGetAction(http *api.HTTPGetAction, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	allErrors := field.ErrorList{}
 | 
						allErrors := field.ErrorList{}
 | 
				
			||||||
	if len(http.Path) == 0 {
 | 
						if len(http.Path) == 0 {
 | 
				
			||||||
		allErrors = append(allErrors, field.Required(fldPath.Child("path"), ""))
 | 
							allErrors = append(allErrors, field.Required(fldPath.Child("path"), ""))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if http.Port.Type == intstr.Int && !validation.IsValidPortNum(http.Port.IntValue()) {
 | 
						allErrors = append(allErrors, ValidatePortNumOrName(http.Port, fldPath.Child("port"))...)
 | 
				
			||||||
		allErrors = append(allErrors, field.Invalid(fldPath.Child("port"), http.Port, PortRangeErrorMsg))
 | 
						if !supportedHTTPSchemes.Has(string(http.Scheme)) {
 | 
				
			||||||
	} else if http.Port.Type == intstr.String && !validation.IsValidPortName(http.Port.StrVal) {
 | 
							allErrors = append(allErrors, field.NotSupported(fldPath.Child("scheme"), http.Scheme, supportedHTTPSchemes.List()))
 | 
				
			||||||
		allErrors = append(allErrors, field.Invalid(fldPath.Child("port"), http.Port.StrVal, PortNameErrorMsg))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	supportedSchemes := sets.NewString(string(api.URISchemeHTTP), string(api.URISchemeHTTPS))
 | 
					 | 
				
			||||||
	if !supportedSchemes.Has(string(http.Scheme)) {
 | 
					 | 
				
			||||||
		allErrors = append(allErrors, field.Invalid(fldPath.Child("scheme"), http.Scheme, fmt.Sprintf("must be one of %v", supportedSchemes.List())))
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, header := range http.HTTPHeaders {
 | 
						for _, header := range http.HTTPHeaders {
 | 
				
			||||||
		if !validation.IsHTTPHeaderName(header.Name) {
 | 
							for _, msg := range validation.IsHTTPHeaderName(header.Name) {
 | 
				
			||||||
			allErrors = append(allErrors, field.Invalid(fldPath.Child("httpHeaders"), header.Name, fmt.Sprintf("name must match %s", validation.HTTPHeaderNameFmt)))
 | 
								allErrors = append(allErrors, field.Invalid(fldPath.Child("httpHeaders"), header.Name, msg))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return allErrors
 | 
						return allErrors
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validateTCPSocketAction(tcp *api.TCPSocketAction, fldPath *field.Path) field.ErrorList {
 | 
					func ValidatePortNumOrName(port intstr.IntOrString, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	allErrors := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
	if tcp.Port.Type == intstr.Int && !validation.IsValidPortNum(tcp.Port.IntValue()) {
 | 
						if port.Type == intstr.Int {
 | 
				
			||||||
		allErrors = append(allErrors, field.Invalid(fldPath.Child("port"), tcp.Port, PortRangeErrorMsg))
 | 
							for _, msg := range validation.IsValidPortNum(port.IntValue()) {
 | 
				
			||||||
	} else if tcp.Port.Type == intstr.String && !validation.IsValidPortName(tcp.Port.StrVal) {
 | 
								allErrs = append(allErrs, field.Invalid(fldPath, port.IntValue(), msg))
 | 
				
			||||||
		allErrors = append(allErrors, field.Invalid(fldPath.Child("port"), tcp.Port.StrVal, PortNameErrorMsg))
 | 
							}
 | 
				
			||||||
 | 
						} else if port.Type == intstr.String {
 | 
				
			||||||
 | 
							for _, msg := range validation.IsValidPortName(port.StrVal) {
 | 
				
			||||||
 | 
								allErrs = append(allErrs, field.Invalid(fldPath, port.StrVal, msg))
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.InternalError(fldPath, fmt.Errorf("unknown type: %v", port.Type)))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return allErrors
 | 
						return allErrs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func validateTCPSocketAction(tcp *api.TCPSocketAction, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
 | 
						return ValidatePortNumOrName(tcp.Port, fldPath.Child("port"))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validateHandler(handler *api.Handler, fldPath *field.Path) field.ErrorList {
 | 
					func validateHandler(handler *api.Handler, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
@@ -1881,16 +1887,19 @@ func ValidatePodSecurityContext(securityContext *api.PodSecurityContext, spec *a
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	if securityContext != nil {
 | 
						if securityContext != nil {
 | 
				
			||||||
		allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers, specPath.Child("containers"))...)
 | 
							allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers, specPath.Child("containers"))...)
 | 
				
			||||||
		if securityContext.FSGroup != nil && !validation.IsValidGroupId(*securityContext.FSGroup) {
 | 
							if securityContext.FSGroup != nil {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("fsGroup"), *(securityContext.FSGroup), IdRangeErrorMsg))
 | 
								for _, msg := range validation.IsValidGroupId(*securityContext.FSGroup) {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(fldPath.Child("fsGroup"), *(securityContext.FSGroup), msg))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if securityContext.RunAsUser != nil && !validation.IsValidUserId(*securityContext.RunAsUser) {
 | 
							if securityContext.RunAsUser != nil {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *(securityContext.RunAsUser), IdRangeErrorMsg))
 | 
								for _, msg := range validation.IsValidUserId(*securityContext.RunAsUser) {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(fldPath.Child("runAsUser"), *(securityContext.RunAsUser), msg))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		for i, gid := range securityContext.SupplementalGroups {
 | 
							for g, gid := range securityContext.SupplementalGroups {
 | 
				
			||||||
			if !validation.IsValidGroupId(gid) {
 | 
								for _, msg := range validation.IsValidGroupId(gid) {
 | 
				
			||||||
				supplementalGroup := fmt.Sprintf(`supplementalGroups[%d]`, i)
 | 
									allErrs = append(allErrs, field.Invalid(fldPath.Child("supplementalGroups").Index(g), gid, msg))
 | 
				
			||||||
				allErrs = append(allErrs, field.Invalid(fldPath.Child(supplementalGroup), gid, IdRangeErrorMsg))
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -2064,10 +2073,13 @@ func ValidateService(service *api.Service) field.ErrorList {
 | 
				
			|||||||
	ipPath := specPath.Child("externalIPs")
 | 
						ipPath := specPath.Child("externalIPs")
 | 
				
			||||||
	for i, ip := range service.Spec.ExternalIPs {
 | 
						for i, ip := range service.Spec.ExternalIPs {
 | 
				
			||||||
		idxPath := ipPath.Index(i)
 | 
							idxPath := ipPath.Index(i)
 | 
				
			||||||
		if ip == "0.0.0.0" {
 | 
							if msgs := validation.IsValidIP(ip); len(msgs) != 0 {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(idxPath, ip, "must be a valid IP address"))
 | 
								for i := range msgs {
 | 
				
			||||||
 | 
									allErrs = append(allErrs, field.Invalid(idxPath, ip, msgs[i]))
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								allErrs = append(allErrs, validateNonSpecialIP(ip, idxPath)...)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		allErrs = append(allErrs, validateIpIsNotLinkLocalOrLoopback(ip, idxPath)...)
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(service.Spec.Type) == 0 {
 | 
						if len(service.Spec.Type) == 0 {
 | 
				
			||||||
@@ -2161,8 +2173,8 @@ func validateServicePort(sp *api.ServicePort, requireName, isHeadlessService boo
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !validation.IsValidPortNum(int(sp.Port)) {
 | 
						for _, msg := range validation.IsValidPortNum(int(sp.Port)) {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), sp.Port, PortRangeErrorMsg))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), sp.Port, msg))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if len(sp.Protocol) == 0 {
 | 
						if len(sp.Protocol) == 0 {
 | 
				
			||||||
@@ -2171,12 +2183,7 @@ func validateServicePort(sp *api.ServicePort, requireName, isHeadlessService boo
 | 
				
			|||||||
		allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), sp.Protocol, supportedPortProtocols.List()))
 | 
							allErrs = append(allErrs, field.NotSupported(fldPath.Child("protocol"), sp.Protocol, supportedPortProtocols.List()))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if sp.TargetPort.Type == intstr.Int && !validation.IsValidPortNum(sp.TargetPort.IntValue()) {
 | 
						allErrs = append(allErrs, ValidatePortNumOrName(sp.TargetPort, fldPath.Child("targetPort"))...)
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, PortRangeErrorMsg))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if sp.TargetPort.Type == intstr.String && !validation.IsValidPortName(sp.TargetPort.StrVal) {
 | 
					 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("targetPort"), sp.TargetPort, PortNameErrorMsg))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// in the v1 API, targetPorts on headless services were tolerated.
 | 
						// in the v1 API, targetPorts on headless services were tolerated.
 | 
				
			||||||
	// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
 | 
						// once we have version-specific validation, we can reject this on newer API versions, but until then, we have to tolerate it for compatibility.
 | 
				
			||||||
@@ -3030,8 +3037,8 @@ func validateEndpointSubsets(subsets []api.EndpointSubset, fldPath *field.Path)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func validateEndpointAddress(address *api.EndpointAddress, fldPath *field.Path) field.ErrorList {
 | 
					func validateEndpointAddress(address *api.EndpointAddress, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
	if !validation.IsValidIP(address.IP) {
 | 
						for _, msg := range validation.IsValidIP(address.IP) {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), address.IP, "must be a valid IP address"))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("ip"), address.IP, msg))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(address.Hostname) > 0 {
 | 
						if len(address.Hostname) > 0 {
 | 
				
			||||||
		for _, msg := range validation.IsDNS1123Label(address.Hostname) {
 | 
							for _, msg := range validation.IsDNS1123Label(address.Hostname) {
 | 
				
			||||||
@@ -3041,18 +3048,24 @@ func validateEndpointAddress(address *api.EndpointAddress, fldPath *field.Path)
 | 
				
			|||||||
	if len(allErrs) > 0 {
 | 
						if len(allErrs) > 0 {
 | 
				
			||||||
		return allErrs
 | 
							return allErrs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return validateIpIsNotLinkLocalOrLoopback(address.IP, fldPath.Child("ip"))
 | 
						allErrs = append(allErrs, validateNonSpecialIP(address.IP, fldPath.Child("ip"))...)
 | 
				
			||||||
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func validateIpIsNotLinkLocalOrLoopback(ipAddress string, fldPath *field.Path) field.ErrorList {
 | 
					func validateNonSpecialIP(ipAddress string, fldPath *field.Path) field.ErrorList {
 | 
				
			||||||
	// We disallow some IPs as endpoints or external-ips.  Specifically, loopback addresses are
 | 
						// We disallow some IPs as endpoints or external-ips.  Specifically,
 | 
				
			||||||
	// nonsensical and link-local addresses tend to be used for node-centric purposes (e.g. metadata service).
 | 
						// unspecified and loopback addresses are nonsensical and link-local
 | 
				
			||||||
 | 
						// addresses tend to be used for node-centric purposes (e.g. metadata
 | 
				
			||||||
 | 
						// service).
 | 
				
			||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
	ip := net.ParseIP(ipAddress)
 | 
						ip := net.ParseIP(ipAddress)
 | 
				
			||||||
	if ip == nil {
 | 
						if ip == nil {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "must be a valid IP address"))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "must be a valid IP address"))
 | 
				
			||||||
		return allErrs
 | 
							return allErrs
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if ip.IsUnspecified() {
 | 
				
			||||||
 | 
							allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be unspecified (0.0.0.0)"))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	if ip.IsLoopback() {
 | 
						if ip.IsLoopback() {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8)"))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath, ipAddress, "may not be in the loopback range (127.0.0.0/8)"))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -3074,8 +3087,8 @@ func validateEndpointPort(port *api.EndpointPort, requireName bool, fldPath *fie
 | 
				
			|||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), port.Name, msg))
 | 
								allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), port.Name, msg))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if !validation.IsValidPortNum(int(port.Port)) {
 | 
						for _, msg := range validation.IsValidPortNum(int(port.Port)) {
 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), port.Port, PortRangeErrorMsg))
 | 
							allErrs = append(allErrs, field.Invalid(fldPath.Child("port"), port.Port, msg))
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if len(port.Protocol) == 0 {
 | 
						if len(port.Protocol) == 0 {
 | 
				
			||||||
		allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
 | 
							allErrs = append(allErrs, field.Required(fldPath.Child("protocol"), ""))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1030,17 +1030,17 @@ func TestValidatePorts(t *testing.T) {
 | 
				
			|||||||
		"name > 15 characters": {
 | 
							"name > 15 characters": {
 | 
				
			||||||
			[]api.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}},
 | 
								[]api.ContainerPort{{Name: strings.Repeat("a", 16), ContainerPort: 80, Protocol: "TCP"}},
 | 
				
			||||||
			field.ErrorTypeInvalid,
 | 
								field.ErrorTypeInvalid,
 | 
				
			||||||
			"name", PortNameErrorMsg,
 | 
								"name", "15",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"name not a IANA svc name ": {
 | 
							"name contains invalid characters": {
 | 
				
			||||||
			[]api.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}},
 | 
								[]api.ContainerPort{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}},
 | 
				
			||||||
			field.ErrorTypeInvalid,
 | 
								field.ErrorTypeInvalid,
 | 
				
			||||||
			"name", PortNameErrorMsg,
 | 
								"name", "alpha-numeric",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"name not a IANA svc name (i.e. a number)": {
 | 
							"name is a number": {
 | 
				
			||||||
			[]api.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}},
 | 
								[]api.ContainerPort{{Name: "80", ContainerPort: 80, Protocol: "TCP"}},
 | 
				
			||||||
			field.ErrorTypeInvalid,
 | 
								field.ErrorTypeInvalid,
 | 
				
			||||||
			"name", PortNameErrorMsg,
 | 
								"name", "at least one letter",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"name not unique": {
 | 
							"name not unique": {
 | 
				
			||||||
			[]api.ContainerPort{
 | 
								[]api.ContainerPort{
 | 
				
			||||||
@@ -1052,18 +1052,18 @@ func TestValidatePorts(t *testing.T) {
 | 
				
			|||||||
		},
 | 
							},
 | 
				
			||||||
		"zero container port": {
 | 
							"zero container port": {
 | 
				
			||||||
			[]api.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}},
 | 
								[]api.ContainerPort{{ContainerPort: 0, Protocol: "TCP"}},
 | 
				
			||||||
			field.ErrorTypeInvalid,
 | 
								field.ErrorTypeRequired,
 | 
				
			||||||
			"containerPort", PortRangeErrorMsg,
 | 
								"containerPort", "",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"invalid container port": {
 | 
							"invalid container port": {
 | 
				
			||||||
			[]api.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}},
 | 
								[]api.ContainerPort{{ContainerPort: 65536, Protocol: "TCP"}},
 | 
				
			||||||
			field.ErrorTypeInvalid,
 | 
								field.ErrorTypeInvalid,
 | 
				
			||||||
			"containerPort", PortRangeErrorMsg,
 | 
								"containerPort", "between",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"invalid host port": {
 | 
							"invalid host port": {
 | 
				
			||||||
			[]api.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}},
 | 
								[]api.ContainerPort{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}},
 | 
				
			||||||
			field.ErrorTypeInvalid,
 | 
								field.ErrorTypeInvalid,
 | 
				
			||||||
			"hostPort", PortRangeErrorMsg,
 | 
								"hostPort", "between",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"invalid protocol case": {
 | 
							"invalid protocol case": {
 | 
				
			||||||
			[]api.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}},
 | 
								[]api.ContainerPort{{ContainerPort: 80, Protocol: "tcp"}},
 | 
				
			||||||
@@ -1155,7 +1155,7 @@ func TestValidateEnv(t *testing.T) {
 | 
				
			|||||||
		{
 | 
							{
 | 
				
			||||||
			name:          "name not a C identifier",
 | 
								name:          "name not a C identifier",
 | 
				
			||||||
			envs:          []api.EnvVar{{Name: "a.b.c"}},
 | 
								envs:          []api.EnvVar{{Name: "a.b.c"}},
 | 
				
			||||||
			expectedError: `[0].name: Invalid value: "a.b.c": must be a C identifier (matching regex [A-Za-z_][A-Za-z0-9_]*): e.g. "my_name" or "MyName"`,
 | 
								expectedError: `[0].name: Invalid value: "a.b.c": must match the regex`,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "value and valueFrom specified",
 | 
								name: "value and valueFrom specified",
 | 
				
			||||||
@@ -3489,12 +3489,19 @@ func TestValidateService(t *testing.T) {
 | 
				
			|||||||
			numErrs: 1,
 | 
								numErrs: 1,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "invalid publicIPs",
 | 
								name: "invalid publicIPs unspecified",
 | 
				
			||||||
			tweakSvc: func(s *api.Service) {
 | 
								tweakSvc: func(s *api.Service) {
 | 
				
			||||||
				s.Spec.ExternalIPs = []string{"0.0.0.0"}
 | 
									s.Spec.ExternalIPs = []string{"0.0.0.0"}
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			numErrs: 1,
 | 
								numErrs: 1,
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								name: "invalid publicIPs loopback",
 | 
				
			||||||
 | 
								tweakSvc: func(s *api.Service) {
 | 
				
			||||||
 | 
									s.Spec.ExternalIPs = []string{"127.0.0.1"}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
								numErrs: 1,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			name: "invalid publicIPs host",
 | 
								name: "invalid publicIPs host",
 | 
				
			||||||
			tweakSvc: func(s *api.Service) {
 | 
								tweakSvc: func(s *api.Service) {
 | 
				
			||||||
@@ -5968,7 +5975,7 @@ func TestValidateEndpoints(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			errorType:   "FieldValueInvalid",
 | 
								errorType:   "FieldValueInvalid",
 | 
				
			||||||
			errorDetail: PortRangeErrorMsg,
 | 
								errorDetail: "between",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"Invalid protocol": {
 | 
							"Invalid protocol": {
 | 
				
			||||||
			endpoints: api.Endpoints{
 | 
								endpoints: api.Endpoints{
 | 
				
			||||||
@@ -6006,7 +6013,7 @@ func TestValidateEndpoints(t *testing.T) {
 | 
				
			|||||||
				},
 | 
									},
 | 
				
			||||||
			},
 | 
								},
 | 
				
			||||||
			errorType:   "FieldValueInvalid",
 | 
								errorType:   "FieldValueInvalid",
 | 
				
			||||||
			errorDetail: PortRangeErrorMsg,
 | 
								errorDetail: "between",
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
		"Port missing protocol": {
 | 
							"Port missing protocol": {
 | 
				
			||||||
			endpoints: api.Endpoints{
 | 
								endpoints: api.Endpoints{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -149,8 +149,8 @@ func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *fiel
 | 
				
			|||||||
	allErrs := field.ErrorList{}
 | 
						allErrs := field.ErrorList{}
 | 
				
			||||||
	switch intOrPercent.Type {
 | 
						switch intOrPercent.Type {
 | 
				
			||||||
	case intstr.String:
 | 
						case intstr.String:
 | 
				
			||||||
		if !validation.IsValidPercent(intOrPercent.StrVal) {
 | 
							for _, msg := range validation.IsValidPercent(intOrPercent.StrVal) {
 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, "must be an integer or percentage (e.g '5%%')"))
 | 
								allErrs = append(allErrs, field.Invalid(fldPath, intOrPercent, msg))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	case intstr.Int:
 | 
						case intstr.Int:
 | 
				
			||||||
		allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...)
 | 
							allErrs = append(allErrs, apivalidation.ValidateNonnegativeField(int64(intOrPercent.IntValue()), fldPath)...)
 | 
				
			||||||
@@ -161,7 +161,10 @@ func ValidatePositiveIntOrPercent(intOrPercent intstr.IntOrString, fldPath *fiel
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) {
 | 
					func getPercentValue(intOrStringValue intstr.IntOrString) (int, bool) {
 | 
				
			||||||
	if intOrStringValue.Type != intstr.String || !validation.IsValidPercent(intOrStringValue.StrVal) {
 | 
						if intOrStringValue.Type != intstr.String {
 | 
				
			||||||
 | 
							return 0, false
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(validation.IsValidPercent(intOrStringValue.StrVal)) != 0 {
 | 
				
			||||||
		return 0, false
 | 
							return 0, false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1])
 | 
						value, _ := strconv.Atoi(intOrStringValue.StrVal[:len(intOrStringValue.StrVal)-1])
 | 
				
			||||||
@@ -425,16 +428,7 @@ func validateIngressBackend(backend *extensions.IngressBackend, fldPath *field.P
 | 
				
			|||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, msg))
 | 
								allErrs = append(allErrs, field.Invalid(fldPath.Child("serviceName"), backend.ServiceName, msg))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if backend.ServicePort.Type == intstr.String {
 | 
						allErrs = append(allErrs, apivalidation.ValidatePortNumOrName(backend.ServicePort, fldPath.Child("servicePort"))...)
 | 
				
			||||||
		for _, msg := range validation.IsDNS1123Label(backend.ServicePort.StrVal) {
 | 
					 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("servicePort"), backend.ServicePort.StrVal, msg))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		if !validation.IsValidPortName(backend.ServicePort.StrVal) {
 | 
					 | 
				
			||||||
			allErrs = append(allErrs, field.Invalid(fldPath.Child("servicePort"), backend.ServicePort.StrVal, apivalidation.PortNameErrorMsg))
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if !validation.IsValidPortNum(backend.ServicePort.IntValue()) {
 | 
					 | 
				
			||||||
		allErrs = append(allErrs, field.Invalid(fldPath.Child("servicePort"), backend.ServicePort, apivalidation.PortRangeErrorMsg))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return allErrs
 | 
						return allErrs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -632,7 +632,7 @@ func TestValidateDeployment(t *testing.T) {
 | 
				
			|||||||
			MaxSurge: intstr.FromString("20Percent"),
 | 
								MaxSurge: intstr.FromString("20Percent"),
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	errorCases["must be an integer or percentage"] = invalidMaxSurgeDeployment
 | 
						errorCases["must match the regex"] = invalidMaxSurgeDeployment
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// MaxSurge and MaxUnavailable cannot both be zero.
 | 
						// MaxSurge and MaxUnavailable cannot both be zero.
 | 
				
			||||||
	invalidRollingUpdateDeployment := validDeployment()
 | 
						invalidRollingUpdateDeployment := validDeployment()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -835,7 +835,10 @@ func parseEnvs(envArray []string) ([]api.EnvVar, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		name := env[:pos]
 | 
							name := env[:pos]
 | 
				
			||||||
		value := env[pos+1:]
 | 
							value := env[pos+1:]
 | 
				
			||||||
		if len(name) == 0 || !validation.IsCIdentifier(name) || len(value) == 0 {
 | 
							if len(name) == 0 || len(value) == 0 {
 | 
				
			||||||
 | 
								return nil, fmt.Errorf("invalid env: %v", env)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if len(validation.IsCIdentifier(name)) != 0 {
 | 
				
			||||||
			return nil, fmt.Errorf("invalid env: %v", env)
 | 
								return nil, fmt.Errorf("invalid env: %v", env)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		envVar := api.EnvVar{Name: name, Value: value}
 | 
							envVar := api.EnvVar{Name: name, Value: value}
 | 
				
			||||||
@@ -853,7 +856,7 @@ func parseV1Envs(envArray []string) ([]v1.EnvVar, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		name := env[:pos]
 | 
							name := env[:pos]
 | 
				
			||||||
		value := env[pos+1:]
 | 
							value := env[pos+1:]
 | 
				
			||||||
		if len(name) == 0 || !validation.IsCIdentifier(name) || len(value) == 0 {
 | 
							if len(name) == 0 || len(validation.IsCIdentifier(name)) != 0 || len(value) == 0 {
 | 
				
			||||||
			return nil, fmt.Errorf("invalid env: %v", env)
 | 
								return nil, fmt.Errorf("invalid env: %v", env)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		envVar := v1.EnvVar{Name: name, Value: value}
 | 
							envVar := v1.EnvVar{Name: name, Value: value}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -145,13 +145,19 @@ var cIdentifierRegexp = regexp.MustCompile("^" + CIdentifierFmt + "$")
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// IsCIdentifier tests for a string that conforms the definition of an identifier
 | 
					// IsCIdentifier tests for a string that conforms the definition of an identifier
 | 
				
			||||||
// in C. This checks the format, but not the length.
 | 
					// in C. This checks the format, but not the length.
 | 
				
			||||||
func IsCIdentifier(value string) bool {
 | 
					func IsCIdentifier(value string) []string {
 | 
				
			||||||
	return cIdentifierRegexp.MatchString(value)
 | 
						if !cIdentifierRegexp.MatchString(value) {
 | 
				
			||||||
 | 
							return []string{RegexError(CIdentifierFmt, "my_name", "MY_NAME", "MyName")}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsValidPortNum tests that the argument is a valid, non-zero port number.
 | 
					// IsValidPortNum tests that the argument is a valid, non-zero port number.
 | 
				
			||||||
func IsValidPortNum(port int) bool {
 | 
					func IsValidPortNum(port int) []string {
 | 
				
			||||||
	return 0 < port && port < 65536
 | 
						if 1 <= port && port <= 65535 {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return []string{InclusiveRangeError(1, 65535)}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
 | 
					// Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
 | 
				
			||||||
@@ -163,67 +169,82 @@ const (
 | 
				
			|||||||
	maxGroupID = math.MaxInt32
 | 
						maxGroupID = math.MaxInt32
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsValidGroupId tests that the argument is a valid gids.
 | 
					// IsValidGroupId tests that the argument is a valid Unix GID.
 | 
				
			||||||
func IsValidGroupId(gid int64) bool {
 | 
					func IsValidGroupId(gid int64) []string {
 | 
				
			||||||
	return minGroupID <= gid && gid <= maxGroupID
 | 
						if minGroupID <= gid && gid <= maxGroupID {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return []string{InclusiveRangeError(minGroupID, maxGroupID)}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsValidUserId tests that the argument is a valid uids.
 | 
					// IsValidUserId tests that the argument is a valid Unix UID.
 | 
				
			||||||
func IsValidUserId(uid int64) bool {
 | 
					func IsValidUserId(uid int64) []string {
 | 
				
			||||||
	return minUserID <= uid && uid <= maxUserID
 | 
						if minUserID <= uid && uid <= maxUserID {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return []string{InclusiveRangeError(minUserID, maxUserID)}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const doubleHyphensFmt string = ".*(--).*"
 | 
					var portNameCharsetRegex = regexp.MustCompile("^[-a-z0-9]+$")
 | 
				
			||||||
 | 
					var portNameOneLetterRegexp = regexp.MustCompile("[a-z]")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var doubleHyphensRegexp = regexp.MustCompile("^" + doubleHyphensFmt + "$")
 | 
					// IsValidPortName check that the argument is valid syntax. It must be
 | 
				
			||||||
 | 
					// non-empty and no more than 15 characters long. It may contain only [-a-z0-9]
 | 
				
			||||||
const IdentifierNoHyphensBeginEndFmt string = "[a-z0-9]([a-z0-9-]*[a-z0-9])*"
 | 
					// and must contain at least one letter [a-z]. It must not start or end with a
 | 
				
			||||||
 | 
					// hyphen, nor contain adjacent hyphens.
 | 
				
			||||||
var identifierNoHyphensBeginEndRegexp = regexp.MustCompile("^" + IdentifierNoHyphensBeginEndFmt + "$")
 | 
					//
 | 
				
			||||||
 | 
					// Note: We only allow lower-case characters, even though RFC 6335 is case
 | 
				
			||||||
const atLeastOneLetterFmt string = ".*[a-z].*"
 | 
					// insensitive.
 | 
				
			||||||
 | 
					func IsValidPortName(port string) []string {
 | 
				
			||||||
var atLeastOneLetterRegexp = regexp.MustCompile("^" + atLeastOneLetterFmt + "$")
 | 
						var errs []string
 | 
				
			||||||
 | 
						if len(port) > 15 {
 | 
				
			||||||
// IsValidPortName check that the argument is valid syntax. It must be non empty and no more than 15 characters long
 | 
							errs = append(errs, MaxLenError(15))
 | 
				
			||||||
// It must contains at least one letter [a-z] and it must contains only [a-z0-9-].
 | 
					 | 
				
			||||||
// Hypens ('-') cannot be leading or trailing character of the string and cannot be adjacent to other hyphens.
 | 
					 | 
				
			||||||
// Although RFC 6335 allows upper and lower case characters but case is ignored for comparison purposes: (HTTP
 | 
					 | 
				
			||||||
// and http denote the same service).
 | 
					 | 
				
			||||||
func IsValidPortName(port string) bool {
 | 
					 | 
				
			||||||
	if len(port) < 1 || len(port) > 15 {
 | 
					 | 
				
			||||||
		return false
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if doubleHyphensRegexp.MatchString(port) {
 | 
						if !portNameCharsetRegex.MatchString(port) {
 | 
				
			||||||
		return false
 | 
							errs = append(errs, "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if identifierNoHyphensBeginEndRegexp.MatchString(port) && atLeastOneLetterRegexp.MatchString(port) {
 | 
						if !portNameOneLetterRegexp.MatchString(port) {
 | 
				
			||||||
		return true
 | 
							errs = append(errs, "must contain at least one letter (a-z)")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false
 | 
						if strings.Contains(port, "--") {
 | 
				
			||||||
 | 
							errs = append(errs, "must not contain consecutive hyphens")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if len(port) > 0 && (port[0] == '-' || port[len(port)-1] == '-') {
 | 
				
			||||||
 | 
							errs = append(errs, "must not begin or end with a hyphen")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return errs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsValidIP tests that the argument is a valid IP address.
 | 
					// IsValidIP tests that the argument is a valid IP address.
 | 
				
			||||||
func IsValidIP(value string) bool {
 | 
					func IsValidIP(value string) []string {
 | 
				
			||||||
	return net.ParseIP(value) != nil
 | 
						if net.ParseIP(value) == nil {
 | 
				
			||||||
 | 
							return []string{"must be a valid IP address, (e.g. 10.9.8.7)"}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const percentFmt string = "[0-9]+%"
 | 
					const percentFmt string = "[0-9]+%"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")
 | 
					var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func IsValidPercent(percent string) bool {
 | 
					func IsValidPercent(percent string) []string {
 | 
				
			||||||
	return percentRegexp.MatchString(percent)
 | 
						if !percentRegexp.MatchString(percent) {
 | 
				
			||||||
 | 
							return []string{RegexError(percentFmt, "1%", "93%")}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const HTTPHeaderNameFmt string = "[-A-Za-z0-9]+"
 | 
					const httpHeaderNameFmt string = "[-A-Za-z0-9]+"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var httpHeaderNameRegexp = regexp.MustCompile("^" + HTTPHeaderNameFmt + "$")
 | 
					var httpHeaderNameRegexp = regexp.MustCompile("^" + httpHeaderNameFmt + "$")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
 | 
					// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
 | 
				
			||||||
// definition of a valid header field name (a stricter subset than RFC7230).
 | 
					// definition of a valid header field name (a stricter subset than RFC7230).
 | 
				
			||||||
func IsHTTPHeaderName(value string) bool {
 | 
					func IsHTTPHeaderName(value string) []string {
 | 
				
			||||||
	return httpHeaderNameRegexp.MatchString(value)
 | 
						if !httpHeaderNameRegexp.MatchString(value) {
 | 
				
			||||||
 | 
							return []string{RegexError(httpHeaderNameFmt, "X-Header-Name")}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// MaxLenError returns a string explanation of a "string too long" validation
 | 
					// MaxLenError returns a string explanation of a "string too long" validation
 | 
				
			||||||
@@ -260,3 +281,9 @@ func prefixEach(msgs []string, prefix string) []string {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return msgs
 | 
						return msgs
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// InclusiveRangeError returns a string explanation of a numeric "must be
 | 
				
			||||||
 | 
					// between" validation failure.
 | 
				
			||||||
 | 
					func InclusiveRangeError(lo, hi int) string {
 | 
				
			||||||
 | 
						return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -119,8 +119,8 @@ func TestIsCIdentifier(t *testing.T) {
 | 
				
			|||||||
		"A", "AB", "AbC", "A1", "_A", "A_", "A_B", "A_1", "A__1__2__B", "__123_ABC",
 | 
							"A", "AB", "AbC", "A1", "_A", "A_", "A_B", "A_1", "A__1__2__B", "__123_ABC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsCIdentifier(val) {
 | 
							if msgs := IsCIdentifier(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for '%s'", val)
 | 
								t.Errorf("expected true for '%s': %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -132,7 +132,7 @@ func TestIsCIdentifier(t *testing.T) {
 | 
				
			|||||||
		"#a#",
 | 
							"#a#",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsCIdentifier(val) {
 | 
							if msgs := IsCIdentifier(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for '%s'", val)
 | 
								t.Errorf("expected false for '%s'", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -141,15 +141,15 @@ func TestIsCIdentifier(t *testing.T) {
 | 
				
			|||||||
func TestIsValidPortNum(t *testing.T) {
 | 
					func TestIsValidPortNum(t *testing.T) {
 | 
				
			||||||
	goodValues := []int{1, 2, 1000, 16384, 32768, 65535}
 | 
						goodValues := []int{1, 2, 1000, 16384, 32768, 65535}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsValidPortNum(val) {
 | 
							if msgs := IsValidPortNum(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for '%d'", val)
 | 
								t.Errorf("expected true for %d, got %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	badValues := []int{0, -1, 65536, 100000}
 | 
						badValues := []int{0, -1, 65536, 100000}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsValidPortNum(val) {
 | 
							if msgs := IsValidPortNum(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for '%d'", val)
 | 
								t.Errorf("expected false for %d", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -157,14 +157,14 @@ func TestIsValidPortNum(t *testing.T) {
 | 
				
			|||||||
func TestIsValidGroupId(t *testing.T) {
 | 
					func TestIsValidGroupId(t *testing.T) {
 | 
				
			||||||
	goodValues := []int64{0, 1, 1000, 65535, 2147483647}
 | 
						goodValues := []int64{0, 1, 1000, 65535, 2147483647}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsValidGroupId(val) {
 | 
							if msgs := IsValidGroupId(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for '%d'", val)
 | 
								t.Errorf("expected true for '%d': %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	badValues := []int64{-1, -1003, 2147483648, 4147483647}
 | 
						badValues := []int64{-1, -1003, 2147483648, 4147483647}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsValidGroupId(val) {
 | 
							if msgs := IsValidGroupId(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for '%d'", val)
 | 
								t.Errorf("expected false for '%d'", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -173,14 +173,14 @@ func TestIsValidGroupId(t *testing.T) {
 | 
				
			|||||||
func TestIsValidUserId(t *testing.T) {
 | 
					func TestIsValidUserId(t *testing.T) {
 | 
				
			||||||
	goodValues := []int64{0, 1, 1000, 65535, 2147483647}
 | 
						goodValues := []int64{0, 1, 1000, 65535, 2147483647}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsValidUserId(val) {
 | 
							if msgs := IsValidUserId(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for '%d'", val)
 | 
								t.Errorf("expected true for '%d': %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	badValues := []int64{-1, -1003, 2147483648, 4147483647}
 | 
						badValues := []int64{-1, -1003, 2147483648, 4147483647}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsValidUserId(val) {
 | 
							if msgs := IsValidUserId(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for '%d'", val)
 | 
								t.Errorf("expected false for '%d'", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -189,14 +189,14 @@ func TestIsValidUserId(t *testing.T) {
 | 
				
			|||||||
func TestIsValidPortName(t *testing.T) {
 | 
					func TestIsValidPortName(t *testing.T) {
 | 
				
			||||||
	goodValues := []string{"telnet", "re-mail-ck", "pop3", "a", "a-1", "1-a", "a-1-b-2-c", "1-a-2-b-3"}
 | 
						goodValues := []string{"telnet", "re-mail-ck", "pop3", "a", "a-1", "1-a", "a-1-b-2-c", "1-a-2-b-3"}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsValidPortName(val) {
 | 
							if msgs := IsValidPortName(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for %q", val)
 | 
								t.Errorf("expected true for %q: %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	badValues := []string{"longerthan15characters", "", "12345", "1-2-3-4", "-begin", "end-", "two--hyphens", "1-2", "whois++"}
 | 
						badValues := []string{"longerthan15characters", "", strings.Repeat("a", 16), "12345", "1-2-3-4", "-begin", "end-", "two--hyphens", "whois++"}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsValidPortName(val) {
 | 
							if msgs := IsValidPortName(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for %q", val)
 | 
								t.Errorf("expected false for %q", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -293,8 +293,8 @@ func TestIsValidIP(t *testing.T) {
 | 
				
			|||||||
		"0.0.0.0",
 | 
							"0.0.0.0",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsValidIP(val) {
 | 
							if msgs := IsValidIP(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for %q", val)
 | 
								t.Errorf("expected true for %q: %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -306,7 +306,7 @@ func TestIsValidIP(t *testing.T) {
 | 
				
			|||||||
		"a",
 | 
							"a",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsValidIP(val) {
 | 
							if msgs := IsValidIP(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for %q", val)
 | 
								t.Errorf("expected false for %q", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -321,8 +321,8 @@ func TestIsHTTPHeaderName(t *testing.T) {
 | 
				
			|||||||
		"A", "AB", "AbC", "A1", "-A", "A-", "A-B", "A-1", "A--1--2--B", "--123-ABC",
 | 
							"A", "AB", "AbC", "A1", "-A", "A-", "A-B", "A-1", "A--1--2--B", "--123-ABC",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, val := range goodValues {
 | 
						for _, val := range goodValues {
 | 
				
			||||||
		if !IsHTTPHeaderName(val) {
 | 
							if msgs := IsHTTPHeaderName(val); len(msgs) != 0 {
 | 
				
			||||||
			t.Errorf("expected true for '%s'", val)
 | 
								t.Errorf("expected true for '%s': %v", val, msgs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -333,8 +333,44 @@ func TestIsHTTPHeaderName(t *testing.T) {
 | 
				
			|||||||
		"?", "@", "{",
 | 
							"?", "@", "{",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, val := range badValues {
 | 
						for _, val := range badValues {
 | 
				
			||||||
		if IsHTTPHeaderName(val) {
 | 
							if msgs := IsHTTPHeaderName(val); len(msgs) == 0 {
 | 
				
			||||||
			t.Errorf("expected false for '%s'", val)
 | 
								t.Errorf("expected false for '%s'", val)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestIsValidPercent(t *testing.T) {
 | 
				
			||||||
 | 
						goodValues := []string{
 | 
				
			||||||
 | 
							"0%",
 | 
				
			||||||
 | 
							"00000%",
 | 
				
			||||||
 | 
							"1%",
 | 
				
			||||||
 | 
							"01%",
 | 
				
			||||||
 | 
							"99%",
 | 
				
			||||||
 | 
							"100%",
 | 
				
			||||||
 | 
							"101%",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, val := range goodValues {
 | 
				
			||||||
 | 
							if msgs := IsValidPercent(val); len(msgs) != 0 {
 | 
				
			||||||
 | 
								t.Errorf("expected true for %q: %v", val, msgs)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						badValues := []string{
 | 
				
			||||||
 | 
							"",
 | 
				
			||||||
 | 
							"0",
 | 
				
			||||||
 | 
							"100",
 | 
				
			||||||
 | 
							"0.0%",
 | 
				
			||||||
 | 
							"99.9%",
 | 
				
			||||||
 | 
							"hundred",
 | 
				
			||||||
 | 
							" 1%",
 | 
				
			||||||
 | 
							"1% ",
 | 
				
			||||||
 | 
							"-0%",
 | 
				
			||||||
 | 
							"-1%",
 | 
				
			||||||
 | 
							"+1%",
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						for _, val := range badValues {
 | 
				
			||||||
 | 
							if msgs := IsValidPercent(val); len(msgs) == 0 {
 | 
				
			||||||
 | 
								t.Errorf("expected false for %q", val)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user