Add network router and resource (#189)

This change adds two new resources, NetworkRouter and NetworkResource,
which enable clusters to expose Kubernetes services to Netbird.

The NetworkRouter is responsible for creating the network, group, setup
key and routing peer all of which are unique to the isntance. Along with
the deployment of the client in the cluster.

The NetworkResource exposes a service by linking to the specific router
it wants to expose to. This makes coupling between the resource and
network easy to understand.

Routers also set a DNS zone which is used to give names to resources
based on the name and namespace of the service being exposed.

Part of #172

Signed-off-by: Philip Laine <philip.laine@gmail.com>
This commit is contained in:
Philip Laine
2026-04-23 08:55:49 +02:00
committed by GitHub
parent af11e31b28
commit 6768a76c9c
38 changed files with 2973 additions and 55 deletions
+1
View File
@@ -6,4 +6,5 @@ const ReadyCondition = "Ready"
const (
ReconciledReason = "Reconciled"
DependencyReason = "Dependency"
)
+90
View File
@@ -0,0 +1,90 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NetworkResourceSpec defines the desired state of NetworkResource.
type NetworkResourceSpec struct {
// NetworkRouterRef is a reference to the network and router where the resource will be created.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
NetworkRouterRef CrossNamespaceReference `json:"networkRouterRef"`
// ServiceRef is a reference to the service to expose in the Network.
ServiceRef corev1.LocalObjectReference `json:"serviceRef"`
// Groups are references to groups that the resource will be a part of.
// +optional
Groups []ResourceReference `json:"groups,omitempty"`
}
// NetworkResourceStatus defines the observed state of NetworkResource.
type NetworkResourceStatus struct {
// ObservedGeneration is the last reconciled generation.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Conditions holds the conditions for the NetworkResource.
// +listType=map
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// NetworkID is the id of the network the resource is created in.
// +optional
NetworkID string `json:"networkID,omitempty"`
// ResourceID is the id of the created resource.
// +optional
ResourceID string `json:"resourceID,omitempty"`
// DNSZoneID is the id of the zone the DNS record is created in.
// +optional
DNSZoneID string `json:"dnsZoneID,omitempty"`
// DNSRecordID is the id of the created DNS record.
// +optional
DNSRecordID string `json:"dnsRecordID,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// NetworkResource is the Schema for the networkresources API.
type NetworkResource struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +required
Spec NetworkResourceSpec `json:"spec"`
// +kubebuilder:default={"observedGeneration":-1}
Status NetworkResourceStatus `json:"status,omitempty"`
}
// GetConditions returns the status conditions of the object.
func (n *NetworkResource) GetConditions() []metav1.Condition {
return n.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (n *NetworkResource) SetConditions(conditions []metav1.Condition) {
n.Status.Conditions = conditions
}
// +kubebuilder:object:root=true
// NetworkResourceList contains a list of NetworkResource.
type NetworkResourceList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []NetworkResource `json:"items"`
}
func init() {
SchemeBuilder.Register(&NetworkResource{}, &NetworkResourceList{})
}
+106
View File
@@ -0,0 +1,106 @@
package v1alpha1
import (
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// NetworkRouterSpec defines the desired state of NetworkRouter.
type NetworkRouterSpec struct {
// DNSZoneRef is a reference to the DNS zone used to create records for resources.
// +required
DNSZoneRef DNSZoneReference `json:"dnsZoneRef"`
// WorkloadOverride contains configuration that will override the default workload.
// +optional
WorkloadOverride *WorkloadOverride `json:"workloadOverride,omitempty"`
}
// DNSZoneReference references a Netbird DNS zone by domain name.
type DNSZoneReference struct {
// Name is the domain name of an existing Netbird DNS zone, e.g. "example.com".
// +required
Name string `json:"name"`
}
type WorkloadOverride struct {
// Labels that will be added.
// +optional
Labels map[string]string `json:"labels"`
// Annotations that will be added.
// +optional
Annotations map[string]string `json:"annotations"`
// Replicas sets the amount of client replicas.
// +optional
Replicas *int32 `json:"replicas"`
// PodTemplate overrides the pod template.
// +optional
// +kubebuilder:pruning:PreserveUnknownFields
// +kubebuilder:validation:Schemaless
PodTemplate *corev1.PodTemplateSpec `json:"podTemplate"`
}
// NetworkRouterStatus defines the observed state of NetworkRouter.
type NetworkRouterStatus struct {
// ObservedGeneration is the last reconciled generation.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Conditions holds the conditions for the NetworkRouter.
// +listType=map
// +listMapKey=type
// +optional
Conditions []metav1.Condition `json:"conditions,omitempty"`
// RoutingPeerID is the id of the created routing peer.
// +optional
RoutingPeerID string `json:"routingPeerID,omitempty"`
// NetworkID is the id of the network the routing peer was created in.
// +optional
NetworkID string `json:"networkID,omitempty"`
}
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource
// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description=""
// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description=""
// NetworkRouter is the Schema for the networkrouters API.
type NetworkRouter struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +required
Spec NetworkRouterSpec `json:"spec"`
// +kubebuilder:default={"observedGeneration":-1}
Status NetworkRouterStatus `json:"status,omitempty"`
}
// GetConditions returns the status conditions of the object.
func (n *NetworkRouter) GetConditions() []metav1.Condition {
return n.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (n *NetworkRouter) SetConditions(conditions []metav1.Condition) {
n.Status.Conditions = conditions
}
// +kubebuilder:object:root=true
// NetworkRouterList contains a list of NetworkRouter.
type NetworkRouterList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []NetworkRouter `json:"items"`
}
func init() {
SchemeBuilder.Register(&NetworkRouter{}, &NetworkRouterList{})
}
+12 -2
View File
@@ -4,11 +4,21 @@ import corev1 "k8s.io/api/core/v1"
// +kubebuilder:validation:XValidation:rule="(has(self.id) && !has(self.localRef)) || (!has(self.id) && has(self.localRef))",message="exactly one of id or localRef must be set"
type ResourceReference struct {
// id of the resource in the Netbird API.
// ID is the id of a resource in the Netbird API.
// +optional
ID *string `json:"id,omitempty"`
// local reference to the object in the same namespace.
// LocalReference is a reference to a object in the same namespace.
// +optional
LocalRef *corev1.LocalObjectReference `json:"localRef,omitempty"`
}
type CrossNamespaceReference struct {
// Name of the referent.
// +required
Name string `json:"name"`
// Namespace of the referent.
// +required
Namespace string `json:"namespace"`
}
+4
View File
@@ -6,6 +6,10 @@ import (
// SetupKeySpec defines the desired state of SetupKey.
type SetupKeySpec struct {
// Name of the setup key.
// +kubebuilder:validation:MinLength=1
Name string `json:"name"`
// Ephemeral decides if peers added with the key are ephemeral or not.
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ephemeral is immutable"
Ephemeral bool `json:"ephemeral"`
+276
View File
@@ -10,6 +10,36 @@ import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CrossNamespaceReference) DeepCopyInto(out *CrossNamespaceReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceReference.
func (in *CrossNamespaceReference) DeepCopy() *CrossNamespaceReference {
if in == nil {
return nil
}
out := new(CrossNamespaceReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DNSZoneReference) DeepCopyInto(out *DNSZoneReference) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSZoneReference.
func (in *DNSZoneReference) DeepCopy() *DNSZoneReference {
if in == nil {
return nil
}
out := new(DNSZoneReference)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *Group) DeepCopyInto(out *Group) {
*out = *in
@@ -106,6 +136,213 @@ func (in *GroupStatus) DeepCopy() *GroupStatus {
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkResource) DeepCopyInto(out *NetworkResource) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResource.
func (in *NetworkResource) DeepCopy() *NetworkResource {
if in == nil {
return nil
}
out := new(NetworkResource)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NetworkResource) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkResourceList) DeepCopyInto(out *NetworkResourceList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]NetworkResource, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResourceList.
func (in *NetworkResourceList) DeepCopy() *NetworkResourceList {
if in == nil {
return nil
}
out := new(NetworkResourceList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NetworkResourceList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkResourceSpec) DeepCopyInto(out *NetworkResourceSpec) {
*out = *in
out.NetworkRouterRef = in.NetworkRouterRef
out.ServiceRef = in.ServiceRef
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]ResourceReference, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResourceSpec.
func (in *NetworkResourceSpec) DeepCopy() *NetworkResourceSpec {
if in == nil {
return nil
}
out := new(NetworkResourceSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkResourceStatus) DeepCopyInto(out *NetworkResourceStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResourceStatus.
func (in *NetworkResourceStatus) DeepCopy() *NetworkResourceStatus {
if in == nil {
return nil
}
out := new(NetworkResourceStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkRouter) DeepCopyInto(out *NetworkRouter) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouter.
func (in *NetworkRouter) DeepCopy() *NetworkRouter {
if in == nil {
return nil
}
out := new(NetworkRouter)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NetworkRouter) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkRouterList) DeepCopyInto(out *NetworkRouterList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]NetworkRouter, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouterList.
func (in *NetworkRouterList) DeepCopy() *NetworkRouterList {
if in == nil {
return nil
}
out := new(NetworkRouterList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *NetworkRouterList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkRouterSpec) DeepCopyInto(out *NetworkRouterSpec) {
*out = *in
out.DNSZoneRef = in.DNSZoneRef
if in.WorkloadOverride != nil {
in, out := &in.WorkloadOverride, &out.WorkloadOverride
*out = new(WorkloadOverride)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouterSpec.
func (in *NetworkRouterSpec) DeepCopy() *NetworkRouterSpec {
if in == nil {
return nil
}
out := new(NetworkRouterSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NetworkRouterStatus) DeepCopyInto(out *NetworkRouterStatus) {
*out = *in
if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions
*out = make([]v1.Condition, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouterStatus.
func (in *NetworkRouterStatus) DeepCopy() *NetworkRouterStatus {
if in == nil {
return nil
}
out := new(NetworkRouterStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ResourceReference) DeepCopyInto(out *ResourceReference) {
*out = *in
@@ -238,3 +475,42 @@ func (in *SetupKeyStatus) DeepCopy() *SetupKeyStatus {
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WorkloadOverride) DeepCopyInto(out *WorkloadOverride) {
*out = *in
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string, len(*in))
for key, val := range *in {
(*out)[key] = val
}
}
if in.Replicas != nil {
in, out := &in.Replicas, &out.Replicas
*out = new(int32)
**out = **in
}
if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate
*out = new(corev1.PodTemplateSpec)
(*in).DeepCopyInto(*out)
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadOverride.
func (in *WorkloadOverride) DeepCopy() *WorkloadOverride {
if in == nil {
return nil
}
out := new(WorkloadOverride)
in.DeepCopyInto(out)
return out
}