Add new resource for Kubernetes API proxy (#279)

This adds a new resource which deploys a Kubernetes API server proxy
that can be used to access the API server without tokens through
Netbird.

Part of #274 

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added ClusterProxy custom resource for cluster API proxying
capabilities

* **Documentation**
  * Added ClusterProxy API reference documentation with schema details

* **Examples**
* Added example ClusterProxy configuration and RBAC setup for cluster
proxy targets

<!-- review_stack_entry_start -->

[![Review Change
Stack](https://storage.googleapis.com/coderabbit_public_assets/review-stack-in-coderabbit-ui.svg)](https://app.coderabbit.ai/change-stack/netbirdio/kubernetes-operator/pull/279?utm_source=github_walkthrough&utm_medium=github&utm_campaign=change_stack)

<!-- review_stack_entry_end -->
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
Philip Laine
2026-06-01 11:41:48 +02:00
committed by GitHub
parent a3546e4804
commit 5dd73dcc80
19 changed files with 1177 additions and 31 deletions
+4
View File
@@ -57,6 +57,8 @@ linters:
alias: metav1 alias: metav1
- pkg: k8s.io/api/core/v1 - pkg: k8s.io/api/core/v1
alias: corev1 alias: corev1
- pkg: k8s.io/api/rbac/v1
alias: rbacv1
- pkg: k8s.io/api/apps/v1 - pkg: k8s.io/api/apps/v1
alias: appsv1 alias: appsv1
- pkg: k8s.io/api/admission/v1 - pkg: k8s.io/api/admission/v1
@@ -67,6 +69,8 @@ linters:
alias: metav1ac alias: metav1ac
- pkg: k8s.io/client-go/applyconfigurations/core/v1 - pkg: k8s.io/client-go/applyconfigurations/core/v1
alias: corev1ac alias: corev1ac
- pkg: k8s.io/client-go/applyconfigurations/rbac/v1
alias: rbacv1ac
- pkg: k8s.io/client-go/applyconfigurations/apps/v1 - pkg: k8s.io/client-go/applyconfigurations/apps/v1
alias: appsv1ac alias: appsv1ac
- pkg: k8s.io/client-go/applyconfigurations/policy/v1 - pkg: k8s.io/client-go/applyconfigurations/policy/v1
+8
View File
@@ -115,4 +115,12 @@ resources:
kind: SidecarProfile kind: SidecarProfile
path: github.com/netbirdio/kubernetes-operator/api/v1alpha1 path: github.com/netbirdio/kubernetes-operator/api/v1alpha1
version: v1alpha1 version: v1alpha1
- api:
crdVersion: v1
namespaced: true
controller: true
domain: netbird.io
kind: ClusterProxy
path: github.com/netbirdio/kubernetes-operator/api/v1alpha1
version: v1alpha1
version: "3" version: "3"
+78
View File
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: BSD-3-Clause
package v1alpha1
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// ClusterProxySpec defines the desired state of ClusterProxy.
type ClusterProxySpec struct {
// ClusterName is the name of the Kubernetes cluster.
// +required
// +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable"
ClusterName string `json:"clusterName"`
// APIServer is the URL of the Kubernetes API server to proxy requests to.
// +required
// +kubebuilder:default="https://kubernetes.default.svc.cluster.local"
APIServer string `json:"apiServer"`
// ServiceAccountName is a reference to the service account used for impersonation.
// +required
ServiceAccountName string `json:"serviceAccountName"`
}
// ClusterProxyStatus defines the observed state of ClusterProxy.
type ClusterProxyStatus struct {
// ObservedGeneration is the last reconciled generation.
// +optional
ObservedGeneration int64 `json:"observedGeneration,omitempty"`
// Conditions holds the conditions for the ClusterProxy.
// +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=""
// ClusterProxy is the Schema for the clusterproxies API
type ClusterProxy struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
// +required
Spec ClusterProxySpec `json:"spec"`
// +kubebuilder:default={"observedGeneration":-1}
Status ClusterProxyStatus `json:"status,omitempty"`
}
// GetConditions returns the status conditions of the object.
func (n *ClusterProxy) GetConditions() []metav1.Condition {
return n.Status.Conditions
}
// SetConditions sets the status conditions on the object.
func (n *ClusterProxy) SetConditions(conditions []metav1.Condition) {
n.Status.Conditions = conditions
}
// +kubebuilder:object:root=true
// ClusterProxyList contains a list of ClusterProxy
type ClusterProxyList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitzero"`
Items []ClusterProxy `json:"items"`
}
func init() {
SchemeBuilder.Register(&ClusterProxy{}, &ClusterProxyList{})
}
+112 -16
View File
@@ -7,39 +7,135 @@
package v1alpha1 package v1alpha1
import ( import (
"k8s.io/api/core/v1" corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1"
runtime "k8s.io/apimachinery/pkg/runtime" runtime "k8s.io/apimachinery/pkg/runtime"
) )
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterProxy) DeepCopyInto(out *ClusterProxy) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
out.Spec = in.Spec
in.Status.DeepCopyInto(&out.Status)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProxy.
func (in *ClusterProxy) DeepCopy() *ClusterProxy {
if in == nil {
return nil
}
out := new(ClusterProxy)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterProxy) 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 *ClusterProxyList) DeepCopyInto(out *ClusterProxyList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterProxy, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProxyList.
func (in *ClusterProxyList) DeepCopy() *ClusterProxyList {
if in == nil {
return nil
}
out := new(ClusterProxyList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterProxyList) 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 *ClusterProxySpec) DeepCopyInto(out *ClusterProxySpec) {
*out = *in
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProxySpec.
func (in *ClusterProxySpec) DeepCopy() *ClusterProxySpec {
if in == nil {
return nil
}
out := new(ClusterProxySpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterProxyStatus) DeepCopyInto(out *ClusterProxyStatus) {
*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 ClusterProxyStatus.
func (in *ClusterProxyStatus) DeepCopy() *ClusterProxyStatus {
if in == nil {
return nil
}
out := new(ClusterProxyStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ContainerOverride) DeepCopyInto(out *ContainerOverride) { func (in *ContainerOverride) DeepCopyInto(out *ContainerOverride) {
*out = *in *out = *in
if in.Env != nil { if in.Env != nil {
in, out := &in.Env, &out.Env in, out := &in.Env, &out.Env
*out = make([]v1.EnvVar, len(*in)) *out = make([]corev1.EnvVar, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
} }
if in.SecurityContext != nil { if in.SecurityContext != nil {
in, out := &in.SecurityContext, &out.SecurityContext in, out := &in.SecurityContext, &out.SecurityContext
*out = new(v1.SecurityContext) *out = new(corev1.SecurityContext)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.StartupProbe != nil { if in.StartupProbe != nil {
in, out := &in.StartupProbe, &out.StartupProbe in, out := &in.StartupProbe, &out.StartupProbe
*out = new(v1.Probe) *out = new(corev1.Probe)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.LivenessProbe != nil { if in.LivenessProbe != nil {
in, out := &in.LivenessProbe, &out.LivenessProbe in, out := &in.LivenessProbe, &out.LivenessProbe
*out = new(v1.Probe) *out = new(corev1.Probe)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.ReadinessProbe != nil { if in.ReadinessProbe != nil {
in, out := &in.ReadinessProbe, &out.ReadinessProbe in, out := &in.ReadinessProbe, &out.ReadinessProbe
*out = new(v1.Probe) *out = new(corev1.Probe)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
} }
@@ -158,7 +254,7 @@ func (in *GroupReference) DeepCopyInto(out *GroupReference) {
} }
if in.LocalRef != nil { if in.LocalRef != nil {
in, out := &in.LocalRef, &out.LocalRef in, out := &in.LocalRef, &out.LocalRef
*out = new(v1.LocalObjectReference) *out = new(corev1.LocalObjectReference)
**out = **in **out = **in
} }
} }
@@ -193,7 +289,7 @@ func (in *GroupStatus) DeepCopyInto(out *GroupStatus) {
*out = *in *out = *in
if in.Conditions != nil { if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.Condition, len(*in)) *out = make([]v1.Condition, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
@@ -298,7 +394,7 @@ func (in *NetworkResourceStatus) DeepCopyInto(out *NetworkResourceStatus) {
*out = *in *out = *in
if in.Conditions != nil { if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.Condition, len(*in)) *out = make([]v1.Condition, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
@@ -400,7 +496,7 @@ func (in *NetworkRouterStatus) DeepCopyInto(out *NetworkRouterStatus) {
*out = *in *out = *in
if in.Conditions != nil { if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.Condition, len(*in)) *out = make([]v1.Condition, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
@@ -481,7 +577,7 @@ func (in *SetupKeySpec) DeepCopyInto(out *SetupKeySpec) {
*out = *in *out = *in
if in.Duration != nil { if in.Duration != nil {
in, out := &in.Duration, &out.Duration in, out := &in.Duration, &out.Duration
*out = new(metav1.Duration) *out = new(v1.Duration)
**out = **in **out = **in
} }
if in.AutoGroups != nil { if in.AutoGroups != nil {
@@ -508,7 +604,7 @@ func (in *SetupKeyStatus) DeepCopyInto(out *SetupKeyStatus) {
*out = *in *out = *in
if in.Conditions != nil { if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.Condition, len(*in)) *out = make([]v1.Condition, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
@@ -590,7 +686,7 @@ func (in *SidecarProfileSpec) DeepCopyInto(out *SidecarProfileSpec) {
out.SetupKeyRef = in.SetupKeyRef out.SetupKeyRef = in.SetupKeyRef
if in.PodSelector != nil { if in.PodSelector != nil {
in, out := &in.PodSelector, &out.PodSelector in, out := &in.PodSelector, &out.PodSelector
*out = new(metav1.LabelSelector) *out = new(v1.LabelSelector)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
if in.ExtraDNSLabels != nil { if in.ExtraDNSLabels != nil {
@@ -620,7 +716,7 @@ func (in *SidecarProfileStatus) DeepCopyInto(out *SidecarProfileStatus) {
*out = *in *out = *in
if in.Conditions != nil { if in.Conditions != nil {
in, out := &in.Conditions, &out.Conditions in, out := &in.Conditions, &out.Conditions
*out = make([]metav1.Condition, len(*in)) *out = make([]v1.Condition, len(*in))
for i := range *in { for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i]) (*in)[i].DeepCopyInto(&(*out)[i])
} }
@@ -661,7 +757,7 @@ func (in *WorkloadOverride) DeepCopyInto(out *WorkloadOverride) {
} }
if in.PodTemplate != nil { if in.PodTemplate != nil {
in, out := &in.PodTemplate, &out.PodTemplate in, out := &in.PodTemplate, &out.PodTemplate
*out = new(v1.PodTemplateSpec) *out = new(corev1.PodTemplateSpec)
(*in).DeepCopyInto(*out) (*in).DeepCopyInto(*out)
} }
} }
@@ -0,0 +1,145 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.1
name: clusterproxies.netbird.io
spec:
group: netbird.io
names:
kind: ClusterProxy
listKind: ClusterProxyList
plural: clusterproxies
singular: clusterproxy
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: ClusterProxy is the Schema for the clusterproxies 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: ClusterProxySpec defines the desired state of ClusterProxy.
properties:
apiServer:
default: https://kubernetes.default.svc.cluster.local
description: APIServer is the URL of the Kubernetes API server to
proxy requests to.
type: string
clusterName:
description: ClusterName is the name of the Kubernetes cluster.
type: string
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
serviceAccountName:
description: ServiceAccountName is a reference to the service account
used for impersonation.
type: string
required:
- apiServer
- clusterName
- serviceAccountName
type: object
status:
default:
observedGeneration: -1
description: ClusterProxyStatus defines the observed state of ClusterProxy.
properties:
conditions:
description: Conditions holds the conditions for the ClusterProxy.
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: {}
@@ -39,6 +39,7 @@ rules:
- networkrouters - networkrouters
- networkresources - networkresources
- sidecarprofiles - sidecarprofiles
- clusterproxies
verbs: verbs:
- get - get
- patch - patch
@@ -59,6 +60,7 @@ rules:
- networkrouters/status - networkrouters/status
- networkresources/status - networkresources/status
- sidecarprofiles/status - sidecarprofiles/status
- clusterproxies/status
verbs: verbs:
- get - get
- patch - patch
+14 -7
View File
@@ -60,7 +60,7 @@ func main() {
var ( var (
runtimeNamespace string runtimeNamespace string
managementURL string managementURL string
clientImage string netbirdClientImage string
clusterName string clusterName string
namespacedNetworks bool namespacedNetworks bool
clusterDNS string clusterDNS string
@@ -71,7 +71,7 @@ func main() {
) )
flag.StringVar(&runtimeNamespace, "runtime-namespace", "", "Namespace the controller is running in") flag.StringVar(&runtimeNamespace, "runtime-namespace", "", "Namespace the controller is running in")
flag.StringVar(&managementURL, "netbird-management-url", "https://api.netbird.io", "Management service URL") flag.StringVar(&managementURL, "netbird-management-url", "https://api.netbird.io", "Management service URL")
flag.StringVar(&clientImage, "netbird-client-image", "", "Image for netbird client container") flag.StringVar(&netbirdClientImage, "netbird-client-image", "", "Image for netbird client container")
flag.StringVar( flag.StringVar(
&clusterName, &clusterName,
"cluster-name", "cluster-name",
@@ -139,8 +139,8 @@ func main() {
setupLog.Error(err, "unable to get runtime namespace") setupLog.Error(err, "unable to get runtime namespace")
os.Exit(1) os.Exit(1)
} }
if clientImage == "" { if netbirdClientImage == "" {
clientImage = version.ClientImage() netbirdClientImage = version.NetbirdClientImage
} }
defaultLabelsMap := make(map[string]string) defaultLabelsMap := make(map[string]string)
@@ -210,7 +210,7 @@ func main() {
} }
if enableWebhooks { if enableWebhooks {
if err = nbwebhookv1.SetupPodWebhookWithManager(mgr, managementURL, clientImage); err != nil { if err = nbwebhookv1.SetupPodWebhookWithManager(mgr, managementURL, netbirdClientImage); err != nil {
setupLog.Error(err, "unable to create webhook", "webhook", "Pod") setupLog.Error(err, "unable to create webhook", "webhook", "Pod")
os.Exit(1) os.Exit(1)
} }
@@ -226,7 +226,7 @@ func main() {
if err = (&controller.NBRoutingPeerReconciler{ if err = (&controller.NBRoutingPeerReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Netbird: nbClient, Netbird: nbClient,
ClientImage: clientImage, ClientImage: netbirdClientImage,
ClusterName: clusterName, ClusterName: clusterName,
ManagementURL: managementURL, ManagementURL: managementURL,
NamespacedNetworks: namespacedNetworks, NamespacedNetworks: namespacedNetworks,
@@ -299,7 +299,7 @@ func main() {
if err := (&controller.NetworkRouterReconciler{ if err := (&controller.NetworkRouterReconciler{
Client: mgr.GetClient(), Client: mgr.GetClient(),
Netbird: nbClient, Netbird: nbClient,
ClientImage: clientImage, ClientImage: netbirdClientImage,
ManagementURL: managementURL, ManagementURL: managementURL,
}).SetupWithManager(mgr); err != nil { }).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "Failed to create controller", "controller", "NetworkRouter") setupLog.Error(err, "Failed to create controller", "controller", "NetworkRouter")
@@ -312,6 +312,13 @@ func main() {
setupLog.Error(err, "Failed to create controller", "controller", "NetworkResource") setupLog.Error(err, "Failed to create controller", "controller", "NetworkResource")
os.Exit(1) os.Exit(1)
} }
if err := (&controller.ClusterProxyReconciler{
Client: mgr.GetClient(),
ApiKey: netbirdAPIKey,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "Failed to create controller", "controller", "ClusterProxy")
os.Exit(1)
}
if gatewayAPIEnabled { if gatewayAPIEnabled {
if err = (&controller.GatewayClassReconciler{ if err = (&controller.GatewayClassReconciler{
@@ -0,0 +1,145 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: v0.20.1
name: clusterproxies.netbird.io
spec:
group: netbird.io
names:
kind: ClusterProxy
listKind: ClusterProxyList
plural: clusterproxies
singular: clusterproxy
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: ClusterProxy is the Schema for the clusterproxies 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: ClusterProxySpec defines the desired state of ClusterProxy.
properties:
apiServer:
default: https://kubernetes.default.svc.cluster.local
description: APIServer is the URL of the Kubernetes API server to
proxy requests to.
type: string
clusterName:
description: ClusterName is the name of the Kubernetes cluster.
type: string
x-kubernetes-validations:
- message: Value is immutable
rule: self == oldSelf
serviceAccountName:
description: ServiceAccountName is a reference to the service account
used for impersonation.
type: string
required:
- apiServer
- clusterName
- serviceAccountName
type: object
status:
default:
observedGeneration: -1
description: ClusterProxyStatus defines the observed state of ClusterProxy.
properties:
conditions:
description: Conditions holds the conditions for the ClusterProxy.
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: {}
+1
View File
@@ -11,4 +11,5 @@ resources:
- bases/netbird.io_networkrouters.yaml - bases/netbird.io_networkrouters.yaml
- bases/netbird.io_setupkeys.yaml - bases/netbird.io_setupkeys.yaml
- bases/netbird.io_sidecarprofiles.yaml - bases/netbird.io_sidecarprofiles.yaml
- bases/netbird.io_clusterproxies.yaml
# +kubebuilder:scaffold:crdkustomizeresource # +kubebuilder:scaffold:crdkustomizeresource
+12
View File
@@ -0,0 +1,12 @@
# This file is for teaching kustomize how to substitute name and namespace reference in CRD
nameReference:
- kind: Service
version: v1
fieldSpecs:
- kind: CustomResourceDefinition
version: v1
group: apiextensions.k8s.io
path: spec/conversion/webhook/clientConfig/service/name
varReference:
- path: metadata/annotations
+57
View File
@@ -9,6 +9,7 @@
Package v1alpha1 contains API Schema definitions for the v1alpha1 API group. Package v1alpha1 contains API Schema definitions for the v1alpha1 API group.
### Resource Types ### Resource Types
- [ClusterProxy](#clusterproxy)
- [Group](#group) - [Group](#group)
- [NetworkResource](#networkresource) - [NetworkResource](#networkresource)
- [NetworkRouter](#networkrouter) - [NetworkRouter](#networkrouter)
@@ -17,6 +18,62 @@ Package v1alpha1 contains API Schema definitions for the v1alpha1 API group.
#### ClusterProxy
ClusterProxy is the Schema for the clusterproxies API
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `apiVersion` _string_ | `netbird.io/v1alpha1` | | |
| `kind` _string_ | `ClusterProxy` | | |
| `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` _[ClusterProxySpec](#clusterproxyspec)_ | | | Required: \{\} <br /> |
| `status` _[ClusterProxyStatus](#clusterproxystatus)_ | | \{ observedGeneration:-1 \} | |
#### ClusterProxySpec
ClusterProxySpec defines the desired state of ClusterProxy.
_Appears in:_
- [ClusterProxy](#clusterproxy)
| Field | Description | Default | Validation |
| --- | --- | --- | --- |
| `clusterName` _string_ | ClusterName is the name of the Kubernetes cluster. | | Required: \{\} <br /> |
| `apiServer` _string_ | APIServer is the URL of the Kubernetes API server to proxy requests to. | https://kubernetes.default.svc.cluster.local | Required: \{\} <br /> |
| `serviceAccountName` _string_ | ServiceAccountName is a reference to the service account used for impersonation. | | Required: \{\} <br /> |
#### ClusterProxyStatus
ClusterProxyStatus defines the observed state of ClusterProxy.
_Appears in:_
- [ClusterProxy](#clusterproxy)
| 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 ClusterProxy. | | Optional: \{\} <br /> |
#### ContainerOverride #### ContainerOverride
+70
View File
@@ -0,0 +1,70 @@
apiVersion: netbird.io/v1alpha1
kind: ClusterProxy
metadata:
name: prod
namespace: netbird
spec:
clusterName: prod
serviceAccountName: clusterproxy-prod
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: clusterproxy-prod
namespace: netbird
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: clusterproxy-prod
rules:
- apiGroups:
- ""
resources:
- users
- groups
verbs:
- impersonate
- apiGroups:
- authentication.k8s.io
resources:
- userextras/*
- uids
verbs:
- impersonate
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterproxy-prod
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: clusterproxy-prod
subjects:
- kind: ServiceAccount
name: clusterproxy-prod
namespace: netbird
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: netbird-cluster-reader
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: netbird-cluster-reader
subjects:
- kind: Group
name: kubernetes-read
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: netbird-cluster-reader
apiGroup: rbac.authorization.k8s.io
@@ -0,0 +1,189 @@
// SPDX-License-Identifier: BSD-3-Clause
package controller
import (
"context"
"crypto/sha256"
"fmt"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/fluxcd/pkg/runtime/patch"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1"
corev1ac "k8s.io/client-go/applyconfigurations/core/v1"
metav1ac "k8s.io/client-go/applyconfigurations/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1"
"github.com/netbirdio/kubernetes-operator/internal/k8sutil"
"github.com/netbirdio/kubernetes-operator/internal/version"
nbv1alpha1ac "github.com/netbirdio/kubernetes-operator/pkg/applyconfigurations/api/v1alpha1"
)
// ClusterProxyReconciler reconciles a ClusterProxy object
type ClusterProxyReconciler struct {
client.Client
ApiKey string
}
// +kubebuilder:rbac:groups=netbird.io,resources=clusterproxies,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=netbird.io,resources=clusterproxies/status,verbs=get;update;patch
// +kubebuilder:rbac:groups=netbird.io,resources=clusterproxies/finalizers,verbs=update
func (r *ClusterProxyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
clusterProxy := &nbv1alpha1.ClusterProxy{}
err := r.Get(ctx, req.NamespacedName, clusterProxy)
if err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
sp := patch.NewSerialPatcher(clusterProxy, r.Client)
if !clusterProxy.DeletionTimestamp.IsZero() {
return ctrl.Result{}, nil
}
ownerRef, err := k8sutil.ControllerReference(clusterProxy, r.Scheme())
if err != nil {
return ctrl.Result{}, err
}
// Calculate unique suffix used for Netbird resources.
sum := sha256.Sum256([]byte(clusterProxy.UID))
uniqueSuffix := fmt.Sprintf("%x", sum[:4])[:8]
// Create the setup key used by routing peers.
setupKeyAC := nbv1alpha1ac.SetupKey(fmt.Sprintf("clusterproxy-%s", clusterProxy.Name), req.Namespace).
WithOwnerReferences(ownerRef).
WithSpec(
nbv1alpha1ac.SetupKeySpec().
WithName(fmt.Sprintf("clusterproxy-%s", uniqueSuffix)).
WithEphemeral(true).
WithAllowExtraDnsLabels(true),
)
err = r.Client.Apply(ctx, setupKeyAC)
if err != nil {
return ctrl.Result{}, err
}
setupKey := nbv1alpha1.SetupKey{
ObjectMeta: metav1.ObjectMeta{
Name: *setupKeyAC.Name,
Namespace: *setupKeyAC.Namespace,
},
}
err = r.Get(ctx, client.ObjectKeyFromObject(&setupKey), &setupKey)
if err != nil {
return ctrl.Result{}, client.IgnoreNotFound(err)
}
if setupKey.Status.SetupKeyID == "" {
return ctrl.Result{}, nil
}
// Create secret for api token.
secretAC := corev1ac.Secret(fmt.Sprintf("clusterproxy-%s", req.Name), req.Namespace).
WithOwnerReferences(ownerRef).
WithStringData(map[string]string{"api-key": r.ApiKey})
err = r.Client.Apply(ctx, secretAC)
if err != nil {
return ctrl.Result{}, err
}
// Create the API proxy deployment.
selectorLabels := map[string]string{
"app.kubernetes.io/name": "clusterproxy",
"app.kubernetes.io/instance": req.Name,
}
podTemplateSpecAC := corev1ac.PodTemplateSpec().
WithLabels(selectorLabels).
WithSpec(corev1ac.PodSpec().
WithTopologySpreadConstraints(
corev1ac.TopologySpreadConstraint().
WithMaxSkew(1).
WithTopologyKey(corev1.LabelHostname).
WithWhenUnsatisfiable(corev1.ScheduleAnyway).
WithLabelSelector(metav1ac.LabelSelector().
WithMatchLabels(selectorLabels),
),
).
WithServiceAccountName(clusterProxy.Spec.ServiceAccountName).
WithContainers(corev1ac.Container().
WithName("proxy").
WithImage(version.KubeApiProxyImage).
WithArgs(
"--setup-key",
"$(SETUP_KEY)",
"--api-key",
"$(API_KEY)",
"--instance-name",
"$(POD_NAME)",
"--cluster-name",
clusterProxy.Spec.ClusterName,
"--kubernetes-api-server",
clusterProxy.Spec.APIServer,
).
WithEnv(
corev1ac.EnvVar().
WithName("POD_NAME").
WithValueFrom(corev1ac.EnvVarSource().
WithFieldRef(corev1ac.ObjectFieldSelector().WithFieldPath("metadata.name")),
),
corev1ac.EnvVar().
WithName("SETUP_KEY").
WithValueFrom(corev1ac.EnvVarSource().
WithSecretKeyRef(corev1ac.SecretKeySelector().
WithName(setupKey.SecretName()).
WithKey(SetupKeySecretKey),
),
),
corev1ac.EnvVar().
WithName("API_KEY").
WithValueFrom(corev1ac.EnvVarSource().
WithSecretKeyRef(corev1ac.SecretKeySelector().
WithName(*secretAC.Name).
WithKey("api-key"),
),
),
).
WithSecurityContext(corev1ac.SecurityContext().
WithAllowPrivilegeEscalation(false).
WithReadOnlyRootFilesystem(true).
WithRunAsNonRoot(true).
WithCapabilities(corev1ac.Capabilities().WithDrop("ALL")),
).
WithResources(corev1ac.ResourceRequirements().
WithRequests(corev1.ResourceList{
corev1.ResourceCPU: resource.MustParse("100m"),
corev1.ResourceMemory: resource.MustParse("128Mi"),
}),
),
),
)
depAC := appsv1ac.Deployment(fmt.Sprintf("clusterproxy-%s", req.Name), req.Namespace).
WithOwnerReferences(ownerRef).
WithLabels(selectorLabels).
WithSpec(appsv1ac.DeploymentSpec().WithReplicas(1).WithSelector(metav1ac.LabelSelector().WithMatchLabels(selectorLabels)).WithTemplate(podTemplateSpecAC))
err = r.Client.Apply(ctx, depAC)
if err != nil {
return ctrl.Result{}, err
}
conditions.MarkTrue(clusterProxy, nbv1alpha1.ReadyCondition, nbv1alpha1.ReconciledReason, "")
err = sp.Patch(ctx, clusterProxy, patch.WithStatusObservedGeneration{})
if err != nil {
return ctrl.Result{}, err
}
return ctrl.Result{}, nil
}
func (r *ClusterProxyReconciler) SetupWithManager(mgr ctrl.Manager) error {
return ctrl.NewControllerManagedBy(mgr).
For(&nbv1alpha1.ClusterProxy{}).
Owns(&nbv1alpha1.SetupKey{}).
Owns(&appsv1.Deployment{}).
Complete(r)
}
+4 -3
View File
@@ -6,9 +6,10 @@ import (
"runtime/debug" "runtime/debug"
) )
func ClientImage() string { const (
return "ghcr.io/netbirdio/netbird:0.71.4@sha256:c4195811bf9999544db5176950a0d6513c880d0195450b59eb156c254e0dd3b5" NetbirdClientImage = "ghcr.io/netbirdio/netbird:0.71.4@sha256:c4195811bf9999544db5176950a0d6513c880d0195450b59eb156c254e0dd3b5"
} KubeApiProxyImage = "ghcr.io/netbirdio/netbird-kubeapi-proxy:v0.0.1@sha256:aa5bbdfc2eca51438f3d50ed4441d61388e9a8d2d5dc886cc4988dacd36ad648"
)
func BuildVersion() string { func BuildVersion() string {
bi, ok := debug.ReadBuildInfo() bi, ok := debug.ReadBuildInfo()
+4 -5
View File
@@ -13,7 +13,7 @@ import (
"github.com/go-openapi/testify/v2/require" "github.com/go-openapi/testify/v2/require"
) )
func TestClientImage(t *testing.T) { func TestNetbirdClientImage(t *testing.T) {
t.Parallel() t.Parallel()
b, err := os.ReadFile("../../go.mod") b, err := os.ReadFile("../../go.mod")
@@ -26,10 +26,9 @@ func TestClientImage(t *testing.T) {
require.GreaterT(t, idx, -1) require.GreaterT(t, idx, -1)
modVersion := strings.TrimPrefix(f.Require[idx].Mod.Version, "v") modVersion := strings.TrimPrefix(f.Require[idx].Mod.Version, "v")
clientImg := ClientImage() start := strings.Index(NetbirdClientImage, ":") + 1
start := strings.Index(clientImg, ":") + 1 end := strings.Index(NetbirdClientImage, "@")
end := strings.Index(clientImg, "@") imgVersion := NetbirdClientImage[start:end]
imgVersion := clientImg[start:end]
require.EqualT(t, modVersion, imgVersion) require.EqualT(t, modVersion, imgVersion)
} }
@@ -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"
)
// ClusterProxyApplyConfiguration represents a declarative configuration of the ClusterProxy type for use
// with apply.
//
// ClusterProxy is the Schema for the clusterproxies API
type ClusterProxyApplyConfiguration struct {
v1.TypeMetaApplyConfiguration `json:",inline"`
*v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"`
Spec *ClusterProxySpecApplyConfiguration `json:"spec,omitempty"`
Status *ClusterProxyStatusApplyConfiguration `json:"status,omitempty"`
}
// ClusterProxy constructs a declarative configuration of the ClusterProxy type for use with
// apply.
func ClusterProxy(name, namespace string) *ClusterProxyApplyConfiguration {
b := &ClusterProxyApplyConfiguration{}
b.WithName(name)
b.WithNamespace(namespace)
b.WithKind("ClusterProxy")
b.WithAPIVersion("netbird.io/v1alpha1")
return b
}
func (b ClusterProxyApplyConfiguration) 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 *ClusterProxyApplyConfiguration) WithKind(value string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithAPIVersion(value string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithName(value string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithGenerateName(value string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithNamespace(value string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithUID(value types.UID) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithResourceVersion(value string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithGeneration(value int64) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithCreationTimestamp(value metav1.Time) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithLabels(entries map[string]string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithAnnotations(entries map[string]string) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithFinalizers(values ...string) *ClusterProxyApplyConfiguration {
b.ensureObjectMetaApplyConfigurationExists()
for i := range values {
b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i])
}
return b
}
func (b *ClusterProxyApplyConfiguration) 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 *ClusterProxyApplyConfiguration) WithSpec(value *ClusterProxySpecApplyConfiguration) *ClusterProxyApplyConfiguration {
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 *ClusterProxyApplyConfiguration) WithStatus(value *ClusterProxyStatusApplyConfiguration) *ClusterProxyApplyConfiguration {
b.Status = value
return b
}
// GetKind retrieves the value of the Kind field in the declarative configuration.
func (b *ClusterProxyApplyConfiguration) GetKind() *string {
return b.TypeMetaApplyConfiguration.Kind
}
// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration.
func (b *ClusterProxyApplyConfiguration) GetAPIVersion() *string {
return b.TypeMetaApplyConfiguration.APIVersion
}
// GetName retrieves the value of the Name field in the declarative configuration.
func (b *ClusterProxyApplyConfiguration) GetName() *string {
b.ensureObjectMetaApplyConfigurationExists()
return b.ObjectMetaApplyConfiguration.Name
}
// GetNamespace retrieves the value of the Namespace field in the declarative configuration.
func (b *ClusterProxyApplyConfiguration) GetNamespace() *string {
b.ensureObjectMetaApplyConfigurationExists()
return b.ObjectMetaApplyConfiguration.Namespace
}
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: BSD-3-Clause
// Code generated by controller-gen. DO NOT EDIT.
package v1alpha1
// ClusterProxySpecApplyConfiguration represents a declarative configuration of the ClusterProxySpec type for use
// with apply.
//
// ClusterProxySpec defines the desired state of ClusterProxy.
type ClusterProxySpecApplyConfiguration struct {
// ClusterName is the name of the Kubernetes cluster.
ClusterName *string `json:"clusterName,omitempty"`
// APIServer is the URL of the Kubernetes API server to proxy requests to.
APIServer *string `json:"apiServer,omitempty"`
// ServiceAccountName is a reference to the service account used for impersonation.
ServiceAccountName *string `json:"serviceAccountName,omitempty"`
}
// ClusterProxySpecApplyConfiguration constructs a declarative configuration of the ClusterProxySpec type for use with
// apply.
func ClusterProxySpec() *ClusterProxySpecApplyConfiguration {
return &ClusterProxySpecApplyConfiguration{}
}
// WithClusterName sets the ClusterName 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 ClusterName field is set to the value of the last call.
func (b *ClusterProxySpecApplyConfiguration) WithClusterName(value string) *ClusterProxySpecApplyConfiguration {
b.ClusterName = &value
return b
}
// WithAPIServer sets the APIServer 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 APIServer field is set to the value of the last call.
func (b *ClusterProxySpecApplyConfiguration) WithAPIServer(value string) *ClusterProxySpecApplyConfiguration {
b.APIServer = &value
return b
}
// WithServiceAccountName sets the ServiceAccountName 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 ServiceAccountName field is set to the value of the last call.
func (b *ClusterProxySpecApplyConfiguration) WithServiceAccountName(value string) *ClusterProxySpecApplyConfiguration {
b.ServiceAccountName = &value
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"
)
// ClusterProxyStatusApplyConfiguration represents a declarative configuration of the ClusterProxyStatus type for use
// with apply.
//
// ClusterProxyStatus defines the observed state of ClusterProxy.
type ClusterProxyStatusApplyConfiguration struct {
// ObservedGeneration is the last reconciled generation.
ObservedGeneration *int64 `json:"observedGeneration,omitempty"`
// Conditions holds the conditions for the ClusterProxy.
Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"`
}
// ClusterProxyStatusApplyConfiguration constructs a declarative configuration of the ClusterProxyStatus type for use with
// apply.
func ClusterProxyStatus() *ClusterProxyStatusApplyConfiguration {
return &ClusterProxyStatusApplyConfiguration{}
}
// 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 *ClusterProxyStatusApplyConfiguration) WithObservedGeneration(value int64) *ClusterProxyStatusApplyConfiguration {
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 *ClusterProxyStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *ClusterProxyStatusApplyConfiguration {
for i := range values {
if values[i] == nil {
panic("nil value passed to WithConditions")
}
b.Conditions = append(b.Conditions, *values[i])
}
return b
}
+6
View File
@@ -18,6 +18,12 @@ import (
func ForKind(kind schema.GroupVersionKind) interface{} { func ForKind(kind schema.GroupVersionKind) interface{} {
switch kind { switch kind {
// Group=netbird.io, Version=v1alpha1 // Group=netbird.io, Version=v1alpha1
case v1alpha1.SchemeGroupVersion.WithKind("ClusterProxy"):
return &apiv1alpha1.ClusterProxyApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ClusterProxySpec"):
return &apiv1alpha1.ClusterProxySpecApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ClusterProxyStatus"):
return &apiv1alpha1.ClusterProxyStatusApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("ContainerOverride"): case v1alpha1.SchemeGroupVersion.WithKind("ContainerOverride"):
return &apiv1alpha1.ContainerOverrideApplyConfiguration{} return &apiv1alpha1.ContainerOverrideApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("CrossNamespaceReference"): case v1alpha1.SchemeGroupVersion.WithKind("CrossNamespaceReference"):