Skip to content
Merged
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
8 changes: 4 additions & 4 deletions .crd-ref-docs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ processor:
- "NetworkGateway"
- "NetworkGatewaySpec"
- "NetworkGatewayStatus"
- "NetworkRule"
- "NetworkRuleProtocol"
- "NetworkRuleSpec"
- "NetworkRuleStatus"
- "NetworkService"
- "NetworkServiceProtocol"
- "NetworkServiceSpec"
- "NetworkServiceStatus"
- "NetworkEgressPolicy"
- "NetworkEgressPolicySpec"
- "NetworkEgressPolicyStatus"
Expand Down
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ All dev tools (golangci-lint, controller-gen, chainsaw, yamlfmt) are installed l

| Group | Version | Resources |
|--------------------|------------|---------------------------------------------------------------------------|
| `network.datumapis.com` | `v1alpha1` | BGPRouter, BGPPeer, BGPAdvertisement, BGPPolicy, BGPVRFInstance, NetworkGateway, NetworkRule |
| `network.datumapis.com` | `v1alpha1` | BGPRouter, BGPPeer, BGPAdvertisement, BGPPolicy, BGPVRFInstance, NetworkGateway, NetworkService |

Source lives in `api/v1alpha1/`. Each resource has its own `*_types.go` file; shared types (RouterTarget, AddressFamily, etc.) live in `shared_types.go`.

