infra_wiki

Topology

The two JSON files that hold the lab's network facts, the format rule they follow, and the Flux, Nix and OpenTofu code that reads them.

Two JSON files are the single source of truth for the lab's network facts: addresses, subnets, API server endpoints and BGP ASNs. Flux, Nix and OpenTofu read each fact from these files, so one change reaches every layer. Name the key and the file on a page or in code, and do not copy the value. Network says what the networks are.

Files#

FileHoldsKeys
clusters/<site>/config/cluster-topology.jsonOne cluster: its node, pod, service and load-balancer ranges, API server, gateway router and BGP ASNs. On offsite it also holds the address of each nodeCLUSTER_NAME, API_SERVER_*, ROUTER_IP, *_CIDR, LB_RANGE, CLUSTER_DNS, BGP_*_ASN, on folly K8S_DHCP_CIDR, the Kubernetes network's DHCP pool inside K8S_NODE_CIDR, and on offsite NODE_ADDRESSES
clusters/folly/config/anchor-topology.jsonThe folly lab: the Anchor network, future and bootstrap ranges, the bootstrap VLAN ID, and the addresses of lab hosts and Windows desktopsANCHOR_CIDR, FUTURE_CIDR, BOOTSTRAP_CIDR, BOOTSTRAP_VLAN, <HOST>_IP, SPORE_BOOTSTRAP_IP

Print the keys and values of a file with jq .data <file>.

Format#

Each file is a Kubernetes ConfigMap in the flux-system namespace. It uses JSON, which is also valid YAML. Flux applies it without conversion.

The data field is a flat map. Both keys and values must be strings because Flux substitution accepts only strings. Readers parse the values into the types they need:

TypeEncodingExample key
ListComma-separated stringCLUSTER_DNS
NumberQuoted stringAPI_SERVER_PORT
MapJSON object inside a stringoffsite's NODE_ADDRESSES, which maps host names to addresses

Readers#

ReaderHow it reads the filesWhere
FluxThe config Flux Kustomization applies each file as a ConfigMap. Other Flux Kustomizations replace ${KEY} in their manifests with values from it, through postBuild.substituteFrom.clusters/<site>/config/kustomization.yaml and clusters/<site>/flux-system/
Nixbuiltins.fromJSON at evaluationnix/lib/networks.nix for folly, and nix/lib/anchor.nix for the Anchor network
OpenTofuThe cluster-topology module returns the data of one file, selected by its site and config_map inputsterraform/modules/cluster-topology/

On folly, only the monitoring and storage Flux Kustomizations substitute from anchor-topology. To find the OpenTofu roots that read a file, run git grep -l modules/cluster-topology -- terraform clusters.

terraform/network/tailscale/fleet.tf.json follows the same pattern for naming facts: the tailnet name and the public DNS zone. The Tailscale root loads it as local.fleet, and nix/lib/fleet.nix reads it.

Checks#

CheckWhat it enforcesWhere it runs
Topology contractEach cluster-topology.json is the flux-system/cluster-topology ConfigMap, has flat string data and every required key, holds valid addresses, CIDRs, port and ASNs, and does not overlap the other cluster. offsite's NODE_ADDRESSES is a JSON object of hostname to address, each address is in K8S_NODE_CIDR and belongs to one node, and API_SERVER_IP is a node's address. folly's file must not set NODE_ADDRESSES, and must set K8S_DHCP_CIDR inside K8S_NODE_CIDR, clear of ROUTER_IP and API_SERVER_IP..github/workflows/topology-contract.yml, with the conftest policy .github/policy/cluster-topology.rego
Anchor address preconditionsEach lab host address in anchor-topology.json matches its reservation in terraform/network/unifi/folly/clients.yamlThe plan of terraform/network/unifi/folly
Node addresses outside DHCPOn folly no hardware inventory address is in K8S_DHCP_CIDR, and a node with a static address in the Talos module is in K8S_NODE_CIDR and outside K8S_DHCP_CIDR. On offsite each node has a MAC under k8s in terraform/network/unifi/offsite/clients.yaml, reserved at its NODE_ADDRESSES value.The plans of terraform/network/unifi/<site> and clusters/folly/talos
Node addressesA folly node's address is in K8S_NODE_CIDR, is shared with no other node, and API_SERVER_IP is a node's addressThe plan of terraform/netbox/folly
Nix evaluationEvery host still evaluates with the new valuesnix-ci.yaml, on a change to any topology file

The topology contract does not check anchor-topology.json.

Rules#

  • Keep every data value a string. Kubernetes rejects a ConfigMap with any other value, and Flux substitutes only strings.
  • A change to a topology file gets no Atlantis autoplan, because ATLANTIS_AUTOPLAN_FILE_LIST matches no .json file. Plan and apply each root that reads the file, as OpenTofu and Atlantis describes.
  • Change a lab host address in anchor-topology.json and clients.yaml in the same pull request, or the precondition fails the plan.
  • A folly node's address is in clusters/folly/inventory/nodes.yaml; the Talos root manages the declared nodes NetBox holds active or failed. An offsite node's address is in NODE_ADDRESSES only, and its MAC is in terraform/network/unifi/offsite/clients.yaml. A folly node's DNS record and static address follow its address, and an offsite node's DNS record and reservation. The roots that read it are terraform/network/unifi/<site>, terraform/network/cloudflare for offsite, clusters/<site>/talos, and terraform/netbox/folly for folly.
  • Some files hold a literal copy of a topology value: the FRR files, whose offsite neighbor lines are the NODE_ADDRESSES values, policy.hujson and the addresses pinned in app manifests. Change them in the same pull request, as the Network rules describe.
  • Put a setting that is not a network fact, such as a time zone or a DNS zone for Apps, in cluster-settings: clusters/base/cluster-settings.yaml, patched by clusters/<site>/config/cluster-settings.yaml.
  • CATHY_IP and PBX_SIP_VIP are addresses in folly's cluster-settings, for the PBX. CATHY_IP is the office phone on Management, which neither topology file covers, and the folly apps Flux Kustomization does not substitute from anchor-topology. terraform/network/unifi/folly/voip.tf checks CATHY_IP against clients.yaml.

Edit on GitHub

↑↓ move ↵ open esc close