Repository: kubernetes-sigs/gateway-api
Pull request #3616: feat: make `Gateway.Spec.Addresses.Value` optional
Author: login: EyalPazz
Labels: name: kind/feature
description: Categorizes issue or PR as related to a new feature.
name: ok-to-test
description: Indicates a non-member PR verified by an org member that is safe to test.
name: approved
description: Indicates a PR has been approved by an approver from all required OWNERS files.
name: release-note
description: Denotes a PR that will be considered when it comes time to generate release notes.
name: cncf-cla: yes
description: Indicates the PR's author has signed the CNCF CLA.
name: lgtm
description: "Looks good to me", indicates that a PR is ready to be merged.
name: size/L
description: Denotes a PR that changes 100-499 lines, ignoring generated files.
name: kind/gep
description: PRs related to Gateway Enhancement Proposal(GEP)
Created: 2025-02-16T09:00:54Z | Merged: 2025-03-16T22:45:48Z


**What type of PR is this?**
/kind feature

**Which issue(s) this PR fixes**:
Fixes #3612 

**Does this PR introduce a user-facing change?**:

```release-note
Make the `value` field in Gateway.Spec.Addresses array optional
```


Linked issue #3612: Gateway.Spec.Addresses.Value should be optional
**What would you like to be added**:
I'd like [Gateway.Spec.Addresses.Value](https://github.com/kubernetes-sigs/gateway-api/blob/64c85004dff1adfdec7599fcf403f2b54fb7e38b/apis/v1/gateway_types.go#L677) to be optional.

