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
111 changes: 111 additions & 0 deletions api/v1alpha1/egressshardparameters_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*
Copyright © 2026 Datum Technology, Inc. All rights reserved.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/

package v1alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// KindEgressShardParameters is the kind an InternetEgressClass names in its
// parametersRef to be served by this controller.
//
// The reference is opaque to everything that carries it: the class states a
// group, a kind and a name, and no component between the class and this
// controller reads them. This controller answers only for its own group and
// this kind, and ignores a class whose parameters some other implementation
// owns, so two implementations can serve two classes in the same cell.
const KindEgressShardParameters = "EgressShardParameters"

// EgressShardParametersSpec selects the egress shards serving a class.
type EgressShardParametersSpec struct {
// ShardNamespace is the namespace holding the EgressShard objects this
// selector may match.
//
// It is required and there is no cluster-wide search. A selector evaluated
// over every namespace would match an EgressShard a tenant created in a
// namespace they write to, which is a tenant naming the node their own
// traffic — and everyone else's on the same class — leaves the platform
// through. Naming the one namespace an operator owns keeps that
// unreachable.
//
// It carries no default even though every deployment today answers
// galactic-system, which is where the galactic data plane's own objects
// live. The namespace names the nodes that every network on this class
// leaves the platform through, and that is worth an operator stating.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
ShardNamespace string `json:"shardNamespace"`

// ShardSelector selects the EgressShards a network on this class egresses
// through, by the network.datumapis.com/egress-* labels an operator sets
// on them.
//
// An empty selector matches every shard in the namespace, which sends a
// consumer's traffic out of an arbitrary cell. Egress is realized per
// cell, so a selector is expected to pin a cell and a pool.
//
// The selector runs one way, as the only binding between a class and the
// shards serving it: a shard names nothing that selects it, which is what
// keeps the data-plane API group independent of the consumer-facing one.
//
// +kubebuilder:validation:Required
ShardSelector metav1.LabelSelector `json:"shardSelector"`
}

// +kubebuilder:object:root=true
// +kubebuilder:resource:scope=Cluster
// +kubebuilder:printcolumn:name="Shard Namespace",type="string",JSONPath=".spec.shardNamespace"
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"

// EgressShardParameters is the configuration this controller reads when an
// InternetEgressClass names it, and it holds which egress shards serve the
// networks that class places in this cell.
//
// It is cluster-scoped because the reference that reaches it carries no
// namespace: a class is cluster-scoped and its parametersRef states a group, a
// kind and a name only, so a namespaced parameters object would be
// unresolvable from the class that names it. The content is an operator's
// statement about the cell's own data plane rather than anything belonging to
// one tenant, and every tenant namespace resolves the same answer from it.
//
// This object is written by an operator. No consumer reads or writes one, and
// a consumer names a class, never these parameters.
type EgressShardParameters struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// Spec is the whole of this object. There is no status: nothing reconciles
// these parameters, and the result of applying them is reported on the
// network context whose egress they served.
Spec EgressShardParametersSpec `json:"spec,omitempty"`
}

// +kubebuilder:object:root=true

// EgressShardParametersList contains a list of EgressShardParameters.
type EgressShardParametersList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []EgressShardParameters `json:"items"`
}

func init() {
SchemeBuilder.Register(&EgressShardParameters{}, &EgressShardParametersList{})
}
88 changes: 88 additions & 0 deletions api/v1alpha1/vpcattachment_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,85 @@ type VPCAttachmentInterface struct {
Addresses []IPAddress `json:"addresses,omitempty"`
}

// InternetEgressAddressFamily is the address family of an egress source
// address.
//
// Only IPv6 is reported. Reaching an IPv4 destination needs a resolver and a
// translator sharing a prefix, which the platform pairs neither of, so the
// value is withheld rather than reported and not delivered. An address written
// today records IPv6, so accepting IPv4 later changes no attachment.
//
// +kubebuilder:validation:Enum=IPv6
type InternetEgressAddressFamily string

// InternetEgressAddressFamilyIPv6 is an IPv6 egress source address.
const InternetEgressAddressFamilyIPv6 InternetEgressAddressFamily = "IPv6"

// InternetEgressAddressStability is how far a consumer may rely on an egress
// source address. It is the consumer-side projection of the serving class's
// sharing, derived here so a consumer never reads a class.
//
// +kubebuilder:validation:Enum=None;Network
type InternetEgressAddressStability string

const (
// InternetEgressAddressStabilityNone means the address may change and
// other networks share it. Allow-listing it admits traffic from other
// networks and loses access when the address changes.
InternetEgressAddressStabilityNone InternetEgressAddressStability = "None"

// InternetEgressAddressStabilityNetwork means the address belongs to this
// network and persists. Allow-listing it is safe.
InternetEgressAddressStabilityNetwork InternetEgressAddressStability = "Network"
)

// InternetEgressSourceAddress is one address outbound traffic leaves on.
//
// +kubebuilder:validation:XValidation:rule="self.family != 'IPv6' || (isIP(self.address) && ip(self.address).family() == 6)",message="an IPv6 source address must be a valid IPv6 address"
type InternetEgressSourceAddress struct {
// Family is the address family of this source address.
// +required
Family InternetEgressAddressFamily `json:"family"`

// Address is the source address translation writes, without a prefix
// length.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=39
// +required
Address string `json:"address"`

// Stability states how far a consumer may rely on this address before
// they act on it.
// +required
Stability InternetEgressAddressStability `json:"stability"`
}

// VPCAttachmentInternetEgressStatus reports the outbound path this attachment
// leaves the platform on.
type VPCAttachmentInternetEgressStatus struct {
// SourceAddresses are the addresses translation writes for this
// attachment, one per family reached.
//
// Absent means this attachment reaches nothing outside the platform, or
// that no address has been reported for a path that does. An absent list
// is never a placeholder: a consumer that allow-listed a guessed address
// would admit the wrong traffic and believe otherwise.
//
// +listType=map
// +listMapKey=family
// +kubebuilder:validation:MaxItems=2
// +optional
SourceAddresses []InternetEgressSourceAddress `json:"sourceAddresses,omitempty"`
}

// VPCAttachmentEgressStatus reports what this attachment reaches outside the
// platform.
type VPCAttachmentEgressStatus struct {
// Internet is the internet egress realized for this attachment.
// +optional
Internet *VPCAttachmentInternetEgressStatus `json:"internet,omitempty"`
}

// VPCAttachmentStatus defines the observed state of VPCAttachment.
//
// Every field but Conditions is optional: an identifier is recorded before a pod
Expand Down Expand Up @@ -181,6 +260,15 @@ type VPCAttachmentStatus struct {
// +kubebuilder:validation:MinLength=1
// +optional
NetworkAttachmentDefinition string `json:"networkAttachmentDefinition,omitempty"`

// Egress reports what this attachment reaches outside the platform.
//
// It is reported per attachment rather than on the network, because the
// interface is what a workload holds and what a consumer reads back
// through. This controller is the only component that resolved which shard
// the network bound to, so it is the only one that can report the answer.
// +optional
Egress *VPCAttachmentEgressStatus `json:"egress,omitempty"`
}

// +kubebuilder:object:root=true
Expand Down
134 changes: 134 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.

Loading
Loading