mirror of
https://github.com/YuzuZensai/netbird-kubernetes-operator.git
synced 2026-09-13 10:49:15 +00:00
Add network egress resource (#357)
This change adds a new import resource which enables exposing Netbird resources as Kubernetes services. This remove the need to add sidecars to every pod. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Added a new `NetworkEgress` custom resource (`netbird.io/v1alpha1`) with CRD, schema validation, and status/conditions. * Extended controller functionality to create egress services and translate egress rules into import `EndpointSlice` resources; egress pods now include a kube-egress-forwarder sidecar. * **Bug Fixes** * Added missing deep-copy and declarative apply support for the new `NetworkEgress` API types. * **Documentation** * Updated README/API reference and added example manifests for `NetworkEgress` (including IP/FQDN target usage). <!-- end of auto-generated comment: release notes by coderabbit.ai --> Signed-off-by: Philip Laine <philip.laine@gmail.com>
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// NetworkEgressSpec defines the desired state of NetworkEgress.
|
||||
type NetworkEgressSpec 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"`
|
||||
|
||||
// Target for egress traffic.
|
||||
Target NetworkEgressTarget `json:"target"`
|
||||
|
||||
// Ports to the resource to route.
|
||||
// +kubebuilder:validation:MinItems=1
|
||||
// +kubebuilder:validation:Required
|
||||
Ports []NetworkEgressPort `json:"ports"`
|
||||
}
|
||||
|
||||
// NetworkEgressTarget describes a single allowed egress destination.
|
||||
// Exactly one of IP or FQDN must be set.
|
||||
// +kubebuilder:validation:XValidation:rule="(has(self.ip) ? 1 : 0) + (has(self.fqdn) ? 1 : 0) == 1",message="exactly one of ip or fqdn must be set"
|
||||
type NetworkEgressTarget struct {
|
||||
// IP targets a single specific IP address (not a CIDR range).
|
||||
// +optional
|
||||
IP *NetworkEgressIPTarget `json:"ip,omitempty"`
|
||||
|
||||
// FQDN targets an exact domain name (no wildcards).
|
||||
// +optional
|
||||
FQDN *NetworkEgressFQDNTarget `json:"fqdn,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressIPTarget is a single IPv4 or IPv6 address.
|
||||
type NetworkEgressIPTarget struct {
|
||||
// Address is a single IP address.
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:XValidation:rule="isIP(self)",message="address must be a valid IPv4 or IPv6 address"
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
// NetworkEgressFQDNTarget matches traffic by an exact domain name (no wildcards).
|
||||
type NetworkEgressFQDNTarget struct {
|
||||
// Hostname is a fully qualified domain name to match exactly.
|
||||
// +kubebuilder:validation:Required
|
||||
// +kubebuilder:validation:Pattern=`^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$`
|
||||
Hostname string `json:"hostname"`
|
||||
}
|
||||
|
||||
type NetworkEgressPort struct {
|
||||
// Name of the port.
|
||||
// +required
|
||||
// +kubebuilder:validation:MinLength=1
|
||||
// +kubebuilder:validation:MaxLength=15
|
||||
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?$`
|
||||
Name string `json:"name,omitempty"`
|
||||
|
||||
// The port that will be exposed by this service.
|
||||
// +required
|
||||
// +kubebuilder:validation:Minimum=1
|
||||
// +kubebuilder:validation:Maximum=65535
|
||||
Port int32 `json:"port"`
|
||||
}
|
||||
|
||||
// NetworkEgressStatus defines the observed state of NetworkEgress.
|
||||
type NetworkEgressStatus struct {
|
||||
// ObservedGeneration is the last reconciled generation.
|
||||
// +optional
|
||||
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
|
||||
|
||||
// Conditions holds the conditions for the NetworkEgress.
|
||||
// +listType=map
|
||||
// +listMapKey=type
|
||||
// +optional
|
||||
Conditions []metav1.Condition `json:"conditions,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=""
|
||||
|
||||
// NetworkEgress is the Schema for the networkegresses API.
|
||||
type NetworkEgress struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||||
|
||||
// +required
|
||||
Spec NetworkEgressSpec `json:"spec"`
|
||||
|
||||
// +kubebuilder:default={"observedGeneration":-1}
|
||||
Status NetworkEgressStatus `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// GetConditions returns the status conditions of the object.
|
||||
func (n *NetworkEgress) GetConditions() []metav1.Condition {
|
||||
return n.Status.Conditions
|
||||
}
|
||||
|
||||
// SetConditions sets the status conditions on the object.
|
||||
func (n *NetworkEgress) SetConditions(conditions []metav1.Condition) {
|
||||
n.Status.Conditions = conditions
|
||||
}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
|
||||
// NetworkEgressList contains a list of NetworkEgress
|
||||
type NetworkEgressList struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
metav1.ListMeta `json:"metadata,omitzero"`
|
||||
Items []NetworkEgress `json:"items"`
|
||||
}
|
||||
|
||||
func init() {
|
||||
SchemeBuilder.Register(&NetworkEgress{}, &NetworkEgressList{})
|
||||
}
|
||||
@@ -313,6 +313,179 @@ 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 *NetworkEgress) DeepCopyInto(out *NetworkEgress) {
|
||||
*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 NetworkEgress.
|
||||
func (in *NetworkEgress) DeepCopy() *NetworkEgress {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgress)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NetworkEgress) 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 *NetworkEgressFQDNTarget) DeepCopyInto(out *NetworkEgressFQDNTarget) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEgressFQDNTarget.
|
||||
func (in *NetworkEgressFQDNTarget) DeepCopy() *NetworkEgressFQDNTarget {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressFQDNTarget)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkEgressIPTarget) DeepCopyInto(out *NetworkEgressIPTarget) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEgressIPTarget.
|
||||
func (in *NetworkEgressIPTarget) DeepCopy() *NetworkEgressIPTarget {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressIPTarget)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkEgressList) DeepCopyInto(out *NetworkEgressList) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ListMeta.DeepCopyInto(&out.ListMeta)
|
||||
if in.Items != nil {
|
||||
in, out := &in.Items, &out.Items
|
||||
*out = make([]NetworkEgress, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEgressList.
|
||||
func (in *NetworkEgressList) DeepCopy() *NetworkEgressList {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressList)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *NetworkEgressList) 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 *NetworkEgressPort) DeepCopyInto(out *NetworkEgressPort) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEgressPort.
|
||||
func (in *NetworkEgressPort) DeepCopy() *NetworkEgressPort {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressPort)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkEgressSpec) DeepCopyInto(out *NetworkEgressSpec) {
|
||||
*out = *in
|
||||
out.NetworkRouterRef = in.NetworkRouterRef
|
||||
in.Target.DeepCopyInto(&out.Target)
|
||||
if in.Ports != nil {
|
||||
in, out := &in.Ports, &out.Ports
|
||||
*out = make([]NetworkEgressPort, len(*in))
|
||||
copy(*out, *in)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEgressSpec.
|
||||
func (in *NetworkEgressSpec) DeepCopy() *NetworkEgressSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkEgressStatus) DeepCopyInto(out *NetworkEgressStatus) {
|
||||
*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 NetworkEgressStatus.
|
||||
func (in *NetworkEgressStatus) DeepCopy() *NetworkEgressStatus {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressStatus)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *NetworkEgressTarget) DeepCopyInto(out *NetworkEgressTarget) {
|
||||
*out = *in
|
||||
if in.IP != nil {
|
||||
in, out := &in.IP, &out.IP
|
||||
*out = new(NetworkEgressIPTarget)
|
||||
**out = **in
|
||||
}
|
||||
if in.FQDN != nil {
|
||||
in, out := &in.FQDN, &out.FQDN
|
||||
*out = new(NetworkEgressFQDNTarget)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkEgressTarget.
|
||||
func (in *NetworkEgressTarget) DeepCopy() *NetworkEgressTarget {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(NetworkEgressTarget)
|
||||
in.DeepCopyInto(out)
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user