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
21 changes: 20 additions & 1 deletion .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@ jobs:
# was removed in helm 3.13, so pin to the last release that accepts it.
version: v3.12.3

# The same installer the federated workflow uses. An explicit `make` list
# here meant the two workflows disagreed about which binaries exist, and
# a tool added to the test-infra tools task reached only one of them.
- name: Install pinned tools
run: make kind kustomize cmctl chainsaw
run: task test-infra:tools

- name: Bring up the prod-fidelity env
run: task test-infra:up
Expand All @@ -62,6 +65,22 @@ jobs:
KUBECONFIG="${UKC}" kubectl -n cert-manager get pods -o wide
KUBECONFIG="${UKC}" kubectl -n network-services-operator-system get certificate,secret
echo "::endgroup::"
echo "::group::IPAM (upstream project control plane)"
KUBECONFIG="${UKC}" kubectl get apiservice v1alpha1.ipam.miloapis.com -o yaml
KUBECONFIG="${UKC}" kubectl -n ipam-system get pods,certificate,configmap -o wide
KUBECONFIG="${UKC}" kubectl -n ipam-system logs -l app=ipam-apiserver --tail=200 --all-containers
KUBECONFIG="${UKC}" kubectl -n ipam-system get cluster.postgresql.cnpg.io -o wide
KUBECONFIG="${UKC}" kubectl -n ipam-system describe cluster.postgresql.cnpg.io ipam-db
KUBECONFIG="${UKC}" kubectl -n ipam-system logs -l cnpg.io/cluster=ipam-db --tail=100 --all-containers
echo "::endgroup::"
echo "::group::CloudNativePG operator"
KUBECONFIG="${UKC}" kubectl -n cnpg-system get pods -o wide
KUBECONFIG="${UKC}" kubectl -n cnpg-system logs deploy/cnpg-cloudnative-pg --tail=200
echo "::endgroup::"
echo "::group::IPAM fixtures"
KUBECONFIG=${TMPDIR}/.ipam-tenant-impersonation.yaml kubectl --context tenant-project-alpha get ipclasses,ippools -o wide
KUBECONFIG=${TMPDIR}/.ipam-tenant-impersonation.yaml kubectl --context tenant-project-beta get ipclasses,ippools -o wide
echo "::endgroup::"
KC=${TMPDIR}/.kind-nso-downstream.yaml
bin/kind-v0.32.0 get kubeconfig --name nso-downstream > "${KC}"
export KUBECONFIG="${KC}"
Expand Down
18 changes: 18 additions & 0 deletions PROJECT
Original file line number Diff line number Diff line change
Expand Up @@ -185,4 +185,22 @@ resources:
kind: ConnectorClass
path: go.datum.net/network-services-operator/api/v1alpha1
version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: datumapis.com
group: networking
kind: NetworkInterface
path: go.datum.net/network-services-operator/api/v1alpha
version: v1alpha
- api:
crdVersion: v1
namespaced: true
controller: true
domain: datumapis.com
group: networking
kind: NetworkInterfaceClaim
path: go.datum.net/network-services-operator/api/v1alpha
version: v1alpha
version: "3"
314 changes: 311 additions & 3 deletions Taskfile.test-infra.yml

Large diffs are not rendered by default.

4 changes: 4 additions & 0 deletions api/v1alpha/groupversion_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&NetworkBindingList{},
&NetworkContext{},
&NetworkContextList{},
&NetworkInterface{},
&NetworkInterfaceList{},
&NetworkInterfaceClaim{},
&NetworkInterfaceClaimList{},
&NetworkPolicy{},
&NetworkPolicyList{},
&Subnet{},
Expand Down
301 changes: 301 additions & 0 deletions api/v1alpha/networkinterface_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
// SPDX-License-Identifier: AGPL-3.0-only

package v1alpha

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

// NetworkInterfaceReclaimPolicy describes what becomes of a network interface,
// and the addresses it holds, when the claim bound to it is deleted.
//
// +kubebuilder:validation:Enum=Delete;Retain
type NetworkInterfaceReclaimPolicy string

