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:
@@ -65,6 +65,8 @@ linters:
|
||||
alias: admissionv1
|
||||
- pkg: k8s.io/api/policy/v1
|
||||
alias: policyv1
|
||||
- pkg: "k8s.io/api/discovery/v1"
|
||||
alias: discoveryv1
|
||||
- pkg: k8s.io/client-go/applyconfigurations/meta/v1
|
||||
alias: metav1ac
|
||||
- pkg: k8s.io/client-go/applyconfigurations/core/v1
|
||||
@@ -75,6 +77,8 @@ linters:
|
||||
alias: appsv1ac
|
||||
- pkg: k8s.io/client-go/applyconfigurations/policy/v1
|
||||
alias: policyv1ac
|
||||
- pkg: k8s.io/client-go/applyconfigurations/discovery/v1
|
||||
alias: discoveryv1ac
|
||||
|
||||
- pkg: sigs.k8s.io/gateway-api/apis/v1
|
||||
alias: gwv1
|
||||
|
||||
@@ -123,4 +123,12 @@ resources:
|
||||
kind: ClusterProxy
|
||||
path: github.com/netbirdio/kubernetes-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
- api:
|
||||
crdVersion: v1
|
||||
namespaced: true
|
||||
controller: true
|
||||
domain: netbird.io
|
||||
kind: NetworkEgress
|
||||
path: github.com/netbirdio/kubernetes-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
version: "3"
|
||||
|
||||
@@ -23,9 +23,10 @@ helm upgrade --install --create-namespace -n netbird netbird-operator oci://ghcr
|
||||
|
||||
| Kind | API Version |
|
||||
|------|-------------|
|
||||
| [Group](docs/api-reference.md#group) | `netbird.io/v1alpha1` |
|
||||
| [NetworkResource](docs/api-reference.md#networkresource) | `netbird.io/v1alpha1` |
|
||||
| [NetworkRouter](docs/api-reference.md#networkrouter) | `netbird.io/v1alpha1` |
|
||||
| [SetupKey](docs/api-reference.md#setupkey) | `netbird.io/v1alpha1` |
|
||||
| [Group](docs/api-reference.md#group) | `netbird.io/v1alpha1` |
|
||||
| [NetworkRouter](docs/api-reference.md#networkrouter) | `netbird.io/v1alpha1` |
|
||||
| [NetworkResource](docs/api-reference.md#networkresource) | `netbird.io/v1alpha1` |
|
||||
| [NetworkEgress](docs/api-reference.md#networkegress) | `netbird.io/v1alpha1` |
|
||||
| [SidecarProfile](docs/api-reference.md#sidecarprofile) | `netbird.io/v1alpha1` |
|
||||
| [ClusterProxy](docs/api-reference.md#clusterproxy) | `netbird.io/v1alpha1` |
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.20.1
|
||||
name: networkegresses.netbird.io
|
||||
spec:
|
||||
group: netbird.io
|
||||
names:
|
||||
kind: NetworkEgress
|
||||
listKind: NetworkEgressList
|
||||
plural: networkegresses
|
||||
singular: networkegress
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: NetworkEgress is the Schema for the networkegresses API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: NetworkEgressSpec defines the desired state of NetworkEgress.
|
||||
properties:
|
||||
networkRouterRef:
|
||||
description: NetworkRouterRef is a reference to the network and router
|
||||
where the resource will be created.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: Value is immutable
|
||||
rule: self == oldSelf
|
||||
ports:
|
||||
description: Ports to the resource to route.
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
description: Name of the port.
|
||||
maxLength: 15
|
||||
minLength: 1
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
port:
|
||||
description: The port that will be exposed by this service.
|
||||
format: int32
|
||||
maximum: 65535
|
||||
minimum: 1
|
||||
type: integer
|
||||
required:
|
||||
- name
|
||||
- port
|
||||
type: object
|
||||
minItems: 1
|
||||
type: array
|
||||
target:
|
||||
description: Target for egress traffic.
|
||||
properties:
|
||||
fqdn:
|
||||
description: FQDN targets an exact domain name (no wildcards).
|
||||
properties:
|
||||
hostname:
|
||||
description: Hostname is a fully qualified domain name to
|
||||
match exactly.
|
||||
pattern: ^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$
|
||||
type: string
|
||||
required:
|
||||
- hostname
|
||||
type: object
|
||||
ip:
|
||||
description: IP targets a single specific IP address (not a CIDR
|
||||
range).
|
||||
properties:
|
||||
address:
|
||||
description: Address is a single IP address.
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- message: address must be a valid IPv4 or IPv6 address
|
||||
rule: isIP(self)
|
||||
required:
|
||||
- address
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: exactly one of ip or fqdn must be set
|
||||
rule: '(has(self.ip) ? 1 : 0) + (has(self.fqdn) ? 1 : 0) == 1'
|
||||
required:
|
||||
- networkRouterRef
|
||||
- ports
|
||||
- target
|
||||
type: object
|
||||
status:
|
||||
default:
|
||||
observedGeneration: -1
|
||||
description: NetworkEgressStatus defines the observed state of NetworkEgress.
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions holds the conditions for the NetworkEgress.
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last reconciled generation.
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -38,6 +38,7 @@ rules:
|
||||
- groups
|
||||
- networkrouters
|
||||
- networkresources
|
||||
- networkegresses
|
||||
- sidecarprofiles
|
||||
- clusterproxies
|
||||
verbs:
|
||||
@@ -59,6 +60,7 @@ rules:
|
||||
- groups/status
|
||||
- networkrouters/status
|
||||
- networkresources/status
|
||||
- networkregresses/status
|
||||
- sidecarprofiles/status
|
||||
- clusterproxies/status
|
||||
verbs:
|
||||
@@ -76,19 +78,60 @@ rules:
|
||||
- groups/finalizers
|
||||
- networkrouters/finalizers
|
||||
- networkresources/finalizers
|
||||
- networkegresses/finalizers
|
||||
- sidecarprofiles/finalizers
|
||||
verbs:
|
||||
- update
|
||||
- apiGroups:
|
||||
- "discovery.k8s.io"
|
||||
resources:
|
||||
- endpointslices
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- delete
|
||||
- apiGroups:
|
||||
- "rbac.authorization.k8s.io"
|
||||
resources:
|
||||
- roles
|
||||
- rolebindings
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
- serviceaccounts
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- services
|
||||
verbs:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- list
|
||||
- watch
|
||||
- update
|
||||
- patch
|
||||
- create
|
||||
- delete
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
@@ -197,18 +240,6 @@ metadata:
|
||||
labels:
|
||||
{{- include "netbird-operator.labels" . | nindent 4 }}
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- configmaps
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- patch
|
||||
- delete
|
||||
- apiGroups:
|
||||
- coordination.k8s.io
|
||||
resources:
|
||||
|
||||
+13
@@ -312,6 +312,19 @@ func main() {
|
||||
setupLog.Error(err, "Failed to create controller", "controller", "NetworkResource")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := (&controller.NetworkEgressReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Netbird: nbClient,
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "Failed to create controller", "controller", "NetworkEgress")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := (&controller.ForwarderServiceReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "Failed to create controller", "controller", "ForwarderService")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := (&controller.ClusterProxyReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
ApiKey: netbirdAPIKey,
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
---
|
||||
apiVersion: apiextensions.k8s.io/v1
|
||||
kind: CustomResourceDefinition
|
||||
metadata:
|
||||
annotations:
|
||||
controller-gen.kubebuilder.io/version: v0.20.1
|
||||
name: networkegresses.netbird.io
|
||||
spec:
|
||||
group: netbird.io
|
||||
names:
|
||||
kind: NetworkEgress
|
||||
listKind: NetworkEgressList
|
||||
plural: networkegresses
|
||||
singular: networkegress
|
||||
scope: Namespaced
|
||||
versions:
|
||||
- additionalPrinterColumns:
|
||||
- jsonPath: .status.conditions[?(@.type=="Ready")].status
|
||||
name: Ready
|
||||
type: string
|
||||
- jsonPath: .metadata.creationTimestamp
|
||||
name: Age
|
||||
type: date
|
||||
name: v1alpha1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: NetworkEgress is the Schema for the networkegresses API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
APIVersion defines the versioned schema of this representation of an object.
|
||||
Servers should convert recognized schemas to the latest internal value, and
|
||||
may reject unrecognized values.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
|
||||
type: string
|
||||
kind:
|
||||
description: |-
|
||||
Kind is a string value representing the REST resource this object represents.
|
||||
Servers may infer this from the endpoint the client submits requests to.
|
||||
Cannot be updated.
|
||||
In CamelCase.
|
||||
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
|
||||
type: string
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: NetworkEgressSpec defines the desired state of NetworkEgress.
|
||||
properties:
|
||||
networkRouterRef:
|
||||
description: NetworkRouterRef is a reference to the network and router
|
||||
where the resource will be created.
|
||||
properties:
|
||||
name:
|
||||
description: Name of the referent.
|
||||
type: string
|
||||
namespace:
|
||||
description: Namespace of the referent.
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- namespace
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: Value is immutable
|
||||
rule: self == oldSelf
|
||||
ports:
|
||||
description: Ports to the resource to route.
|
||||
items:
|
||||
properties:
|
||||
name:
|
||||
description: Name of the port.
|
||||
maxLength: 15
|
||||
minLength: 1
|
||||
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
|
||||
type: string
|
||||
port:
|
||||
description: The port that will be exposed by this service.
|
||||
format: int32
|
||||
maximum: 65535
|
||||
minimum: 1
|
||||
type: integer
|
||||
required:
|
||||
- name
|
||||
- port
|
||||
type: object
|
||||
minItems: 1
|
||||
type: array
|
||||
target:
|
||||
description: Target for egress traffic.
|
||||
properties:
|
||||
fqdn:
|
||||
description: FQDN targets an exact domain name (no wildcards).
|
||||
properties:
|
||||
hostname:
|
||||
description: Hostname is a fully qualified domain name to
|
||||
match exactly.
|
||||
pattern: ^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$
|
||||
type: string
|
||||
required:
|
||||
- hostname
|
||||
type: object
|
||||
ip:
|
||||
description: IP targets a single specific IP address (not a CIDR
|
||||
range).
|
||||
properties:
|
||||
address:
|
||||
description: Address is a single IP address.
|
||||
type: string
|
||||
x-kubernetes-validations:
|
||||
- message: address must be a valid IPv4 or IPv6 address
|
||||
rule: isIP(self)
|
||||
required:
|
||||
- address
|
||||
type: object
|
||||
type: object
|
||||
x-kubernetes-validations:
|
||||
- message: exactly one of ip or fqdn must be set
|
||||
rule: '(has(self.ip) ? 1 : 0) + (has(self.fqdn) ? 1 : 0) == 1'
|
||||
required:
|
||||
- networkRouterRef
|
||||
- ports
|
||||
- target
|
||||
type: object
|
||||
status:
|
||||
default:
|
||||
observedGeneration: -1
|
||||
description: NetworkEgressStatus defines the observed state of NetworkEgress.
|
||||
properties:
|
||||
conditions:
|
||||
description: Conditions holds the conditions for the NetworkEgress.
|
||||
items:
|
||||
description: Condition contains details for one aspect of the current
|
||||
state of this API Resource.
|
||||
properties:
|
||||
lastTransitionTime:
|
||||
description: |-
|
||||
lastTransitionTime is the last time the condition transitioned from one status to another.
|
||||
This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
|
||||
format: date-time
|
||||
type: string
|
||||
message:
|
||||
description: |-
|
||||
message is a human readable message indicating details about the transition.
|
||||
This may be an empty string.
|
||||
maxLength: 32768
|
||||
type: string
|
||||
observedGeneration:
|
||||
description: |-
|
||||
observedGeneration represents the .metadata.generation that the condition was set based upon.
|
||||
For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date
|
||||
with respect to the current state of the instance.
|
||||
format: int64
|
||||
minimum: 0
|
||||
type: integer
|
||||
reason:
|
||||
description: |-
|
||||
reason contains a programmatic identifier indicating the reason for the condition's last transition.
|
||||
Producers of specific condition types may define expected values and meanings for this field,
|
||||
and whether the values are considered a guaranteed API.
|
||||
The value should be a CamelCase string.
|
||||
This field may not be empty.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
|
||||
type: string
|
||||
status:
|
||||
description: status of the condition, one of True, False, Unknown.
|
||||
enum:
|
||||
- "True"
|
||||
- "False"
|
||||
- Unknown
|
||||
type: string
|
||||
type:
|
||||
description: type of condition in CamelCase or in foo.example.com/CamelCase.
|
||||
maxLength: 316
|
||||
pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
|
||||
type: string
|
||||
required:
|
||||
- lastTransitionTime
|
||||
- message
|
||||
- reason
|
||||
- status
|
||||
- type
|
||||
type: object
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- type
|
||||
x-kubernetes-list-type: map
|
||||
observedGeneration:
|
||||
description: ObservedGeneration is the last reconciled generation.
|
||||
format: int64
|
||||
type: integer
|
||||
type: object
|
||||
required:
|
||||
- spec
|
||||
type: object
|
||||
served: true
|
||||
storage: true
|
||||
subresources:
|
||||
status: {}
|
||||
@@ -12,4 +12,5 @@ resources:
|
||||
- bases/netbird.io_setupkeys.yaml
|
||||
- bases/netbird.io_sidecarprofiles.yaml
|
||||
- bases/netbird.io_clusterproxies.yaml
|
||||
- bases/netbird.io_networkegresses.yaml
|
||||
# +kubebuilder:scaffold:crdkustomizeresource
|
||||
|
||||
@@ -11,6 +11,7 @@ Package v1alpha1 contains API Schema definitions for the v1alpha1 API group.
|
||||
### Resource Types
|
||||
- [ClusterProxy](#clusterproxy)
|
||||
- [Group](#group)
|
||||
- [NetworkEgress](#networkegress)
|
||||
- [NetworkResource](#networkresource)
|
||||
- [NetworkRouter](#networkrouter)
|
||||
- [SetupKey](#setupkey)
|
||||
@@ -105,6 +106,7 @@ _Appears in:_
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgressSpec](#networkegressspec)
|
||||
- [NetworkResourceSpec](#networkresourcespec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
@@ -222,6 +224,129 @@ _Appears in:_
|
||||
| `Container` | InjectionModeContainer injects the client as a regular container.<br /> |
|
||||
|
||||
|
||||
#### NetworkEgress
|
||||
|
||||
|
||||
|
||||
NetworkEgress is the Schema for the networkegresses API.
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `apiVersion` _string_ | `netbird.io/v1alpha1` | | |
|
||||
| `kind` _string_ | `NetworkEgress` | | |
|
||||
| `kind` _string_ | Kind is a string value representing the REST resource this object represents.<br />Servers may infer this from the endpoint the client submits requests to.<br />Cannot be updated.<br />In CamelCase.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\} <br /> |
|
||||
| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.<br />Servers should convert recognized schemas to the latest internal value, and<br />may reject unrecognized values.<br />More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\} <br /> |
|
||||
| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | |
|
||||
| `spec` _[NetworkEgressSpec](#networkegressspec)_ | | | Required: \{\} <br /> |
|
||||
| `status` _[NetworkEgressStatus](#networkegressstatus)_ | | \{ observedGeneration:-1 \} | |
|
||||
|
||||
|
||||
#### NetworkEgressFQDNTarget
|
||||
|
||||
|
||||
|
||||
NetworkEgressFQDNTarget matches traffic by an exact domain name (no wildcards).
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgressTarget](#networkegresstarget)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `hostname` _string_ | Hostname is a fully qualified domain name to match exactly. | | Pattern: `^([a-zA-Z0-9]([a-zA-Z0-9-]\{0,61\}[a-zA-Z0-9])?\.)+[a-zA-Z]\{2,\}$` <br />Required: \{\} <br /> |
|
||||
|
||||
|
||||
#### NetworkEgressIPTarget
|
||||
|
||||
|
||||
|
||||
NetworkEgressIPTarget is a single IPv4 or IPv6 address.
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgressTarget](#networkegresstarget)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `address` _string_ | Address is a single IP address. | | Required: \{\} <br /> |
|
||||
|
||||
|
||||
#### NetworkEgressPort
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgressSpec](#networkegressspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `name` _string_ | Name of the port. | | MaxLength: 15 <br />MinLength: 1 <br />Pattern: `^[a-z0-9]([-a-z0-9]*[a-z0-9])?$` <br />Required: \{\} <br /> |
|
||||
| `port` _integer_ | The port that will be exposed by this service. | | Maximum: 65535 <br />Minimum: 1 <br />Required: \{\} <br /> |
|
||||
|
||||
|
||||
#### NetworkEgressSpec
|
||||
|
||||
|
||||
|
||||
NetworkEgressSpec defines the desired state of NetworkEgress.
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgress](#networkegress)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `networkRouterRef` _[CrossNamespaceReference](#crossnamespacereference)_ | NetworkRouterRef is a reference to the network and router where the resource will be created. | | |
|
||||
| `target` _[NetworkEgressTarget](#networkegresstarget)_ | Target for egress traffic. | | |
|
||||
| `ports` _[NetworkEgressPort](#networkegressport) array_ | Ports to the resource to route. | | MinItems: 1 <br />Required: \{\} <br /> |
|
||||
|
||||
|
||||
#### NetworkEgressStatus
|
||||
|
||||
|
||||
|
||||
NetworkEgressStatus defines the observed state of NetworkEgress.
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgress](#networkegress)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `observedGeneration` _integer_ | ObservedGeneration is the last reconciled generation. | | Optional: \{\} <br /> |
|
||||
| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | Conditions holds the conditions for the NetworkEgress. | | Optional: \{\} <br /> |
|
||||
|
||||
|
||||
#### NetworkEgressTarget
|
||||
|
||||
|
||||
|
||||
NetworkEgressTarget describes a single allowed egress destination.
|
||||
Exactly one of IP or FQDN must be set.
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [NetworkEgressSpec](#networkegressspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `ip` _[NetworkEgressIPTarget](#networkegressiptarget)_ | IP targets a single specific IP address (not a CIDR range). | | Optional: \{\} <br /> |
|
||||
| `fqdn` _[NetworkEgressFQDNTarget](#networkegressfqdntarget)_ | FQDN targets an exact domain name (no wildcards). | | Optional: \{\} <br /> |
|
||||
|
||||
|
||||
#### NetworkResource
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
apiVersion: netbird.io/v1alpha1
|
||||
kind: NetworkEgress
|
||||
metadata:
|
||||
name: nginx
|
||||
namespace: default
|
||||
spec:
|
||||
networkRouterRef:
|
||||
name: prod
|
||||
namespace: netbird
|
||||
target:
|
||||
ip:
|
||||
address: 10.96.92.40
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
@@ -9,6 +9,7 @@ require (
|
||||
github.com/go-logr/logr v1.4.3
|
||||
github.com/go-openapi/testify/v2 v2.6.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/netbirdio/kube-egress-forwarder v0.0.2
|
||||
github.com/netbirdio/netbird v0.72.4
|
||||
github.com/onsi/ginkgo/v2 v2.32.0
|
||||
github.com/onsi/gomega v1.42.1
|
||||
@@ -79,7 +80,7 @@ require (
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/oauth2 v0.36.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/sys v0.46.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/text v0.38.0 // indirect
|
||||
|
||||
@@ -340,6 +340,8 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/netbirdio/dex v0.244.1-0.20260512110716-8d70ad8647c1 h1:4TaYr9O4xX0D2kszeOLclTiCbA3eHq3xWV+9ILJbIYs=
|
||||
github.com/netbirdio/dex v0.244.1-0.20260512110716-8d70ad8647c1/go.mod h1:IHH+H8vK2GfqtIt5u/5OdPh18yk0oDHuj2vz5+Goetg=
|
||||
github.com/netbirdio/kube-egress-forwarder v0.0.2 h1:jUlgA8lSDKyqn9tUdRFtEadXdDCFA63ZFPenQrMGv7E=
|
||||
github.com/netbirdio/kube-egress-forwarder v0.0.2/go.mod h1:Lyt9k/93DTWaeOiGQWM63WtQNj/9NnMTR/PivDgWXXE=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42 h1:F3zS5fT9xzD1OFLfcdAE+3FfyiwjGukF1hvj0jErgs8=
|
||||
github.com/netbirdio/management-integrations/integrations v0.0.0-20260416123949-2355d972be42/go.mod h1:n47r67ZSPgwSmT/Z1o48JjZQW9YJ6m/6Bd/uAXkL3Pg=
|
||||
github.com/netbirdio/netbird v0.72.4 h1:Pjs5PZqIvjJ4gqTW+RNh2sBMjsgOFd1Rf0SDoYsHqLM=
|
||||
@@ -509,8 +511,8 @@ golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/netip"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
discoveryv1 "k8s.io/api/discovery/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/selection"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
|
||||
discoveryv1ac "k8s.io/client-go/applyconfigurations/discovery/v1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
"github.com/netbirdio/kube-egress-forwarder/pkg/forwarder"
|
||||
|
||||
nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1"
|
||||
"github.com/netbirdio/kubernetes-operator/internal/k8sutil"
|
||||
)
|
||||
|
||||
const (
|
||||
ForwarderRouterNameLabel = "netbird.io/forwarder-router-name"
|
||||
EgressRouterNameLabel = "netbird.io/egress-router-name"
|
||||
EgressRouterNamespaceLabel = "netbird.io/egress-router-namespace"
|
||||
LastUpdatedLabel = "netbird.io/last-updated"
|
||||
)
|
||||
|
||||
// ForwarderServiceReconciler reconciles a EndpointSlice object
|
||||
type ForwarderServiceReconciler struct {
|
||||
client.Client
|
||||
}
|
||||
|
||||
func (r *ForwarderServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
svc := &corev1.Service{}
|
||||
err := r.Get(ctx, req.NamespacedName, svc)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
if !svc.DeletionTimestamp.IsZero() {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
ownerRef, err := k8sutil.ControllerReference(svc, r.Scheme())
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
routerName, ok := svc.Labels[ForwarderRouterNameLabel]
|
||||
if !ok {
|
||||
return ctrl.Result{}, errors.New("missing forwarder router label")
|
||||
}
|
||||
|
||||
// Load port manager state.
|
||||
ruleConfigmap := &corev1.ConfigMap{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: req.Name,
|
||||
Namespace: req.Namespace,
|
||||
},
|
||||
}
|
||||
err = r.Client.Get(ctx, client.ObjectKeyFromObject(ruleConfigmap), ruleConfigmap)
|
||||
if err != nil && !kerrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
ruleMgr, err := forwarder.NewRuleManager(ruleConfigmap.Data)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Get endpoint slices for forwarder service.
|
||||
endpointSliceList := &discoveryv1.EndpointSliceList{}
|
||||
err = r.Client.List(ctx, endpointSliceList, &client.MatchingLabels{discoveryv1.LabelServiceName: svc.Name})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Get services for egress resources.
|
||||
egressSvcList := &corev1.ServiceList{}
|
||||
err = r.Client.List(ctx, egressSvcList, &client.MatchingLabels{EgressRouterNameLabel: routerName, EgressRouterNamespaceLabel: req.Namespace})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Copy router endpoint slices to egress services.
|
||||
lastUpdated := fmt.Sprintf("%d", time.Now().Unix())
|
||||
|
||||
for _, egressSvc := range egressSvcList.Items {
|
||||
netEgress := &nbv1alpha1.NetworkEgress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: egressSvc.OwnerReferences[0].Name,
|
||||
Namespace: egressSvc.Namespace,
|
||||
},
|
||||
}
|
||||
err = r.Get(ctx, client.ObjectKeyFromObject(netEgress), netEgress)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
targetPorts := []int32{}
|
||||
for _, port := range egressSvc.Spec.Ports {
|
||||
dest, err := func() (string, error) {
|
||||
switch {
|
||||
case netEgress.Spec.Target.IP != nil:
|
||||
addr, err := netip.ParseAddr(netEgress.Spec.Target.IP.Address)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return netip.AddrPortFrom(addr, uint16(port.Port)).String(), nil
|
||||
case netEgress.Spec.Target.FQDN != nil:
|
||||
return fmt.Sprintf("%s:%d", netEgress.Spec.Target.FQDN.Hostname, port.Port), nil
|
||||
}
|
||||
return "", errors.New("egress target not found")
|
||||
}()
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
rule := ruleMgr.Allocate(port.Protocol, dest)
|
||||
targetPorts = append(targetPorts, rule.Port)
|
||||
}
|
||||
portACs := toPortApplyConfigurations(egressSvc.Spec.Ports, targetPorts)
|
||||
|
||||
egressSvcOwnerRef, err := k8sutil.ControllerReference(&egressSvc, r.Scheme())
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
for _, endpointSlice := range endpointSliceList.Items {
|
||||
nameSuffx := strings.TrimPrefix(endpointSlice.Name, endpointSlice.GenerateName)
|
||||
|
||||
labels := map[string]string{
|
||||
discoveryv1.LabelServiceName: egressSvc.Name,
|
||||
discoveryv1.LabelManagedBy: "netbird-operator.netbird.io",
|
||||
LastUpdatedLabel: lastUpdated,
|
||||
}
|
||||
maps.Copy(labels, egressSvc.Labels)
|
||||
|
||||
endpointACs := toEndpointApplyConfigurations(endpointSlice.Endpoints)
|
||||
endpointSliceAC := discoveryv1ac.EndpointSlice(fmt.Sprintf("%s-%s", egressSvc.Name, nameSuffx), egressSvc.Namespace).
|
||||
WithLabels(labels).
|
||||
WithOwnerReferences(egressSvcOwnerRef).
|
||||
WithAddressType(endpointSlice.AddressType).
|
||||
WithEndpoints(endpointACs...).
|
||||
WithPorts(portACs...)
|
||||
err = r.Client.Apply(ctx, endpointSliceAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write the port config to the port conversion.
|
||||
data, err := ruleMgr.Data()
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
cmAC := corev1ac.ConfigMap(req.Name, req.Namespace).
|
||||
WithOwnerReferences(ownerRef).
|
||||
WithData(data)
|
||||
err = r.Client.Apply(ctx, cmAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Cleanup old endpoint slices.
|
||||
updateReq, err := labels.NewRequirement(LastUpdatedLabel, selection.NotEquals, []string{lastUpdated})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
egressNameReq, err := labels.NewRequirement(EgressRouterNameLabel, selection.Equals, []string{routerName})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
egressNamespaceReq, err := labels.NewRequirement(EgressRouterNamespaceLabel, selection.Equals, []string{req.Namespace})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
sourceReq, err := labels.NewRequirement(discoveryv1.LabelServiceName, selection.NotEquals, []string{svc.Name})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
deleteSelector := labels.NewSelector().Add(*updateReq).Add(*egressNameReq).Add(*egressNamespaceReq).Add(*sourceReq)
|
||||
|
||||
err = r.Client.List(ctx, endpointSliceList, client.MatchingLabelsSelector{Selector: deleteSelector})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
for _, item := range endpointSliceList.Items {
|
||||
if err := r.Client.Delete(ctx, &item); err != nil && !kerrors.IsNotFound(err) {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
}
|
||||
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *ForwarderServiceReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
forwarderSvcSelector := metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: ForwarderRouterNameLabel,
|
||||
Operator: metav1.LabelSelectorOpExists,
|
||||
},
|
||||
},
|
||||
}
|
||||
forwarderSvcPred, err := predicate.LabelSelectorPredicate(forwarderSvcSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
egressSvcSelector := metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: EgressRouterNameLabel,
|
||||
Operator: metav1.LabelSelectorOpExists,
|
||||
},
|
||||
{
|
||||
Key: EgressRouterNamespaceLabel,
|
||||
Operator: metav1.LabelSelectorOpExists,
|
||||
},
|
||||
},
|
||||
}
|
||||
egressSvcPred, err := predicate.LabelSelectorPredicate(egressSvcSelector)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
Named("forwarderservice").
|
||||
For(&corev1.Service{}, builder.WithPredicates(forwarderSvcPred)).
|
||||
Owns(&discoveryv1.EndpointSlice{}, builder.WithPredicates(forwarderSvcPred)).
|
||||
Owns(&corev1.ConfigMap{}).
|
||||
Watches(&nbv1alpha1.NetworkEgress{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
imp, ok := obj.(*nbv1alpha1.NetworkEgress)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: fmt.Sprintf("networkrouter-%s-forwarder", imp.Spec.NetworkRouterRef.Name),
|
||||
Namespace: imp.Spec.NetworkRouterRef.Namespace,
|
||||
},
|
||||
},
|
||||
}
|
||||
})).
|
||||
Watches(&corev1.Service{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
return []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: fmt.Sprintf("networkrouter-%s-forwarder", obj.GetLabels()[EgressRouterNameLabel]),
|
||||
Namespace: obj.GetLabels()[EgressRouterNamespaceLabel],
|
||||
},
|
||||
},
|
||||
}
|
||||
}), builder.WithPredicates(egressSvcPred)).
|
||||
Watches(&discoveryv1.EndpointSlice{}, handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
return []reconcile.Request{
|
||||
{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: fmt.Sprintf("networkrouter-%s-forwarder", obj.GetLabels()[EgressRouterNameLabel]),
|
||||
Namespace: obj.GetLabels()[EgressRouterNamespaceLabel],
|
||||
},
|
||||
},
|
||||
}
|
||||
}), builder.WithPredicates(egressSvcPred)).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
func toEndpointApplyConfigurations(endpoints []discoveryv1.Endpoint) []*discoveryv1ac.EndpointApplyConfiguration {
|
||||
endpointACs := make([]*discoveryv1ac.EndpointApplyConfiguration, 0, len(endpoints))
|
||||
for _, endpoint := range endpoints {
|
||||
conditionAC := discoveryv1ac.EndpointConditions()
|
||||
if endpoint.Conditions.Ready != nil {
|
||||
conditionAC.WithReady(*endpoint.Conditions.Ready)
|
||||
}
|
||||
if endpoint.Conditions.Serving != nil {
|
||||
conditionAC.WithServing(*endpoint.Conditions.Serving)
|
||||
}
|
||||
if endpoint.Conditions.Terminating != nil {
|
||||
conditionAC.WithTerminating(*endpoint.Conditions.Terminating)
|
||||
}
|
||||
|
||||
endpointAC := discoveryv1ac.Endpoint().
|
||||
WithAddresses(endpoint.Addresses...).
|
||||
WithConditions(conditionAC)
|
||||
|
||||
if endpoint.NodeName != nil {
|
||||
endpointAC = endpointAC.WithNodeName(*endpoint.NodeName)
|
||||
}
|
||||
if endpoint.Zone != nil {
|
||||
endpointAC = endpointAC.WithZone(*endpoint.Zone)
|
||||
}
|
||||
if endpoint.Hints != nil {
|
||||
hintAC := discoveryv1ac.EndpointHints()
|
||||
for _, hint := range endpoint.Hints.ForNodes {
|
||||
hintAC = hintAC.WithForNodes(discoveryv1ac.ForNode().WithName(hint.Name))
|
||||
}
|
||||
for _, hint := range endpoint.Hints.ForZones {
|
||||
hintAC = hintAC.WithForZones(discoveryv1ac.ForZone().WithName(hint.Name))
|
||||
}
|
||||
endpointAC = endpointAC.WithHints(hintAC)
|
||||
}
|
||||
if endpoint.TargetRef != nil {
|
||||
endpointAC = endpointAC.WithTargetRef(corev1ac.ObjectReference().
|
||||
WithResourceVersion(endpoint.TargetRef.ResourceVersion).
|
||||
WithFieldPath(endpoint.TargetRef.FieldPath).
|
||||
WithUID(endpoint.TargetRef.UID).
|
||||
WithAPIVersion(endpoint.TargetRef.APIVersion).
|
||||
WithKind(endpoint.TargetRef.Kind).
|
||||
WithName(endpoint.TargetRef.Name).
|
||||
WithNamespace(endpoint.TargetRef.Namespace),
|
||||
)
|
||||
}
|
||||
|
||||
endpointACs = append(endpointACs, endpointAC)
|
||||
}
|
||||
return endpointACs
|
||||
}
|
||||
|
||||
func toPortApplyConfigurations(ports []corev1.ServicePort, targetPorts []int32) []*discoveryv1ac.EndpointPortApplyConfiguration {
|
||||
portACs := make([]*discoveryv1ac.EndpointPortApplyConfiguration, 0, len(ports))
|
||||
for i, port := range ports {
|
||||
portAC := discoveryv1ac.EndpointPort().WithName(port.Name).WithPort(targetPorts[i]).WithProtocol(port.Protocol)
|
||||
if port.AppProtocol != nil {
|
||||
portAC = portAC.WithAppProtocol(*port.AppProtocol)
|
||||
}
|
||||
portACs = append(portACs, portAC)
|
||||
}
|
||||
return portACs
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/fluxcd/pkg/runtime/conditions"
|
||||
"github.com/fluxcd/pkg/runtime/patch"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/builder"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/handler"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
netbird "github.com/netbirdio/netbird/shared/management/client/rest"
|
||||
|
||||
nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1"
|
||||
"github.com/netbirdio/kubernetes-operator/internal/k8sutil"
|
||||
)
|
||||
|
||||
// NetworkEgressReconciler reconciles a NetworkEgress object
|
||||
type NetworkEgressReconciler struct {
|
||||
client.Client
|
||||
|
||||
Netbird *netbird.Client
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=netbird.io,resources=networkegresses,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=netbird.io,resources=networkegresses/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=netbird.io,resources=networkegresses/finalizers,verbs=update
|
||||
|
||||
func (r *NetworkEgressReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
|
||||
netEgress := &nbv1alpha1.NetworkEgress{}
|
||||
err := r.Get(ctx, req.NamespacedName, netEgress)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, client.IgnoreNotFound(err)
|
||||
}
|
||||
sp := patch.NewSerialPatcher(netEgress, r.Client)
|
||||
|
||||
if !netEgress.DeletionTimestamp.IsZero() {
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
ownerRef, err := k8sutil.ControllerReference(netEgress, r.Scheme())
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
netRouter := &nbv1alpha1.NetworkRouter{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: netEgress.Spec.NetworkRouterRef.Name,
|
||||
Namespace: netEgress.Spec.NetworkRouterRef.Namespace,
|
||||
},
|
||||
}
|
||||
err = r.Get(ctx, client.ObjectKeyFromObject(netRouter), netRouter)
|
||||
if err != nil {
|
||||
if kerrors.IsNotFound(err) {
|
||||
conditions.MarkFalse(netEgress, nbv1alpha1.ReadyCondition, nbv1alpha1.DependencyReason, "Referenced NetworkRouter cannot be found.")
|
||||
err = sp.Patch(ctx, netEgress)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Create service for egress resource to do port remapping.
|
||||
ports := []*corev1ac.ServicePortApplyConfiguration{}
|
||||
for _, port := range netEgress.Spec.Ports {
|
||||
ports = append(ports, corev1ac.ServicePort().WithName(port.Name).WithPort(port.Port))
|
||||
}
|
||||
routerSvcAC := corev1ac.Service(netEgress.Name, netEgress.Namespace).
|
||||
WithLabels(map[string]string{EgressRouterNameLabel: netRouter.Name, EgressRouterNamespaceLabel: netRouter.Namespace}).
|
||||
WithOwnerReferences(ownerRef).
|
||||
WithSpec(
|
||||
corev1ac.ServiceSpec().
|
||||
WithPorts(ports...),
|
||||
)
|
||||
err = r.Client.Apply(ctx, routerSvcAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
conditions.MarkTrue(netEgress, nbv1alpha1.ReadyCondition, nbv1alpha1.ReconciledReason, "")
|
||||
err = sp.Patch(ctx, netEgress, patch.WithStatusObservedGeneration{})
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
return ctrl.Result{}, nil
|
||||
}
|
||||
|
||||
// SetupWithManager sets up the controller with the Manager.
|
||||
func (r *NetworkEgressReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
err := mgr.GetFieldIndexer().IndexField(context.Background(), &nbv1alpha1.NetworkEgress{}, ".spec.networkRouterRef", func(obj client.Object) []string {
|
||||
netEgress := obj.(*nbv1alpha1.NetworkEgress)
|
||||
ref := netEgress.Spec.NetworkRouterRef
|
||||
if ref.Name == "" {
|
||||
return nil
|
||||
}
|
||||
if ref.Namespace == "" {
|
||||
ref.Namespace = netEgress.Namespace
|
||||
}
|
||||
return []string{fmt.Sprintf("%s/%s", ref.Name, ref.Namespace)}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return ctrl.NewControllerManagedBy(mgr).
|
||||
For(&nbv1alpha1.NetworkEgress{}).
|
||||
Owns(&corev1.Service{}).
|
||||
Watches(
|
||||
&nbv1alpha1.NetworkRouter{},
|
||||
handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
||||
netEgressList := &nbv1alpha1.NetworkEgressList{}
|
||||
err := r.List(ctx, netEgressList, client.MatchingFields{".spec.networkRouterRef": fmt.Sprintf("%s/%s", obj.GetName(), obj.GetNamespace())})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
requests := make([]reconcile.Request, len(netEgressList.Items))
|
||||
for i, item := range netEgressList.Items {
|
||||
requests[i] = reconcile.Request{
|
||||
NamespacedName: types.NamespacedName{
|
||||
Name: item.Name,
|
||||
Namespace: item.Namespace,
|
||||
},
|
||||
}
|
||||
}
|
||||
return requests
|
||||
}),
|
||||
builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}),
|
||||
).
|
||||
Complete(r)
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
package controller
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
||||
|
||||
nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
var _ = Describe("NetworkEgress Controller", func() {
|
||||
Context("When reconciling a resource", func() {
|
||||
ctx := context.Background()
|
||||
|
||||
var netEgressRec *NetworkEgressReconciler
|
||||
var forwarderRec *ForwarderServiceReconciler
|
||||
|
||||
nn := client.ObjectKey{
|
||||
Name: "test-resource",
|
||||
Namespace: "network-egress",
|
||||
}
|
||||
BeforeEach(func() {
|
||||
netEgressRec = &NetworkEgressReconciler{
|
||||
Client: k8sClient,
|
||||
}
|
||||
forwarderRec = &ForwarderServiceReconciler{
|
||||
Client: k8sClient,
|
||||
}
|
||||
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nn.Namespace,
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, ns)).To(Succeed())
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
ns := &corev1.Namespace{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nn.Namespace,
|
||||
},
|
||||
}
|
||||
err := k8sClient.Get(ctx, client.ObjectKeyFromObject(ns), ns)
|
||||
if kerrors.IsNotFound(err) {
|
||||
return
|
||||
}
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(k8sClient.Delete(ctx, ns)).To(Succeed())
|
||||
})
|
||||
|
||||
It("creates a egress service with endpoint slices", func() {
|
||||
netRouter := &nbv1alpha1.NetworkRouter{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "egress-router",
|
||||
Namespace: nn.Namespace,
|
||||
},
|
||||
Spec: nbv1alpha1.NetworkRouterSpec{
|
||||
DNSZoneRef: nbv1alpha1.DNSZoneReference{
|
||||
Name: "foo.bar",
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, netRouter)).To(Succeed())
|
||||
|
||||
netEgress := &nbv1alpha1.NetworkEgress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nn.Name,
|
||||
Namespace: nn.Namespace,
|
||||
},
|
||||
Spec: nbv1alpha1.NetworkEgressSpec{
|
||||
NetworkRouterRef: nbv1alpha1.CrossNamespaceReference{
|
||||
Name: netRouter.Name,
|
||||
Namespace: nn.Namespace,
|
||||
},
|
||||
Target: nbv1alpha1.NetworkEgressTarget{
|
||||
FQDN: &nbv1alpha1.NetworkEgressFQDNTarget{
|
||||
Hostname: "example.com",
|
||||
},
|
||||
},
|
||||
Ports: []nbv1alpha1.NetworkEgressPort{
|
||||
{
|
||||
Name: "http",
|
||||
Port: 80,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
Expect(k8sClient.Create(ctx, netEgress)).To(Succeed())
|
||||
_, err := netEgressRec.Reconcile(ctx, reconcile.Request{NamespacedName: nn})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
egressSvc := &corev1.Service{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: nn.Name,
|
||||
Namespace: nn.Namespace,
|
||||
},
|
||||
}
|
||||
err = k8sClient.Get(ctx, client.ObjectKeyFromObject(egressSvc), egressSvc)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(egressSvc.Labels[EgressRouterNameLabel]).To(Equal(netRouter.Name))
|
||||
Expect(egressSvc.Labels[EgressRouterNamespaceLabel]).To(Equal(netRouter.Namespace))
|
||||
Expect(egressSvc.Spec.Ports).To(HaveLen(1))
|
||||
Expect(egressSvc.Spec.Ports[0].TargetPort.String()).To(Equal("80"))
|
||||
|
||||
_, err = forwarderRec.Reconcile(ctx, reconcile.Request{NamespacedName: client.ObjectKey{Name: fmt.Sprintf("networkrouter-%s-forwarder", netRouter.Name), Namespace: nn.Namespace}})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
appsv1 "k8s.io/api/apps/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
policyv1 "k8s.io/api/policy/v1"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
kerrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -24,6 +25,7 @@ import (
|
||||
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
|
||||
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
|
||||
policyv1ac "k8s.io/client-go/applyconfigurations/policy/v1"
|
||||
rbacv1ac "k8s.io/client-go/applyconfigurations/rbac/v1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
@@ -35,6 +37,7 @@ import (
|
||||
"github.com/netbirdio/kubernetes-operator/internal/k8sutil"
|
||||
"github.com/netbirdio/kubernetes-operator/internal/netbirdutil"
|
||||
nbv1alpha1ac "github.com/netbirdio/kubernetes-operator/pkg/applyconfigurations/api/v1alpha1"
|
||||
"github.com/netbirdio/kubernetes-operator/pkg/version"
|
||||
)
|
||||
|
||||
type NetworkRouterReconciler struct {
|
||||
@@ -191,7 +194,7 @@ func (r *NetworkRouterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Create the deployment.
|
||||
// Setup router configuration.
|
||||
selectorLabels := map[string]string{
|
||||
"app.kubernetes.io/name": "networkrouter",
|
||||
"app.kubernetes.io/instance": req.Name,
|
||||
@@ -207,6 +210,44 @@ func (r *NetworkRouterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
clientImage = netRouter.Spec.Image
|
||||
}
|
||||
|
||||
// Create forwarder resources.
|
||||
forwarderName := fmt.Sprintf("networkrouter-%s-forwarder", req.Name)
|
||||
forwarderSvcAC := corev1ac.Service(forwarderName, req.Namespace).
|
||||
WithLabels(map[string]string{ForwarderRouterNameLabel: netRouter.Name}).
|
||||
WithOwnerReferences(ownerRef).
|
||||
WithSpec(
|
||||
corev1ac.ServiceSpec().
|
||||
WithClusterIP("None").
|
||||
WithSelector(selectorLabels),
|
||||
)
|
||||
err = r.Client.Apply(ctx, forwarderSvcAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
saAC := corev1ac.ServiceAccount(forwarderName, req.Namespace).
|
||||
WithOwnerReferences(ownerRef)
|
||||
err = r.Client.Apply(ctx, saAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
roleAC := rbacv1ac.Role(forwarderName, req.Namespace).
|
||||
WithOwnerReferences(ownerRef).
|
||||
WithRules(rbacv1ac.PolicyRule().WithAPIGroups("").WithResources("configmaps").WithVerbs("get", "watch"))
|
||||
err = r.Client.Apply(ctx, roleAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
roleBindAC := rbacv1ac.RoleBinding(forwarderName, req.Namespace).
|
||||
WithOwnerReferences(ownerRef).
|
||||
WithSubjects(rbacv1ac.Subject().WithKind(*saAC.Kind).WithName(*saAC.Name)).
|
||||
WithRoleRef(rbacv1ac.RoleRef().WithKind(*roleAC.Kind).WithName(*roleAC.Name))
|
||||
err = r.Client.Apply(ctx, roleBindAC, client.ForceOwnership)
|
||||
if err != nil {
|
||||
return ctrl.Result{}, err
|
||||
}
|
||||
|
||||
// Create the deployment.
|
||||
podTemplateSpecAC := corev1ac.PodTemplateSpec().
|
||||
WithLabels(selectorLabels).
|
||||
WithSpec(corev1ac.PodSpec().
|
||||
@@ -232,65 +273,84 @@ func (r *NetworkRouterReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
WithReadOnlyRootFilesystem(true),
|
||||
),
|
||||
).
|
||||
WithContainers(corev1ac.Container().
|
||||
WithName("netbird").
|
||||
WithImage(clientImage).
|
||||
WithEnv(
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_SETUP_KEY").
|
||||
WithValueFrom(corev1ac.EnvVarSource().
|
||||
WithSecretKeyRef(corev1ac.SecretKeySelector().
|
||||
WithName(setupKey.SecretName()).
|
||||
WithKey(SetupKeySecretKey),
|
||||
WithContainers(
|
||||
corev1ac.Container().
|
||||
WithName("netbird").
|
||||
WithImage(clientImage).
|
||||
WithEnv(
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_SETUP_KEY").
|
||||
WithValueFrom(corev1ac.EnvVarSource().
|
||||
WithSecretKeyRef(corev1ac.SecretKeySelector().
|
||||
WithName(setupKey.SecretName()).
|
||||
WithKey(SetupKeySecretKey),
|
||||
),
|
||||
),
|
||||
),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_MANAGEMENT_URL").
|
||||
WithValue(r.ManagementURL),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_LOG_LEVEL").
|
||||
WithValue(logLevel),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_LOG_FILE").
|
||||
WithValue("console"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_DISABLE_PROFILES").
|
||||
WithValue("true"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_DISABLE_UPDATE_SETTINGS").
|
||||
WithValue("true"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_DAEMON_ADDR").
|
||||
WithValue("unix:///var/run/netbird/netbird.sock"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_ENTRYPOINT_SERVICE_TIMEOUT").
|
||||
WithValue("0"),
|
||||
).
|
||||
WithStartupProbe(corev1ac.Probe().WithExec(corev1ac.ExecAction().WithCommand("netbird", "status", "--check", "startup"))).
|
||||
WithReadinessProbe(corev1ac.Probe().WithExec(corev1ac.ExecAction().WithCommand("netbird", "status", "--check", "ready"))).
|
||||
WithVolumeMounts(
|
||||
corev1ac.VolumeMount().WithName("netbird-run").WithMountPath("/var/run/netbird"),
|
||||
corev1ac.VolumeMount().WithName("netbird-lib").WithMountPath("/var/lib/netbird"),
|
||||
corev1ac.VolumeMount().WithName("ssh-etc").WithMountPath("/etc/ssh"),
|
||||
corev1ac.VolumeMount().WithName("resolv-conf").WithMountPath("/etc/resolv.conf").WithSubPath("resolv.conf"),
|
||||
corev1ac.VolumeMount().WithName("resolv-conf").WithMountPath("/etc/resolv.conf.original.netbird").WithSubPath("resolv.conf.original.netbird"),
|
||||
).
|
||||
WithSecurityContext(corev1ac.SecurityContext().
|
||||
WithReadOnlyRootFilesystem(true).
|
||||
WithCapabilities(corev1ac.Capabilities().
|
||||
WithAdd("NET_ADMIN").
|
||||
WithAdd("SYS_RESOURCE").
|
||||
WithAdd("SYS_ADMIN"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_MANAGEMENT_URL").
|
||||
WithValue(r.ManagementURL),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_LOG_LEVEL").
|
||||
WithValue(logLevel),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_LOG_FILE").
|
||||
WithValue("console"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_DISABLE_PROFILES").
|
||||
WithValue("true"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_DISABLE_UPDATE_SETTINGS").
|
||||
WithValue("true"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_DAEMON_ADDR").
|
||||
WithValue("unix:///var/run/netbird/netbird.sock"),
|
||||
corev1ac.EnvVar().
|
||||
WithName("NB_ENTRYPOINT_SERVICE_TIMEOUT").
|
||||
WithValue("0"),
|
||||
).
|
||||
WithPrivileged(true),
|
||||
).
|
||||
WithResources(corev1ac.ResourceRequirements().
|
||||
WithRequests(corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
||||
}),
|
||||
),
|
||||
WithStartupProbe(corev1ac.Probe().WithExec(corev1ac.ExecAction().WithCommand("netbird", "status", "--check", "startup"))).
|
||||
WithReadinessProbe(corev1ac.Probe().WithExec(corev1ac.ExecAction().WithCommand("netbird", "status", "--check", "ready"))).
|
||||
WithVolumeMounts(
|
||||
corev1ac.VolumeMount().WithName("netbird-run").WithMountPath("/var/run/netbird"),
|
||||
corev1ac.VolumeMount().WithName("netbird-lib").WithMountPath("/var/lib/netbird"),
|
||||
corev1ac.VolumeMount().WithName("ssh-etc").WithMountPath("/etc/ssh"),
|
||||
corev1ac.VolumeMount().WithName("resolv-conf").WithMountPath("/etc/resolv.conf").WithSubPath("resolv.conf"),
|
||||
corev1ac.VolumeMount().WithName("resolv-conf").WithMountPath("/etc/resolv.conf.original.netbird").WithSubPath("resolv.conf.original.netbird"),
|
||||
).
|
||||
WithSecurityContext(corev1ac.SecurityContext().
|
||||
WithReadOnlyRootFilesystem(true).
|
||||
WithCapabilities(corev1ac.Capabilities().
|
||||
WithAdd("NET_ADMIN").
|
||||
WithAdd("SYS_RESOURCE").
|
||||
WithAdd("SYS_ADMIN"),
|
||||
).
|
||||
WithPrivileged(true),
|
||||
).
|
||||
WithResources(corev1ac.ResourceRequirements().
|
||||
WithRequests(corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
||||
}),
|
||||
),
|
||||
corev1ac.Container().
|
||||
WithName("kube-egress-forwarder").
|
||||
WithImage(version.KubeEgressForwarderImage).
|
||||
WithArgs("--configmap-name", forwarderName, "--configmap-namespace", req.Namespace).
|
||||
WithVolumeMounts(
|
||||
corev1ac.VolumeMount().WithName("resolv-conf").WithMountPath("/etc/resolv.conf").WithSubPath("resolv.conf"),
|
||||
).
|
||||
WithSecurityContext(corev1ac.SecurityContext().
|
||||
WithCapabilities(corev1ac.Capabilities().WithDrop("ALL")).
|
||||
WithReadOnlyRootFilesystem(true),
|
||||
).
|
||||
WithResources(corev1ac.ResourceRequirements().
|
||||
WithRequests(corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("100m"),
|
||||
corev1.ResourceMemory: resource.MustParse("128Mi"),
|
||||
}),
|
||||
),
|
||||
).
|
||||
WithServiceAccountName(*saAC.Name).
|
||||
WithVolumes(
|
||||
corev1ac.Volume().WithName("netbird-run").WithEmptyDir(corev1ac.EmptyDirVolumeSource()),
|
||||
corev1ac.Volume().WithName("netbird-lib").WithEmptyDir(corev1ac.EmptyDirVolumeSource()),
|
||||
@@ -421,5 +481,9 @@ func (r *NetworkRouterReconciler) SetupWithManager(mgr ctrl.Manager) error {
|
||||
Owns(&nbv1alpha1.Group{}).
|
||||
Owns(&nbv1alpha1.SetupKey{}).
|
||||
Owns(&appsv1.Deployment{}).
|
||||
Owns(&corev1.Service{}).
|
||||
Owns(&corev1.ServiceAccount{}).
|
||||
Owns(&rbacv1.Role{}).
|
||||
Owns(&rbacv1.RoleBinding{}).
|
||||
Complete(r)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// FQDNTargetApplyConfiguration represents a declarative configuration of the FQDNTarget type for use
|
||||
// with apply.
|
||||
//
|
||||
// FQDNTarget matches traffic by an exact domain name (no wildcards).
|
||||
type FQDNTargetApplyConfiguration struct {
|
||||
// Hostname is a fully qualified domain name to match exactly.
|
||||
Hostname *string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
// FQDNTargetApplyConfiguration constructs a declarative configuration of the FQDNTarget type for use with
|
||||
// apply.
|
||||
func FQDNTarget() *FQDNTargetApplyConfiguration {
|
||||
return &FQDNTargetApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithHostname sets the Hostname field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Hostname field is set to the value of the last call.
|
||||
func (b *FQDNTargetApplyConfiguration) WithHostname(value string) *FQDNTargetApplyConfiguration {
|
||||
b.Hostname = &value
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// IPAddressTargetApplyConfiguration represents a declarative configuration of the IPAddressTarget type for use
|
||||
// with apply.
|
||||
//
|
||||
// IPAddressTarget is a single IPv4 or IPv6 address.
|
||||
type IPAddressTargetApplyConfiguration struct {
|
||||
// Address is a single IP address, e.g. "10.0.0.5" or "2001:db8::1".
|
||||
// CIDR notation (e.g. "10.0.0.0/24") is rejected.
|
||||
Address *string `json:"address,omitempty"`
|
||||
}
|
||||
|
||||
// IPAddressTargetApplyConfiguration constructs a declarative configuration of the IPAddressTarget type for use with
|
||||
// apply.
|
||||
func IPAddressTarget() *IPAddressTargetApplyConfiguration {
|
||||
return &IPAddressTargetApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithAddress sets the Address field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Address field is set to the value of the last call.
|
||||
func (b *IPAddressTargetApplyConfiguration) WithAddress(value string) *IPAddressTargetApplyConfiguration {
|
||||
b.Address = &value
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// IPTargetApplyConfiguration represents a declarative configuration of the IPTarget type for use
|
||||
// with apply.
|
||||
//
|
||||
// IPTarget is a single IPv4 or IPv6 address.
|
||||
type IPTargetApplyConfiguration struct {
|
||||
// Address is a single IP address.
|
||||
Address *string `json:"address,omitempty"`
|
||||
}
|
||||
|
||||
// IPTargetApplyConfiguration constructs a declarative configuration of the IPTarget type for use with
|
||||
// apply.
|
||||
func IPTarget() *IPTargetApplyConfiguration {
|
||||
return &IPTargetApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithAddress sets the Address field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Address field is set to the value of the last call.
|
||||
func (b *IPTargetApplyConfiguration) WithAddress(value string) *IPTargetApplyConfiguration {
|
||||
b.Address = &value
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,231 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
types "k8s.io/apimachinery/pkg/types"
|
||||
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
|
||||
)
|
||||
|
||||
// NetworkEgressApplyConfiguration represents a declarative configuration of the NetworkEgress type for use
|
||||
// with apply.
|
||||
//
|
||||
// NetworkEgress is the Schema for the networkegresses API.
|
||||
type NetworkEgressApplyConfiguration struct {
|
||||
v1.TypeMetaApplyConfiguration `json:",inline"`
|
||||
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
|
||||
Spec *NetworkEgressSpecApplyConfiguration `json:"spec,omitempty"`
|
||||
Status *NetworkEgressStatusApplyConfiguration `json:"status,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgress constructs a declarative configuration of the NetworkEgress type for use with
|
||||
// apply.
|
||||
func NetworkEgress(name, namespace string) *NetworkEgressApplyConfiguration {
|
||||
b := &NetworkEgressApplyConfiguration{}
|
||||
b.WithName(name)
|
||||
b.WithNamespace(namespace)
|
||||
b.WithKind("NetworkEgress")
|
||||
b.WithAPIVersion("netbird.io/v1alpha1")
|
||||
return b
|
||||
}
|
||||
|
||||
func (b NetworkEgressApplyConfiguration) IsApplyConfiguration() {}
|
||||
|
||||
// WithKind sets the Kind field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Kind field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithKind(value string) *NetworkEgressApplyConfiguration {
|
||||
b.TypeMetaApplyConfiguration.Kind = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAPIVersion sets the APIVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the APIVersion field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithAPIVersion(value string) *NetworkEgressApplyConfiguration {
|
||||
b.TypeMetaApplyConfiguration.APIVersion = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithName sets the Name field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Name field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithName(value string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.Name = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithGenerateName sets the GenerateName field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the GenerateName field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithGenerateName(value string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.GenerateName = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithNamespace sets the Namespace field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Namespace field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithNamespace(value string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.Namespace = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithUID sets the UID field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the UID field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithUID(value types.UID) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.UID = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the ResourceVersion field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithResourceVersion(value string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.ResourceVersion = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithGeneration sets the Generation field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Generation field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithGeneration(value int64) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.Generation = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the CreationTimestamp field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithCreationTimestamp(value metav1.Time) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.CreationTimestamp = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the DeletionTimestamp field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithLabels puts the entries into the Labels field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, the entries provided by each call will be put on the Labels field,
|
||||
// overwriting an existing map entries in Labels field with the same key.
|
||||
func (b *NetworkEgressApplyConfiguration) WithLabels(entries map[string]string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 {
|
||||
b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries))
|
||||
}
|
||||
for k, v := range entries {
|
||||
b.ObjectMetaApplyConfiguration.Labels[k] = v
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithAnnotations puts the entries into the Annotations field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, the entries provided by each call will be put on the Annotations field,
|
||||
// overwriting an existing map entries in Annotations field with the same key.
|
||||
func (b *NetworkEgressApplyConfiguration) WithAnnotations(entries map[string]string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 {
|
||||
b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries))
|
||||
}
|
||||
for k, v := range entries {
|
||||
b.ObjectMetaApplyConfiguration.Annotations[k] = v
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the OwnerReferences field.
|
||||
func (b *NetworkEgressApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithOwnerReferences")
|
||||
}
|
||||
b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
// WithFinalizers adds the given value to the Finalizers field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Finalizers field.
|
||||
func (b *NetworkEgressApplyConfiguration) WithFinalizers(values ...string) *NetworkEgressApplyConfiguration {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
for i := range values {
|
||||
b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *NetworkEgressApplyConfiguration) ensureObjectMetaApplyConfigurationExists() {
|
||||
if b.ObjectMetaApplyConfiguration == nil {
|
||||
b.ObjectMetaApplyConfiguration = &v1.ObjectMetaApplyConfiguration{}
|
||||
}
|
||||
}
|
||||
|
||||
// WithSpec sets the Spec field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Spec field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithSpec(value *NetworkEgressSpecApplyConfiguration) *NetworkEgressApplyConfiguration {
|
||||
b.Spec = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithStatus sets the Status field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Status field is set to the value of the last call.
|
||||
func (b *NetworkEgressApplyConfiguration) WithStatus(value *NetworkEgressStatusApplyConfiguration) *NetworkEgressApplyConfiguration {
|
||||
b.Status = value
|
||||
return b
|
||||
}
|
||||
|
||||
// GetKind retrieves the value of the Kind field in the declarative configuration.
|
||||
func (b *NetworkEgressApplyConfiguration) GetKind() *string {
|
||||
return b.TypeMetaApplyConfiguration.Kind
|
||||
}
|
||||
|
||||
// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration.
|
||||
func (b *NetworkEgressApplyConfiguration) GetAPIVersion() *string {
|
||||
return b.TypeMetaApplyConfiguration.APIVersion
|
||||
}
|
||||
|
||||
// GetName retrieves the value of the Name field in the declarative configuration.
|
||||
func (b *NetworkEgressApplyConfiguration) GetName() *string {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
return b.ObjectMetaApplyConfiguration.Name
|
||||
}
|
||||
|
||||
// GetNamespace retrieves the value of the Namespace field in the declarative configuration.
|
||||
func (b *NetworkEgressApplyConfiguration) GetNamespace() *string {
|
||||
b.ensureObjectMetaApplyConfigurationExists()
|
||||
return b.ObjectMetaApplyConfiguration.Namespace
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// NetworkEgressFQDNTargetApplyConfiguration represents a declarative configuration of the NetworkEgressFQDNTarget type for use
|
||||
// with apply.
|
||||
//
|
||||
// NetworkEgressFQDNTarget matches traffic by an exact domain name (no wildcards).
|
||||
type NetworkEgressFQDNTargetApplyConfiguration struct {
|
||||
// Hostname is a fully qualified domain name to match exactly.
|
||||
Hostname *string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressFQDNTargetApplyConfiguration constructs a declarative configuration of the NetworkEgressFQDNTarget type for use with
|
||||
// apply.
|
||||
func NetworkEgressFQDNTarget() *NetworkEgressFQDNTargetApplyConfiguration {
|
||||
return &NetworkEgressFQDNTargetApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithHostname sets the Hostname field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Hostname field is set to the value of the last call.
|
||||
func (b *NetworkEgressFQDNTargetApplyConfiguration) WithHostname(value string) *NetworkEgressFQDNTargetApplyConfiguration {
|
||||
b.Hostname = &value
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// NetworkEgressIPTargetApplyConfiguration represents a declarative configuration of the NetworkEgressIPTarget type for use
|
||||
// with apply.
|
||||
//
|
||||
// NetworkEgressIPTarget is a single IPv4 or IPv6 address.
|
||||
type NetworkEgressIPTargetApplyConfiguration struct {
|
||||
// Address is a single IP address.
|
||||
Address *string `json:"address,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressIPTargetApplyConfiguration constructs a declarative configuration of the NetworkEgressIPTarget type for use with
|
||||
// apply.
|
||||
func NetworkEgressIPTarget() *NetworkEgressIPTargetApplyConfiguration {
|
||||
return &NetworkEgressIPTargetApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithAddress sets the Address field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Address field is set to the value of the last call.
|
||||
func (b *NetworkEgressIPTargetApplyConfiguration) WithAddress(value string) *NetworkEgressIPTargetApplyConfiguration {
|
||||
b.Address = &value
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// NetworkEgressPortApplyConfiguration represents a declarative configuration of the NetworkEgressPort type for use
|
||||
// with apply.
|
||||
type NetworkEgressPortApplyConfiguration struct {
|
||||
// Name of the port.
|
||||
Name *string `json:"name,omitempty"`
|
||||
// The port that will be exposed by this service.
|
||||
Port *int32 `json:"port,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressPortApplyConfiguration constructs a declarative configuration of the NetworkEgressPort type for use with
|
||||
// apply.
|
||||
func NetworkEgressPort() *NetworkEgressPortApplyConfiguration {
|
||||
return &NetworkEgressPortApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithName sets the Name field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Name field is set to the value of the last call.
|
||||
func (b *NetworkEgressPortApplyConfiguration) WithName(value string) *NetworkEgressPortApplyConfiguration {
|
||||
b.Name = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPort sets the Port field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Port field is set to the value of the last call.
|
||||
func (b *NetworkEgressPortApplyConfiguration) WithPort(value int32) *NetworkEgressPortApplyConfiguration {
|
||||
b.Port = &value
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// NetworkEgressSpecApplyConfiguration represents a declarative configuration of the NetworkEgressSpec type for use
|
||||
// with apply.
|
||||
//
|
||||
// NetworkEgressSpec defines the desired state of NetworkEgress.
|
||||
type NetworkEgressSpecApplyConfiguration struct {
|
||||
// NetworkRouterRef is a reference to the network and router where the resource will be created.
|
||||
NetworkRouterRef *CrossNamespaceReferenceApplyConfiguration `json:"networkRouterRef,omitempty"`
|
||||
// Target for egress traffic.
|
||||
Target *NetworkEgressTargetApplyConfiguration `json:"target,omitempty"`
|
||||
// Ports to the resource to route.
|
||||
Ports []NetworkEgressPortApplyConfiguration `json:"ports,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressSpecApplyConfiguration constructs a declarative configuration of the NetworkEgressSpec type for use with
|
||||
// apply.
|
||||
func NetworkEgressSpec() *NetworkEgressSpecApplyConfiguration {
|
||||
return &NetworkEgressSpecApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithNetworkRouterRef sets the NetworkRouterRef field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the NetworkRouterRef field is set to the value of the last call.
|
||||
func (b *NetworkEgressSpecApplyConfiguration) WithNetworkRouterRef(value *CrossNamespaceReferenceApplyConfiguration) *NetworkEgressSpecApplyConfiguration {
|
||||
b.NetworkRouterRef = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithTarget sets the Target field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the Target field is set to the value of the last call.
|
||||
func (b *NetworkEgressSpecApplyConfiguration) WithTarget(value *NetworkEgressTargetApplyConfiguration) *NetworkEgressSpecApplyConfiguration {
|
||||
b.Target = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithPorts adds the given value to the Ports field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Ports field.
|
||||
func (b *NetworkEgressSpecApplyConfiguration) WithPorts(values ...*NetworkEgressPortApplyConfiguration) *NetworkEgressSpecApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithPorts")
|
||||
}
|
||||
b.Ports = append(b.Ports, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
|
||||
)
|
||||
|
||||
// NetworkEgressStatusApplyConfiguration represents a declarative configuration of the NetworkEgressStatus type for use
|
||||
// with apply.
|
||||
//
|
||||
// NetworkEgressStatus defines the observed state of NetworkEgress.
|
||||
type NetworkEgressStatusApplyConfiguration struct {
|
||||
// ObservedGeneration is the last reconciled generation.
|
||||
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
|
||||
// Conditions holds the conditions for the NetworkEgress.
|
||||
Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressStatusApplyConfiguration constructs a declarative configuration of the NetworkEgressStatus type for use with
|
||||
// apply.
|
||||
func NetworkEgressStatus() *NetworkEgressStatusApplyConfiguration {
|
||||
return &NetworkEgressStatusApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithObservedGeneration sets the ObservedGeneration field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the ObservedGeneration field is set to the value of the last call.
|
||||
func (b *NetworkEgressStatusApplyConfiguration) WithObservedGeneration(value int64) *NetworkEgressStatusApplyConfiguration {
|
||||
b.ObservedGeneration = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithConditions adds the given value to the Conditions field in the declarative configuration
|
||||
// and returns the receiver, so that objects can be build by chaining "With" function invocations.
|
||||
// If called multiple times, values provided by each call will be appended to the Conditions field.
|
||||
func (b *NetworkEgressStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *NetworkEgressStatusApplyConfiguration {
|
||||
for i := range values {
|
||||
if values[i] == nil {
|
||||
panic("nil value passed to WithConditions")
|
||||
}
|
||||
b.Conditions = append(b.Conditions, *values[i])
|
||||
}
|
||||
return b
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
// Code generated by controller-gen. DO NOT EDIT.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
// NetworkEgressTargetApplyConfiguration represents a declarative configuration of the NetworkEgressTarget type for use
|
||||
// with apply.
|
||||
//
|
||||
// NetworkEgressTarget describes a single allowed egress destination.
|
||||
// Exactly one of IP or FQDN must be set.
|
||||
type NetworkEgressTargetApplyConfiguration struct {
|
||||
// IP targets a single specific IP address (not a CIDR range).
|
||||
IP *NetworkEgressIPTargetApplyConfiguration `json:"ip,omitempty"`
|
||||
// FQDN targets an exact domain name (no wildcards).
|
||||
FQDN *NetworkEgressFQDNTargetApplyConfiguration `json:"fqdn,omitempty"`
|
||||
}
|
||||
|
||||
// NetworkEgressTargetApplyConfiguration constructs a declarative configuration of the NetworkEgressTarget type for use with
|
||||
// apply.
|
||||
func NetworkEgressTarget() *NetworkEgressTargetApplyConfiguration {
|
||||
return &NetworkEgressTargetApplyConfiguration{}
|
||||
}
|
||||
|
||||
// WithIP sets the IP field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the IP field is set to the value of the last call.
|
||||
func (b *NetworkEgressTargetApplyConfiguration) WithIP(value *NetworkEgressIPTargetApplyConfiguration) *NetworkEgressTargetApplyConfiguration {
|
||||
b.IP = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithFQDN sets the FQDN field in the declarative configuration to the given value
|
||||
// and returns the receiver, so that objects can be built by chaining "With" function invocations.
|
||||
// If called multiple times, the FQDN field is set to the value of the last call.
|
||||
func (b *NetworkEgressTargetApplyConfiguration) WithFQDN(value *NetworkEgressFQDNTargetApplyConfiguration) *NetworkEgressTargetApplyConfiguration {
|
||||
b.FQDN = value
|
||||
return b
|
||||
}
|
||||
@@ -38,6 +38,20 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
|
||||
return &apiv1alpha1.GroupSpecApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("GroupStatus"):
|
||||
return &apiv1alpha1.GroupStatusApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgress"):
|
||||
return &apiv1alpha1.NetworkEgressApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgressFQDNTarget"):
|
||||
return &apiv1alpha1.NetworkEgressFQDNTargetApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgressIPTarget"):
|
||||
return &apiv1alpha1.NetworkEgressIPTargetApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgressPort"):
|
||||
return &apiv1alpha1.NetworkEgressPortApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgressSpec"):
|
||||
return &apiv1alpha1.NetworkEgressSpecApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgressStatus"):
|
||||
return &apiv1alpha1.NetworkEgressStatusApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkEgressTarget"):
|
||||
return &apiv1alpha1.NetworkEgressTargetApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkResource"):
|
||||
return &apiv1alpha1.NetworkResourceApplyConfiguration{}
|
||||
case v1alpha1.SchemeGroupVersion.WithKind("NetworkResourceSpec"):
|
||||
|
||||
@@ -7,8 +7,9 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
NetbirdClientImage = "ghcr.io/netbirdio/netbird:0.72.4@sha256:6c6c20baffae4a3ec50f29ec9361608a420625185505e8cd6f0c44d71c5d4798"
|
||||
KubeApiProxyImage = "ghcr.io/netbirdio/netbird-kubeapi-proxy:v0.0.4@sha256:bffa4f093abc19b4934ae37657bac76fa3b390cbd39aadac987634215eb750f5"
|
||||
NetbirdClientImage = "ghcr.io/netbirdio/netbird:0.72.4@sha256:6c6c20baffae4a3ec50f29ec9361608a420625185505e8cd6f0c44d71c5d4798"
|
||||
KubeApiProxyImage = "ghcr.io/netbirdio/netbird-kubeapi-proxy:v0.0.4@sha256:bffa4f093abc19b4934ae37657bac76fa3b390cbd39aadac987634215eb750f5"
|
||||
KubeEgressForwarderImage = "ghcr.io/netbirdio/kube-egress-forwarder:v0.0.2@sha256:f3b4637122cbda3c1915d49e6f96edff7e3a3accfceb87b821c829056abe8a6f"
|
||||
)
|
||||
|
||||
func BuildVersion() string {
|
||||
|
||||
+14
-4
@@ -25,7 +25,7 @@ import (
|
||||
netbird "github.com/netbirdio/netbird/shared/management/client/rest"
|
||||
"github.com/netbirdio/netbird/shared/management/http/api"
|
||||
"helm.sh/helm/v4/pkg/action"
|
||||
"helm.sh/helm/v4/pkg/chart/loader"
|
||||
"helm.sh/helm/v4/pkg/chart/v2/loader"
|
||||
"helm.sh/helm/v4/pkg/downloader"
|
||||
"helm.sh/helm/v4/pkg/getter"
|
||||
"helm.sh/helm/v4/pkg/kube"
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
kruntime "k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
kruntimeutil "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/yaml"
|
||||
"k8s.io/cli-runtime/pkg/genericclioptions"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
@@ -290,8 +291,8 @@ func TestE2E(t *testing.T) {
|
||||
}
|
||||
err = k8sClient.Create(t.Context(), secret)
|
||||
require.NoError(t, err)
|
||||
installOperator(t, kcPath, false, managementURL)
|
||||
installOperator(t, kcPath, true, managementURL)
|
||||
installOperator(t, k8sClient, kcPath, false, managementURL)
|
||||
installOperator(t, k8sClient, kcPath, true, managementURL)
|
||||
|
||||
t.Run("cluster proxy", testClusterProxy(k8sClient, nbClient))
|
||||
})
|
||||
@@ -393,7 +394,7 @@ func testClusterProxy(k8sClient client.Client, nbClient *netbird.Client) func(*t
|
||||
}
|
||||
}
|
||||
|
||||
func installOperator(t *testing.T, kcPath string, dev bool, managementURL string) {
|
||||
func installOperator(t *testing.T, k8sClient client.Client, kcPath string, dev bool, managementURL string) {
|
||||
t.Helper()
|
||||
|
||||
regClient, err := registry.NewClient()
|
||||
@@ -455,6 +456,15 @@ func installOperator(t *testing.T, kcPath string, dev bool, managementURL string
|
||||
_, err = install.RunWithContext(t.Context(), charter, vals)
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
for _, crd := range charter.CRDObjects() {
|
||||
docs := yaml.NewYAMLOrJSONDecoder(bytes.NewReader(crd.File.Data), 4096)
|
||||
var obj unstructured.Unstructured
|
||||
err := docs.Decode(&obj)
|
||||
require.NoError(t, err)
|
||||
err = k8sClient.Patch(t.Context(), &obj, client.Apply, client.ForceOwnership, client.FieldOwner("helm"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
upgrade := action.NewUpgrade(actionCfg)
|
||||
upgrade.Namespace = netbirdNamespace
|
||||
upgrade.WaitStrategy = kube.StatusWatcherStrategy
|
||||
|
||||
Reference in New Issue
Block a user