endpointmeta

package
v0.0.0-...-7c0b2e9 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2026 License: Apache-2.0 Imports: 10 Imported by: 0

Documentation

Overview

Package endpointmeta parses the endpoint.aether.io/* pod annotations into the ServiceEndpoint fields every registry backend needs.

It is a leaf package, shared with the backends the same way registry/export is, so both the CNI registration path (//registry, which builds an endpoint from a cniv1.CNIPod) and the Kubernetes backend (//registry/internal/k8s, which builds one from a corev1.Pod) read the same annotations through the same code.

That sharing is the point. The two paths previously carried their own copies, and the copies drifted by OMISSION rather than by disagreement: the Kubernetes backend never grew a reader for endpoint.aether.io/protocol or endpoint.aether.io/ports, so a pod declaring either was silently registered as if it had not (#878, and the per-port EDS membership of proposal 005 never worked on that backend at all). A missing parser is invisible in a way a wrong one is not: nothing fails, the field is simply zero.

Every function here takes a plain map[string]string so it is indifferent to whether the annotations came from a CNI ADD or the API server.

Index

Constants

View Source
const (
	// RowNoPortProtocols: the row carries no port_protocols at all. A writer
	// from before proposal 037 wrote such rows; none has since.
	RowNoPortProtocols = "no_port_protocols"
	// RowPortNotTyped: the row lists a port (port, ports) that its
	// port_protocols does not name, or names with no class, or with a class
	// this build does not know.
	RowPortNotTyped = "port_not_typed"
	// RowPortNotListed: the row's port_protocols names a port that neither
	// its port nor its ports lists.
	RowPortNotListed = "port_not_listed"
)

Why a registry row is not served (RowProblem). The values are metric attributes and log fields.

Variables

View Source
var ErrRowNotTyped = errors.New("registry row does not say the protocol of each of its ports (port_protocols is required)")

ErrRowNotTyped is returned by every write of a row RowProblem refuses.

RowProblems is every value RowProblem returns besides "", in a fixed order: what a metric that counts rows by reason has a series for.

Functions

func CheckRow

func CheckRow(ep *registryv1.ServiceEndpoint) error

CheckRow is RowProblem as an error for a write path.

func ElectPortClass

func ElectPortClass(votes map[registryv1.PortProtocol]int) registryv1.PortProtocol

ElectPortClass picks the one class a port number of a service has when its endpoints declare it with several (a rollout that retypes a port): the class most endpoints declare, ties to HTTP, then raw TCP, then UDP. votes is, per class, how many endpoints declare the port with it; the answer reads only the counts, so it does not depend on the order the endpoints were listed in. PORT_PROTOCOL_UNSPECIFIED when there is no vote.

It is ONE function because two components elect: the registrar, for the ports of the Service it generates, and the node agent, for its route tables and chains. A route's status reads the first and the proxy the second, and for the same rows they must not differ.

func Metadata

func Metadata(annotations map[string]string) map[string]string

Metadata returns the endpoint metadata carried by annotations under the endpoint-metadata prefix, with the prefix removed from each key.

func Port

func Port(annotations map[string]string) (uint16, error)

Port returns the endpoint's primary port from endpoint.aether.io/port, defaulting to constants.DefaultEndpointPort.

func PortProtocolFromService

func PortProtocolFromService(p registryv1.Service_Protocol) registryv1.PortProtocol

PortProtocolFromService converts a service-level protocol to the per-port enum. The two vocabularies are numerically identical and TestPortProtocolMatchesServiceProtocol pins that; this function exists so the conversion is named and searchable rather than an unexplained cast.

func PortProtocols

func PortProtocols(annotations map[string]string) (map[uint32]registryv1.PortProtocol, error)

PortProtocols returns the L4 class of every port the pod advertises (proposal 037).

The grammar is the existing endpoint.aether.io/ports suffix, extended:

endpoint.aether.io/ports:    "8080,9090=h2,9000=tcp,5432=tcp"
endpoint.aether.io/protocol: "http"   # the DEFAULT for unsuffixed ports

A port with no suffix takes the pod-level endpoint.aether.io/protocol value, which is why that annotation is KEPT rather than deprecated: it becomes the default for the port set, and every manifest written before this proposal means exactly what it meant before. A pod with `protocol: tcp` and `ports: "9000,8080=h1"` has a TCP primary and an HTTP secondary.

Suffix vocabulary, and what each one says:

h1, http1, (none)  HTTP -- the loopback hop speaks HTTP/1.1
h2, http2          HTTP -- the loopback hop speaks h2c
tcp                TCP  -- raw mTLS passthrough through the capture floor
udp                UDP  -- datagrams through the UDP floor, in PLAINTEXT

h1 and h2 differ only in the agent-local loopback codec, which the registry does not carry (AppPortProtocols reads it from the same annotation on the agent side). Both are PORT_PROTOCOL_HTTP here.

`grpc` is deliberately NOT accepted. gRPC is h2 on the wire and the mesh does nothing gRPC-specific at L4, so accepting it would advertise a distinction the data plane does not make. Add it when something consumes it.

An unrecognised suffix is an ERROR, not a default. The alternative — treating it as HTTP, which is what the agent-side AppPortProtocols does today with an unknown suffix — is how a typo becomes a port silently served over the wrong protocol, which is the failure this whole proposal exists to remove.

func Ports

func Ports(annotations map[string]string, defaultPort uint16) ([]uint32, error)

Ports returns the full served-port set from endpoint.aether.io/ports (comma-separated), sorted and de-duplicated. defaultPort is always a member, so the result is never empty and a pod with no annotation yields exactly {defaultPort}.

An entry may carry an optional "=proto" suffix (e.g. "9090=h2"). It is stripped here: that suffix selects the agent-local loopback codec (h1 vs h2c) and the registry carries only the numeric set, which is what per-port EDS membership is keyed on (proposal 005).

func Protocol

func Protocol(annotations map[string]string) (registryv1.Service_Protocol, error)

Protocol returns the mesh protocol the pod serves, from endpoint.aether.io/protocol. Absent or "http" is HTTP; "tcp" registers a non-HTTP TCP-over-mTLS service.

An unrecognised value is an error rather than a default, so a typo can never silently register a service under the wrong protocol — which, on a backend that keys by protocol, means registering it where nothing will look for it.

func ProtocolsServed

func ProtocolsServed(portProtocols map[uint32]registryv1.PortProtocol) []registryv1.Service_Protocol

ProtocolsServed returns the distinct L4 classes a pod serves across all its advertised ports, which is the set of registry keys it must be registered under (proposal 037: a pod serving both an HTTP and a raw-TCP port is registered once per protocol, with both registrations carrying the same full port_protocols map).

func ReservedPorts

func ReservedPorts(annotations map[string]string) []uint32

ReservedPorts returns, sorted, the ports a pod's annotations declare (endpoint.aether.io/port and endpoint.aether.io/ports) that are reserved for the mesh (mesh.ReservedPort), in any class. Annotations that do not parse declare nothing here: that is another error, reported where they are read.

func RowProblem

func RowProblem(ep *registryv1.ServiceEndpoint) string

RowProblem returns why a registry row cannot be served, or "" when it can.

port_protocols is REQUIRED (proposal 043): it is the one statement of what speaks on each port of an endpoint. A row without it used to take the class of the listing it was read from, for every port; that fallback is gone. A class that is guessed is how a port gets served over the wrong protocol, and the guess had three readers that had to agree. Such a row is refused where it is written (ErrRowNotTyped), filtered by the registrar where it is already stored, and contributes nothing in an agent that receives it anyway, each of them saying so.

A typed row names every port it lists, each with a class, and names no other port.

  • A class is one this build knows. PortProtocol is an open enum: a value a newer writer defines travels through every store and stream as a number, and "not UNSPECIFIED" took it for a class, of a port nothing then built anything for.
  • The map names no port the lists leave out. Membership of a port's cluster is read from the lists and the class from the map; a port in the map alone was a port with a class and no member, dropped by every reader without a word. No writer produces one (the map and the list are built from the same annotations), so a row that has one was not written by a node agent, and says so here.

func ServiceProtocolFromPort

func ServiceProtocolFromPort(p registryv1.PortProtocol) registryv1.Service_Protocol

ServiceProtocolFromPort is the inverse of PortProtocolFromService.

func Weight

func Weight(annotations map[string]string) (uint32, error)

Weight returns the endpoint's load-balancing weight from endpoint.aether.io/weight, defaulting to constants.DefaultEndpointWeight.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL