diff --git a/.golangci.yml b/.golangci.yml index 22deb05..ed003c9 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -57,6 +57,8 @@ linters: alias: metav1 - pkg: k8s.io/api/core/v1 alias: corev1 + - pkg: k8s.io/api/rbac/v1 + alias: rbacv1 - pkg: k8s.io/api/apps/v1 alias: appsv1 - pkg: k8s.io/api/admission/v1 @@ -67,6 +69,8 @@ linters: alias: metav1ac - pkg: k8s.io/client-go/applyconfigurations/core/v1 alias: corev1ac + - pkg: k8s.io/client-go/applyconfigurations/rbac/v1 + alias: rbacv1ac - pkg: k8s.io/client-go/applyconfigurations/apps/v1 alias: appsv1ac - pkg: k8s.io/client-go/applyconfigurations/policy/v1 diff --git a/PROJECT b/PROJECT index 68b0780..5564e55 100644 --- a/PROJECT +++ b/PROJECT @@ -115,4 +115,12 @@ resources: kind: SidecarProfile path: github.com/netbirdio/kubernetes-operator/api/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" diff --git a/api/v1alpha1/clusterproxy_types.go b/api/v1alpha1/clusterproxy_types.go new file mode 100644 index 0000000..e799bf2 --- /dev/null +++ b/api/v1alpha1/clusterproxy_types.go @@ -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{}) +} diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 8b2d340..29058ee 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -7,39 +7,135 @@ package v1alpha1 import ( - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1" 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. func (in *ContainerOverride) DeepCopyInto(out *ContainerOverride) { *out = *in if in.Env != nil { in, out := &in.Env, &out.Env - *out = make([]v1.EnvVar, len(*in)) + *out = make([]corev1.EnvVar, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } if in.SecurityContext != nil { in, out := &in.SecurityContext, &out.SecurityContext - *out = new(v1.SecurityContext) + *out = new(corev1.SecurityContext) (*in).DeepCopyInto(*out) } if in.StartupProbe != nil { in, out := &in.StartupProbe, &out.StartupProbe - *out = new(v1.Probe) + *out = new(corev1.Probe) (*in).DeepCopyInto(*out) } if in.LivenessProbe != nil { in, out := &in.LivenessProbe, &out.LivenessProbe - *out = new(v1.Probe) + *out = new(corev1.Probe) (*in).DeepCopyInto(*out) } if in.ReadinessProbe != nil { in, out := &in.ReadinessProbe, &out.ReadinessProbe - *out = new(v1.Probe) + *out = new(corev1.Probe) (*in).DeepCopyInto(*out) } } @@ -158,7 +254,7 @@ func (in *GroupReference) DeepCopyInto(out *GroupReference) { } if in.LocalRef != nil { in, out := &in.LocalRef, &out.LocalRef - *out = new(v1.LocalObjectReference) + *out = new(corev1.LocalObjectReference) **out = **in } } @@ -193,7 +289,7 @@ func (in *GroupStatus) DeepCopyInto(out *GroupStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -298,7 +394,7 @@ func (in *NetworkResourceStatus) DeepCopyInto(out *NetworkResourceStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -400,7 +496,7 @@ func (in *NetworkRouterStatus) DeepCopyInto(out *NetworkRouterStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -481,7 +577,7 @@ func (in *SetupKeySpec) DeepCopyInto(out *SetupKeySpec) { *out = *in if in.Duration != nil { in, out := &in.Duration, &out.Duration - *out = new(metav1.Duration) + *out = new(v1.Duration) **out = **in } if in.AutoGroups != nil { @@ -508,7 +604,7 @@ func (in *SetupKeyStatus) DeepCopyInto(out *SetupKeyStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -590,7 +686,7 @@ func (in *SidecarProfileSpec) DeepCopyInto(out *SidecarProfileSpec) { out.SetupKeyRef = in.SetupKeyRef if in.PodSelector != nil { in, out := &in.PodSelector, &out.PodSelector - *out = new(metav1.LabelSelector) + *out = new(v1.LabelSelector) (*in).DeepCopyInto(*out) } if in.ExtraDNSLabels != nil { @@ -620,7 +716,7 @@ func (in *SidecarProfileStatus) DeepCopyInto(out *SidecarProfileStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]metav1.Condition, len(*in)) + *out = make([]v1.Condition, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -661,7 +757,7 @@ func (in *WorkloadOverride) DeepCopyInto(out *WorkloadOverride) { } if in.PodTemplate != nil { in, out := &in.PodTemplate, &out.PodTemplate - *out = new(v1.PodTemplateSpec) + *out = new(corev1.PodTemplateSpec) (*in).DeepCopyInto(*out) } } diff --git a/charts/netbird-operator/crds/netbird.io_clusterproxies.yaml b/charts/netbird-operator/crds/netbird.io_clusterproxies.yaml new file mode 100644 index 0000000..8ea4b4c --- /dev/null +++ b/charts/netbird-operator/crds/netbird.io_clusterproxies.yaml @@ -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: {} diff --git a/charts/netbird-operator/templates/rbac.yaml b/charts/netbird-operator/templates/rbac.yaml index 9d4c860..8909c43 100644 --- a/charts/netbird-operator/templates/rbac.yaml +++ b/charts/netbird-operator/templates/rbac.yaml @@ -39,6 +39,7 @@ rules: - networkrouters - networkresources - sidecarprofiles + - clusterproxies verbs: - get - patch @@ -59,6 +60,7 @@ rules: - networkrouters/status - networkresources/status - sidecarprofiles/status + - clusterproxies/status verbs: - get - patch diff --git a/cmd/main.go b/cmd/main.go index 3e9967a..bf84da9 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -60,7 +60,7 @@ func main() { var ( runtimeNamespace string managementURL string - clientImage string + netbirdClientImage string clusterName string namespacedNetworks bool clusterDNS string @@ -71,7 +71,7 @@ func main() { ) 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(&clientImage, "netbird-client-image", "", "Image for netbird client container") + flag.StringVar(&netbirdClientImage, "netbird-client-image", "", "Image for netbird client container") flag.StringVar( &clusterName, "cluster-name", @@ -139,8 +139,8 @@ func main() { setupLog.Error(err, "unable to get runtime namespace") os.Exit(1) } - if clientImage == "" { - clientImage = version.ClientImage() + if netbirdClientImage == "" { + netbirdClientImage = version.NetbirdClientImage } defaultLabelsMap := make(map[string]string) @@ -210,7 +210,7 @@ func main() { } 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") os.Exit(1) } @@ -226,7 +226,7 @@ func main() { if err = (&controller.NBRoutingPeerReconciler{ Client: mgr.GetClient(), Netbird: nbClient, - ClientImage: clientImage, + ClientImage: netbirdClientImage, ClusterName: clusterName, ManagementURL: managementURL, NamespacedNetworks: namespacedNetworks, @@ -299,7 +299,7 @@ func main() { if err := (&controller.NetworkRouterReconciler{ Client: mgr.GetClient(), Netbird: nbClient, - ClientImage: clientImage, + ClientImage: netbirdClientImage, ManagementURL: managementURL, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "Failed to create controller", "controller", "NetworkRouter") @@ -312,6 +312,13 @@ func main() { setupLog.Error(err, "Failed to create controller", "controller", "NetworkResource") 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 err = (&controller.GatewayClassReconciler{ diff --git a/config/crd/bases/netbird.io_clusterproxies.yaml b/config/crd/bases/netbird.io_clusterproxies.yaml new file mode 100644 index 0000000..8ea4b4c --- /dev/null +++ b/config/crd/bases/netbird.io_clusterproxies.yaml @@ -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: {} diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index f883e06..0e7f6bc 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -11,4 +11,5 @@ resources: - bases/netbird.io_networkrouters.yaml - bases/netbird.io_setupkeys.yaml - bases/netbird.io_sidecarprofiles.yaml +- bases/netbird.io_clusterproxies.yaml # +kubebuilder:scaffold:crdkustomizeresource diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml new file mode 100644 index 0000000..61361ff --- /dev/null +++ b/config/crd/kustomizeconfig.yaml @@ -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 diff --git a/docs/api-reference.md b/docs/api-reference.md index 0f98018..90c0493 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -9,6 +9,7 @@ Package v1alpha1 contains API Schema definitions for the v1alpha1 API group. ### Resource Types +- [ClusterProxy](#clusterproxy) - [Group](#group) - [NetworkResource](#networkresource) - [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.
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 | | Optional: \{\}
| +| `apiVersion` _string_ | 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 | | Optional: \{\}
| +| `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: \{\}
| +| `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: \{\}
| +| `apiServer` _string_ | APIServer is the URL of the Kubernetes API server to proxy requests to. | https://kubernetes.default.svc.cluster.local | Required: \{\}
| +| `serviceAccountName` _string_ | ServiceAccountName is a reference to the service account used for impersonation. | | Required: \{\}
| + + +#### 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: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.35/#condition-v1-meta) array_ | Conditions holds the conditions for the ClusterProxy. | | Optional: \{\}
| + + #### ContainerOverride diff --git a/examples/api-proxy/api-proxy.yaml b/examples/api-proxy/api-proxy.yaml new file mode 100644 index 0000000..0cc2d15 --- /dev/null +++ b/examples/api-proxy/api-proxy.yaml @@ -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 diff --git a/internal/controller/clusterproxy_controller.go b/internal/controller/clusterproxy_controller.go new file mode 100644 index 0000000..79b3cbe --- /dev/null +++ b/internal/controller/clusterproxy_controller.go @@ -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) +} diff --git a/internal/version/version.go b/internal/version/version.go index c25275a..2958523 100644 --- a/internal/version/version.go +++ b/internal/version/version.go @@ -6,9 +6,10 @@ import ( "runtime/debug" ) -func ClientImage() string { - return "ghcr.io/netbirdio/netbird:0.71.4@sha256:c4195811bf9999544db5176950a0d6513c880d0195450b59eb156c254e0dd3b5" -} +const ( + 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 { bi, ok := debug.ReadBuildInfo() diff --git a/internal/version/version_test.go b/internal/version/version_test.go index d1250da..56521f1 100644 --- a/internal/version/version_test.go +++ b/internal/version/version_test.go @@ -13,7 +13,7 @@ import ( "github.com/go-openapi/testify/v2/require" ) -func TestClientImage(t *testing.T) { +func TestNetbirdClientImage(t *testing.T) { t.Parallel() b, err := os.ReadFile("../../go.mod") @@ -26,10 +26,9 @@ func TestClientImage(t *testing.T) { require.GreaterT(t, idx, -1) modVersion := strings.TrimPrefix(f.Require[idx].Mod.Version, "v") - clientImg := ClientImage() - start := strings.Index(clientImg, ":") + 1 - end := strings.Index(clientImg, "@") - imgVersion := clientImg[start:end] + start := strings.Index(NetbirdClientImage, ":") + 1 + end := strings.Index(NetbirdClientImage, "@") + imgVersion := NetbirdClientImage[start:end] require.EqualT(t, modVersion, imgVersion) } diff --git a/pkg/applyconfigurations/api/v1alpha1/clusterproxy.go b/pkg/applyconfigurations/api/v1alpha1/clusterproxy.go new file mode 100644 index 0000000..cd1b79d --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/clusterproxy.go @@ -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 +} diff --git a/pkg/applyconfigurations/api/v1alpha1/clusterproxyspec.go b/pkg/applyconfigurations/api/v1alpha1/clusterproxyspec.go new file mode 100644 index 0000000..5be3f70 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/clusterproxyspec.go @@ -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 +} diff --git a/pkg/applyconfigurations/api/v1alpha1/clusterproxystatus.go b/pkg/applyconfigurations/api/v1alpha1/clusterproxystatus.go new file mode 100644 index 0000000..1928f17 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/clusterproxystatus.go @@ -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 +} diff --git a/pkg/applyconfigurations/utils.go b/pkg/applyconfigurations/utils.go index 9ecc3ad..7a18c17 100644 --- a/pkg/applyconfigurations/utils.go +++ b/pkg/applyconfigurations/utils.go @@ -18,6 +18,12 @@ import ( func ForKind(kind schema.GroupVersionKind) interface{} { switch kind { // 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"): return &apiv1alpha1.ContainerOverrideApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("CrossNamespaceReference"):