Skip to content

Commit

Permalink
Merge pull request #1178 from youngnick/update-addresstype
Browse files Browse the repository at this point in the history
Update AddressType definition to add domain-prefixed strings as an option
  • Loading branch information
k8s-ci-robot authored Jun 7, 2022
2 parents 310acc3 + 0d6eaff commit 3805bf6
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 25 deletions.
1 change: 0 additions & 1 deletion apis/v1alpha2/gateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,6 @@ type GatewayAddress struct {
// Type of the address.
//
// +optional
// +kubebuilder:validation:Enum=IPAddress;Hostname;NamedAddress
// +kubebuilder:default=IPAddress
Type *AddressType `json:"type,omitempty"`

Expand Down
20 changes: 13 additions & 7 deletions apis/v1alpha2/shared_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,19 @@ type AnnotationKey string
type AnnotationValue string

// AddressType defines how a network address is represented as a text string.
// This may take two possible forms:
//
// * A predefined CamelCase string identifier (currently limited to `IPAddress` or `Hostname`)
// * A domain-prefixed string identifier (like `acme.io/CustomAddressType`)
//
// Values `IPAddress` and `Hostname` have Extended support.
//
// All other values, including domain-prefixed values have Custom support,
// which are used in implementation-specific behaviors.
//
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9])+$|^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\/[a-zA-Z0-9]+$`
type AddressType string

const (
Expand All @@ -502,11 +515,4 @@ const (
//
// Support: Extended
HostnameAddressType AddressType = "Hostname"

// A NamedAddress provides a way to reference a specific IP address by name.
// For example, this may be a name or other unique identifier that refers
// to a resource on a cloud provider such as a static IP.
//
// Support: Implementation-Specific
NamedAddressType AddressType = "NamedAddress"
)
39 changes: 38 additions & 1 deletion apis/v1alpha2/validation/gateway.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package validation

import (
"fmt"
"regexp"

"k8s.io/apimachinery/pkg/util/validation/field"

Expand All @@ -36,6 +37,11 @@ var (
gatewayv1a2.UDPProtocolType: {},
gatewayv1a2.TCPProtocolType: {},
}

addressTypesValid = map[gatewayv1a2.AddressType]struct{}{
gatewayv1a2.HostnameAddressType: {},
gatewayv1a2.IPAddressType: {},
}
)

// ValidateGateway validates gw according to the Gateway API specification.
Expand All @@ -51,7 +57,10 @@ func ValidateGateway(gw *gatewayv1a2.Gateway) field.ErrorList {
// validateGatewaySpec validates whether required fields of spec are set according to the
// Gateway API specification.
func validateGatewaySpec(spec *gatewayv1a2.GatewaySpec, path *field.Path) field.ErrorList {
return validateGatewayListeners(spec.Listeners, path.Child("listeners"))
var errs field.ErrorList
errs = append(errs, validateGatewayListeners(spec.Listeners, path.Child("listeners"))...)
errs = append(errs, validateAddresses(spec.Addresses, path.Child("addresses"))...)
return errs
}

// validateGatewayListeners validates whether required fields of listeners are set according
Expand Down Expand Up @@ -89,3 +98,31 @@ func validateListenerHostname(listeners []gatewayv1a2.Listener, path *field.Path
}
return errs
}

// domainPrefixedStringRegex is a regex used in validation to determine whether
// a provided string is a domain-prefixed string. Domain-prefixed strings are used
// to indicate custom (implementation-specific) address types.
var domainPrefixedStringRegex = regexp.MustCompile(`^([a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9]\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])\/[a-zA-Z0-9]+$`)

// validateAddresses validates each listener address
// if there are addresses set. Otherwise, returns no error.
func validateAddresses(addresses []gatewayv1a2.GatewayAddress, path *field.Path) field.ErrorList {
var errs field.ErrorList

for i, a := range addresses {
if a.Type == nil {
continue
}
_, ok := addressTypesValid[*a.Type]
if !ok {
// Found something that's not one of the upstream AddressTypes
// Next, check for a domain-prefixed string
match := domainPrefixedStringRegex.Match([]byte(*a.Type))
if !match {
errs = append(errs, field.Invalid(path.Index(i).Child("type"), a.Type, "should either be a defined constant or a domain-prefixed string (example.com/Type)"))
}
}

}
return errs
}
34 changes: 34 additions & 0 deletions apis/v1alpha2/validation/gateway_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,11 @@ func TestValidateGateway(t *testing.T) {
Hostname: nil,
},
}
addresses := []gatewayv1a2.GatewayAddress{
{
Type: nil,
},
}
baseGateway := gatewayv1a2.Gateway{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
Expand All @@ -38,6 +43,7 @@ func TestValidateGateway(t *testing.T) {
Spec: gatewayv1a2.GatewaySpec{
GatewayClassName: "foo",
Listeners: listeners,
Addresses: addresses,
},
}
tlsConfig := gatewayv1a2.GatewayTLSConfig{}
Expand Down Expand Up @@ -76,6 +82,34 @@ func TestValidateGateway(t *testing.T) {
},
expectErrsOnFields: []string{"spec.listeners[0].hostname"},
},
"Address present with IPAddress": {
mutate: func(gw *gatewayv1a2.Gateway) {
ip := gatewayv1a2.IPAddressType
gw.Spec.Addresses[0].Type = &ip
},
expectErrsOnFields: []string{},
},
"Address present with Hostname": {
mutate: func(gw *gatewayv1a2.Gateway) {
host := gatewayv1a2.HostnameAddressType
gw.Spec.Addresses[0].Type = &host
},
expectErrsOnFields: []string{},
},
"Address present with example.com/CustomAddress": {
mutate: func(gw *gatewayv1a2.Gateway) {
customAddress := gatewayv1a2.AddressType("example.com/CustomAddress")
gw.Spec.Addresses[0].Type = &customAddress
},
expectErrsOnFields: []string{},
},
"Address present with invalid Type": {
mutate: func(gw *gatewayv1a2.Gateway) {
customAddress := gatewayv1a2.AddressType("CustomAddress")
gw.Spec.Addresses[0].Type = &customAddress
},
expectErrsOnFields: []string{"spec.addresses[0].type"},
},
}

for name, tc := range testCases {
Expand Down
14 changes: 6 additions & 8 deletions config/crd/experimental/gateway.networking.k8s.io_gateways.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 6 additions & 8 deletions config/crd/stable/gateway.networking.k8s.io_gateways.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3805bf6

Please sign in to comment.