Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions api/v1alpha1/rule_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,19 @@ type NetworkRuleSpec struct {
// +kubebuilder:validation:XValidation:rule="(has(self.matchLabels) && size(self.matchLabels) > 0) || (has(self.matchExpressions) && size(self.matchExpressions) > 0)",message="backendSelector must not be empty"
BackendSelector metav1.LabelSelector `json:"backendSelector"`

// BackendAddresses, when set, limits the selected VPCAttachment backends
// to these IPv6 addresses. An explicitly empty list selects no backends;
// omitting the field preserves selector-only behavior. The gateway and
// backend nodes apply the same filter, so removing an address removes both
// its ingress selection and its ServiceVIPBinding. The addresses must still
// belong to selected attachments in VPCRef; this field cannot authorize a
// backend in another VPC.
// +optional
// +kubebuilder:validation:MaxItems=256
// +kubebuilder:validation:XValidation:rule="self.all(a, isIP(a) && a.contains(':'))",message="backendAddresses must contain IPv6 addresses"
// +listType=set
BackendAddresses *[]string `json:"backendAddresses,omitempty"`

// BackendPort is the destination port on every selected backend.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Minimum=1
Expand Down
24 changes: 24 additions & 0 deletions api/v1alpha1/rule_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,17 +31,23 @@ func newTestRule() *NetworkRule {
// copy: mutations to slices in the copy must not affect the original.
func TestNetworkRuleDeepCopy(t *testing.T) {
orig := newTestRule()
addresses := []string{"fd00:10::1"}
orig.Spec.BackendAddresses = &addresses
dup := orig.DeepCopy()

dup.Spec.VIPAddresses[0] = "2001:db8:1::20"
dup.Spec.BackendSelector.MatchLabels["app"] = "api"
(*dup.Spec.BackendAddresses)[0] = "fd00:10::2"

if orig.Spec.VIPAddresses[0] != "2001:db8:1::10" {
t.Errorf("VIPAddresses[0] mutated: got %q", orig.Spec.VIPAddresses[0])
}
if orig.Spec.BackendSelector.MatchLabels["app"] != "web" {
t.Errorf("BackendSelector.MatchLabels mutated: got %v", orig.Spec.BackendSelector.MatchLabels)
}
if (*orig.Spec.BackendAddresses)[0] != "fd00:10::1" {
t.Errorf("BackendAddresses mutated: got %v", *orig.Spec.BackendAddresses)
}
}

// TestNetworkRuleDeepCopyNil verifies DeepCopy on a nil pointer returns nil.
Expand Down Expand Up @@ -145,6 +151,24 @@ func TestNetworkRuleBackendFieldNames(t *testing.T) {
if _, ok := m["backends"]; ok {
t.Errorf("unexpected \"backends\" key: %v", m)
}
if _, ok := m["backendAddresses"]; ok {
t.Errorf("omitted backendAddresses must preserve selector-only behavior: %v", m)
}

empty := []string{}
rule := newTestRule()
rule.Spec.BackendAddresses = &empty
data, err = json.Marshal(rule.Spec)
if err != nil {
t.Fatalf("Marshal explicit empty backendAddresses: %v", err)
}
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("Unmarshal explicit empty backendAddresses: %v", err)
}
addresses, ok := m["backendAddresses"].([]any)
if !ok || len(addresses) != 0 {
t.Errorf("explicit empty backendAddresses = %v, want []", m["backendAddresses"])
}
}

// TestNetworkRuleProtocolValues is a regression test pinning the accepted
Expand Down
9 changes: 9 additions & 0 deletions api/v1alpha1/zz_generated.deepcopy.go

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

17 changes: 17 additions & 0 deletions config/crd/network.datumapis.com_networkrules.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,23 @@ spec:
NetworkRuleSpec defines the desired ingress load-balancing state for a
tenant VPC.
properties:
backendAddresses:
description: |-
BackendAddresses, when set, limits the selected VPCAttachment backends
to these IPv6 addresses. An explicitly empty list selects no backends;
omitting the field preserves selector-only behavior. The gateway and
backend nodes apply the same filter, so removing an address removes both
its ingress selection and its ServiceVIPBinding. The addresses must still
belong to selected attachments in VPCRef; this field cannot authorize a
backend in another VPC.
items:
type: string
maxItems: 256
type: array
x-kubernetes-list-type: set
x-kubernetes-validations:
- message: backendAddresses must contain IPv6 addresses
rule: self.all(a, isIP(a) && a.contains(':'))
backendPort:
description: BackendPort is the destination port on every selected
backend.
Expand Down
1 change: 1 addition & 0 deletions docs/api/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,7 @@ _Appears in:_
| `protocol` _[NetworkRuleProtocol](#networkruleprotocol)_ | Protocol is the transport protocol matched by VIPAddresses/Port. | | Enum: [tcp udp] <br />Required: \{\} <br /> |
| `port` _integer_ | Port is the ingress port on VIPAddresses that this rule load-balances. | | Maximum: 65535 <br />Minimum: 1 <br />Required: \{\} <br /> |
| `backendSelector` _[LabelSelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v/#labelselector-v1-meta)_ | BackendSelector selects the VPCAttachments (cloud.datumapis.com/v1alpha1,<br />in any namespace) that serve this rule, matched against each<br />VPCAttachment object's own labels. Only attachments whose status.vpc<br />equals VPCRef are candidates, whatever their labels, so a selector cannot<br />reach into another tenant's VPC. Each selected attachment contributes the<br />IPv6 addresses in its spec.interface.addresses as backends, on<br />BackendPort; the DSR datapath carries IPv6 backends only. An attachment<br />with no status.node or no IPv6 address is not a backend until it has<br />both. An empty selector is rejected rather than read as "every attachment<br />in the VPC". | | Required: \{\} <br /> |
| `backendAddresses` _string_ | BackendAddresses, when set, limits the selected VPCAttachment backends<br />to these IPv6 addresses. An explicitly empty list selects no backends;<br />omitting the field preserves selector-only behavior. The gateway and<br />backend nodes apply the same filter, so removing an address removes both<br />its ingress selection and its ServiceVIPBinding. The addresses must still<br />belong to selected attachments in VPCRef; this field cannot authorize a<br />backend in another VPC. | | MaxItems: 256 <br /> |
| `backendPort` _integer_ | BackendPort is the destination port on every selected backend. | | Maximum: 65535 <br />Minimum: 1 <br />Required: \{\} <br /> |


Expand Down
Loading