const (
// NetworkInterfaceReclaimPolicyDelete deletes the interface and releases its
// addresses when the claim bound to it is deleted.
NetworkInterfaceReclaimPolicyDelete NetworkInterfaceReclaimPolicy = "Delete"

// NetworkInterfaceReclaimPolicyRetain keeps the interface, and the addresses
// it holds, when the claim bound to it is deleted. The interface returns to
// the Available phase and waits for a claim of the same name to return.
NetworkInterfaceReclaimPolicyRetain NetworkInterfaceReclaimPolicy = "Retain"
)

// NetworkInterfacePhase describes whether an interface is currently held by a
// claim.
//
// +kubebuilder:validation:Enum=Available;Bound
type NetworkInterfacePhase string

const (
// NetworkInterfacePhaseAvailable indicates the interface is allocated and
// holding its addresses, but is not bound to a claim. A retained interface
// whose claim was deleted returns here, and the next claim of the same name
// binds it.
NetworkInterfacePhaseAvailable NetworkInterfacePhase = "Available"

// NetworkInterfacePhaseBound indicates the interface is held by the claim
// named in spec.claimRef.
NetworkInterfacePhaseBound NetworkInterfacePhase = "Bound"
)

const (
// NetworkInterfaceAllocated indicates that every address the interface must
// carry has been allocated.
NetworkInterfaceAllocated = "Allocated"

// NetworkInterfaceProgrammed indicates that the data plane can carry the
// interface's addresses.
NetworkInterfaceProgrammed = "Programmed"
)

// NetworkInterfaceAddress is an address the interface carries inside its
// network.
type NetworkInterfaceAddress struct {
// The address family this entry carries.
//
// +kubebuilder:validation:Required
Family IPFamily `json:"family"`

// The address the interface holds, in CIDR notation.
//
// For IPv6 this may be a block delegated to the endpoint rather than a
// single address, in which case the interface owns the block and assigns
// within it.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=45
Address string `json:"address"`

// The gateway the interface routes through for this family.
//
// Read from the subnet backing the network in this location, so a provider
// never has to resolve it.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MaxLength=45
Gateway string `json:"gateway,omitempty"`

// Whether this is the interface's primary address.
//
// One address is primary for the whole interface, not one per family. An
// interface holds at most one address of each family, so what this marks
// is which family leads: it is the address of the first family listed on
// the claim, and the one projected into single-address fields such as an
// instance's reported network IP.
//
// +kubebuilder:validation:Optional
Primary bool `json:"primary,omitempty"`

// The IPAM class this address was allocated from.
//
// Recorded so an operator can tell which policy produced the address.
// Nothing is required to interpret it to configure a NIC.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MaxLength=63
Class string `json:"class,omitempty"`
}

// NetworkInterfaceExternalAddress is an address reachable from outside the
// network, mapped onto an address the interface holds inside it.
type NetworkInterfaceExternalAddress struct {
// The address family this entry carries.
//
// +kubebuilder:validation:Required
Family IPFamily `json:"family"`

// The externally reachable address.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=45
Address string `json:"address"`

// The IPAM class this address was allocated from, matching the class the
// claim asked for.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
Class string `json:"class"`
}

// NetworkInterfaceClaimRef identifies the claim currently holding an interface.
type NetworkInterfaceClaimRef struct {
// The claim name.
//
// A claim name identifies the slot an interface serves, and it is stable
// across every instance that ever fills that slot. A replacement instance
// asks for the same name and comes back to the interface, and the
// addresses, its predecessor held.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Name string `json:"name"`
}

// LocalNetworkInterfaceRef references a network interface in the same
// namespace.
type LocalNetworkInterfaceRef struct {
// The network interface name
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Name string `json:"name"`
}

// NetworkInterfaceAttachmentRef references the resource realizing an interface
// on the data plane.
type NetworkInterfaceAttachmentRef struct {
// API group of the referent.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
APIGroup string `json:"apiGroup"`

// Kind of the referent.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=63
Kind string `json:"kind"`

// Name of the referent.
//
// +kubebuilder:validation:Required
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
Name string `json:"name"`
}

