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
19 changes: 19 additions & 0 deletions api/v1alpha1/egressshard_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ type AddressClaimRef struct {
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.shardAddressIPv6ClaimRef) || has(self.shardAddressIPv6ClaimRef)",message="shardAddressIPv6ClaimRef cannot be unassigned once assigned"
// +kubebuilder:validation:XValidation:rule="!has(oldSelf.shardAddressIPv4ClaimRef) || has(self.shardAddressIPv4ClaimRef)",message="shardAddressIPv4ClaimRef cannot be unassigned once assigned"
// +kubebuilder:validation:XValidation:rule="!(has(self.translateWellKnownPrefix) && self.translateWellKnownPrefix) || (has(self.nat64Prefix) && size(self.nat64Prefix) > 0)",message="translateWellKnownPrefix requires nat64Prefix"
// +kubebuilder:validation:XValidation:rule="!(has(self.nat64Standby) && self.nat64Standby) || (has(self.shardAddressIPv4) && size(self.shardAddressIPv4) > 0)",message="nat64Standby requires shardAddressIPv4"
type EgressShardSpec struct {
// TargetRef identifies the Node this shard executes on.
// +kubebuilder:validation:Required
Expand Down Expand Up @@ -330,6 +331,24 @@ type EgressShardSpec struct {
// it.
// +optional
Drain bool `json:"drain,omitempty"`

// NAT64Standby marks this shard's ShardAddressIPv4 as not attracted to its
// node while a sibling NAT64 shard is up: replies to it arrive at that
// sibling, which holds no session for them and drops them. Compute nodes
// that spread egress across a cluster's shards therefore keep NAT64
// tenants off a standby shard while any shard without the flag can serve
// that NAT64 class, and place them on it only when none can. Requires
// ShardAddressIPv4.
//
// It changes nothing on the shard itself, and NAT66 placement is
// unaffected: the shard's IPv6 address is advertised from its own node and
// keeps taking NAT66 tenants. Mutable, so the flag can follow the
// upstream announcement it describes.
//
// Compute nodes that send each tenant to a single configured shard ignore
// it.
// +optional
NAT64Standby bool `json:"nat64Standby,omitempty"`
}

// EgressShardStatus defines the observed state of an EgressShard.
Expand Down
6 changes: 5 additions & 1 deletion api/v1alpha1/egressshard_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,7 @@ func TestEgressShardSpecFieldNames(t *testing.T) {
orig.Spec.NAT64Prefix = "64:ff9b::/96"
orig.Spec.TranslateWellKnownPrefix = true
orig.Spec.Drain = true
orig.Spec.NAT64Standby = true

data, err := json.Marshal(orig.Spec)
if err != nil {
Expand Down Expand Up @@ -172,6 +173,9 @@ func TestEgressShardSpecFieldNames(t *testing.T) {
if raw, ok := m["drain"]; !ok || raw != true {
t.Errorf("drain: got %v, want true", raw)
}
if raw, ok := m["nat64Standby"]; !ok || raw != true {
t.Errorf("nat64Standby: got %v, want true", raw)
}
if _, ok := m["targetRef"]; !ok {
t.Error("expected \"targetRef\" key to be present")
}
Expand Down Expand Up @@ -213,7 +217,7 @@ func TestEgressShardSpecOmitEmpty(t *testing.T) {
for _, key := range []string{
"shardSID", "shardAddressIPv6", "shardAddressIPv4", "nat64Prefix",
"shardAddressIPv6ClaimRef", "shardAddressIPv4ClaimRef",
"translateWellKnownPrefix", "drain",
"translateWellKnownPrefix", "drain", "nat64Standby",
} {
if _, ok := m[key]; ok {
t.Errorf("expected %q key to be absent when empty", key)
Expand Down
21 changes: 21 additions & 0 deletions config/crd/network.datumapis.com_egressshards.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,24 @@ spec:
rule: self == '' || isCIDR(self)
- message: nat64Prefix is immutable once assigned
rule: self == oldSelf || oldSelf == ''
nat64Standby:
description: |-
NAT64Standby marks this shard's ShardAddressIPv4 as not attracted to its
node while a sibling NAT64 shard is up: replies to it arrive at that
sibling, which holds no session for them and drops them. Compute nodes
that spread egress across a cluster's shards therefore keep NAT64
tenants off a standby shard while any shard without the flag can serve
that NAT64 class, and place them on it only when none can. Requires
ShardAddressIPv4.

It changes nothing on the shard itself, and NAT66 placement is
unaffected: the shard's IPv6 address is advertised from its own node and
keeps taking NAT66 tenants. Mutable, so the flag can follow the
upstream announcement it describes.

Compute nodes that send each tenant to a single configured shard ignore
it.
type: boolean
shardAddressIPv4:
description: |-
ShardAddressIPv4 is the dedicated, publicly-routable IPv4 address this
Expand Down Expand Up @@ -362,6 +380,9 @@ spec:
- message: translateWellKnownPrefix requires nat64Prefix
rule: '!(has(self.translateWellKnownPrefix) && self.translateWellKnownPrefix)
|| (has(self.nat64Prefix) && size(self.nat64Prefix) > 0)'
- message: nat64Standby requires shardAddressIPv4
rule: '!(has(self.nat64Standby) && self.nat64Standby) || (has(self.shardAddressIPv4)
&& size(self.shardAddressIPv4) > 0)'
status:
description: |-
EgressShardStatus defines the observed state of an EgressShard.
Expand Down
1 change: 1 addition & 0 deletions docs/api/bgp.md
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ _Appears in:_
| `nat64Prefix` _string_ | NAT64Prefix is the IPv6 prefix whose synthesized addresses this shard<br />translates to IPv4 — a Datum-operated Network-Specific Prefix, shared<br />fabric-wide, never per-tenant. TranslateWellKnownPrefix adds the RFC 6052<br />Well-Known Prefix alongside it. It must be the prefix the resolver<br />synthesizes into; a shard translating for a different one is a<br />blackhole with no symptom on either side.<br />Set together with ShardAddressIPv4 or not at all: an address with no<br />prefix has nothing to translate for, and a prefix with no address has<br />nothing to translate into. Write-once for the same reason the addresses<br />are: a shard that starts translating a different prefix blackholes every<br />destination the resolver already synthesized into the old one. | | |
| `translateWellKnownPrefix` _boolean_ | TranslateWellKnownPrefix additionally translates destinations inside the<br />RFC 6052 Well-Known Prefix 64:ff9b::/96 to IPv4 from ShardAddressIPv4, so<br />clients using a public DNS64 resolver reach IPv4 destinations. Requires<br />NAT64Prefix.<br />RFC 6052 §3.1 forbids the Well-Known Prefix from representing non-global<br />IPv4 addresses; the implementation must refuse such destinations.<br />Mutable, unlike NAT64Prefix. Disabling it blackholes every destination<br />clients already resolved into the Well-Known Prefix until their DNS TTLs<br />expire. | | |
| `drain` _boolean_ | Drain stops this shard from taking new tenants while it keeps serving<br />the ones it has. Compute nodes that spread egress across a cluster's<br />shards give a draining shard no share of new tenant addresses, but keep<br />sending it every flow already pinned to it until that flow goes idle, so<br />established sessions finish where their translation state lives.<br />It changes nothing on the shard itself: the shard keeps translating,<br />advertising its SID and receiving replies. A shard is safe to remove<br />once its session count has fallen to zero. Clearing it puts the shard<br />back into rotation.<br />Compute nodes that send each tenant to a single configured shard ignore<br />it. | | |
| `nat64Standby` _boolean_ | NAT64Standby marks this shard's ShardAddressIPv4 as not attracted to its<br />node while a sibling NAT64 shard is up: replies to it arrive at that<br />sibling, which holds no session for them and drops them. Compute nodes<br />that spread egress across a cluster's shards therefore keep NAT64<br />tenants off a standby shard while any shard without the flag can serve<br />that NAT64 class, and place them on it only when none can. Requires<br />ShardAddressIPv4.<br />It changes nothing on the shard itself, and NAT66 placement is<br />unaffected: the shard's IPv6 address is advertised from its own node and<br />keeps taking NAT66 tenants. Mutable, so the flag can follow the<br />upstream announcement it describes.<br />Compute nodes that send each tenant to a single configured shard ignore<br />it. | | |


#### EgressShardStatus
Expand Down
1 change: 1 addition & 0 deletions docs/api/gateway.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,7 @@ _Appears in:_
| `nat64Prefix` _string_ | NAT64Prefix is the IPv6 prefix whose synthesized addresses this shard<br />translates to IPv4 — a Datum-operated Network-Specific Prefix, shared<br />fabric-wide, never per-tenant. TranslateWellKnownPrefix adds the RFC 6052<br />Well-Known Prefix alongside it. It must be the prefix the resolver<br />synthesizes into; a shard translating for a different one is a<br />blackhole with no symptom on either side.<br />Set together with ShardAddressIPv4 or not at all: an address with no<br />prefix has nothing to translate for, and a prefix with no address has<br />nothing to translate into. Write-once for the same reason the addresses<br />are: a shard that starts translating a different prefix blackholes every<br />destination the resolver already synthesized into the old one. | | |
| `translateWellKnownPrefix` _boolean_ | TranslateWellKnownPrefix additionally translates destinations inside the<br />RFC 6052 Well-Known Prefix 64:ff9b::/96 to IPv4 from ShardAddressIPv4, so<br />clients using a public DNS64 resolver reach IPv4 destinations. Requires<br />NAT64Prefix.<br />RFC 6052 §3.1 forbids the Well-Known Prefix from representing non-global<br />IPv4 addresses; the implementation must refuse such destinations.<br />Mutable, unlike NAT64Prefix. Disabling it blackholes every destination<br />clients already resolved into the Well-Known Prefix until their DNS TTLs<br />expire. | | |
| `drain` _boolean_ | Drain stops this shard from taking new tenants while it keeps serving<br />the ones it has. Compute nodes that spread egress across a cluster's<br />shards give a draining shard no share of new tenant addresses, but keep<br />sending it every flow already pinned to it until that flow goes idle, so<br />established sessions finish where their translation state lives.<br />It changes nothing on the shard itself: the shard keeps translating,<br />advertising its SID and receiving replies. A shard is safe to remove<br />once its session count has fallen to zero. Clearing it puts the shard<br />back into rotation.<br />Compute nodes that send each tenant to a single configured shard ignore<br />it. | | |
| `nat64Standby` _boolean_ | NAT64Standby marks this shard's ShardAddressIPv4 as not attracted to its<br />node while a sibling NAT64 shard is up: replies to it arrive at that<br />sibling, which holds no session for them and drops them. Compute nodes<br />that spread egress across a cluster's shards therefore keep NAT64<br />tenants off a standby shard while any shard without the flag can serve<br />that NAT64 class, and place them on it only when none can. Requires<br />ShardAddressIPv4.<br />It changes nothing on the shard itself, and NAT66 placement is<br />unaffected: the shard's IPv6 address is advertised from its own node and<br />keeps taking NAT66 tenants. Mutable, so the flag can follow the<br />upstream announcement it describes.<br />Compute nodes that send each tenant to a single configured shard ignore<br />it. | | |


#### EgressShardStatus
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ spec:
kind, and that an address and a claim reference are independently
optional. Also verifies that translateWellKnownPrefix requires nat64Prefix
and, unlike it, can be turned off again. Also verifies that drain can be set
and cleared on a shard whose addresses are fixed. Also verifies that status remains writable,
and cleared on a shard whose addresses are fixed, and that nat64Standby
requires shardAddressIPv4 and can be set and cleared. Also verifies that status remains writable,
since status reports what the datapath actually programmed and must still
converge. Does not require any operator to be running — every rule under
test is enforced by the API server alone.
Expand Down Expand Up @@ -423,6 +424,54 @@ spec:
fi
echo "OK: drain set on an addressed shard, then cleared"

- name: reject-nat64-standby-without-ipv4
try:
- script:
content: |
set +e
OUTPUT=$(kubectl apply -n "$NAMESPACE" -f - 2>&1 <<'EOF'
apiVersion: network.datumapis.com/v1alpha1
kind: EgressShard
metadata:
name: e2e-egress-shard-standby-v6-only
spec:
targetRef:
kind: Node
name: bgp-e2e-worker
shardAddressIPv6: 2001:db8:f00d::102
nat64Standby: true
EOF
)
EXIT=$?
set -e
if [ "$EXIT" -eq 0 ]; then
echo "ERROR: expected rejection of nat64Standby with no shardAddressIPv4 but the resource was accepted"
kubectl delete egressshard e2e-egress-shard-standby-v6-only -n "$NAMESPACE" 2>/dev/null || true
exit 1
fi
case "$OUTPUT" in
*"nat64Standby requires shardAddressIPv4"*) ;;
*) echo "ERROR: rejected for the wrong reason: $OUTPUT"; exit 1 ;;
esac
echo "OK: nat64Standby with no shardAddressIPv4 correctly rejected"

- name: toggle-nat64-standby
try:
- script:
content: |
set -e
kubectl patch egressshard e2e-egress-shard -n "$NAMESPACE" --type=merge -p '
{"spec":{"nat64Standby":true}}'
ON=$(kubectl get egressshard e2e-egress-shard -n "$NAMESPACE" -o jsonpath='{.spec.nat64Standby}')
kubectl patch egressshard e2e-egress-shard -n "$NAMESPACE" --type=merge -p '
{"spec":{"nat64Standby":false}}'
OFF=$(kubectl get egressshard e2e-egress-shard -n "$NAMESPACE" -o jsonpath='{.spec.nat64Standby}')
if [ "$ON" != "true" ] || [ "$OFF" != "false" ]; then
echo "ERROR: nat64Standby toggled to $ON then $OFF, want true then false"
exit 1
fi
echo "OK: nat64Standby set on a shard with an IPv4 address, then cleared"

- name: accept-status-changes
try:
- script:
Expand Down
Loading