mirror of
https://github.com/containers/podman.git
synced 2025-05-17 06:59:07 +08:00

Fixes new spotted issues around printf() formats and using os.Setenv() in tests. Signed-off-by: Paul Holzinger <pholzing@redhat.com>
126 lines
4.1 KiB
Go
126 lines
4.1 KiB
Go
package annotations
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/containers/podman/v5/libpod/define"
|
|
)
|
|
|
|
// regexErrorMsg returns a string explanation of a regex validation failure.
|
|
func regexErrorMsg(msg string, fmt string, examples ...string) string {
|
|
if len(examples) == 0 {
|
|
return msg + " (regex used for validation is '" + fmt + "')"
|
|
}
|
|
msg += " (e.g. "
|
|
for i := range examples {
|
|
if i > 0 {
|
|
msg += " or "
|
|
}
|
|
msg += "'" + examples[i] + "', "
|
|
}
|
|
msg += "regex used for validation is '" + fmt + "')"
|
|
return msg
|
|
}
|
|
|
|
const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
|
|
const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
|
|
const dns1123SubdomainErrorMsg string = "annotations must be formatted as a valid lowercase RFC1123 subdomain of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character"
|
|
|
|
// DNS1123SubdomainMaxLength is a subdomain's max length in DNS (RFC 1123)
|
|
const DNS1123SubdomainMaxLength int = 253
|
|
|
|
var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")
|
|
|
|
// isDNS1123Subdomain tests for a string that conforms to the definition of a
|
|
// subdomain in DNS (RFC 1123).
|
|
func isDNS1123Subdomain(value string) error {
|
|
if len(value) > DNS1123SubdomainMaxLength {
|
|
return fmt.Errorf("prefix part must be no more than %d characters", DNS1123SubdomainMaxLength)
|
|
}
|
|
|
|
if !dns1123SubdomainRegexp.MatchString(value) {
|
|
return errors.New(regexErrorMsg(dns1123SubdomainErrorMsg, dns1123SubdomainFmt, "example.com"))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
const qnameCharFmt string = "[A-Za-z0-9]"
|
|
const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
|
|
const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
|
|
const qualifiedNameErrMsg string = "must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
|
|
const qualifiedNameMaxLength int = 63
|
|
|
|
var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")
|
|
|
|
// isQualifiedName tests whether the value passed is what Kubernetes calls a
|
|
// "qualified name". This is a format used in various places throughout the
|
|
// system. If the value is not valid, a list of error strings is returned.
|
|
// Otherwise an empty list (or nil) is returned.
|
|
func isQualifiedName(value string) error {
|
|
parts := strings.Split(value, "/")
|
|
var name string
|
|
|
|
switch len(parts) {
|
|
case 1:
|
|
name = parts[0]
|
|
case 2:
|
|
var prefix string
|
|
prefix, name = parts[0], parts[1]
|
|
if len(prefix) == 0 {
|
|
return fmt.Errorf("prefix part of %s must be non-empty", value)
|
|
} else if err := isDNS1123Subdomain(prefix); err != nil {
|
|
return err
|
|
}
|
|
default:
|
|
return fmt.Errorf("a qualified name of %s "+
|
|
regexErrorMsg(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc")+
|
|
" with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')", value)
|
|
}
|
|
|
|
if len(name) == 0 {
|
|
return fmt.Errorf("name part of %s must be non-empty", value)
|
|
} else if len(name) > qualifiedNameMaxLength {
|
|
return fmt.Errorf("name part of %s must be no more than %d characters", value, qualifiedNameMaxLength)
|
|
}
|
|
|
|
if !qualifiedNameRegexp.MatchString(name) {
|
|
return fmt.Errorf("name part of %s "+
|
|
regexErrorMsg(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc"), value)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func validateAnnotationsSize(annotations map[string]string) error {
|
|
var totalSize int64
|
|
for k, v := range annotations {
|
|
totalSize += (int64)(len(k)) + (int64)(len(v))
|
|
}
|
|
if totalSize > (int64)(define.TotalAnnotationSizeLimitB) {
|
|
return fmt.Errorf("annotations size %d is larger than limit %d", totalSize, define.TotalAnnotationSizeLimitB)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ValidateAnnotations validates that a set of annotations are correctly
|
|
// defined.
|
|
func ValidateAnnotations(annotations map[string]string) error {
|
|
for k := range annotations {
|
|
// The rule is QualifiedName except that case doesn't matter,
|
|
// so convert to lowercase before checking.
|
|
if err := isQualifiedName(strings.ToLower(k)); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
if err := validateAnnotationsSize(annotations); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|