Expand Down
4 changes: 2 additions & 2 deletions api/v1alpha1/egresspolicy_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import (

// NetworkEgressPolicy enables internet egress for a single tenant
// VPC/VPCAttachment, served by the sharded, stateful galactic-nat tier
// (see EgressShard). Unlike NetworkRule, it carries no VIP/backend/port:
// (see EgressShard). Unlike NetworkService, it carries no VIP/backend/port:
// egress is on or off for a (vpcRef, vpcAttachmentRef) pair,
// existence-implies-enabled, not a per-flow rule — because the destination
// of an egress flow is an arbitrary internet address, not a pre-configured
// backend list.
//
// It is namespaced (deployed to galactic-system) and tenant-writable; like
// NetworkRule's vpcRef, vpcRef/vpcAttachmentRef are opaque string identifiers
// NetworkService's vpcRef, vpcRef/vpcAttachmentRef are opaque string identifiers
// because the VPC API is owned by a separate companion operator, not this
// repo. An admission webhook (implemented by the consuming controller) must
// verify the requester is authorized for vpcRef/vpcAttachmentRef before a
Expand Down
4 changes: 2 additions & 2 deletions api/v1alpha1/gateway_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import (
// Maglev/DSR consistent-hash L4 load-balancer engine. Exactly one
// NetworkGateway exists per gateway node (spec.targetRef.name is the
// Kubernetes node name), mirroring the BGPRouter node-scoped root object
// pattern. NetworkRule resources are served by every NetworkGateway in the
// namespace equally (anycast — see NetworkRuleStatus's doc comment); this
// pattern. NetworkService resources are served by every NetworkGateway in the
// namespace equally (anycast — see NetworkServiceStatus's doc comment); this
// object's only job is to identify which nodes participate at all and
// surface each node's engine health via Conditions.
//
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// NetworkRuleProtocol is the transport protocol matched by a NetworkRule's
// NetworkServiceProtocol is the transport protocol matched by a NetworkService's
// ingress VIP.
//
// +kubebuilder:validation:Enum=tcp;udp
type NetworkRuleProtocol string
type NetworkServiceProtocol string

const (
// NetworkRuleProtocolTCP matches TCP traffic.
NetworkRuleProtocolTCP NetworkRuleProtocol = "tcp"
// NetworkServiceProtocolTCP matches TCP traffic.
NetworkServiceProtocolTCP NetworkServiceProtocol = "tcp"

// NetworkRuleProtocolUDP matches UDP traffic.
NetworkRuleProtocolUDP NetworkRuleProtocol = "udp"
// NetworkServiceProtocolUDP matches UDP traffic.
NetworkServiceProtocolUDP NetworkServiceProtocol = "udp"
)

// Accepted reasons — used as Accepted.Reason on NetworkRule (and reused by
// Accepted reasons — used as Accepted.Reason on NetworkService (and reused by
// NetworkEgressPolicy), set by the admission webhook that verifies the
// requester is authorized for the VPC resources the object references.
const (
Expand All @@ -32,58 +32,58 @@ const (
AcceptedReasonOwnershipDenied string = "OwnershipDenied"
)

// NetworkRule defines ingress load-balancing for a single tenant VPC, served
// NetworkService defines ingress load-balancing for a single tenant VPC, served
// by every NetworkGateway node identically (anycast Direct Server Return —
// see NetworkGateway's doc comment). It is namespaced (deployed to
// galactic-system) and tenant-writable; vpcRef is an opaque string identifier
// because the VPC API is owned by a separate companion operator, not this
// repo. An admission webhook (implemented by the consuming controller) must
// verify the requester is authorized for vpcRef before a rule is accepted —
// verify the requester is authorized for vpcRef before a service is accepted —
// see the Accepted condition.
//
// Unlike the earlier Full-NAT design this type originally described, there
// is no primary/secondary gateway node for a rule: every NetworkGateway
// advertises every accepted rule's vipAddresses at equal BGP preference,
// is no primary/secondary gateway node for a service: every NetworkGateway
// advertises every accepted service's vipAddresses at equal BGP preference,
// consistent-hashes the same backend list to the same backend for the same
// flow (internal/maglev), and forwards without rewriting anything —
// backend selection never needs a single "owning" node the way Full-NAT's
// SNAT-source model did.
//
// Backends are not listed. BackendSelector picks the VPCAttachments that
// serve the rule, so the backend set follows attachments as they are
// serve the service, so the backend set follows attachments as they are
// created, deleted or moved to another node, and galactic-router on each
// node generates the ServiceVIPBinding its backends need from the same
// selection.
//
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:scope=Namespaced,shortName=netrule
// +kubebuilder:resource:scope=Namespaced,shortName=netsvc
// +kubebuilder:printcolumn:name="VPC",type="string",JSONPath=".spec.vpcRef"
// +kubebuilder:printcolumn:name="PROTOCOL",type="string",JSONPath=".spec.protocol"
// +kubebuilder:printcolumn:name="PORT",type="integer",JSONPath=".spec.port"
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
type NetworkRule struct {
type NetworkService struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

Spec NetworkRuleSpec `json:"spec,omitempty"`
Status NetworkRuleStatus `json:"status,omitempty"`
Spec NetworkServiceSpec `json:"spec,omitempty"`
Status NetworkServiceStatus `json:"status,omitempty"`
}

// NetworkRuleSpec defines the desired ingress load-balancing state for a
// NetworkServiceSpec defines the desired ingress load-balancing state for a
// tenant VPC.
type NetworkRuleSpec struct {
// VPCRef is the opaque identifier of the target VPC this rule applies
type NetworkServiceSpec struct {
// VPCRef is the opaque identifier of the target VPC this service applies
// to. This repo does not own the VPC API and does not validate the
// identifier beyond non-emptiness; the admission webhook of the
// consuming controller is responsible for verifying the requester is
// authorized for this VPC before the rule is accepted.
// authorized for this VPC before the service is accepted.
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
VPCRef string `json:"vpcRef"`

// VIPAddresses is the list of ingress VIP addresses (IPv4 and/or IPv6)
// this rule provisions on the assigned gateway node(s).
// this service provisions on the assigned gateway node(s).
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinItems=1
// +kubebuilder:validation:MaxItems=8
Expand All @@ -94,16 +94,16 @@ type NetworkRuleSpec struct {

// Protocol is the transport protocol matched by VIPAddresses/Port.
// +kubebuilder:validation:Required
Protocol NetworkRuleProtocol `json:"protocol"`
Protocol NetworkServiceProtocol `json:"protocol"`

// Port is the ingress port on VIPAddresses that this rule load-balances.
// Port is the ingress port on VIPAddresses that this service load-balances.
// +kubebuilder:validation:Required
// +kubebuilder:validation:Minimum=1
// +kubebuilder:validation:Maximum=65535
Port int32 `json:"port"`

// BackendSelector selects the VPCAttachments (cloud.datumapis.com/v1alpha1,
// in any namespace) that serve this rule, matched against each
// in any namespace) that serve this service, matched against each
// VPCAttachment object's own labels. Only attachments whose status.vpc
// equals VPCRef are candidates, whatever their labels, so a selector cannot
// reach into another tenant's VPC. Each selected attachment contributes the
Expand All @@ -123,8 +123,8 @@ type NetworkRuleSpec struct {
BackendPort int32 `json:"backendPort"`
}

// NetworkRuleStatus defines the observed state of a NetworkRule.
type NetworkRuleStatus struct {
// NetworkServiceStatus defines the observed state of a NetworkService.
type NetworkServiceStatus struct {
// ObservedGeneration is the .metadata.generation this status was computed from.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
Expand All @@ -137,14 +137,14 @@ type NetworkRuleStatus struct {
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// NetworkRuleList is a list of NetworkRule resources.
// NetworkServiceList is a list of NetworkService resources.
// +kubebuilder:object:root=true
type NetworkRuleList struct {
type NetworkServiceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []NetworkRule `json:"items"`
Items []NetworkService `json:"items"`
}

func init() {
SchemeBuilder.Register(&NetworkRule{}, &NetworkRuleList{})
SchemeBuilder.Register(&NetworkService{}, &NetworkServiceList{})
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

func newTestRule() *NetworkRule {
return &NetworkRule{
func newTestService() *NetworkService {
return &NetworkService{
TypeMeta: metav1.TypeMeta{
APIVersion: "network.datumapis.com/v1alpha1",
Kind: "NetworkRule",
Kind: "NetworkService",
},
ObjectMeta: metav1.ObjectMeta{Name: "test-rule", Namespace: "galactic-system"},
Spec: NetworkRuleSpec{
ObjectMeta: metav1.ObjectMeta{Name: "test-service", Namespace: "galactic-system"},
Spec: NetworkServiceSpec{
VPCRef: "vpc-a",
VIPAddresses: []string{"2001:db8:1::10"},
Protocol: NetworkRuleProtocolTCP,
Protocol: NetworkServiceProtocolTCP,
Port: 443,
BackendSelector: metav1.LabelSelector{
MatchLabels: map[string]string{"app": "web"},
Expand All @@ -27,10 +27,10 @@ func newTestRule() *NetworkRule {
}
}

// TestNetworkRuleDeepCopy verifies that DeepCopy produces an independent
// TestNetworkServiceDeepCopy verifies that DeepCopy produces an independent
// copy: mutations to slices in the copy must not affect the original.
func TestNetworkRuleDeepCopy(t *testing.T) {
orig := newTestRule()
func TestNetworkServiceDeepCopy(t *testing.T) {
orig := newTestService()
dup := orig.DeepCopy()

dup.Spec.VIPAddresses[0] = "2001:db8:1::20"
Expand All @@ -44,18 +44,18 @@ func TestNetworkRuleDeepCopy(t *testing.T) {
}
}

// TestNetworkRuleDeepCopyNil verifies DeepCopy on a nil pointer returns nil.
func TestNetworkRuleDeepCopyNil(t *testing.T) {
var r *NetworkRule
// TestNetworkServiceDeepCopyNil verifies DeepCopy on a nil pointer returns nil.
func TestNetworkServiceDeepCopyNil(t *testing.T) {
var r *NetworkService
if r.DeepCopy() != nil {
t.Error("DeepCopy on nil pointer should return nil")
}
}

// TestNetworkRuleJSONRoundTrip verifies that the struct serialises and
// TestNetworkServiceJSONRoundTrip verifies that the struct serialises and
// deserialises through JSON without data loss.
func TestNetworkRuleJSONRoundTrip(t *testing.T) {
orig := newTestRule()
func TestNetworkServiceJSONRoundTrip(t *testing.T) {
orig := newTestService()
orig.Spec.BackendSelector.MatchExpressions = []metav1.LabelSelectorRequirement{
{Key: "tier", Operator: metav1.LabelSelectorOpIn, Values: []string{"frontend"}},
}
Expand All @@ -68,7 +68,7 @@ func TestNetworkRuleJSONRoundTrip(t *testing.T) {
t.Fatalf("Marshal: %v", err)
}

var got NetworkRule
var got NetworkService
if err := json.Unmarshal(data, &got); err != nil {
t.Fatalf("Unmarshal: %v", err)
}
Expand All @@ -87,11 +87,11 @@ func TestNetworkRuleJSONRoundTrip(t *testing.T) {
}
}

// TestNetworkRuleListDeepCopy verifies that NetworkRuleList.DeepCopy
// TestNetworkServiceListDeepCopy verifies that NetworkServiceList.DeepCopy
// produces independent copies of each item.
func TestNetworkRuleListDeepCopy(t *testing.T) {
list := &NetworkRuleList{
Items: []NetworkRule{*newTestRule()},
func TestNetworkServiceListDeepCopy(t *testing.T) {
list := &NetworkServiceList{
Items: []NetworkService{*newTestService()},
}
copied := list.DeepCopy()
copied.Items[0].Spec.VPCRef = "vpc-b"
Expand All @@ -101,12 +101,12 @@ func TestNetworkRuleListDeepCopy(t *testing.T) {
}
}

// TestNetworkRuleStatusHasNoPrimaryNode is a regression test: the earlier
// active-passive Full-NAT design assigned a single primaryNode per rule.
// This design's anycast/DSR model has every NetworkGateway serve every rule
// TestNetworkServiceStatusHasNoPrimaryNode is a regression test: the earlier
// active-passive Full-NAT design assigned a single primaryNode per service.
// This design's anycast/DSR model has every NetworkGateway serve every service
// identically, so no such field should exist any more.
func TestNetworkRuleStatusHasNoPrimaryNode(t *testing.T) {
orig := newTestRule()
func TestNetworkServiceStatusHasNoPrimaryNode(t *testing.T) {
orig := newTestService()
orig.Status.Conditions = []metav1.Condition{{Type: ConditionTypeAccepted}}

data, err := json.Marshal(orig.Status)
Expand All @@ -124,11 +124,11 @@ func TestNetworkRuleStatusHasNoPrimaryNode(t *testing.T) {
}
}

// TestNetworkRuleBackendFieldNames verifies the JSON keys for the backend
// TestNetworkServiceBackendFieldNames verifies the JSON keys for the backend
// selection fields match the CRD schema, and that the static backend list
// they replaced is gone.
func TestNetworkRuleBackendFieldNames(t *testing.T) {
data, err := json.Marshal(newTestRule().Spec)
func TestNetworkServiceBackendFieldNames(t *testing.T) {
data, err := json.Marshal(newTestService().Spec)
if err != nil {
t.Fatalf("Marshal: %v", err)
}
Expand All @@ -147,13 +147,13 @@ func TestNetworkRuleBackendFieldNames(t *testing.T) {
}
}

// TestNetworkRuleProtocolValues is a regression test pinning the accepted
// NetworkRuleProtocol enum values.
func TestNetworkRuleProtocolValues(t *testing.T) {
if NetworkRuleProtocolTCP != "tcp" {
t.Errorf("NetworkRuleProtocolTCP: got %q, want %q", NetworkRuleProtocolTCP, "tcp")
// TestNetworkServiceProtocolValues is a regression test pinning the accepted
// NetworkServiceProtocol enum values.
func TestNetworkServiceProtocolValues(t *testing.T) {
if NetworkServiceProtocolTCP != "tcp" {
t.Errorf("NetworkServiceProtocolTCP: got %q, want %q", NetworkServiceProtocolTCP, "tcp")
}
if NetworkRuleProtocolUDP != "udp" {
t.Errorf("NetworkRuleProtocolUDP: got %q, want %q", NetworkRuleProtocolUDP, "udp")
if NetworkServiceProtocolUDP != "udp" {
t.Errorf("NetworkServiceProtocolUDP: got %q, want %q", NetworkServiceProtocolUDP, "udp")
}
}
4 changes: 2 additions & 2 deletions api/v1alpha1/serviceroute_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ type ServiceEndpointSpec struct {

// Protocol is the transport protocol accepted by the endpoint.
// +kubebuilder:validation:Required
Protocol NetworkRuleProtocol `json:"protocol"`
Protocol NetworkServiceProtocol `json:"protocol"`

// DeliveryMode defines how a backing attachment is selected relative to a
// consumer. PreferNodeLocal uses a ready attachment on the consumer's node
Expand Down Expand Up @@ -198,7 +198,7 @@ type ServiceRouteFrontend struct {
type ServiceRouteProtocolPort struct {
// Protocol is the transport protocol.
// +kubebuilder:validation:Required
Protocol NetworkRuleProtocol `json:"protocol"`
Protocol NetworkServiceProtocol `json:"protocol"`

// Port is the transport port.
// +kubebuilder:validation:Required
Expand Down
2 changes: 1 addition & 1 deletion api/v1alpha1/serviceroute_types_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ func TestServiceRoutePolicyDeepCopy(t *testing.T) {
Spec: ServiceRoutePolicySpec{
ServiceRef: ServiceEndpointReference{Name: "ipv6-egress-dns"},
AttachmentSelector: metav1.LabelSelector{MatchLabels: map[string]string{"egress": "enabled"}},
ProtocolPorts: []ServiceRouteProtocolPort{{Protocol: NetworkRuleProtocolUDP, Port: 53}},
ProtocolPorts: []ServiceRouteProtocolPort{{Protocol: NetworkServiceProtocolUDP, Port: 53}},
Region: "us-central-1",
},
Status: ServiceRoutePolicyStatus{Conditions: []metav1.Condition{{Type: "Accepted"}}},
Expand Down
Loading
Loading