// NetworkInterfaceSpec defines the desired state of NetworkInterface.
//
// Everything required to configure a NIC is here, and nothing here requires a
// second lookup to interpret.
type NetworkInterfaceSpec struct {
// The network this interface belongs to.
//
// +kubebuilder:validation:Required
Network LocalNetworkRef `json:"network"`

// The claim currently holding this interface.
//
// Empty when the interface is retained and bound to nothing.
//
// +kubebuilder:validation:Optional
ClaimRef *NetworkInterfaceClaimRef `json:"claimRef,omitempty"`

// The name the interface presents to the guest.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=15
// +kubebuilder:default="eth0"
InterfaceName string `json:"interfaceName,omitempty"`

// The MTU the interface must be configured with.
//
// Resolved from the network's MTU, so a provider never has to read the
// network.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:Minimum=1300
// +kubebuilder:validation:Maximum=8856
MTU int32 `json:"mtu,omitempty"`

// The addresses the interface holds inside its network, one entry per
// address family, exactly one of them primary.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MaxItems=4
// +kubebuilder:validation:XValidation:message="Exactly one address must be primary",rule="size(self) == 0 || self.filter(a, has(a.primary) && a.primary).size() == 1"
// +kubebuilder:validation:XValidation:message="Only one address may be held per address family",rule="self.all(a, self.exists_one(b, b.family == a.family))"
Addresses []NetworkInterfaceAddress `json:"addresses,omitempty"`

// The addresses reachable from outside the network, each mapped onto the
// interface's address of the same family.
//
// +kubebuilder:validation:Optional
// +kubebuilder:validation:MaxItems=4
// +kubebuilder:validation:XValidation:message="External addresses must be unique",rule="self.all(a, self.exists_one(b, b.address == a.address))"
ExternalAddresses []NetworkInterfaceExternalAddress `json:"externalAddresses,omitempty"`

// What becomes of this interface when the claim holding it is deleted.
//
// +kubebuilder:validation:Optional
// +kubebuilder:default="Delete"
ReclaimPolicy NetworkInterfaceReclaimPolicy `json:"reclaimPolicy,omitempty"`
}

// NetworkInterfaceStatus defines the observed state of NetworkInterface.
//
// Only what the operator observed lives here: where the network is, what
// realizes the interface, and whether programming succeeded.
type NetworkInterfaceStatus struct {
// Whether the interface is currently held by a claim.
//
// +kubebuilder:validation:Optional
Phase NetworkInterfacePhase `json:"phase,omitempty"`

// The network's presence in this location, resolved or created while
// fulfilling the claim.
//
// Recorded as a breadcrumb for operators. Nothing needs it to configure a
// NIC.
//
// +kubebuilder:validation:Optional
NetworkContextRef *LocalNetworkContextRef `json:"networkContextRef,omitempty"`

// The data-plane realization of this interface, once one exists.
//
// +kubebuilder:validation:Optional
AttachmentRef *NetworkInterfaceAttachmentRef `json:"attachmentRef,omitempty"`

// The base62 identifier of the VPC backing this network in this location,
// matching the identifier the fabric keys on.
//
// Learned when the attachment is programmed.
//
// +kubebuilder:validation:Optional
VPC string `json:"vpc,omitempty"`

// Represents the observations of a network interface's current state.
Conditions []metav1.Condition `json:"conditions,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status

// NetworkInterface is the Schema for the networkinterfaces API
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Network",type=string,JSONPath=".spec.network.name"
// +kubebuilder:printcolumn:name="Phase",type=string,JSONPath=".status.phase"
// +kubebuilder:printcolumn:name="Claim",type=string,JSONPath=".spec.claimRef.name"
// +kubebuilder:printcolumn:name="Allocated",type=string,JSONPath=`.status.conditions[?(@.type=="Allocated")].status`
// +kubebuilder:printcolumn:name="Programmed",type=string,JSONPath=`.status.conditions[?(@.type=="Programmed")].status`
type NetworkInterface struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`

// +kubebuilder:validation:Required
Spec NetworkInterfaceSpec `json:"spec,omitempty"`

// +kubebuilder:default={conditions:{{type:"Allocated",status:"Unknown",reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"},{type:"Programmed",status:"Unknown",reason:"Pending", message:"Waiting for controller", lastTransitionTime: "1970-01-01T00:00:00Z"}}}
Status NetworkInterfaceStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

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