**Why this is needed**:
It should be valid to request a _type_ of address without specifying the value that address should have. For example, on Google Cloud, we have a [Cross Region ILB](https://cloud.google.com/load-balancing/docs/l7-internal/setting-up-l7-cross-reg-internal) and we'd like users to be able to request ephemeral addresses in specific locations. That is currently quite challenging when the `value` of a requested address must be specified.

Note: Per our [versioning policy](https://gateway-api.sigs.k8s.io/concepts/versioning/#minor-version-eg-v040-v050) it is valid to make required fields optional in a minor release across any release channel.
Linked issue comments: user: login: EyalPazz
created_at: 2025-02-16T08:25:05Z
body: /assign
author_association: MEMBER

Diff:
diff --git a/apis/v1/gateway_types.go b/apis/v1/gateway_types.go
index e2cf5967b5..78b0a527f5 100644
--- a/apis/v1/gateway_types.go
+++ b/apis/v1/gateway_types.go
@@ -264,7 +264,7 @@ type GatewaySpec struct {
 	// +kubebuilder:validation:MaxItems=16
 	// +kubebuilder:validation:XValidation:message="IPAddress values must be unique",rule="self.all(a1, a1.type == 'IPAddress' ? self.exists_one(a2, a2.type == a1.type && a2.value == a1.value) : true )"
 	// +kubebuilder:validation:XValidation:message="Hostname values must be unique",rule="self.all(a1, a1.type == 'Hostname' ? self.exists_one(a2, a2.type == a1.type && a2.value == a1.value) : true )"
-	Addresses []GatewayAddress `json:"addresses,omitempty"`
+	Addresses []GatewaySpecAddress `json:"addresses,omitempty"`
 
 	// Infrastructure defines infrastructure level attributes about this Gateway instance.
 	//
@@ -724,24 +724,27 @@ type RouteGroupKind struct {
 	Kind Kind `json:"kind"`
 }
 
-// GatewayAddress describes an address that can be bound to a Gateway.
+// GatewaySpecAddress describes an address that can be bound to a Gateway.
 //
 // +kubebuilder:validation:XValidation:message="Hostname value must only contain valid characters (matching ^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$)",rule="self.type == 'Hostname' ? self.value.matches(r\"\"\"^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\"\"\"): true"
-type GatewayAddress struct {
+type GatewaySpecAddress struct {
 	// Type of the address.
 	//
 	// +optional
 	// +kubebuilder:default=IPAddress
 	Type *AddressType `json:"type,omitempty"`
 
-	// Value of the address. The validity of the values will depend
-	// on the type and support by the controller.
+	// When a value is unspecified, an implementation SHOULD automatically
+	// assign an address matching the requested type if possible.
+	//
+	// If an implementation does not support an empty value, they MUST set the
+	// "Programmed" condition in status to False with a reason of "AddressNotAssigned".
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
-	// +kubebuilder:validation:MinLength=1
+	// +optional
 	// +kubebuilder:validation:MaxLength=253
-	Value string `json:"value"`
+	Value string `json:"value,omitempty"`
 }
 
 // GatewayStatusAddress describes a network address that is bound to a Gateway.
diff --git a/apis/v1/zz_generated.deepcopy.go b/apis/v1/zz_generated.deepcopy.go
index 68aa5e1a61..6b8a13485a 100644
--- a/apis/v1/zz_generated.deepcopy.go
+++ b/apis/v1/zz_generated.deepcopy.go
@@ -523,26 +523,6 @@ func (in *Gateway) DeepCopyObject() runtime.Object {
 	return nil
 }
 
-// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
-func (in *GatewayAddress) DeepCopyInto(out *GatewayAddress) {
-	*out = *in
-	if in.Type != nil {
-		in, out := &in.Type, &out.Type
-		*out = new(AddressType)
-		**out = **in
-	}
-}
-
-// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayAddress.
-func (in *GatewayAddress) DeepCopy() *GatewayAddress {
-	if in == nil {
-		return nil
-	}
-	out := new(GatewayAddress)
-	in.DeepCopyInto(out)
-	return out
-}
-
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *GatewayBackendTLS) DeepCopyInto(out *GatewayBackendTLS) {
 	*out = *in
@@ -752,7 +732,7 @@ func (in *GatewaySpec) DeepCopyInto(out *GatewaySpec) {
 	}
 	if in.Addresses != nil {
 		in, out := &in.Addresses, &out.Addresses
-		*out = make([]GatewayAddress, len(*in))
+		*out = make([]GatewaySpecAddress, len(*in))
 		for i := range *in {
 			(*in)[i].DeepCopyInto(&(*out)[i])
 		}
@@ -784,6 +764,26 @@ func (in *GatewaySpec) DeepCopy() *GatewaySpec {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GatewaySpecAddress) DeepCopyInto(out *GatewaySpecAddress) {
+	*out = *in
+	if in.Type != nil {
+		in, out := &in.Type, &out.Type
+		*out = new(AddressType)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewaySpecAddress.
+func (in *GatewaySpecAddress) DeepCopy() *GatewaySpecAddress {
+	if in == nil {
+		return nil
+	}
+	out := new(GatewaySpecAddress)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *GatewayStatus) DeepCopyInto(out *GatewayStatus) {
 	*out = *in
diff --git a/apis/v1beta1/gateway_types.go b/apis/v1beta1/gateway_types.go
index 2834915ca7..80c746fcdd 100644
--- a/apis/v1beta1/gateway_types.go
+++ b/apis/v1beta1/gateway_types.go
@@ -133,7 +133,7 @@ type RouteGroupKind = v1.RouteGroupKind
 
 // GatewayAddress describes an address that can be bound to a Gateway.
 // +k8s:deepcopy-gen=false
-type GatewayAddress = v1.GatewayAddress
+type GatewaySpecAddress = v1.GatewaySpecAddress
 
 // GatewayStatus defines the observed state of Gateway.
 // +k8s:deepcopy-gen=false
diff --git a/applyconfiguration/apis/v1/gatewayspec.go b/applyconfiguration/apis/v1/gatewayspec.go
index 9ba7ecb20d..65e3e1991d 100644
--- a/applyconfiguration/apis/v1/gatewayspec.go
+++ b/applyconfiguration/apis/v1/gatewayspec.go
@@ -27,7 +27,7 @@ import (
 type GatewaySpecApplyConfiguration struct {
 	GatewayClassName *v1.ObjectName                           `json:"gatewayClassName,omitempty"`
 	Listeners        []ListenerApplyConfiguration             `json:"listeners,omitempty"`
-	Addresses        []GatewayAddressApplyConfiguration       `json:"addresses,omitempty"`
+	Addresses        []GatewaySpecAddressApplyConfiguration   `json:"addresses,omitempty"`
 	Infrastructure   *GatewayInfrastructureApplyConfiguration `json:"infrastructure,omitempty"`
 	BackendTLS       *GatewayBackendTLSApplyConfiguration     `json:"backendTLS,omitempty"`
 	AllowedListeners *AllowedListenersApplyConfiguration      `json:"allowedListeners,omitempty"`
@@ -63,7 +63,7 @@ func (b *GatewaySpecApplyConfiguration) WithListeners(values ...*ListenerApplyCo
 // WithAddresses adds the given value to the Addresses field in the declarative configuration
 // and returns the receiver, so that objects can be build by chaining "With" function invocations.
 // If called multiple times, values provided by each call will be appended to the Addresses field.
-func (b *GatewaySpecApplyConfiguration) WithAddresses(values ...*GatewayAddressApplyConfiguration) *GatewaySpecApplyConfiguration {
+func (b *GatewaySpecApplyConfiguration) WithAddresses(values ...*GatewaySpecAddressApplyConfiguration) *GatewaySpecApplyConfiguration {
 	for i := range values {
 		if values[i] == nil {
 			panic("nil value passed to WithAddresses")
diff --git a/applyconfiguration/apis/v1/gatewayspecaddress.go b/applyconfiguration/apis/v1/gatewayspecaddress.go
new file mode 100644
index 0000000000..f633012fbd
--- /dev/null
+++ b/applyconfiguration/apis/v1/gatewayspecaddress.go
@@ -0,0 +1,52 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1
+
+import (
+	v1 "sigs.k8s.io/gateway-api/apis/v1"
+)
+
+// GatewaySpecAddressApplyConfiguration represents a declarative configuration of the GatewaySpecAddress type for use
+// with apply.
+type GatewaySpecAddressApplyConfiguration struct {
+	Type  *v1.AddressType `json:"type,omitempty"`
+	Value *string         `json:"value,omitempty"`
+}
+
+// GatewaySpecAddressApplyConfiguration constructs a declarative configuration of the GatewaySpecAddress type for use with
+// apply.
+func GatewaySpecAddress() *GatewaySpecAddressApplyConfiguration {
+	return &GatewaySpecAddressApplyConfiguration{}
+}
+
+// WithType sets the Type field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Type field is set to the value of the last call.
+func (b *GatewaySpecAddressApplyConfiguration) WithType(value v1.AddressType) *GatewaySpecAddressApplyConfiguration {
+	b.Type = &value
+	return b
+}
+
+// WithValue sets the Value field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Value field is set to the value of the last call.
+func (b *GatewaySpecAddressApplyConfiguration) WithValue(value string) *GatewaySpecAddressApplyConfiguration {
+	b.Value = &value
+	return b
+}
diff --git a/applyconfiguration/internal/internal.go b/applyconfiguration/internal/internal.go
index e92cf26573..2474806fc2 100644
--- a/applyconfiguration/internal/internal.go
+++ b/applyconfiguration/internal/internal.go
@@ -493,16 +493,6 @@ var schemaYAML = typed.YAMLObject(`types:
       type:
         namedType: io.k8s.sigs.gateway-api.apis.v1.GatewayStatus
       default: {}
-- name: io.k8s.sigs.gateway-api.apis.v1.GatewayAddress
-  map:
-    fields:
-    - name: type
-      type:
-        scalar: string
-    - name: value
-      type:
-        scalar: string
-      default: ""
 - name: io.k8s.sigs.gateway-api.apis.v1.GatewayBackendTLS
   map:
     fields:
@@ -585,7 +575,7 @@ var schemaYAML = typed.YAMLObject(`types:
       type:
         list:
           elementType:
-            namedType: io.k8s.sigs.gateway-api.apis.v1.GatewayAddress
+            namedType: io.k8s.sigs.gateway-api.apis.v1.GatewaySpecAddress
           elementRelationship: atomic
     - name: allowedListeners
       type:
@@ -608,6 +598,15 @@ var schemaYAML = typed.YAMLObject(`types:
           elementRelationship: associative
           keys:
           - name
+- name: io.k8s.sigs.gateway-api.apis.v1.GatewaySpecAddress
+  map:
+    fields:
+    - name: type
+      type:
+        scalar: string
+    - name: value
+      type:
+        scalar: string
 - name: io.k8s.sigs.gateway-api.apis.v1.GatewayStatus
   map:
     fields:
diff --git a/applyconfiguration/utils.go b/applyconfiguration/utils.go
index 4f424a73b2..3219d6482e 100644
--- a/applyconfiguration/utils.go
+++ b/applyconfiguration/utils.go
@@ -58,8 +58,6 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
 		return &apisv1.FrontendTLSValidationApplyConfiguration{}
 	case v1.SchemeGroupVersion.WithKind("Gateway"):
 		return &apisv1.GatewayApplyConfiguration{}
-	case v1.SchemeGroupVersion.WithKind("GatewayAddress"):
-		return &apisv1.GatewayAddressApplyConfiguration{}
 	case v1.SchemeGroupVersion.WithKind("GatewayBackendTLS"):
 		return &apisv1.GatewayBackendTLSApplyConfiguration{}
 	case v1.SchemeGroupVersion.WithKind("GatewayClass"):
@@ -72,6 +70,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
 		return &apisv1.GatewayInfrastructureApplyConfiguration{}
 	case v1.SchemeGroupVersion.WithKind("GatewaySpec"):
 		return &apisv1.GatewaySpecApplyConfiguration{}
+	case v1.SchemeGroupVersion.WithKind("GatewaySpecAddress"):
+		return &apisv1.GatewaySpecAddressApplyConfiguration{}
 	case v1.SchemeGroupVersion.WithKind("GatewayStatus"):
 		return &apisv1.GatewayStatusApplyConfiguration{}
 	case v1.SchemeGroupVersion.WithKind("GatewayStatusAddress"):
diff --git a/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml b/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml
index a8e07236a7..bcb7044ad8 100644
--- a/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml
+++ b/config/crd/experimental/gateway.networking.k8s.io_gateways.yaml
@@ -83,8 +83,8 @@ spec:
 
                   Support: Extended
                 items:
-                  description: GatewayAddress describes an address that can be bound
-                    to a Gateway.
+                  description: GatewaySpecAddress describes an address that can be
+                    bound to a Gateway.
                   oneOf:
                   - properties:
                       type:
@@ -109,15 +109,15 @@ spec:
                       type: string
                     value:
                       description: |-
-                        Value of the address. The validity of the values will depend
-                        on the type and support by the controller.
+                        When a value is unspecified, an implementation SHOULD automatically
+                        assign an address matching the requested type if possible.
+
+                        If an implementation does not support an empty value, they MUST set the
+                        "Programmed" condition in status to False with a reason of "AddressNotAssigned".
 
                         Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
                       maxLength: 253
-                      minLength: 1
                       type: string
-                  required:
-                  - value
                   type: object
                   x-kubernetes-validations:
                   - message: Hostname value must only contain valid characters (matching
@@ -1422,8 +1422,8 @@ spec:
 
                   Support: Extended
                 items:
-                  description: GatewayAddress describes an address that can be bound
-                    to a Gateway.
+                  description: GatewaySpecAddress describes an address that can be
+                    bound to a Gateway.
                   oneOf:
                   - properties:
                       type:
@@ -1448,15 +1448,15 @@ spec:
                       type: string
                     value:
                       description: |-
-                        Value of the address. The validity of the values will depend
-                        on the type and support by the controller.
+                        When a value is unspecified, an implementation SHOULD automatically
+                        assign an address matching the requested type if possible.
+
+                        If an implementation does not support an empty value, they MUST set the
+                        "Programmed" condition in status to False with a reason of "AddressNotAssigned".
 
                         Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
                       maxLength: 253
-                      minLength: 1
                       type: string
-                  required:
-                  - value
                   type: object
                   x-kubernetes-validations:
                   - message: Hostname value must only contain valid characters (matching
diff --git a/config/crd/standard/gateway.networking.k8s.io_gateways.yaml b/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
index 15cbc96b81..e9f724a7e5 100644
--- a/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
+++ b/config/crd/standard/gateway.networking.k8s.io_gateways.yaml
@@ -83,8 +83,8 @@ spec:
 
                   Support: Extended
                 items:
-                  description: GatewayAddress describes an address that can be bound
-                    to a Gateway.
+                  description: GatewaySpecAddress describes an address that can be
+                    bound to a Gateway.
                   oneOf:
                   - properties:
                       type:
@@ -109,15 +109,15 @@ spec:
                       type: string
                     value:
                       description: |-
-                        Value of the address. The validity of the values will depend
-                        on the type and support by the controller.
+                        When a value is unspecified, an implementation SHOULD automatically
+                        assign an address matching the requested type if possible.
+
+                        If an implementation does not support an empty value, they MUST set the
+                        "Programmed" condition in status to False with a reason of "AddressNotAssigned".
 
                         Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
                       maxLength: 253
-                      minLength: 1
                       type: string
-                  required:
-                  - value
                   type: object
                   x-kubernetes-validations:
                   - message: Hostname value must only contain valid characters (matching
@@ -1190,8 +1190,8 @@ spec:
 
                   Support: Extended
                 items:
-                  description: GatewayAddress describes an address that can be bound
-                    to a Gateway.
+                  description: GatewaySpecAddress describes an address that can be
+                    bound to a Gateway.
                   oneOf:
                   - properties:
                       type:
@@ -1216,15 +1216,15 @@ spec:
                       type: string
                     value:
                       description: |-
-                        Value of the address. The validity of the values will depend
-                        on the type and support by the controller.
+                        When a value is unspecified, an implementation SHOULD automatically
+                        assign an address matching the requested type if possible.
+
+                        If an implementation does not support an empty value, they MUST set the
+                        "Programmed" condition in status to False with a reason of "AddressNotAssigned".
 
                         Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
                       maxLength: 253
-                      minLength: 1
                       type: string
-                  required:
-                  - value
                   type: object
                   x-kubernetes-validations:
                   - message: Hostname value must only contain valid characters (matching
diff --git a/conformance/tests/gateway-static-addresses.go b/conformance/tests/gateway-static-addresses.go
index 150975dfa8..fcee1b3f99 100644
--- a/conformance/tests/gateway-static-addresses.go
+++ b/conformance/tests/gateway-static-addresses.go
@@ -160,7 +160,7 @@ func extractStatusAddresses(addresses []v1.GatewayStatusAddress) []string {
 // Private Helper Functions
 // -----------------------------------------------------------------------------
 
-func filterAddr(addrs []v1.GatewayAddress, filter v1.GatewayAddress) (newAddrs []v1.GatewayAddress) {
+func filterAddr(addrs []v1.GatewaySpecAddress, filter v1.GatewaySpecAddress) (newAddrs []v1.GatewaySpecAddress) {
 	for _, addr := range addrs {
 		if addr.Value != filter.Value {
 			newAddrs = append(newAddrs, addr)
diff --git a/conformance/utils/kubernetes/apply.go b/conformance/utils/kubernetes/apply.go
index 983e0715e1..1b3b01d5aa 100644
--- a/conformance/utils/kubernetes/apply.go
+++ b/conformance/utils/kubernetes/apply.go
@@ -49,6 +49,10 @@ type Applier struct {
 	// GatewayClass will be used as the spec.gatewayClassName when applying Gateway resources
 	GatewayClass string
 
+	// AddressType is a type that is expected to be supported AND usable
+	// for Gateways in the underlying implementation
+	AddressType string
+
 	// ControllerName will be used as the spec.controllerName when applying GatewayClass resources
 	ControllerName string
 
@@ -57,11 +61,11 @@ type Applier struct {
 
 	// UsableNetworkAddresses is a list of addresses that are expected to be
 	// supported AND usable for Gateways in the underlying implementation.
-	UsableNetworkAddresses []v1beta1.GatewayAddress
+	UsableNetworkAddresses []v1beta1.GatewaySpecAddress
 
 	// UnusableNetworkAddresses is a list of addresses that are expected to be
 	// supported, but not usable for Gateways in the underlying implementation.
-	UnusableNetworkAddresses []v1beta1.GatewayAddress
+	UnusableNetworkAddresses []v1beta1.GatewaySpecAddress
 }
 
 // prepareGateway adjusts the gatewayClassName.
@@ -104,7 +108,7 @@ func (a Applier) prepareGateway(t *testing.T, uObj *unstructured.Unstructured) {
 		// Note: I would really love to find a better way to do this kind of
 		// thing in the future.
 		var overlayUsable, overlayUnusable bool
-		var specialAddrs []v1beta1.GatewayAddress
+		var specialAddrs []v1beta1.GatewaySpecAddress
 		for _, addr := range gwspec.Addresses {
 			switch addr.Value {
 			case "PLACEHOLDER_USABLE_ADDRS":
@@ -353,7 +357,7 @@ func getContentsFromPathOrURL(manifestFS []fs.FS, location string, timeoutConfig
 // convertGatewayAddrsToPrimitives converts a slice of Gateway addresses
 // to a slice of primitive types and then returns them as a []interface{} so that
 // they can be applied back to an unstructured Gateway.
-func convertGatewayAddrsToPrimitives(gwaddrs []v1beta1.GatewayAddress) (raw []interface{}) {
+func convertGatewayAddrsToPrimitives(gwaddrs []v1beta1.GatewaySpecAddress) (raw []interface{}) {
 	for _, addr := range gwaddrs {
 		addrType := string(v1beta1.IPAddressType)
 		if addr.Type != nil {
diff --git a/conformance/utils/suite/suite.go b/conformance/utils/suite/suite.go
index dad285050d..81599a8d4c 100644
--- a/conformance/utils/suite/suite.go
+++ b/conformance/utils/suite/suite.go
@@ -75,8 +75,8 @@ type ConformanceTestSuite struct {
 	SkipProvisionalTests     bool
 	RunTest                  string
 	ManifestFS               []fs.FS
-	UsableNetworkAddresses   []v1beta1.GatewayAddress
-	UnusableNetworkAddresses []v1beta1.GatewayAddress
+	UsableNetworkAddresses   []v1beta1.GatewaySpecAddress
+	UnusableNetworkAddresses []v1beta1.GatewaySpecAddress
 
 	// mode is the operating mode of the implementation.
 	// The default value for it is "default".
@@ -128,6 +128,7 @@ type ConformanceOptions struct {
 	Clientset            clientset.Interface
 	RestConfig           *rest.Config
 	GatewayClassName     string
+	AddressType          string
 	Debug                bool
 	RoundTripper         roundtripper.RoundTripper
 	GRPCClient           grpc.Client
@@ -156,12 +157,12 @@ type ConformanceOptions struct {
 
 	// UsableNetworkAddresses is an optional pool of usable addresses for
 	// Gateways for tests which need to test manual address assignments.
-	UsableNetworkAddresses []v1beta1.GatewayAddress
+	UsableNetworkAddresses []v1beta1.GatewaySpecAddress
 
 	// UnusableNetworkAddresses is an optional pool of unusable addresses for
 	// Gateways for tests which need to test failures with manual Gateway
 	// address assignment.
-	UnusableNetworkAddresses []v1beta1.GatewayAddress
+	UnusableNetworkAddresses []v1beta1.GatewaySpecAddress
 
 	Mode                string
 	AllowCRDsMismatch   bool
@@ -249,6 +250,7 @@ func NewConformanceTestSuite(options ConformanceOptions) (*ConformanceTestSuite,
 		Applier: kubernetes.Applier{
 			NamespaceLabels:      options.NamespaceLabels,
 			NamespaceAnnotations: options.NamespaceAnnotations,
+			AddressType:          options.AddressType,
 		},
 		SupportedFeatures:           options.SupportedFeatures,
 		TimeoutConfig:               options.TimeoutConfig,
diff --git a/pkg/features/gateway.go b/pkg/features/gateway.go
index 7fc1a20db0..8def8db568 100644
--- a/pkg/features/gateway.go
+++ b/pkg/features/gateway.go
@@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
 )
 
 var (
@@ -83,6 +87,12 @@ var (
 		Name:    SupportGatewayInfrastructurePropagation,
 		Channel: FeatureChannelExperimental,
 	}
+
+	// GatewayAddressEmptyFeature contains metadata for the SupportGatewayAddressEmpty feature.
+	GatewayEmptyAddressFeature = Feature{
+		Name:    SupportGatewayAddressEmpty,
+		Channel: FeatureChannelExperimental,
+	}
 )
 
 // GatewayExtendedFeatures are extra generic features that implementations may
@@ -92,4 +102,5 @@ var GatewayExtendedFeatures = sets.New(
 	GatewayStaticAddressesFeature,
 	GatewayHTTPListenerIsolationFeature,
 	GatewayInfrastructurePropagationFeature,
+	GatewayEmptyAddressFeature,
 )
diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go
index 03801655f7..13d0d7ab2d 100644
--- a/pkg/generated/openapi/zz_generated.openapi.go
+++ b/pkg/generated/openapi/zz_generated.openapi.go
@@ -101,7 +101,6 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"sigs.k8s.io/gateway-api/apis/v1.GRPCRouteSpec":                                   schema_sigsk8sio_gateway_api_apis_v1_GRPCRouteSpec(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GRPCRouteStatus":                                 schema_sigsk8sio_gateway_api_apis_v1_GRPCRouteStatus(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.Gateway":                                         schema_sigsk8sio_gateway_api_apis_v1_Gateway(ref),
-		"sigs.k8s.io/gateway-api/apis/v1.GatewayAddress":                                  schema_sigsk8sio_gateway_api_apis_v1_GatewayAddress(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayBackendTLS":                               schema_sigsk8sio_gateway_api_apis_v1_GatewayBackendTLS(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayClass":                                    schema_sigsk8sio_gateway_api_apis_v1_GatewayClass(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayClassList":                                schema_sigsk8sio_gateway_api_apis_v1_GatewayClassList(ref),
@@ -110,6 +109,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayInfrastructure":                           schema_sigsk8sio_gateway_api_apis_v1_GatewayInfrastructure(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayList":                                     schema_sigsk8sio_gateway_api_apis_v1_GatewayList(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewaySpec":                                     schema_sigsk8sio_gateway_api_apis_v1_GatewaySpec(ref),
+		"sigs.k8s.io/gateway-api/apis/v1.GatewaySpecAddress":                              schema_sigsk8sio_gateway_api_apis_v1_GatewaySpecAddress(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayStatus":                                   schema_sigsk8sio_gateway_api_apis_v1_GatewayStatus(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayStatusAddress":                            schema_sigsk8sio_gateway_api_apis_v1_GatewayStatusAddress(ref),
 		"sigs.k8s.io/gateway-api/apis/v1.GatewayTLSConfig":                                schema_sigsk8sio_gateway_api_apis_v1_GatewayTLSConfig(ref),
@@ -3598,35 +3598,6 @@ func schema_sigsk8sio_gateway_api_apis_v1_Gateway(ref common.ReferenceCallback)
 	}
 }
 
-func schema_sigsk8sio_gateway_api_apis_v1_GatewayAddress(ref common.ReferenceCallback) common.OpenAPIDefinition {
-	return common.OpenAPIDefinition{
-		Schema: spec.Schema{
-			SchemaProps: spec.SchemaProps{
-				Description: "GatewayAddress describes an address that can be bound to a Gateway.",
-				Type:        []string{"object"},
-				Properties: map[string]spec.Schema{
-					"type": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Type of the address.",
-							Type:        []string{"string"},
-							Format:      "",
-						},
-					},
-					"value": {
-						SchemaProps: spec.SchemaProps{
-							Description: "Value of the address. The validity of the values will depend on the type and support by the controller.\n\nExamples: `1.2.3.4`, `128::1`, `my-ip-address`.",
-							Default:     "",
-							Type:        []string{"string"},
-							Format:      "",
-						},
-					},
-				},
-				Required: []string{"value"},
-			},
-		},
-	}
-}
-
 func schema_sigsk8sio_gateway_api_apis_v1_GatewayBackendTLS(ref common.ReferenceCallback) common.OpenAPIDefinition {
 	return common.OpenAPIDefinition{
 		Schema: spec.Schema{
@@ -3989,7 +3960,7 @@ func schema_sigsk8sio_gateway_api_apis_v1_GatewaySpec(ref common.ReferenceCallba
 								Schema: &spec.Schema{
 									SchemaProps: spec.SchemaProps{
 										Default: map[string]interface{}{},
-										Ref:     ref("sigs.k8s.io/gateway-api/apis/v1.GatewayAddress"),
+										Ref:     ref("sigs.k8s.io/gateway-api/apis/v1.GatewaySpecAddress"),
 									},
 								},
 							},
@@ -4018,7 +3989,34 @@ func schema_sigsk8sio_gateway_api_apis_v1_GatewaySpec(ref common.ReferenceCallba
 			},
 		},
 		Dependencies: []string{
-			"sigs.k8s.io/gateway-api/apis/v1.AllowedListeners", "sigs.k8s.io/gateway-api/apis/v1.GatewayAddress", "sigs.k8s.io/gateway-api/apis/v1.GatewayBackendTLS", "sigs.k8s.io/gateway-api/apis/v1.GatewayInfrastructure", "sigs.k8s.io/gateway-api/apis/v1.Listener"},
+			"sigs.k8s.io/gateway-api/apis/v1.AllowedListeners", "sigs.k8s.io/gateway-api/apis/v1.GatewayBackendTLS", "sigs.k8s.io/gateway-api/apis/v1.GatewayInfrastructure", "sigs.k8s.io/gateway-api/apis/v1.GatewaySpecAddress", "sigs.k8s.io/gateway-api/apis/v1.Listener"},
+	}
+}
+
+func schema_sigsk8sio_gateway_api_apis_v1_GatewaySpecAddress(ref common.ReferenceCallback) common.OpenAPIDefinition {
+	return common.OpenAPIDefinition{
+		Schema: spec.Schema{
+			SchemaProps: spec.SchemaProps{
+				Description: "GatewayAddress describes an address that can be bound to a Gateway.",
+				Type:        []string{"object"},
+				Properties: map[string]spec.Schema{
+					"type": {
+						SchemaProps: spec.SchemaProps{
+							Description: "Type of the address.",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+					"value": {
+						SchemaProps: spec.SchemaProps{
+							Description: "When a value is unspecified, an implementation SHOULD automatically assign an address matching the requested type if possible.\n\nIf an implementation does not support an empty value, they MUST set the \"Programmed\" condition in status to False with a reason of \"AddressNotAssigned\".\n\nExamples: `1.2.3.4`, `128::1`, `my-ip-address`.",
+							Type:        []string{"string"},
+							Format:      "",
+						},
+					},
+				},
+			},
+		},
 	}
 }
 
diff --git a/pkg/test/cel/gateway_test.go b/pkg/test/cel/gateway_test.go
index 4c24020ead..7f8d867642 100644
--- a/pkg/test/cel/gateway_test.go
+++ b/pkg/test/cel/gateway_test.go
@@ -485,7 +485,7 @@ func TestValidateGateway(t *testing.T) {
 		{
 			desc: "ip address and hostname in addresses are valid",
 			mutate: func(gw *gatewayv1.Gateway) {
-				gw.Spec.Addresses = []gatewayv1.GatewayAddress{
+				gw.Spec.Addresses = []gatewayv1.GatewaySpecAddress{
 					{
 						Type:  ptrTo(gatewayv1.IPAddressType),
 						Value: "1.2.3.4",
@@ -504,7 +504,7 @@ func TestValidateGateway(t *testing.T) {
 		{
 			desc: "ip address and hostname in addresses are invalid",
 			mutate: func(gw *gatewayv1.Gateway) {
-				gw.Spec.Addresses = []gatewayv1.GatewayAddress{
+				gw.Spec.Addresses = []gatewayv1.GatewaySpecAddress{
 					{
 						Type:  ptrTo(gatewayv1.IPAddressType),
 						Value: "1.2.3.4:8080",
@@ -563,7 +563,7 @@ func TestValidateGateway(t *testing.T) {
 		{
 			desc: "duplicate ip address or hostname",
 			mutate: func(gw *gatewayv1.Gateway) {
-				gw.Spec.Addresses = []gatewayv1.GatewayAddress{
+				gw.Spec.Addresses = []gatewayv1.GatewaySpecAddress{
 					{
 						Type:  ptrTo(gatewayv1.IPAddressType),
 						Value: "1.2.3.4",


Reviews:
user: login: youngnick
body: This needs more specification; changing a field to optional is not a _breaking_ change, but it is a _big_ one that will require code updates from all controllers. If you are proposing this, including how to handle empty values for implementations that support it and do not will be important.

We may also need an extended feature to cover this `GatewayAddressEmpty` or something, so that we can slice the conformance tests accordingly.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-18T06:02:52Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-18T08:47:38Z
user: login: robscott
body: Thanks for taking this on @EyalPazz!
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-20T02:13:07Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-20T08:16:00Z
user: login: youngnick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-26T06:32:00Z
user: login: youngnick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-02-26T06:32:53Z
user: login: robscott
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-26T07:24:33Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T08:49:59Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T09:21:58Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-27T09:22:59Z
user: login: robscott
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-28T02:12:21Z
user: login: robscott
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-02-28T02:14:45Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-02T09:06:03Z
user: login: robscott
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-02T23:13:43Z
user: login: robscott
body: Thanks @EyalPazz!
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T01:13:01Z
user: login: EyalPazz
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T18:57:30Z
user: login: youngnick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T02:20:21Z
user: login: youngnick
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T02:21:19Z
user: login: robscott
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T02:50:39Z
user: login: robscott
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-14T03:08:53Z
user: login: robscott
body: Thanks @EyalPazz! This mostly LGTM, just needs another codegen run and a bit of clarification in the spec.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-15T01:13:33Z

Review comments:
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
path: apis/v1/gateway_types.go
user: login: youngnick
body: This needs some more explanation:

* what behavior is expected if an implementation supports an empty `Value` here?
* what behavior is expected if an implementation *does not* support an empty `Value` here?

This should then guide you to adding some conformance tests for implementations to test against.
created_at: 2025-02-18T06:01:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
path: apis/v1/gateway_types.go
user: login: EyalPazz
body: amazing thanks!
created_at: 2025-02-18T08:47:38Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
 	// +kubebuilder:validation:MinLength=1
 	// +kubebuilder:validation:MaxLength=253
 	Value string `json:"value"`
path: apis/v1/gateway_types.go
user: login: robscott
body: Although the `+optional` is a convention, we have two options to make this optional:

A) A pointer to allow for nil/unspecified values
```suggestion
	// +kubebuilder:validation:MinLength=1
	// +kubebuilder:validation:MaxLength=253
	Value *string `json:"value,omitempty"`
```

B) Allow empty string (no need to distinguish between empty string and unspecified here, so this is likely better)
```suggestion
	// +kubebuilder:validation:MaxLength=253
	Value string `json:"value,omitempty"`
```

Would appreciate some other input on this from others like @youngnick before committing to an approach.
created_at: 2025-02-20T01:57:19Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
path: apis/v1/gateway_types.go
user: login: robscott
body: Can't reach quite high enough in this code review, but likely worth renaming the containing struct type from `GatewayAddress` to `GatewaySpecAddress`. This would fit well with `GatewayStatusAddress`.
created_at: 2025-02-20T01:58:14Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
path: apis/v1/gateway_types.go
user: login: robscott
body: +1 to @youngnick's comments, a couple suggestions as a starting point:

1. If an implementation does support an empty value, we expect them to assign an address matching the type that has been requested with a value of the implementation's choice.
2. If an implementation does _not_ support an empty value, we expect them to set the `Programmed` condition in status to `False` with a reason of `AddressNotAssigned`. Here's what that reason means as a reference:

https://github.com/kubernetes-sigs/gateway-api/blob/d936258b0ed256e2d4eaa31da01fa703f0e954b0/apis/v1/gateway_types.go#L874-L886
created_at: 2025-02-20T02:02:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,15 @@
+---
+apiVersion: gateway.networking.k8s.io/v1
+kind: Gateway
+metadata:
+  name: gateway-without-address-value
+  namespace: gateway-conformance-infra
+spec:
+  gatewayClassName: "{GATEWAY_CLASS_NAME}"
+  addresses:
+  # How should we inject a valid type here?
+  - type: "VALID_ADDRESS_TYPE_PLACEHOLDER"
path: conformance/tests/gateway-optional-address-value.yaml
user: login: robscott
body: This seems like the fundamental challenge of writing a conformance test for this. As far as I know, none of our conformance tests so far require a specific address type. I like your idea of allowing this to be a configurable flag on the test suite and that's probably sufficient as a starting point. It could be useful to expand this in the future to have a separate `SupportedFeature` per type though. 


created_at: 2025-02-20T02:05:57Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: robscott
body: This feature name doesn't quite seem right. Maybe useful to describe the address options that will be available after this PR merges:

1. Gateway.spec.addresses unspecified - implementation automatically assigns address(es) of whatever type(s) they choose
2. Gateway.spec.addresses.value unspecified (new in this PR) - implementation automatically assigns address(es) of the type requested (if possible)
3. Gateway.spec.addresses fully specified - implementation automatically assigns address requested (if possible)

So that's a long way of saying that the thing that's new here is the ability to request a _type_ of address without specifying the desired value. So maybe a better name would be `GatewayTypedAddressAllocation` although that's awfully verbose. Naming is decidedly not my specialty, but something in that general direction would probably be helpful.
created_at: 2025-02-20T02:12:58Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: EyalPazz
body: can't a fully specified Gateway.spec.addresses also be considered a `TypedAddress`?
created_at: 2025-02-20T08:16:00Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
 	// +kubebuilder:validation:MinLength=1
 	// +kubebuilder:validation:MaxLength=253
 	Value string `json:"value"`
path: apis/v1/gateway_types.go
user: login: youngnick
body: Yes, I agree that because empty string means the same thing as unspecified, I'm fine with not moving to a pointer, and just allowing this to default to `""`.
created_at: 2025-02-26T06:32:00Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: youngnick
body: How about `GatewayAutoAddressAssignment` (as opposed to the existing static assignment)?
created_at: 2025-02-26T06:32:53Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: robscott
body: >can't a fully specified Gateway.spec.addresses also be considered a TypedAddress?

Fair point.

>How about GatewayAutoAddressAssignment (as opposed to the existing static assignment)?

Isn't this what happens by default with most/all Gateways when `spec.addresses` is empty?

Maybe a generic name like "GatewayAddressByType" would work, but even that doesn't feel quite right. The more I think about this, the more it seems like starting with names per address type like "GatewayIPAddressAllocation" would be the most future proof path here. 
created_at: 2025-02-26T07:24:33Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
 	// +kubebuilder:validation:MinLength=1
 	// +kubebuilder:validation:MaxLength=253
 	Value string `json:"value"`
path: apis/v1/gateway_types.go
user: login: EyalPazz
body: what are scenarios where we'll want to use a pointer?
created_at: 2025-02-27T08:49:58Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: EyalPazz
body: 
> The more I think about this, the more it seems like starting with names per address type like "GatewayIPAddressAllocation" would be the most future proof path here.

and then add a feature for each type of each provider? 

created_at: 2025-02-27T09:21:58Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: EyalPazz
body: or have IPAddress | Hostname | Dynamic
created_at: 2025-02-27T09:22:59Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
 	// +kubebuilder:validation:MinLength=1
 	// +kubebuilder:validation:MaxLength=253
 	Value string `json:"value"`
path: apis/v1/gateway_types.go
user: login: robscott
body: Good question, I didn't make that very clear.

The pointer is useful when you want to be able to distinguish between unspecified (nil) and empty (""). In our case we don't so a string without a min length is fine but in some cases it is a useful distinction.
created_at: 2025-02-28T02:12:21Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: robscott
body: > and then add a feature for each type of each provider?

Theoretically yes, but I think all existing Gateway API implementations support IP Address, so that's probably a sufficient starting point.

>or have IPAddress | Hostname | Dynamic

What would Dynamic mean here? My intention with `GatewayIPAddressAllocation` was that it would indicate that a Gateway would provision an IPAddress if requested in `spec.addresses.type`. (I think all do by default today without that request, but it's still a reasonable test to be sure).
created_at: 2025-02-28T02:14:44Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: EyalPazz
body: Dynamic would mean everything that is provider-specific, but maybe that's not the correct term
Isn't it kinda weird that for each type you would need to have a new Feature Request instead of just letting the implementation worry about it, and just make sure it conforms from our side?
created_at: 2025-03-02T09:06:03Z
author_association: MEMBER
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: robscott
body: While I expect lots of implementation-specific values to exist here, I don't think there's really a good way to conformance test implementation-specific support like this, so I'd leave that out of scope.
created_at: 2025-03-02T23:13:42Z
author_association: MEMBER
diff_hunk: @@ -747,7 +747,6 @@ type LabelValue string
 //
 // +kubebuilder:validation:MinLength=1
 // +kubebuilder:validation:MaxLength=253
-// +kubebuilder:validation:Pattern=`^Hostname|IPAddress|NamedAddress|[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*\/[A-Za-z0-9\/\-._~%!$&'()*+,;=:]+$`
path: apis/v1/shared_types.go
user: login: robscott
body: We still want to leave this in place, no changes here, the only change would be to make the value optional in spec.
created_at: 2025-03-05T01:11:39Z
author_association: MEMBER
diff_hunk: @@ -739,9 +739,10 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
 	// +kubebuilder:validation:MinLength=1
path: apis/v1/gateway_types.go
user: login: robscott
body: Since we decided against making this a pointer, we need to remove the minLength to make this optional
```suggestion
```
created_at: 2025-03-05T01:12:35Z
author_association: MEMBER
diff_hunk: @@ -739,9 +739,10 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
 	// +kubebuilder:validation:MinLength=1
path: apis/v1/gateway_types.go
user: login: EyalPazz
body: Makes sense :)
created_at: 2025-03-05T18:57:30Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
path: apis/v1/gateway_types.go
user: login: youngnick
body: I think the extra doc requirement here got missed. I'd still like to see some description of what should happen in both cases _on this field_, if it's supported and not.
created_at: 2025-03-14T02:20:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: youngnick
body: So what was the decision here? Are we going to stick with `GatewayAddressEmpty`?
created_at: 2025-03-14T02:21:19Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,6 +60,10 @@ const (
 	// SupportGatewayInfrastructureAnnotations option indicates support for
 	// spec.infrastructure.annotations and spec.infrastructure.labels
 	SupportGatewayInfrastructurePropagation FeatureName = "GatewayInfrastructurePropagation"
+
+	//  SupportGatewayAddressEmpty option indicates support for an empty
+	//  spec.addresses.value field
+	SupportGatewayAddressEmpty FeatureName = "GatewayAddressEmpty"
path: pkg/features/gateway.go
user: login: robscott
body: I think we should hold off on assigning a feature name until we add conformance tests for this.
created_at: 2025-03-14T02:50:39Z
author_association: MEMBER
diff_hunk: @@ -672,6 +672,7 @@ type GatewayAddress struct {
 	//
 	// Examples: `1.2.3.4`, `128::1`, `my-ip-address`.
 	//
+	// +optional
path: apis/v1/gateway_types.go
user: login: robscott
body: Thanks for catching this @youngnick! @EyalPazz any chance you can squeeze this in in the next day or two? Really hoping we can fit this into v1.3 if possible, and we'll need to cut a RC pretty soon.
created_at: 2025-03-14T03:08:53Z
author_association: MEMBER
diff_hunk: @@ -724,24 +724,29 @@ type RouteGroupKind struct {
 	Kind Kind `json:"kind"`
 }
 
-// GatewayAddress describes an address that can be bound to a Gateway.
+// GatewaySpecAddress describes an address that can be bound to a Gateway.
 //
 // +kubebuilder:validation:XValidation:message="Hostname value must only contain valid characters (matching ^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$)",rule="self.type == 'Hostname' ? self.value.matches(r\"\"\"^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$\"\"\"): true"
-type GatewayAddress struct {
+type GatewaySpecAddress struct {
 	// Type of the address.
 	//
 	// +optional
 	// +kubebuilder:default=IPAddress
 	Type *AddressType `json:"type,omitempty"`
 
-	// Value of the address. The validity of the values will depend
+	
+	// If the implementation supports the `SupportGatewayAddressEmpty` feature, 
+	// this value can be omitted, and an address will be assigned based on the `Type` of the address (Given that the
+	// implementation supports it for the given `Type`).
+	//
+	// If the value is provided, its validity will depend
path: apis/v1/gateway_types.go
user: login: robscott
body: ```suggestion
	// When a value is unspecified, an implementation SHOULD automatically
	// assign an address matching the requested type if possible.
	//
	// If an implementation does not support an empty value, they MUST set the 
	// "Programmed" condition in status to False with a reason of "AddressNotAssigned".
```
created_at: 2025-03-15T01:13:07Z
author_association: MEMBER

PR comments:
user: login: k8s-ci-robot
created_at: 2025-02-16T09:01:03Z
body: Hi @EyalPazz. Thanks for your PR.

I'm waiting for a [kubernetes-sigs](https://github.com/orgs/kubernetes-sigs/people) member to verify that this patch is reasonable to test. If it is, they should reply with `/ok-to-test` on its own line. Until that is done, I will not automatically test new commits in this PR, but the usual testing commands by org members will still work. Regular contributors should [join the org](https://git.k8s.io/community/community-membership.md#member) to skip this step.

Once the patch is verified, the new status will be reflected by the `ok-to-test` label.

I understand the commands that are listed [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fgateway-api).

<details>

Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md).  If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes-sigs/prow](https://github.com/kubernetes-sigs/prow/issues/new?title=Prow%20issue:) repository.
</details>

author_association: CONTRIBUTOR
user: login: EyalPazz
created_at: 2025-02-19T14:09:16Z
body: @youngnick, this isn't ready for merge yet, i wanted your opinion on injecting a valid `type` value to the test suite
Should we take a similar approach to the `GatewayStaticAddresses` test?
author_association: MEMBER
user: login: EyalPazz
created_at: 2025-02-19T14:16:50Z
body: @shaneutt , i'd also appreciate your opinion here, as i saw you wrote the `PLACEHOLDER_**_ADDRS` hacks
author_association: MEMBER
user: login: youngnick
created_at: 2025-02-20T00:36:21Z
body: > @youngnick, this isn't ready for merge yet, i wanted your opinion on injecting a valid `type` value to the test suite Should we take a similar approach to the `GatewayStaticAddresses` test?

That seems reasonable, yes.
author_association: CONTRIBUTOR
user: login: EyalPazz
created_at: 2025-02-24T12:26:10Z
body: @youngnick @robscott can you please re-review this?
author_association: MEMBER
user: login: robscott
created_at: 2025-02-28T02:14:58Z
body: /ok-to-test
author_association: MEMBER
user: login: EyalPazz
created_at: 2025-03-02T08:52:46Z
body: /retest 
author_association: MEMBER
user: login: robscott
created_at: 2025-03-05T17:45:25Z
body: Hey @EyalPazz, I chatted with @youngnick about this and we agreed that it would make sense to move the conformance test for this into a follow up PR. The rationale is that as long as we have a clearly defined API spec for this, the conformance test would be covering with 99+% of implementations already do (automatically assign an IP address to a Gateway), so it's not quite as critical as we'd initially thought.

Will you have time to update the PR in the next day or two to reflect this new smaller scope? Would love to get this into v1.3 if possible.
author_association: MEMBER
user: login: EyalPazz
created_at: 2025-03-05T18:57:51Z
body: > Hey @EyalPazz, I chatted with @youngnick about this and we agreed that it would make sense to move the conformance test for this into a follow up PR. The rationale is that as long as we have a clearly defined API spec for this, the conformance test would be covering with 99+% of implementations already do (automatically assign an IP address to a Gateway), so it's not quite as critical as we'd initially thought.
> 
> Will you have time to update the PR in the next day or two to reflect this new smaller scope? Would love to get this into v1.3 if possible.

Yea for sure, i'll do it tomorrow morning
author_association: MEMBER
user: login: EyalPazz
created_at: 2025-03-06T08:48:17Z
body: Should be ok now :) @robscott 
author_association: MEMBER
user: login: robscott
created_at: 2025-03-07T06:23:30Z
body: Thanks @EyalPazz! Will leave final LGTM for @youngnick or someone else.

/approve
author_association: MEMBER
user: login: k8s-ci-robot
created_at: 2025-03-07T06:23:38Z
body: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/kubernetes-sigs/gateway-api/pull/3616#" title="Author self-approved">EyalPazz</a>*, *<a href="https://github.com/kubernetes-sigs/gateway-api/pull/3616#issuecomment-2705631926" title="Approved">robscott</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=kubernetes-sigs%2Fgateway-api).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/kubernetes-sigs/gateway-api/blob/main/OWNERS)~~ [robscott]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
author_association: CONTRIBUTOR
user: login: robscott
created_at: 2025-03-14T03:09:18Z
body: I missed something when I approved. Adding a hold until https://github.com/kubernetes-sigs/gateway-api/pull/3616#discussion_r1994571894 is addressed.

/hold
author_association: MEMBER
user: login: robscott
created_at: 2025-03-16T17:45:51Z
body: Thanks @EyalPazz! This LGTM, will defer to @youngnick for final approval.

/hold cancel
author_association: MEMBER
user: login: youngnick
created_at: 2025-03-16T22:43:45Z
body: Thanks for the extra changes @EyalPazz, great work.

/lgtm
author_association: CONTRIBUTOR