...

Source file src/sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/validation.go

Documentation: sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation

     1  // Code generated by k8scopy from k8s.io/apimachinery@v0.19.8; DO NOT EDIT.
     2  // File content copied from k8s.io/apimachinery@v0.19.8/pkg/util/validation/validation.go
     3  
     4  /*
     5  Copyright 2014 The Kubernetes Authors.
     6  
     7  Licensed under the Apache License, Version 2.0 (the "License");
     8  you may not use this file except in compliance with the License.
     9  You may obtain a copy of the License at
    10  
    11      http://www.apache.org/licenses/LICENSE-2.0
    12  
    13  Unless required by applicable law or agreed to in writing, software
    14  distributed under the License is distributed on an "AS IS" BASIS,
    15  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  See the License for the specific language governing permissions and
    17  limitations under the License.
    18  */
    19  
    20  package validation
    21  
    22  import (
    23  	"fmt"
    24  	"math"
    25  	"net"
    26  	"regexp"
    27  	"strconv"
    28  	"strings"
    29  
    30  	"sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/field"
    31  )
    32  
    33  const qnameCharFmt string = "[A-Za-z0-9]"
    34  const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
    35  const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
    36  const qualifiedNameErrMsg string = "must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
    37  const qualifiedNameMaxLength int = 63
    38  
    39  var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")
    40  
    41  // IsQualifiedName tests whether the value passed is what Kubernetes calls a
    42  // "qualified name".  This is a format used in various places throughout the
    43  // system.  If the value is not valid, a list of error strings is returned.
    44  // Otherwise an empty list (or nil) is returned.
    45  func IsQualifiedName(value string) []string {
    46  	var errs []string
    47  	parts := strings.Split(value, "/")
    48  	var name string
    49  	switch len(parts) {
    50  	case 1:
    51  		name = parts[0]
    52  	case 2:
    53  		var prefix string
    54  		prefix, name = parts[0], parts[1]
    55  		if len(prefix) == 0 {
    56  			errs = append(errs, "prefix part "+EmptyError())
    57  		} else if msgs := IsDNS1123Subdomain(prefix); len(msgs) != 0 {
    58  			errs = append(errs, prefixEach(msgs, "prefix part ")...)
    59  		}
    60  	default:
    61  		return append(errs, "a qualified name "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc")+
    62  			" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')")
    63  	}
    64  
    65  	if len(name) == 0 {
    66  		errs = append(errs, "name part "+EmptyError())
    67  	} else if len(name) > qualifiedNameMaxLength {
    68  		errs = append(errs, "name part "+MaxLenError(qualifiedNameMaxLength))
    69  	}
    70  	if !qualifiedNameRegexp.MatchString(name) {
    71  		errs = append(errs, "name part "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc"))
    72  	}
    73  	return errs
    74  }
    75  
    76  // IsFullyQualifiedName checks if the name is fully qualified. This is similar
    77  // to IsFullyQualifiedDomainName but requires a minimum of 3 segments instead of
    78  // 2 and does not accept a trailing . as valid.
    79  // TODO: This function is deprecated and preserved until all callers migrate to
    80  // IsFullyQualifiedDomainName; please don't add new callers.
    81  func IsFullyQualifiedName(fldPath *field.Path, name string) field.ErrorList {
    82  	var allErrors field.ErrorList
    83  	if len(name) == 0 {
    84  		return append(allErrors, field.Required(fldPath, ""))
    85  	}
    86  	if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
    87  		return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
    88  	}
    89  	if len(strings.Split(name, ".")) < 3 {
    90  		return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least three segments separated by dots"))
    91  	}
    92  	return allErrors
    93  }
    94  
    95  // IsFullyQualifiedDomainName checks if the domain name is fully qualified. This
    96  // is similar to IsFullyQualifiedName but only requires a minimum of 2 segments
    97  // instead of 3 and accepts a trailing . as valid.
    98  func IsFullyQualifiedDomainName(fldPath *field.Path, name string) field.ErrorList {
    99  	var allErrors field.ErrorList
   100  	if len(name) == 0 {
   101  		return append(allErrors, field.Required(fldPath, ""))
   102  	}
   103  	if strings.HasSuffix(name, ".") {
   104  		name = name[:len(name)-1]
   105  	}
   106  	if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
   107  		return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
   108  	}
   109  	if len(strings.Split(name, ".")) < 2 {
   110  		return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least two segments separated by dots"))
   111  	}
   112  	for _, label := range strings.Split(name, ".") {
   113  		if errs := IsDNS1123Label(label); len(errs) > 0 {
   114  			return append(allErrors, field.Invalid(fldPath, label, strings.Join(errs, ",")))
   115  		}
   116  	}
   117  	return allErrors
   118  }
   119  
   120  // Allowed characters in an HTTP Path as defined by RFC 3986. A HTTP path may
   121  // contain:
   122  // * unreserved characters (alphanumeric, '-', '.', '_', '~')
   123  // * percent-encoded octets
   124  // * sub-delims ("!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "=")
   125  // * a colon character (":")
   126  const httpPathFmt string = `[A-Za-z0-9/\-._~%!$&'()*+,;=:]+`
   127  
   128  var httpPathRegexp = regexp.MustCompile("^" + httpPathFmt + "$")
   129  
   130  // IsDomainPrefixedPath checks if the given string is a domain-prefixed path
   131  // (e.g. acme.io/foo). All characters before the first "/" must be a valid
   132  // subdomain as defined by RFC 1123. All characters trailing the first "/" must
   133  // be valid HTTP Path characters as defined by RFC 3986.
   134  func IsDomainPrefixedPath(fldPath *field.Path, dpPath string) field.ErrorList {
   135  	var allErrs field.ErrorList
   136  	if len(dpPath) == 0 {
   137  		return append(allErrs, field.Required(fldPath, ""))
   138  	}
   139  
   140  	segments := strings.SplitN(dpPath, "/", 2)
   141  	if len(segments) != 2 || len(segments[0]) == 0 || len(segments[1]) == 0 {
   142  		return append(allErrs, field.Invalid(fldPath, dpPath, "must be a domain-prefixed path (such as \"acme.io/foo\")"))
   143  	}
   144  
   145  	host := segments[0]
   146  	for _, err := range IsDNS1123Subdomain(host) {
   147  		allErrs = append(allErrs, field.Invalid(fldPath, host, err))
   148  	}
   149  
   150  	path := segments[1]
   151  	if !httpPathRegexp.MatchString(path) {
   152  		return append(allErrs, field.Invalid(fldPath, path, RegexError("Invalid path", httpPathFmt)))
   153  	}
   154  
   155  	return allErrs
   156  }
   157  
   158  const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
   159  const labelValueErrMsg string = "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
   160  
   161  // LabelValueMaxLength is a label's max length
   162  const LabelValueMaxLength int = 63
   163  
   164  var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$")
   165  
   166  // IsValidLabelValue tests whether the value passed is a valid label value.  If
   167  // the value is not valid, a list of error strings is returned.  Otherwise an
   168  // empty list (or nil) is returned.
   169  func IsValidLabelValue(value string) []string {
   170  	var errs []string
   171  	if len(value) > LabelValueMaxLength {
   172  		errs = append(errs, MaxLenError(LabelValueMaxLength))
   173  	}
   174  	if !labelValueRegexp.MatchString(value) {
   175  		errs = append(errs, RegexError(labelValueErrMsg, labelValueFmt, "MyValue", "my_value", "12345"))
   176  	}
   177  	return errs
   178  }
   179  
   180  const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
   181  const dns1123LabelErrMsg string = "a DNS-1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character"
   182  
   183  // DNS1123LabelMaxLength is a label's max length in DNS (RFC 1123)
   184  const DNS1123LabelMaxLength int = 63
   185  
   186  var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")
   187  
   188  // IsDNS1123Label tests for a string that conforms to the definition of a label in
   189  // DNS (RFC 1123).
   190  func IsDNS1123Label(value string) []string {
   191  	var errs []string
   192  	if len(value) > DNS1123LabelMaxLength {
   193  		errs = append(errs, MaxLenError(DNS1123LabelMaxLength))
   194  	}
   195  	if !dns1123LabelRegexp.MatchString(value) {
   196  		errs = append(errs, RegexError(dns1123LabelErrMsg, dns1123LabelFmt, "my-name", "123-abc"))
   197  	}
   198  	return errs
   199  }
   200  
   201  const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
   202  const dns1123SubdomainErrorMsg string = "a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character"
   203  
   204  // DNS1123SubdomainMaxLength is a subdomain's max length in DNS (RFC 1123)
   205  const DNS1123SubdomainMaxLength int = 253
   206  
   207  var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")
   208  
   209  // IsDNS1123Subdomain tests for a string that conforms to the definition of a
   210  // subdomain in DNS (RFC 1123).
   211  func IsDNS1123Subdomain(value string) []string {
   212  	var errs []string
   213  	if len(value) > DNS1123SubdomainMaxLength {
   214  		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
   215  	}
   216  	if !dns1123SubdomainRegexp.MatchString(value) {
   217  		errs = append(errs, RegexError(dns1123SubdomainErrorMsg, dns1123SubdomainFmt, "example.com"))
   218  	}
   219  	return errs
   220  }
   221  
   222  const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
   223  const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character"
   224  
   225  // DNS1035LabelMaxLength is a label's max length in DNS (RFC 1035)
   226  const DNS1035LabelMaxLength int = 63
   227  
   228  var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$")
   229  
   230  // IsDNS1035Label tests for a string that conforms to the definition of a label in
   231  // DNS (RFC 1035).
   232  func IsDNS1035Label(value string) []string {
   233  	var errs []string
   234  	if len(value) > DNS1035LabelMaxLength {
   235  		errs = append(errs, MaxLenError(DNS1035LabelMaxLength))
   236  	}
   237  	if !dns1035LabelRegexp.MatchString(value) {
   238  		errs = append(errs, RegexError(dns1035LabelErrMsg, dns1035LabelFmt, "my-name", "abc-123"))
   239  	}
   240  	return errs
   241  }
   242  
   243  // wildcard definition - RFC 1034 section 4.3.3.
   244  // examples:
   245  // - valid: *.bar.com, *.foo.bar.com
   246  // - invalid: *.*.bar.com, *.foo.*.com, *bar.com, f*.bar.com, *
   247  const wildcardDNS1123SubdomainFmt = "\\*\\." + dns1123SubdomainFmt
   248  const wildcardDNS1123SubdomainErrMsg = "a wildcard DNS-1123 subdomain must start with '*.', followed by a valid DNS subdomain, which must consist of lower case alphanumeric characters, '-' or '.' and end with an alphanumeric character"
   249  
   250  // IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a
   251  // wildcard subdomain in DNS (RFC 1034 section 4.3.3).
   252  func IsWildcardDNS1123Subdomain(value string) []string {
   253  	wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^" + wildcardDNS1123SubdomainFmt + "$")
   254  
   255  	var errs []string
   256  	if len(value) > DNS1123SubdomainMaxLength {
   257  		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
   258  	}
   259  	if !wildcardDNS1123SubdomainRegexp.MatchString(value) {
   260  		errs = append(errs, RegexError(wildcardDNS1123SubdomainErrMsg, wildcardDNS1123SubdomainFmt, "*.example.com"))
   261  	}
   262  	return errs
   263  }
   264  
   265  const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"
   266  const identifierErrMsg string = "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_'"
   267  
   268  var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$")
   269  
   270  // IsCIdentifier tests for a string that conforms the definition of an identifier
   271  // in C. This checks the format, but not the length.
   272  func IsCIdentifier(value string) []string {
   273  	if !cIdentifierRegexp.MatchString(value) {
   274  		return []string{RegexError(identifierErrMsg, cIdentifierFmt, "my_name", "MY_NAME", "MyName")}
   275  	}
   276  	return nil
   277  }
   278  
   279  // IsValidPortNum tests that the argument is a valid, non-zero port number.
   280  func IsValidPortNum(port int) []string {
   281  	if 1 <= port && port <= 65535 {
   282  		return nil
   283  	}
   284  	return []string{InclusiveRangeError(1, 65535)}
   285  }
   286  
   287  // IsInRange tests that the argument is in an inclusive range.
   288  func IsInRange(value int, min int, max int) []string {
   289  	if value >= min && value <= max {
   290  		return nil
   291  	}
   292  	return []string{InclusiveRangeError(min, max)}
   293  }
   294  
   295  // Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
   296  // TODO: once we have a type for UID/GID we should make these that type.
   297  const (
   298  	minUserID  = 0
   299  	maxUserID  = math.MaxInt32
   300  	minGroupID = 0
   301  	maxGroupID = math.MaxInt32
   302  )
   303  
   304  // IsValidGroupID tests that the argument is a valid Unix GID.
   305  func IsValidGroupID(gid int64) []string {
   306  	if minGroupID <= gid && gid <= maxGroupID {
   307  		return nil
   308  	}
   309  	return []string{InclusiveRangeError(minGroupID, maxGroupID)}
   310  }
   311  
   312  // IsValidUserID tests that the argument is a valid Unix UID.
   313  func IsValidUserID(uid int64) []string {
   314  	if minUserID <= uid && uid <= maxUserID {
   315  		return nil
   316  	}
   317  	return []string{InclusiveRangeError(minUserID, maxUserID)}
   318  }
   319  
   320  var portNameCharsetRegex = regexp.MustCompile("^[-a-z0-9]+$")
   321  var portNameOneLetterRegexp = regexp.MustCompile("[a-z]")
   322  
   323  // IsValidPortName check that the argument is valid syntax. It must be
   324  // non-empty and no more than 15 characters long. It may contain only [-a-z0-9]
   325  // and must contain at least one letter [a-z]. It must not start or end with a
   326  // hyphen, nor contain adjacent hyphens.
   327  //
   328  // Note: We only allow lower-case characters, even though RFC 6335 is case
   329  // insensitive.
   330  func IsValidPortName(port string) []string {
   331  	var errs []string
   332  	if len(port) > 15 {
   333  		errs = append(errs, MaxLenError(15))
   334  	}
   335  	if !portNameCharsetRegex.MatchString(port) {
   336  		errs = append(errs, "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)")
   337  	}
   338  	if !portNameOneLetterRegexp.MatchString(port) {
   339  		errs = append(errs, "must contain at least one letter or number (a-z, 0-9)")
   340  	}
   341  	if strings.Contains(port, "--") {
   342  		errs = append(errs, "must not contain consecutive hyphens")
   343  	}
   344  	if len(port) > 0 && (port[0] == '-' || port[len(port)-1] == '-') {
   345  		errs = append(errs, "must not begin or end with a hyphen")
   346  	}
   347  	return errs
   348  }
   349  
   350  // IsValidIP tests that the argument is a valid IP address.
   351  func IsValidIP(value string) []string {
   352  	if net.ParseIP(value) == nil {
   353  		return []string{"must be a valid IP address, (e.g. 10.9.8.7)"}
   354  	}
   355  	return nil
   356  }
   357  
   358  // IsValidIPv4Address tests that the argument is a valid IPv4 address.
   359  func IsValidIPv4Address(fldPath *field.Path, value string) field.ErrorList {
   360  	var allErrors field.ErrorList
   361  	ip := net.ParseIP(value)
   362  	if ip == nil || ip.To4() == nil {
   363  		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv4 address"))
   364  	}
   365  	return allErrors
   366  }
   367  
   368  // IsValidIPv6Address tests that the argument is a valid IPv6 address.
   369  func IsValidIPv6Address(fldPath *field.Path, value string) field.ErrorList {
   370  	var allErrors field.ErrorList
   371  	ip := net.ParseIP(value)
   372  	if ip == nil || ip.To4() != nil {
   373  		allErrors = append(allErrors, field.Invalid(fldPath, value, "must be a valid IPv6 address"))
   374  	}
   375  	return allErrors
   376  }
   377  
   378  const percentFmt string = "[0-9]+%"
   379  const percentErrMsg string = "a valid percent string must be a numeric string followed by an ending '%'"
   380  
   381  var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")
   382  
   383  // IsValidPercent checks that string is in the form of a percentage
   384  func IsValidPercent(percent string) []string {
   385  	if !percentRegexp.MatchString(percent) {
   386  		return []string{RegexError(percentErrMsg, percentFmt, "1%", "93%")}
   387  	}
   388  	return nil
   389  }
   390  
   391  const httpHeaderNameFmt string = "[-A-Za-z0-9]+"
   392  const httpHeaderNameErrMsg string = "a valid HTTP header must consist of alphanumeric characters or '-'"
   393  
   394  var httpHeaderNameRegexp = regexp.MustCompile("^" + httpHeaderNameFmt + "$")
   395  
   396  // IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
   397  // definition of a valid header field name (a stricter subset than RFC7230).
   398  func IsHTTPHeaderName(value string) []string {
   399  	if !httpHeaderNameRegexp.MatchString(value) {
   400  		return []string{RegexError(httpHeaderNameErrMsg, httpHeaderNameFmt, "X-Header-Name")}
   401  	}
   402  	return nil
   403  }
   404  
   405  const envVarNameFmt = "[-._a-zA-Z][-._a-zA-Z0-9]*"
   406  const envVarNameFmtErrMsg string = "a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit"
   407  
   408  var envVarNameRegexp = regexp.MustCompile("^" + envVarNameFmt + "$")
   409  
   410  // IsEnvVarName tests if a string is a valid environment variable name.
   411  func IsEnvVarName(value string) []string {
   412  	var errs []string
   413  	if !envVarNameRegexp.MatchString(value) {
   414  		errs = append(errs, RegexError(envVarNameFmtErrMsg, envVarNameFmt, "my.env-name", "MY_ENV.NAME", "MyEnvName1"))
   415  	}
   416  
   417  	errs = append(errs, hasChDirPrefix(value)...)
   418  	return errs
   419  }
   420  
   421  const configMapKeyFmt = `[-._a-zA-Z0-9]+`
   422  const configMapKeyErrMsg string = "a valid config key must consist of alphanumeric characters, '-', '_' or '.'"
   423  
   424  var configMapKeyRegexp = regexp.MustCompile("^" + configMapKeyFmt + "$")
   425  
   426  // IsConfigMapKey tests for a string that is a valid key for a ConfigMap or Secret
   427  func IsConfigMapKey(value string) []string {
   428  	var errs []string
   429  	if len(value) > DNS1123SubdomainMaxLength {
   430  		errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
   431  	}
   432  	if !configMapKeyRegexp.MatchString(value) {
   433  		errs = append(errs, RegexError(configMapKeyErrMsg, configMapKeyFmt, "key.name", "KEY_NAME", "key-name"))
   434  	}
   435  	errs = append(errs, hasChDirPrefix(value)...)
   436  	return errs
   437  }
   438  
   439  // MaxLenError returns a string explanation of a "string too long" validation
   440  // failure.
   441  func MaxLenError(length int) string {
   442  	return fmt.Sprintf("must be no more than %d characters", length)
   443  }
   444  
   445  // RegexError returns a string explanation of a regex validation failure.
   446  func RegexError(msg string, fmt string, examples ...string) string {
   447  	if len(examples) == 0 {
   448  		return msg + " (regex used for validation is '" + fmt + "')"
   449  	}
   450  	msg += " (e.g. "
   451  	for i := range examples {
   452  		if i > 0 {
   453  			msg += " or "
   454  		}
   455  		msg += "'" + examples[i] + "', "
   456  	}
   457  	msg += "regex used for validation is '" + fmt + "')"
   458  	return msg
   459  }
   460  
   461  // EmptyError returns a string explanation of a "must not be empty" validation
   462  // failure.
   463  func EmptyError() string {
   464  	return "must be non-empty"
   465  }
   466  
   467  func prefixEach(msgs []string, prefix string) []string {
   468  	for i := range msgs {
   469  		msgs[i] = prefix + msgs[i]
   470  	}
   471  	return msgs
   472  }
   473  
   474  // InclusiveRangeError returns a string explanation of a numeric "must be
   475  // between" validation failure.
   476  func InclusiveRangeError(lo, hi int) string {
   477  	return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
   478  }
   479  
   480  func hasChDirPrefix(value string) []string {
   481  	var errs []string
   482  	switch {
   483  	case value == ".":
   484  		errs = append(errs, `must not be '.'`)
   485  	case value == "..":
   486  		errs = append(errs, `must not be '..'`)
   487  	case strings.HasPrefix(value, ".."):
   488  		errs = append(errs, `must not start with '..'`)
   489  	}
   490  	return errs
   491  }
   492  
   493  // IsValidSocketAddr checks that string represents a valid socket address
   494  // as defined in RFC 789. (e.g 0.0.0.0:10254 or [::]:10254))
   495  func IsValidSocketAddr(value string) []string {
   496  	var errs []string
   497  	ip, port, err := net.SplitHostPort(value)
   498  	if err != nil {
   499  		errs = append(errs, "must be a valid socket address format, (e.g. 0.0.0.0:10254 or [::]:10254)")
   500  		return errs
   501  	}
   502  	portInt, _ := strconv.Atoi(port)
   503  	errs = append(errs, IsValidPortNum(portInt)...)
   504  	errs = append(errs, IsValidIP(ip)...)
   505  	return errs
   506  }
   507  

View as plain text