Merge pull request #375 from thockin/valid/dns_lengths

Simplify DNS validation checks
This commit is contained in:
brendandburns 2014-07-08 21:48:46 -07:00
commit 2ea8a68001
4 changed files with 35 additions and 26 deletions

View File

@ -31,19 +31,17 @@ import (
// This defines the format, but not the length restriction, which should be // This defines the format, but not the length restriction, which should be
// specified at the definition of any field of this type. // specified at the definition of any field of this type.
// //
// DNS_LABEL: This is a string that conforms to the definition of a "label" // DNS_LABEL: This is a string, no more than 63 characters long, that conforms
// in RFCs 1035 and 1123. This is captured by the following regex: // to the definition of a "label" in RFCs 1035 and 1123. This is captured
// by the following regex:
// [a-z0-9]([-a-z0-9]*[a-z0-9])? // [a-z0-9]([-a-z0-9]*[a-z0-9])?
// This defines the format, but not the length restriction, which should be
// specified at the definition of any field of this type.
// //
// DNS_SUBDOMAIN: This is a string that conforms to the definition of a // DNS_SUBDOMAIN: This is a string, no more than 253 characters long, that conforms
// "subdomain" in RFCs 1035 and 1123. This is captured by the following regex: // to the definition of a "subdomain" in RFCs 1035 and 1123. This is captured
// by the following regex:
// [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)* // [a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*
// or more simply: // or more simply:
// DNS_LABEL(\.DNS_LABEL)* // DNS_LABEL(\.DNS_LABEL)*
// This defines the format, but not the length restriction, which should be
// specified at the definition of any field of this type.
// ContainerManifest corresponds to the Container Manifest format, documented at: // ContainerManifest corresponds to the Container Manifest format, documented at:
// https://developers.google.com/compute/docs/containers/container_vms#container_manifest // https://developers.google.com/compute/docs/containers/container_vms#container_manifest
@ -51,7 +49,7 @@ import (
type ContainerManifest struct { type ContainerManifest struct {
// Required: This must be a supported version string, such as "v1beta1". // Required: This must be a supported version string, such as "v1beta1".
Version string `yaml:"version" json:"version"` Version string `yaml:"version" json:"version"`
// Required: This must be a DNS_SUBDOMAIN, 255 characters or less. // Required: This must be a DNS_SUBDOMAIN.
ID string `yaml:"id" json:"id"` ID string `yaml:"id" json:"id"`
Volumes []Volume `yaml:"volumes" json:"volumes"` Volumes []Volume `yaml:"volumes" json:"volumes"`
Containers []Container `yaml:"containers" json:"containers"` Containers []Container `yaml:"containers" json:"containers"`
@ -59,17 +57,18 @@ type ContainerManifest struct {
// Volume represents a named volume in a pod that may be accessed by any containers in the pod. // Volume represents a named volume in a pod that may be accessed by any containers in the pod.
type Volume struct { type Volume struct {
// Required: This must be a DNS_LABEL, 63 characters or less. Each volume in a pod // Required: This must be a DNS_LABEL. Each volume in a pod must have
// must have a unique name. // a unique name.
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
} }
// Port represents a network port in a single container // Port represents a network port in a single container
type Port struct { type Port struct {
// Optional: If specified, this must be a DNS_LABEL, 63 characters or less. Each // Optional: If specified, this must be a DNS_LABEL. Each named port
// container in a pod must have a unique name. // in a pod must have a unique name.
Name string `yaml:"name,omitempty" json:"name,omitempty"` Name string `yaml:"name,omitempty" json:"name,omitempty"`
// Optional: Defaults to ContainerPort. // Optional: Defaults to ContainerPort. If specified, this must be a
// valid port number, 0 < x < 65536.
HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"` HostPort int `yaml:"hostPort,omitempty" json:"hostPort,omitempty"`
// Required: This must be a valid port number, 0 < x < 65536. // Required: This must be a valid port number, 0 < x < 65536.
ContainerPort int `yaml:"containerPort" json:"containerPort"` ContainerPort int `yaml:"containerPort" json:"containerPort"`
@ -102,8 +101,8 @@ type EnvVar struct {
// Container represents a single container that is expected to be run on the host. // Container represents a single container that is expected to be run on the host.
type Container struct { type Container struct {
// Required: This must be a DNS_LABEL, 63 characters or less. Each container in a // Required: This must be a DNS_LABEL. Each container in a pod must
// pod must have a unique name. // have a unique name.
Name string `yaml:"name" json:"name"` Name string `yaml:"name" json:"name"`
// Required. // Required.
Image string `yaml:"image" json:"image"` Image string `yaml:"image" json:"image"`

View File

@ -65,7 +65,7 @@ func validateVolumes(volumes []Volume) (util.StringSet, error) {
allNames := util.StringSet{} allNames := util.StringSet{}
for i := range volumes { for i := range volumes {
vol := &volumes[i] // so we can set default values vol := &volumes[i] // so we can set default values
if len(vol.Name) > 63 || !util.IsDNSLabel(vol.Name) { if !util.IsDNSLabel(vol.Name) {
return util.StringSet{}, makeInvalidError("Volume.Name", vol.Name) return util.StringSet{}, makeInvalidError("Volume.Name", vol.Name)
} }
if allNames.Has(vol.Name) { if allNames.Has(vol.Name) {
@ -99,7 +99,7 @@ func validateContainers(containers []Container, volumes util.StringSet) error {
allNames := util.StringSet{} allNames := util.StringSet{}
for i := range containers { for i := range containers {
ctr := &containers[i] // so we can set default values ctr := &containers[i] // so we can set default values
if len(ctr.Name) > 63 || !util.IsDNSLabel(ctr.Name) { if !util.IsDNSLabel(ctr.Name) {
return makeInvalidError("Container.Name", ctr.Name) return makeInvalidError("Container.Name", ctr.Name)
} }
if allNames.Has(ctr.Name) { if allNames.Has(ctr.Name) {
@ -130,7 +130,7 @@ func ValidateManifest(manifest *ContainerManifest) error {
if !supportedManifestVersions.Has(manifest.Version) { if !supportedManifestVersions.Has(manifest.Version) {
return makeNotSupportedError("ContainerManifest.Version", manifest.Version) return makeNotSupportedError("ContainerManifest.Version", manifest.Version)
} }
if len(manifest.ID) > 255 || !util.IsDNSSubdomain(manifest.ID) { if !util.IsDNSSubdomain(manifest.ID) {
return makeInvalidError("ContainerManifest.ID", manifest.ID) return makeInvalidError("ContainerManifest.ID", manifest.ID)
} }
allVolumes, err := validateVolumes(manifest.Volumes) allVolumes, err := validateVolumes(manifest.Volumes)

View File

@ -20,25 +20,32 @@ import (
"regexp" "regexp"
) )
var dnsLabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?" const dnsLabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
var dnsLabelRegexp *regexp.Regexp = regexp.MustCompile("^" + dnsLabelFmt + "$") var dnsLabelRegexp *regexp.Regexp = regexp.MustCompile("^" + dnsLabelFmt + "$")
const dnsLabelMaxLength int = 63
// IsDNSLabel tests for a string that conforms to the definition of a label in // IsDNSLabel tests for a string that conforms to the definition of a label in
// DNS (RFC 1035/1123). This checks the format, but not the length. // DNS (RFC 1035/1123).
func IsDNSLabel(value string) bool { func IsDNSLabel(value string) bool {
return dnsLabelRegexp.MatchString(value) return len(value) <= dnsLabelMaxLength && dnsLabelRegexp.MatchString(value)
} }
var dnsSubdomainFmt string = dnsLabelFmt + "(\\." + dnsLabelFmt + ")*" const dnsSubdomainFmt string = dnsLabelFmt + "(\\." + dnsLabelFmt + ")*"
var dnsSubdomainRegexp *regexp.Regexp = regexp.MustCompile("^" + dnsSubdomainFmt + "$") var dnsSubdomainRegexp *regexp.Regexp = regexp.MustCompile("^" + dnsSubdomainFmt + "$")
const dnsSubdomainMaxLength int = 253
// IsDNSSubdomain tests for a string that conforms to the definition of a // IsDNSSubdomain tests for a string that conforms to the definition of a
// subdomain in DNS (RFC 1035/1123). This checks the format, but not the length. // subdomain in DNS (RFC 1035/1123).
func IsDNSSubdomain(value string) bool { func IsDNSSubdomain(value string) bool {
return dnsSubdomainRegexp.MatchString(value) return len(value) <= dnsSubdomainMaxLength && dnsSubdomainRegexp.MatchString(value)
} }
var cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*" const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"
var cIdentifierRegexp *regexp.Regexp = regexp.MustCompile("^" + cIdentifierFmt + "$") var cIdentifierRegexp *regexp.Regexp = 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

View File

@ -17,6 +17,7 @@ limitations under the License.
package util package util
import ( import (
"strings"
"testing" "testing"
) )
@ -37,6 +38,7 @@ func TestIsDNSLabel(t *testing.T) {
"_", "a_", "_a", "a_b", "1_", "_1", "1_2", "_", "a_", "_a", "a_b", "1_", "_1", "1_2",
".", "a.", ".a", "a.b", "1.", ".1", "1.2", ".", "a.", ".a", "a.b", "1.", ".1", "1.2",
" ", "a ", " a", "a b", "1 ", " 1", "1 2", " ", "a ", " a", "a b", "1 ", " 1", "1 2",
strings.Repeat("a", 64),
} }
for _, val := range badValues { for _, val := range badValues {
if IsDNSLabel(val) { if IsDNSLabel(val) {
@ -73,6 +75,7 @@ func TestIsDNSSubdomain(t *testing.T) {
"A.B.C.D.E", "AA.BB.CC.DD.EE", "a.B.c.d.e", "aa.bB.cc.dd.ee", "A.B.C.D.E", "AA.BB.CC.DD.EE", "a.B.c.d.e", "aa.bB.cc.dd.ee",
"a@b", "a,b", "a_b", "a;b", "a@b", "a,b", "a_b", "a;b",
"a:b", "a%b", "a?b", "a$b", "a:b", "a%b", "a?b", "a$b",
strings.Repeat("a", 254),
} }
for _, val := range badValues { for _, val := range badValues {
if IsDNSSubdomain(val) { if IsDNSSubdomain(val) {