diff --git a/Makefile b/Makefile index 953fd54..374b69d 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,7 @@ endif .PHONY: install install: manifests ## Install CRDs into the K8s cluster specified in ~/.kube/config. - $(KUBECTL) apply -f helm/kubernetes-operator/crds + $(KUBECTL) apply --server-side -f helm/kubernetes-operator/crds .PHONY: uninstall uninstall: manifests ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. diff --git a/PROJECT b/PROJECT index 166eb1c..7dfe0d1 100644 --- a/PROJECT +++ b/PROJECT @@ -91,4 +91,20 @@ resources: kind: Group path: github.com/netbirdio/kubernetes-operator/api/v1alpha1 version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: netbird.io + kind: NetworkRouter + path: github.com/netbirdio/kubernetes-operator/api/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: netbird.io + kind: NetworkResource + path: github.com/netbirdio/kubernetes-operator/api/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/v1alpha1/condition_types.go b/api/v1alpha1/condition_types.go index 0958898..24a8cd6 100644 --- a/api/v1alpha1/condition_types.go +++ b/api/v1alpha1/condition_types.go @@ -6,4 +6,5 @@ const ReadyCondition = "Ready" const ( ReconciledReason = "Reconciled" + DependencyReason = "Dependency" ) diff --git a/api/v1alpha1/networkresource_types.go b/api/v1alpha1/networkresource_types.go new file mode 100644 index 0000000..68effbd --- /dev/null +++ b/api/v1alpha1/networkresource_types.go @@ -0,0 +1,90 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NetworkResourceSpec defines the desired state of NetworkResource. +type NetworkResourceSpec struct { + // NetworkRouterRef is a reference to the network and router where the resource will be created. + // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="Value is immutable" + NetworkRouterRef CrossNamespaceReference `json:"networkRouterRef"` + + // ServiceRef is a reference to the service to expose in the Network. + ServiceRef corev1.LocalObjectReference `json:"serviceRef"` + + // Groups are references to groups that the resource will be a part of. + // +optional + Groups []ResourceReference `json:"groups,omitempty"` +} + +// NetworkResourceStatus defines the observed state of NetworkResource. +type NetworkResourceStatus struct { + // ObservedGeneration is the last reconciled generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions holds the conditions for the NetworkResource. + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // NetworkID is the id of the network the resource is created in. + // +optional + NetworkID string `json:"networkID,omitempty"` + + // ResourceID is the id of the created resource. + // +optional + ResourceID string `json:"resourceID,omitempty"` + + // DNSZoneID is the id of the zone the DNS record is created in. + // +optional + DNSZoneID string `json:"dnsZoneID,omitempty"` + + // DNSRecordID is the id of the created DNS record. + // +optional + DNSRecordID string `json:"dnsRecordID,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" + +// NetworkResource is the Schema for the networkresources API. +type NetworkResource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +required + Spec NetworkResourceSpec `json:"spec"` + + // +kubebuilder:default={"observedGeneration":-1} + Status NetworkResourceStatus `json:"status,omitempty"` +} + +// GetConditions returns the status conditions of the object. +func (n *NetworkResource) GetConditions() []metav1.Condition { + return n.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (n *NetworkResource) SetConditions(conditions []metav1.Condition) { + n.Status.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// NetworkResourceList contains a list of NetworkResource. +type NetworkResourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitzero"` + Items []NetworkResource `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NetworkResource{}, &NetworkResourceList{}) +} diff --git a/api/v1alpha1/networkrouter_types.go b/api/v1alpha1/networkrouter_types.go new file mode 100644 index 0000000..10640bc --- /dev/null +++ b/api/v1alpha1/networkrouter_types.go @@ -0,0 +1,106 @@ +package v1alpha1 + +import ( + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// NetworkRouterSpec defines the desired state of NetworkRouter. +type NetworkRouterSpec struct { + // DNSZoneRef is a reference to the DNS zone used to create records for resources. + // +required + DNSZoneRef DNSZoneReference `json:"dnsZoneRef"` + + // WorkloadOverride contains configuration that will override the default workload. + // +optional + WorkloadOverride *WorkloadOverride `json:"workloadOverride,omitempty"` +} + +// DNSZoneReference references a Netbird DNS zone by domain name. +type DNSZoneReference struct { + // Name is the domain name of an existing Netbird DNS zone, e.g. "example.com". + // +required + Name string `json:"name"` +} + +type WorkloadOverride struct { + // Labels that will be added. + // +optional + Labels map[string]string `json:"labels"` + + // Annotations that will be added. + // +optional + Annotations map[string]string `json:"annotations"` + + // Replicas sets the amount of client replicas. + // +optional + Replicas *int32 `json:"replicas"` + + // PodTemplate overrides the pod template. + // +optional + // +kubebuilder:pruning:PreserveUnknownFields + // +kubebuilder:validation:Schemaless + PodTemplate *corev1.PodTemplateSpec `json:"podTemplate"` +} + +// NetworkRouterStatus defines the observed state of NetworkRouter. +type NetworkRouterStatus struct { + // ObservedGeneration is the last reconciled generation. + // +optional + ObservedGeneration int64 `json:"observedGeneration,omitempty"` + + // Conditions holds the conditions for the NetworkRouter. + // +listType=map + // +listMapKey=type + // +optional + Conditions []metav1.Condition `json:"conditions,omitempty"` + + // RoutingPeerID is the id of the created routing peer. + // +optional + RoutingPeerID string `json:"routingPeerID,omitempty"` + + // NetworkID is the id of the network the routing peer was created in. + // +optional + NetworkID string `json:"networkID,omitempty"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource +// +kubebuilder:printcolumn:name="Ready",type="string",JSONPath=".status.conditions[?(@.type==\"Ready\")].status",description="" +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp",description="" + +// NetworkRouter is the Schema for the networkrouters API. +type NetworkRouter struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // +required + Spec NetworkRouterSpec `json:"spec"` + + // +kubebuilder:default={"observedGeneration":-1} + Status NetworkRouterStatus `json:"status,omitempty"` +} + +// GetConditions returns the status conditions of the object. +func (n *NetworkRouter) GetConditions() []metav1.Condition { + return n.Status.Conditions +} + +// SetConditions sets the status conditions on the object. +func (n *NetworkRouter) SetConditions(conditions []metav1.Condition) { + n.Status.Conditions = conditions +} + +// +kubebuilder:object:root=true + +// NetworkRouterList contains a list of NetworkRouter. +type NetworkRouterList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitzero"` + Items []NetworkRouter `json:"items"` +} + +func init() { + SchemeBuilder.Register(&NetworkRouter{}, &NetworkRouterList{}) +} diff --git a/api/v1alpha1/reference_type.go b/api/v1alpha1/reference_type.go index 670170d..b6cbe8d 100644 --- a/api/v1alpha1/reference_type.go +++ b/api/v1alpha1/reference_type.go @@ -4,11 +4,21 @@ import corev1 "k8s.io/api/core/v1" // +kubebuilder:validation:XValidation:rule="(has(self.id) && !has(self.localRef)) || (!has(self.id) && has(self.localRef))",message="exactly one of id or localRef must be set" type ResourceReference struct { - // id of the resource in the Netbird API. + // ID is the id of a resource in the Netbird API. // +optional ID *string `json:"id,omitempty"` - // local reference to the object in the same namespace. + // LocalReference is a reference to a object in the same namespace. // +optional LocalRef *corev1.LocalObjectReference `json:"localRef,omitempty"` } + +type CrossNamespaceReference struct { + // Name of the referent. + // +required + Name string `json:"name"` + + // Namespace of the referent. + // +required + Namespace string `json:"namespace"` +} diff --git a/api/v1alpha1/setupkey_types.go b/api/v1alpha1/setupkey_types.go index e7a106c..22de03e 100644 --- a/api/v1alpha1/setupkey_types.go +++ b/api/v1alpha1/setupkey_types.go @@ -6,6 +6,10 @@ import ( // SetupKeySpec defines the desired state of SetupKey. type SetupKeySpec struct { + // Name of the setup key. + // +kubebuilder:validation:MinLength=1 + Name string `json:"name"` + // Ephemeral decides if peers added with the key are ephemeral or not. // +kubebuilder:validation:XValidation:rule="self == oldSelf",message="ephemeral is immutable" Ephemeral bool `json:"ephemeral"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index ad01638..6cdd245 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -10,6 +10,36 @@ import ( runtime "k8s.io/apimachinery/pkg/runtime" ) +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *CrossNamespaceReference) DeepCopyInto(out *CrossNamespaceReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CrossNamespaceReference. +func (in *CrossNamespaceReference) DeepCopy() *CrossNamespaceReference { + if in == nil { + return nil + } + out := new(CrossNamespaceReference) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *DNSZoneReference) DeepCopyInto(out *DNSZoneReference) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DNSZoneReference. +func (in *DNSZoneReference) DeepCopy() *DNSZoneReference { + if in == nil { + return nil + } + out := new(DNSZoneReference) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Group) DeepCopyInto(out *Group) { *out = *in @@ -106,6 +136,213 @@ func (in *GroupStatus) DeepCopy() *GroupStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkResource) DeepCopyInto(out *NetworkResource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResource. +func (in *NetworkResource) DeepCopy() *NetworkResource { + if in == nil { + return nil + } + out := new(NetworkResource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkResource) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkResourceList) DeepCopyInto(out *NetworkResourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NetworkResource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResourceList. +func (in *NetworkResourceList) DeepCopy() *NetworkResourceList { + if in == nil { + return nil + } + out := new(NetworkResourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkResourceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkResourceSpec) DeepCopyInto(out *NetworkResourceSpec) { + *out = *in + out.NetworkRouterRef = in.NetworkRouterRef + out.ServiceRef = in.ServiceRef + if in.Groups != nil { + in, out := &in.Groups, &out.Groups + *out = make([]ResourceReference, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResourceSpec. +func (in *NetworkResourceSpec) DeepCopy() *NetworkResourceSpec { + if in == nil { + return nil + } + out := new(NetworkResourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkResourceStatus) DeepCopyInto(out *NetworkResourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkResourceStatus. +func (in *NetworkResourceStatus) DeepCopy() *NetworkResourceStatus { + if in == nil { + return nil + } + out := new(NetworkResourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkRouter) DeepCopyInto(out *NetworkRouter) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouter. +func (in *NetworkRouter) DeepCopy() *NetworkRouter { + if in == nil { + return nil + } + out := new(NetworkRouter) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkRouter) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkRouterList) DeepCopyInto(out *NetworkRouterList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]NetworkRouter, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouterList. +func (in *NetworkRouterList) DeepCopy() *NetworkRouterList { + if in == nil { + return nil + } + out := new(NetworkRouterList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *NetworkRouterList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkRouterSpec) DeepCopyInto(out *NetworkRouterSpec) { + *out = *in + out.DNSZoneRef = in.DNSZoneRef + if in.WorkloadOverride != nil { + in, out := &in.WorkloadOverride, &out.WorkloadOverride + *out = new(WorkloadOverride) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouterSpec. +func (in *NetworkRouterSpec) DeepCopy() *NetworkRouterSpec { + if in == nil { + return nil + } + out := new(NetworkRouterSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkRouterStatus) DeepCopyInto(out *NetworkRouterStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make([]v1.Condition, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkRouterStatus. +func (in *NetworkRouterStatus) DeepCopy() *NetworkRouterStatus { + if in == nil { + return nil + } + out := new(NetworkRouterStatus) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResourceReference) DeepCopyInto(out *ResourceReference) { *out = *in @@ -238,3 +475,42 @@ func (in *SetupKeyStatus) DeepCopy() *SetupKeyStatus { in.DeepCopyInto(out) return out } + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *WorkloadOverride) DeepCopyInto(out *WorkloadOverride) { + *out = *in + if in.Labels != nil { + in, out := &in.Labels, &out.Labels + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Annotations != nil { + in, out := &in.Annotations, &out.Annotations + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Replicas != nil { + in, out := &in.Replicas, &out.Replicas + *out = new(int32) + **out = **in + } + if in.PodTemplate != nil { + in, out := &in.PodTemplate, &out.PodTemplate + *out = new(corev1.PodTemplateSpec) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new WorkloadOverride. +func (in *WorkloadOverride) DeepCopy() *WorkloadOverride { + if in == nil { + return nil + } + out := new(WorkloadOverride) + in.DeepCopyInto(out) + return out +} diff --git a/cmd/main.go b/cmd/main.go index ce22865..c032e63 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -29,7 +29,7 @@ import ( // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" - netbirdrest "github.com/netbirdio/netbird/shared/management/client/rest" + netbird "github.com/netbirdio/netbird/shared/management/client/rest" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" @@ -222,11 +222,15 @@ func main() { } if len(netbirdAPIKey) > 0 { - netbird := netbirdrest.New(managementURL, netbirdAPIKey) + nbClient := netbird.NewWithOptions( + netbird.WithManagementURL(managementURL), + netbird.WithBearerToken(netbirdAPIKey), + netbird.WithUserAgent("netbird-operator"), + ) if err = (&controller.NBRoutingPeerReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, ClientImage: clientImage, ClusterName: clusterName, ManagementURL: managementURL, @@ -251,7 +255,7 @@ func main() { if err = (&controller.NBResourceReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, AllowAutomaticPolicyCreation: allowAutomaticPolicyCreation, ClusterName: clusterName, DefaultLabels: defaultLabelsMap, @@ -262,7 +266,7 @@ func main() { if err = (&controller.NBGroupReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "NBGroup") os.Exit(1) @@ -270,7 +274,7 @@ func main() { if err = (&controller.NBPolicyReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "NBPolicy") os.Exit(1) @@ -285,18 +289,34 @@ func main() { if err := (&controller.SetupKeyReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "Failed to create controller", "controller", "SetupKey") os.Exit(1) } if err := (&controller.GroupReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "Failed to create controller", "controller", "Group") os.Exit(1) } + if err := (&controller.NetworkRouterReconciler{ + Client: mgr.GetClient(), + Netbird: nbClient, + ClientImage: clientImage, + ManagementURL: managementURL, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "Failed to create controller", "controller", "NetworkRouter") + os.Exit(1) + } + if err := (&controller.NetworkResourceReconciler{ + Client: mgr.GetClient(), + Netbird: nbClient, + }).SetupWithManager(mgr); err != nil { + setupLog.Error(err, "Failed to create controller", "controller", "NetworkResource") + os.Exit(1) + } if gatewayAPIEnabled { if err = (&controller.GatewayClassReconciler{ @@ -313,7 +333,7 @@ func main() { } if err = (&controller.HTTPRouteReconciler{ Client: mgr.GetClient(), - Netbird: netbird, + Netbird: nbClient, ClusterDNS: clusterDNS, }).SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "HTTPRoute") diff --git a/examples/refactor/group.yaml b/examples/refactor/group.yaml index db9b9c6..70c726e 100644 --- a/examples/refactor/group.yaml +++ b/examples/refactor/group.yaml @@ -2,6 +2,6 @@ apiVersion: netbird.io/v1alpha1 kind: Group metadata: name: test - namespace: default + namespace: netbird spec: name: test diff --git a/examples/refactor/networkresource.yaml b/examples/refactor/networkresource.yaml new file mode 100644 index 0000000..0d8648b --- /dev/null +++ b/examples/refactor/networkresource.yaml @@ -0,0 +1,55 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: nginx + namespace: default + labels: + app: nginx +spec: + replicas: 1 + selector: + matchLabels: + app: nginx + strategy: + rollingUpdate: + maxSurge: 25% + maxUnavailable: 25% + type: RollingUpdate + template: + metadata: + labels: + app: nginx + spec: + containers: + - image: nginx + imagePullPolicy: Always + name: nginx +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + namespace: default + labels: + app: nginx +spec: + type: ClusterIP + ports: + - name: http + port: 80 + protocol: TCP + targetPort: 80 + selector: + app: nginx +--- +apiVersion: netbird.io/v1alpha1 +kind: NetworkResource +metadata: + name: test + namespace: default +spec: + networkRouterRef: + name: test + namespace: netbird + serviceRef: + name: nginx diff --git a/examples/refactor/networkrouter.yaml b/examples/refactor/networkrouter.yaml new file mode 100644 index 0000000..cdca337 --- /dev/null +++ b/examples/refactor/networkrouter.yaml @@ -0,0 +1,8 @@ +apiVersion: netbird.io/v1alpha1 +kind: NetworkRouter +metadata: + name: test + namespace: netbird +spec: + dnsZoneRef: + name: cluster.local diff --git a/examples/refactor/setup-key.yaml b/examples/refactor/setup-key.yaml index 7d54b06..7cd9899 100644 --- a/examples/refactor/setup-key.yaml +++ b/examples/refactor/setup-key.yaml @@ -2,6 +2,6 @@ apiVersion: netbird.io/v1alpha1 kind: SetupKey metadata: name: test - namespace: default + namespace: netbird spec: - ephemeral: true + name: test diff --git a/helm/kubernetes-operator/crds/netbird.io_networkresources.yaml b/helm/kubernetes-operator/crds/netbird.io_networkresources.yaml new file mode 100644 index 0000000..0f350f0 --- /dev/null +++ b/helm/kubernetes-operator/crds/netbird.io_networkresources.yaml @@ -0,0 +1,204 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: networkresources.netbird.io +spec: + group: netbird.io + names: + kind: NetworkResource + listKind: NetworkResourceList + plural: networkresources + singular: networkresource + 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: NetworkResource is the Schema for the networkresources 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: NetworkResourceSpec defines the desired state of NetworkResource. + properties: + groups: + description: Groups are references to groups that the resource will + be a part of. + items: + properties: + id: + description: ID is the id of a resource in the Netbird API. + type: string + localRef: + description: LocalReference is a reference to a object in the + same namespace. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + type: object + x-kubernetes-validations: + - message: exactly one of id or localRef must be set + rule: (has(self.id) && !has(self.localRef)) || (!has(self.id) + && has(self.localRef)) + type: array + networkRouterRef: + description: NetworkRouterRef is a reference to the network and router + where the resource will be created. + properties: + name: + description: Name of the referent. + type: string + namespace: + description: Namespace of the referent. + type: string + required: + - name + - namespace + type: object + x-kubernetes-validations: + - message: Value is immutable + rule: self == oldSelf + serviceRef: + description: ServiceRef is a reference to the service to expose in + the Network. + properties: + name: + default: "" + description: |- + Name of the referent. + This field is effectively required, but due to backwards compatibility is + allowed to be empty. Instances of this type with an empty value here are + almost certainly wrong. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + type: string + type: object + x-kubernetes-map-type: atomic + required: + - networkRouterRef + - serviceRef + type: object + status: + default: + observedGeneration: -1 + description: NetworkResourceStatus defines the observed state of NetworkResource. + properties: + conditions: + description: Conditions holds the conditions for the NetworkResource. + 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 + dnsRecordID: + description: DNSRecordID is the id of the created DNS record. + type: string + dnsZoneID: + description: DNSZoneID is the id of the zone the DNS record is created + in. + type: string + networkID: + description: NetworkID is the id of the network the resource is created + in. + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + resourceID: + description: ResourceID is the id of the created resource. + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/kubernetes-operator/crds/netbird.io_networkrouters.yaml b/helm/kubernetes-operator/crds/netbird.io_networkrouters.yaml new file mode 100644 index 0000000..6644007 --- /dev/null +++ b/helm/kubernetes-operator/crds/netbird.io_networkrouters.yaml @@ -0,0 +1,168 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.20.1 + name: networkrouters.netbird.io +spec: + group: netbird.io + names: + kind: NetworkRouter + listKind: NetworkRouterList + plural: networkrouters + singular: networkrouter + 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: NetworkRouter is the Schema for the networkrouters 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: NetworkRouterSpec defines the desired state of NetworkRouter. + properties: + dnsZoneRef: + description: DNSZoneRef is a reference to the DNS zone used to create + records for resources. + properties: + name: + description: Name is the domain name of an existing Netbird DNS + zone, e.g. "example.com". + type: string + required: + - name + type: object + workloadOverride: + description: WorkloadOverride contains configuration that will override + the default workload. + properties: + annotations: + additionalProperties: + type: string + description: Annotations that will be added. + type: object + labels: + additionalProperties: + type: string + description: Labels that will be added. + type: object + podTemplate: + description: PodTemplate overrides the pod template. + x-kubernetes-preserve-unknown-fields: true + replicas: + description: Replicas sets the amount of client replicas. + format: int32 + type: integer + type: object + required: + - dnsZoneRef + type: object + status: + default: + observedGeneration: -1 + description: NetworkRouterStatus defines the observed state of NetworkRouter. + properties: + conditions: + description: Conditions holds the conditions for the NetworkRouter. + 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 + networkID: + description: NetworkID is the id of the network the routing peer was + created in. + type: string + observedGeneration: + description: ObservedGeneration is the last reconciled generation. + format: int64 + type: integer + routingPeerID: + description: RoutingPeerID is the id of the created routing peer. + type: string + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/helm/kubernetes-operator/crds/netbird.io_setupkeys.yaml b/helm/kubernetes-operator/crds/netbird.io_setupkeys.yaml index 78ce1de..9ed68d4 100644 --- a/helm/kubernetes-operator/crds/netbird.io_setupkeys.yaml +++ b/helm/kubernetes-operator/crds/netbird.io_setupkeys.yaml @@ -52,10 +52,11 @@ spec: items: properties: id: - description: id of the resource in the Netbird API. + description: ID is the id of a resource in the Netbird API. type: string localRef: - description: local reference to the object in the same namespace. + description: LocalReference is a reference to a object in the + same namespace. properties: name: default: "" @@ -87,8 +88,13 @@ spec: x-kubernetes-validations: - message: ephemeral is immutable rule: self == oldSelf + name: + description: Name of the setup key. + minLength: 1 + type: string required: - ephemeral + - name type: object status: default: diff --git a/internal/controller/networkresource_controller.go b/internal/controller/networkresource_controller.go new file mode 100644 index 0000000..90bf28c --- /dev/null +++ b/internal/controller/networkresource_controller.go @@ -0,0 +1,301 @@ +package controller + +import ( + "context" + "fmt" + "strings" + "time" + + "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/patch" + netbird "github.com/netbirdio/netbird/shared/management/client/rest" + "github.com/netbirdio/netbird/shared/management/http/api" + corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/builder" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + "github.com/netbirdio/kubernetes-operator/internal/netbirdutil" +) + +type NetworkResourceReconciler struct { + client.Client + + Netbird *netbird.Client +} + +// +kubebuilder:rbac:groups=netbird.io,resources=networkresources,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=netbird.io,resources=networkresources/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=netbird.io,resources=networkresources/finalizers,verbs=update + +// nolint:gocyclo +func (r *NetworkResourceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + netResource := &nbv1alpha1.NetworkResource{} + err := r.Get(ctx, req.NamespacedName, netResource) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + sp := patch.NewSerialPatcher(netResource, r.Client) + + if !netResource.DeletionTimestamp.IsZero() { + return r.reconcileDelete(ctx, sp, netResource) + } + + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: netResource.Spec.ServiceRef.Name, + Namespace: netResource.Namespace, + }, + } + err = r.Get(ctx, client.ObjectKeyFromObject(svc), svc) + if err != nil { + if kerrors.IsNotFound(err) { + conditions.MarkFalse(netResource, nbv1alpha1.ReadyCondition, nbv1alpha1.DependencyReason, "Referenced Service cannot be found.") + err = sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + if svc.Spec.Type != corev1.ServiceTypeClusterIP { + conditions.MarkFalse(netResource, nbv1alpha1.ReadyCondition, nbv1alpha1.DependencyReason, "Referenced Service is not of type ClusterIP.") + err = sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + if svc.Spec.ClusterIP == "" || svc.Spec.ClusterIP == corev1.ClusterIPNone { + conditions.MarkFalse(netResource, nbv1alpha1.ReadyCondition, nbv1alpha1.DependencyReason, "Referenced Service does not have a ClusterIP set.") + err = sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + netRouter := &nbv1alpha1.NetworkRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: netResource.Spec.NetworkRouterRef.Name, + Namespace: netResource.Spec.NetworkRouterRef.Namespace, + }, + } + err = r.Get(ctx, client.ObjectKeyFromObject(netRouter), netRouter) + if err != nil { + if kerrors.IsNotFound(err) { + conditions.MarkFalse(netResource, nbv1alpha1.ReadyCondition, nbv1alpha1.DependencyReason, "Referenced NetworkRouter cannot be found.") + err = sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + return ctrl.Result{}, err + } + if netRouter.Status.NetworkID == "" || netRouter.Status.RoutingPeerID == "" { + conditions.MarkFalse(netResource, nbv1alpha1.ReadyCondition, nbv1alpha1.DependencyReason, "Referenced NetworkRouter is not ready.") + err = sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil + } + + groupIDs, err := netbirdutil.GetGroupIDs(ctx, r.Client, r.Netbird, netResource.Spec.Groups, netResource.Namespace) + if err != nil { + return ctrl.Result{}, err + } + + controllerutil.AddFinalizer(netResource, nbv1alpha1.NetbirdFinalizer) + + resourceID, err := func() (string, error) { + netReq := api.NetworkResourceRequest{ + Name: svc.Name + "/" + svc.Namespace, + Address: svc.Spec.ClusterIP, + Enabled: true, + Groups: groupIDs, + } + if netResource.Status.ResourceID != "" { + netResp, err := r.Netbird.Networks.Resources(netRouter.Status.NetworkID).Update(ctx, netResource.Status.ResourceID, netReq) + if err != nil && !netbird.IsNotFound(err) { + return "", err + } + if err == nil { + return netResp.Id, nil + } + } + netResp, err := r.Netbird.Networks.Resources(netRouter.Status.NetworkID).Create(ctx, netReq) + if err != nil { + return "", err + } + return netResp.Id, nil + }() + if err != nil { + return ctrl.Result{}, err + } + netResource.Status.NetworkID = netRouter.Status.NetworkID + netResource.Status.ResourceID = resourceID + err = sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + + // Create DNS records for resource. + zone, err := netbirdutil.GetDNSZoneByName(ctx, r.Netbird, netRouter.Spec.DNSZoneRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + // If zone has changed we need to delete the old records. + if netResource.Status.DNSZoneID != "" && netResource.Status.DNSZoneID != zone.Id { + err = r.Netbird.DNSZones.DeleteRecord(ctx, netResource.Status.DNSZoneID, netResource.Status.DNSRecordID) + if err != nil && !netbird.IsNotFound(err) { + return ctrl.Result{}, err + } + netResource.Status.DNSZoneID = "" + netResource.Status.DNSRecordID = "" + } + + recordID, err := func() (string, error) { + dnsReq := api.DNSRecordRequest{ + Content: svc.Spec.ClusterIP, + Name: strings.Join([]string{svc.Name, svc.Namespace, zone.Name}, "."), + Ttl: int(5 * time.Minute / time.Second), + Type: api.DNSRecordTypeA, + } + if netResource.Status.DNSZoneID != "" && netResource.Status.DNSRecordID != "" { + recordResp, err := r.Netbird.DNSZones.UpdateRecord(ctx, netResource.Status.DNSZoneID, netResource.Status.DNSRecordID, dnsReq) + if err != nil && !netbird.IsNotFound(err) { + return "", err + } + if err == nil { + return recordResp.Id, nil + } + } + recordResp, err := r.Netbird.DNSZones.CreateRecord(ctx, zone.Id, dnsReq) + if err != nil { + return "", err + } + return recordResp.Id, nil + }() + if err != nil { + return ctrl.Result{}, err + } + netResource.Status.DNSZoneID = zone.Id + netResource.Status.DNSRecordID = recordID + + conditions.MarkTrue(netResource, nbv1alpha1.ReadyCondition, nbv1alpha1.ReconciledReason, "") + err = sp.Patch(ctx, netResource, patch.WithStatusObservedGeneration{}) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil +} + +func (r *NetworkResourceReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, netResource *nbv1alpha1.NetworkResource) (ctrl.Result, error) { + if netResource.Status.NetworkID != "" && netResource.Status.ResourceID != "" { + err := r.Netbird.Networks.Resources(netResource.Status.NetworkID).Delete(ctx, netResource.Status.ResourceID) + if err != nil && !netbird.IsNotFound(err) { + return ctrl.Result{}, err + } + } + if netResource.Status.DNSZoneID != "" && netResource.Status.DNSRecordID != "" { + err := r.Netbird.DNSZones.DeleteRecord(ctx, netResource.Status.DNSZoneID, netResource.Status.DNSRecordID) + if err != nil && !netbird.IsNotFound(err) { + return ctrl.Result{}, err + } + } + + controllerutil.RemoveFinalizer(netResource, nbv1alpha1.NetbirdFinalizer) + err := sp.Patch(ctx, netResource) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil +} + +func (r *NetworkResourceReconciler) SetupWithManager(mgr ctrl.Manager) error { + err := mgr.GetFieldIndexer().IndexField(context.Background(), &nbv1alpha1.NetworkResource{}, ".spec.networkRouterRef", func(obj client.Object) []string { + netResource := obj.(*nbv1alpha1.NetworkResource) + ref := netResource.Spec.NetworkRouterRef + if ref.Name == "" { + return nil + } + if ref.Namespace == "" { + ref.Namespace = netResource.Namespace + } + return []string{fmt.Sprintf("%s/%s", ref.Name, ref.Namespace)} + }) + if err != nil { + return err + } + err = mgr.GetFieldIndexer().IndexField(context.Background(), &nbv1alpha1.NetworkResource{}, ".spec.serviceRef", func(obj client.Object) []string { + netResource := obj.(*nbv1alpha1.NetworkResource) + ref := netResource.Spec.ServiceRef + if ref.Name == "" { + return nil + } + return []string{netResource.Spec.ServiceRef.Name} + }) + if err != nil { + return err + } + + return ctrl.NewControllerManagedBy(mgr). + For(&nbv1alpha1.NetworkResource{}). + Watches( + &nbv1alpha1.NetworkRouter{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + netResourceList := &nbv1alpha1.NetworkResourceList{} + err := r.List(ctx, netResourceList, client.MatchingFields{".spec.networkRouterRef": fmt.Sprintf("%s/%s", obj.GetName(), obj.GetNamespace())}) + if err != nil { + return nil + } + + requests := make([]reconcile.Request, len(netResourceList.Items)) + for i, item := range netResourceList.Items { + requests[i] = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.Name, + Namespace: item.Namespace, + }, + } + } + return requests + }), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). + Watches( + &corev1.Service{}, + handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request { + netResourceList := &nbv1alpha1.NetworkResourceList{} + err := r.List(ctx, netResourceList, client.InNamespace(obj.GetNamespace()), client.MatchingFields{".spec.serviceRef": obj.GetName()}) + if err != nil { + return nil + } + + requests := make([]reconcile.Request, len(netResourceList.Items)) + for i, item := range netResourceList.Items { + requests[i] = reconcile.Request{ + NamespacedName: types.NamespacedName{ + Name: item.Name, + Namespace: item.Namespace, + }, + } + } + return requests + }), + builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}), + ). + Complete(r) +} diff --git a/internal/controller/networkresource_controller_test.go b/internal/controller/networkresource_controller_test.go new file mode 100644 index 0000000..e88d228 --- /dev/null +++ b/internal/controller/networkresource_controller_test.go @@ -0,0 +1,150 @@ +package controller + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + "github.com/netbirdio/kubernetes-operator/internal/netbirdmock" + "github.com/netbirdio/netbird/shared/management/http/api" +) + +var _ = Describe("NetworkResource Controller", func() { + Context("When reconciling a resource", func() { + ctx := context.Background() + + var netResourceRec *NetworkResourceReconciler + var netRouterRec *NetworkRouterReconciler + var setupKeyRec *SetupKeyReconciler + var groupRec *GroupReconciler + + nn := client.ObjectKey{ + Name: "test-resource", + Namespace: "network-resource", + } + + BeforeEach(func() { + nbClient := netbirdmock.Client() + netResourceRec = &NetworkResourceReconciler{ + Client: k8sClient, + Netbird: nbClient, + } + netRouterRec = &NetworkRouterReconciler{ + Client: k8sClient, + Netbird: nbClient, + ClientImage: "docker.io/netbirdio/netbird:latest", + ManagementURL: "https://netbird.io", + } + setupKeyRec = &SetupKeyReconciler{ + Client: k8sClient, + Netbird: nbClient, + } + groupRec = &GroupReconciler{ + Client: k8sClient, + Netbird: nbClient, + } + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Namespace, + }, + } + Expect(k8sClient.Create(ctx, ns)).To(Succeed()) + }) + + AfterEach(func() { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Namespace, + }, + } + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(ns), ns) + if kerrors.IsNotFound(err) { + return + } + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient.Delete(ctx, ns)).To(Succeed()) + }) + + It("creates a network resource and DNS record", func() { + zoneReq := api.ZoneRequest{ + Name: "cluster.local", + Domain: "cluster.local", + } + _, err := netRouterRec.Netbird.DNSZones.CreateZone(ctx, zoneReq) + Expect(err).ToNot(HaveOccurred()) + + // Create network router that we reference. + netRouter := &nbv1alpha1.NetworkRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Spec: nbv1alpha1.NetworkRouterSpec{ + DNSZoneRef: nbv1alpha1.DNSZoneReference{ + Name: "cluster.local", + }, + }, + } + Expect(k8sClient.Create(ctx, netRouter)).To(Succeed()) + for range 3 { + _, err := netRouterRec.Reconcile(ctx, reconcile.Request{NamespacedName: nn}) + Expect(err).NotTo(HaveOccurred()) + key := client.ObjectKey{Name: fmt.Sprintf("networkrouter-%s", netRouter.Name), Namespace: nn.Namespace} + _, err = groupRec.Reconcile(ctx, reconcile.Request{NamespacedName: key}) + Expect(err).NotTo(HaveOccurred()) + _, err = setupKeyRec.Reconcile(ctx, reconcile.Request{NamespacedName: key}) + Expect(err).NotTo(HaveOccurred()) + } + + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "test", + Namespace: nn.Namespace, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Port: 8080, + }, + }, + }, + } + Expect(k8sClient.Create(ctx, svc)).To(Succeed()) + + netResource := &nbv1alpha1.NetworkResource{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Spec: nbv1alpha1.NetworkResourceSpec{ + NetworkRouterRef: nbv1alpha1.CrossNamespaceReference{ + Name: netRouter.Name, + Namespace: netRouter.Namespace, + }, + ServiceRef: corev1.LocalObjectReference{ + Name: svc.Name, + }, + }, + } + Expect(k8sClient.Create(ctx, netResource)).To(Succeed()) + _, err = netResourceRec.Reconcile(ctx, reconcile.Request{NamespacedName: nn}) + Expect(err).NotTo(HaveOccurred()) + + err = k8sClient.Get(ctx, nn, netResource) + Expect(err).NotTo(HaveOccurred()) + Expect(netResource.Status.NetworkID).NotTo(BeEmpty()) + Expect(netResource.Status.ResourceID).NotTo(BeEmpty()) + Expect(netResource.Status.DNSZoneID).NotTo(BeEmpty()) + Expect(netResource.Status.DNSRecordID).NotTo(BeEmpty()) + }) + }) +}) diff --git a/internal/controller/networkrouter_controller.go b/internal/controller/networkrouter_controller.go new file mode 100644 index 0000000..dae9378 --- /dev/null +++ b/internal/controller/networkrouter_controller.go @@ -0,0 +1,322 @@ +package controller + +import ( + "context" + "crypto/sha256" + "encoding/json" + "fmt" + "maps" + "time" + + "github.com/fluxcd/pkg/runtime/conditions" + "github.com/fluxcd/pkg/runtime/patch" + "github.com/netbirdio/kubernetes-operator/internal/netbirdutil" + "github.com/netbirdio/kubernetes-operator/internal/ssautil" + netbird "github.com/netbirdio/netbird/shared/management/client/rest" + "github.com/netbirdio/netbird/shared/management/http/api" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/strategicpatch" + appsv1ac "k8s.io/client-go/applyconfigurations/apps/v1" + corev1ac "k8s.io/client-go/applyconfigurations/core/v1" + metav1ac "k8s.io/client-go/applyconfigurations/meta/v1" + "k8s.io/utils/ptr" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + nbv1alpha1ac "github.com/netbirdio/kubernetes-operator/pkg/applyconfigurations/api/v1alpha1" +) + +type NetworkRouterReconciler struct { + client.Client + + Netbird *netbird.Client + ManagementURL string + ClientImage string +} + +// +kubebuilder:rbac:groups=netbird.io,resources=networkrouters,verbs=get;list;watch;create;update;patch;delete +// +kubebuilder:rbac:groups=netbird.io,resources=networkrouters/status,verbs=get;update;patch +// +kubebuilder:rbac:groups=netbird.io,resources=networkrouters/finalizers,verbs=update + +// nolint:gocyclo +func (r *NetworkRouterReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + netRouter := &nbv1alpha1.NetworkRouter{} + err := r.Get(ctx, req.NamespacedName, netRouter) + if err != nil { + return ctrl.Result{}, client.IgnoreNotFound(err) + } + sp := patch.NewSerialPatcher(netRouter, r.Client) + + if !netRouter.DeletionTimestamp.IsZero() { + return r.reconcileDelete(ctx, sp, netRouter) + } + + ownerRef, err := ssautil.OwnerReference(netRouter, r.Scheme()) + if err != nil { + return ctrl.Result{}, err + } + + // Ensure the DNS Zone exists. + _, err = netbirdutil.GetDNSZoneByName(ctx, r.Netbird, netRouter.Spec.DNSZoneRef.Name) + if err != nil { + return ctrl.Result{}, err + } + + controllerutil.AddFinalizer(netRouter, nbv1alpha1.NetbirdFinalizer) + + networkID, err := func() (string, error) { + networkReq := api.NetworkRequest{ + Name: netRouter.Name, + } + if netRouter.Status.NetworkID != "" { + networkResp, err := r.Netbird.Networks.Update(ctx, netRouter.Status.NetworkID, networkReq) + if err != nil && !netbird.IsNotFound(err) { + return "", err + } + if err == nil { + return networkResp.Id, nil + } + } + networkResp, err := r.Netbird.Networks.Create(ctx, networkReq) + if err != nil { + return "", err + } + return networkResp.Id, nil + }() + if err != nil { + return ctrl.Result{}, err + } + netRouter.Status.NetworkID = networkID + err = sp.Patch(ctx, netRouter) + if err != nil { + return ctrl.Result{}, err + } + + // Calculate unique suffix used for Netbird resources. + sum := sha256.Sum256([]byte(netRouter.UID)) + uniqueSuffix := networkID + "-" + fmt.Sprintf("%x", sum[:4])[:8] + + // Create the group used by the router to discover peers. + groupAC := nbv1alpha1ac.Group(fmt.Sprintf("networkrouter-%s", netRouter.Name), req.Namespace). + WithOwnerReferences(ownerRef). + WithSpec( + nbv1alpha1ac.GroupSpec(). + WithName(fmt.Sprintf("networkrouter-%s", uniqueSuffix)), + ) + err = r.Client.Apply(ctx, groupAC) + if err != nil { + return ctrl.Result{}, err + } + group := &nbv1alpha1.Group{ + ObjectMeta: metav1.ObjectMeta{ + Name: *groupAC.Name, + Namespace: *groupAC.Namespace, + }, + } + err = r.Client.Get(ctx, client.ObjectKeyFromObject(group), group) + if err != nil { + return ctrl.Result{}, err + } + if group.Status.GroupID == "" { + return ctrl.Result{}, nil + } + + // Create the setup key used by routing peers. + setupKeyAC := nbv1alpha1ac.SetupKey(fmt.Sprintf("networkrouter-%s", netRouter.Name), req.Namespace). + WithOwnerReferences(ownerRef). + WithSpec( + nbv1alpha1ac.SetupKeySpec(). + WithName(fmt.Sprintf("networkrouter-%s", uniqueSuffix)). + WithEphemeral(true). + WithAutoGroups(nbv1alpha1ac.ResourceReference().WithID(group.Status.GroupID)), + ) + 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{}, err + } + if setupKey.Status.SetupKeyID == "" { + return ctrl.Result{}, nil + } + + // Create the routing peer in netbird. + routingPeerID, err := func() (string, error) { + routerReq := api.NetworkRouterRequest{ + Enabled: true, + Masquerade: true, + Metric: 9999, + PeerGroups: ptr.To([]string{group.Status.GroupID}), + } + if netRouter.Status.RoutingPeerID != "" { + resp, err := r.Netbird.Networks.Routers(networkID).Update(ctx, netRouter.Status.RoutingPeerID, routerReq) + if err != nil && !netbird.IsNotFound(err) { + return "", err + } + if err == nil { + return resp.Id, nil + } + } + resp, err := r.Netbird.Networks.Routers(networkID).Create(ctx, routerReq) + if err != nil { + return "", err + } + return resp.Id, nil + }() + if err != nil { + return ctrl.Result{}, err + } + netRouter.Status.RoutingPeerID = routingPeerID + err = sp.Patch(ctx, netRouter, patch.WithStatusObservedGeneration{}) + if err != nil { + return ctrl.Result{}, err + } + + // Create the deployment. + selectorLabels := map[string]string{ + "app.kubernetes.io/name": "networkrouter", + "app.kubernetes.io/instance": req.Name, + } + + podTemplateSpecAC := corev1ac.PodTemplateSpec(). + WithLabels(selectorLabels). + WithSpec(corev1ac.PodSpec(). + WithContainers(corev1ac.Container(). + WithName("netbird"). + WithImage(r.ClientImage). + WithEnv( + corev1ac.EnvVar(). + WithName("NB_SETUP_KEY"). + WithValueFrom(corev1ac.EnvVarSource(). + WithSecretKeyRef(corev1ac.SecretKeySelector(). + WithName(setupKey.SecretName()). + WithKey(SetupKeySecretKey), + ), + ), + corev1ac.EnvVar(). + WithName("NB_MANAGEMENT_URL"). + WithValue(r.ManagementURL), + corev1ac.EnvVar(). + WithName("NB_LOG_LEVEL"). + WithValue("info"), + ). + WithStartupProbe(corev1ac.Probe().WithExec(corev1ac.ExecAction().WithCommand("netbird", "status", "--check", "startup"))). + WithReadinessProbe(corev1ac.Probe().WithExec(corev1ac.ExecAction().WithCommand("netbird", "status", "--check", "ready"))). + WithSecurityContext(corev1ac.SecurityContext(). + WithCapabilities(corev1ac.Capabilities(). + WithAdd("NET_ADMIN"). + WithAdd("SYS_RESOURCE"). + WithAdd("SYS_ADMIN"), + ). + WithPrivileged(true), + ), + ), + ) + + depLabels := map[string]string{} + depAnnotations := map[string]string{} + replicas := int32(3) + if netRouter.Spec.WorkloadOverride != nil { + if netRouter.Spec.WorkloadOverride.Labels != nil { + depLabels = netRouter.Spec.WorkloadOverride.Labels + } + if netRouter.Spec.WorkloadOverride.Annotations != nil { + depAnnotations = netRouter.Spec.WorkloadOverride.Annotations + } + if netRouter.Spec.WorkloadOverride.Replicas != nil { + replicas = *netRouter.Spec.WorkloadOverride.Replicas + } + if netRouter.Spec.WorkloadOverride.PodTemplate != nil { + baseJSON, err := json.Marshal(&podTemplateSpecAC) + if err != nil { + return ctrl.Result{}, err + } + overrideJSON, err := json.Marshal(netRouter.Spec.WorkloadOverride.PodTemplate) + if err != nil { + return ctrl.Result{}, err + } + mergedJSON, err := strategicpatch.StrategicMergePatch(baseJSON, overrideJSON, corev1.PodTemplateSpec{}) + if err != nil { + return ctrl.Result{}, err + } + err = json.Unmarshal(mergedJSON, &podTemplateSpecAC) + if err != nil { + return ctrl.Result{}, err + } + } + } + maps.Copy(depLabels, selectorLabels) + + depAC := appsv1ac.Deployment(fmt.Sprintf("networkrouter-%s", req.Name), req.Namespace). + WithOwnerReferences(ownerRef). + WithLabels(depLabels). + WithAnnotations(depAnnotations). + WithSpec(appsv1ac.DeploymentSpec().WithReplicas(replicas).WithSelector(metav1ac.LabelSelector().WithMatchLabels(selectorLabels)).WithTemplate(podTemplateSpecAC)) + err = r.Client.Apply(ctx, depAC) + if err != nil { + return ctrl.Result{}, err + } + dep := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: *depAC.Name, + Namespace: *depAC.Namespace, + }, + } + err = r.Client.Get(ctx, client.ObjectKeyFromObject(dep), dep) + if err != nil { + return ctrl.Result{}, err + } + if dep.Status.ReadyReplicas != dep.Status.Replicas { + return ctrl.Result{}, nil + } + + conditions.MarkTrue(netRouter, nbv1alpha1.ReadyCondition, nbv1alpha1.ReconciledReason, "") + err = sp.Patch(ctx, netRouter, patch.WithStatusObservedGeneration{}) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{RequeueAfter: 15 * time.Minute}, nil +} + +func (r *NetworkRouterReconciler) reconcileDelete(ctx context.Context, sp *patch.SerialPatcher, netRouter *nbv1alpha1.NetworkRouter) (ctrl.Result, error) { + if netRouter.Status.RoutingPeerID != "" { + err := r.Netbird.Networks.Routers(netRouter.Status.NetworkID).Delete(ctx, netRouter.Status.RoutingPeerID) + if err != nil && !netbird.IsNotFound(err) { + return ctrl.Result{}, err + } + } + if netRouter.Status.NetworkID != "" { + err := r.Netbird.Networks.Delete(ctx, netRouter.Status.NetworkID) + if err != nil && !netbird.IsNotFound(err) { + return ctrl.Result{}, err + } + } + + controllerutil.RemoveFinalizer(netRouter, nbv1alpha1.NetbirdFinalizer) + err := sp.Patch(ctx, netRouter) + if err != nil { + return ctrl.Result{}, err + } + return ctrl.Result{}, nil +} + +func (r *NetworkRouterReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&nbv1alpha1.NetworkRouter{}). + Owns(&nbv1alpha1.Group{}). + Owns(&nbv1alpha1.SetupKey{}). + Owns(&appsv1.Deployment{}). + Complete(r) +} diff --git a/internal/controller/networkrouter_controller_test.go b/internal/controller/networkrouter_controller_test.go new file mode 100644 index 0000000..1eb95c6 --- /dev/null +++ b/internal/controller/networkrouter_controller_test.go @@ -0,0 +1,147 @@ +package controller + +import ( + "context" + "fmt" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + "github.com/netbirdio/kubernetes-operator/internal/netbirdmock" + "github.com/netbirdio/netbird/shared/management/http/api" +) + +var _ = Describe("NetworkRouter Controller", func() { + Context("When reconciling a resource", func() { + ctx := context.Background() + + var netRouterRec *NetworkRouterReconciler + var setupKeyRec *SetupKeyReconciler + var groupRec *GroupReconciler + + nn := client.ObjectKey{ + Name: "test-resource", + Namespace: "network-router", + } + + BeforeEach(func() { + nbClient := netbirdmock.Client() + netRouterRec = &NetworkRouterReconciler{ + Client: k8sClient, + Netbird: nbClient, + ClientImage: "docker.io/netbirdio/netbird:latest", + ManagementURL: "https://netbird.io", + } + setupKeyRec = &SetupKeyReconciler{ + Client: k8sClient, + Netbird: nbClient, + } + groupRec = &GroupReconciler{ + Client: k8sClient, + Netbird: nbClient, + } + + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Namespace, + }, + } + Expect(k8sClient.Create(ctx, ns)).To(Succeed()) + }) + + AfterEach(func() { + ns := &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Namespace, + }, + } + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(ns), ns) + if kerrors.IsNotFound(err) { + return + } + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient.Delete(ctx, ns)).To(Succeed()) + }) + + It("creates a routing peer along with a deployment", func() { + zoneReq := api.ZoneRequest{ + Name: "cluster.local", + Domain: "cluster.local", + } + _, err := netRouterRec.Netbird.DNSZones.CreateZone(ctx, zoneReq) + Expect(err).ToNot(HaveOccurred()) + + netRouter := &nbv1alpha1.NetworkRouter{ + ObjectMeta: metav1.ObjectMeta{ + Name: nn.Name, + Namespace: nn.Namespace, + }, + Spec: nbv1alpha1.NetworkRouterSpec{ + DNSZoneRef: nbv1alpha1.DNSZoneReference{ + Name: "cluster.local", + }, + }, + } + Expect(k8sClient.Create(ctx, netRouter)).To(Succeed()) + + group := &nbv1alpha1.Group{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("networkrouter-%s", netRouter.Name), + Namespace: nn.Namespace, + }, + } + setupKey := &nbv1alpha1.SetupKey{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("networkrouter-%s", netRouter.Name), + Namespace: nn.Namespace, + }, + } + dep := &appsv1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("networkrouter-%s", netRouter.Name), + Namespace: nn.Namespace, + }, + } + + for range 3 { + _, err := netRouterRec.Reconcile(ctx, reconcile.Request{NamespacedName: nn}) + Expect(err).NotTo(HaveOccurred()) + + _, err = groupRec.Reconcile(ctx, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(group)}) + Expect(err).NotTo(HaveOccurred()) + + _, err = setupKeyRec.Reconcile(ctx, reconcile.Request{NamespacedName: client.ObjectKeyFromObject(setupKey)}) + Expect(err).NotTo(HaveOccurred()) + } + + err = k8sClient.Get(ctx, nn, netRouter) + Expect(err).NotTo(HaveOccurred()) + Expect(netRouter.Status.NetworkID).ToNot(BeEmpty()) + _, err = netRouterRec.Netbird.Networks.Routers(netRouter.Status.NetworkID).Get(ctx, netRouter.Status.RoutingPeerID) + Expect(err).NotTo(HaveOccurred()) + + err = k8sClient.Get(ctx, client.ObjectKeyFromObject(group), group) + Expect(err).NotTo(HaveOccurred()) + Expect(group.OwnerReferences[0].UID).To(Equal(netRouter.UID)) + + err = k8sClient.Get(ctx, client.ObjectKeyFromObject(setupKey), setupKey) + Expect(err).NotTo(HaveOccurred()) + Expect(setupKey.OwnerReferences[0].UID).To(Equal(netRouter.UID)) + + err = k8sClient.Get(ctx, client.ObjectKeyFromObject(dep), dep) + Expect(err).NotTo(HaveOccurred()) + Expect(dep.OwnerReferences[0].UID).To(Equal(netRouter.UID)) + + routingPeerResp, err := netRouterRec.Netbird.Networks.Routers(netRouter.Status.NetworkID).Get(ctx, netRouter.Status.RoutingPeerID) + Expect(err).NotTo(HaveOccurred()) + Expect((*routingPeerResp.PeerGroups)[0]).To(Equal(group.Status.GroupID)) + }) + }) +}) diff --git a/internal/controller/setupkey_controller.go b/internal/controller/setupkey_controller.go index 3d283a5..c259e84 100644 --- a/internal/controller/setupkey_controller.go +++ b/internal/controller/setupkey_controller.go @@ -3,7 +3,6 @@ package controller import ( "context" "errors" - "fmt" "time" "github.com/fluxcd/pkg/runtime/conditions" @@ -12,7 +11,6 @@ import ( "github.com/netbirdio/netbird/shared/management/http/api" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" corev1ac "k8s.io/client-go/applyconfigurations/core/v1" "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" @@ -20,6 +18,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" + "github.com/netbirdio/kubernetes-operator/internal/netbirdutil" "github.com/netbirdio/kubernetes-operator/internal/ssautil" ) @@ -52,32 +51,9 @@ func (r *SetupKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c return r.reconcileDelete(ctx, sp, setupKey) } - // Get ids for auto groups. - autoGroupIDs := []string{} - for _, ref := range setupKey.Spec.AutoGroups { - switch { - case ref.ID != nil: - _, err := r.Netbird.Groups.Get(ctx, *ref.ID) - if err != nil { - return ctrl.Result{}, err - } - autoGroupIDs = append(autoGroupIDs, *ref.ID) - case ref.LocalRef != nil: - group := nbv1alpha1.Group{ - ObjectMeta: metav1.ObjectMeta{ - Name: ref.LocalRef.Name, - Namespace: setupKey.Namespace, - }, - } - err = r.Client.Get(ctx, client.ObjectKeyFromObject(&group), &group) - if err != nil { - return ctrl.Result{}, err - } - if group.Status.GroupID == "" { - return ctrl.Result{}, fmt.Errorf("group %s in auto groups list is not ready", group.Name) - } - autoGroupIDs = append(autoGroupIDs, group.Status.GroupID) - } + autoGroupIDs, err := netbirdutil.GetGroupIDs(ctx, r.Client, r.Netbird, setupKey.Spec.AutoGroups, setupKey.Namespace) + if err != nil { + return ctrl.Result{}, err } controllerutil.AddFinalizer(setupKey, nbv1alpha1.NetbirdFinalizer) @@ -151,7 +127,7 @@ func (r *SetupKeyReconciler) Reconcile(ctx context.Context, req ctrl.Request) (c AutoGroups: autoGroupIDs, Ephemeral: ptr.To(setupKey.Spec.Ephemeral), ExpiresIn: expiresIn, - Name: req.Name, + Name: setupKey.Spec.Name, Type: "reusable", UsageLimit: 0, } diff --git a/internal/controller/setupkey_controller_test.go b/internal/controller/setupkey_controller_test.go index 585cb87..7d796d7 100644 --- a/internal/controller/setupkey_controller_test.go +++ b/internal/controller/setupkey_controller_test.go @@ -50,6 +50,9 @@ var _ = Describe("SetupKey Controller", func() { Name: nn.Name, Namespace: nn.Namespace, }, + Spec: nbv1alpha1.SetupKeySpec{ + Name: "test", + }, } Expect(k8sClient.Create(ctx, setupKey)).To(Succeed()) _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{NamespacedName: nn}) @@ -80,6 +83,9 @@ var _ = Describe("SetupKey Controller", func() { Name: nn.Name, Namespace: nn.Namespace, }, + Spec: nbv1alpha1.SetupKeySpec{ + Name: "test", + }, } Expect(k8sClient.Create(ctx, setupKey)).To(Succeed()) _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{NamespacedName: nn}) diff --git a/internal/netbirdmock/netbirdmock.go b/internal/netbirdmock/netbirdmock.go index f2080a9..dbc03c4 100644 --- a/internal/netbirdmock/netbirdmock.go +++ b/internal/netbirdmock/netbirdmock.go @@ -31,6 +31,46 @@ func Client() *netbird.Client { } return output }) + addHandler(mux, "networks", func(id string, input api.NetworkRequest, output api.Network) api.Network { + output.Id = id + output.Name = input.Name + output.Description = input.Description + return output + }) + addHandler(mux, "networks/{network}/routers", func(id string, input api.NetworkRouterRequest, output api.NetworkRouter) api.NetworkRouter { + output.Id = id + output.Enabled = input.Enabled + output.Masquerade = input.Masquerade + output.Metric = input.Metric + output.PeerGroups = input.PeerGroups + return output + }) + addHandler(mux, "networks/{network}/resources", func(id string, input api.NetworkResourceRequest, output api.NetworkResource) api.NetworkResource { + output.Id = id + output.Address = input.Address + output.Description = input.Description + output.Enabled = input.Enabled + return output + }) + addHandler(mux, "dns/zones", func(id string, input api.ZoneRequest, output api.Zone) api.Zone { + output.Id = id + output.Name = input.Name + output.Domain = input.Domain + output.DistributionGroups = input.DistributionGroups + output.EnableSearchDomain = input.EnableSearchDomain + if input.Enabled != nil { + output.Enabled = *input.Enabled + } + return output + }) + addHandler(mux, "dns/zones/{zone}/records", func(id string, input api.DNSRecordRequest, output api.DNSRecord) api.DNSRecord { + output.Id = id + output.Name = input.Name + output.Ttl = input.Ttl + output.Type = input.Type + output.Content = input.Content + return output + }) srv := httptest.NewServer(mux) return netbird.New(srv.URL, "ABC") @@ -38,14 +78,33 @@ func Client() *netbird.Client { func addHandler[T, U any](mux *http.ServeMux, resource string, convertFn func(string, U, T) T) { var itemMx sync.RWMutex - items := map[string]T{} + store := map[string]T{} + mux.Handle(fmt.Sprintf("GET /api/%s", resource), http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { + itemMx.RLock() + defer itemMx.RUnlock() + + items := make([]T, 0, len(store)) + for _, v := range store { + items = append(items, v) + } + b, err := json.Marshal(items) + if err != nil { + util.WriteErrorResponse("Marshal Error", http.StatusInternalServerError, rw) + return + } + _, err = rw.Write(b) + if err != nil { + util.WriteErrorResponse("Write Error", http.StatusInternalServerError, rw) + return + } + })) mux.Handle(fmt.Sprintf("GET /api/%s/{id}", resource), http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { itemMx.RLock() defer itemMx.RUnlock() id := req.PathValue("id") - respData, ok := items[id] + respData, ok := store[id] if !ok { util.WriteErrorResponse("Not Found", http.StatusNotFound, rw) return @@ -79,7 +138,7 @@ func addHandler[T, U any](mux *http.ServeMux, resource string, convertFn func(st id := fmt.Sprintf("id-%d", rand.Int64()) var zero T respData := convertFn(id, reqData, zero) - items[id] = respData + store[id] = respData b, err = json.Marshal(respData) if err != nil { util.WriteErrorResponse("Marshal Error", http.StatusInternalServerError, rw) @@ -96,7 +155,7 @@ func addHandler[T, U any](mux *http.ServeMux, resource string, convertFn func(st defer itemMx.Unlock() id := req.PathValue("id") - respData, ok := items[id] + respData, ok := store[id] if !ok { util.WriteErrorResponse("Not Found", http.StatusNotFound, rw) return @@ -114,7 +173,7 @@ func addHandler[T, U any](mux *http.ServeMux, resource string, convertFn func(st return } respData = convertFn(id, reqData, respData) - items[id] = respData + store[id] = respData b, err = json.Marshal(respData) if err != nil { util.WriteErrorResponse("Marshal Error", http.StatusInternalServerError, rw) @@ -131,11 +190,11 @@ func addHandler[T, U any](mux *http.ServeMux, resource string, convertFn func(st defer itemMx.Unlock() id := req.PathValue("id") - _, ok := items[id] + _, ok := store[id] if !ok { util.WriteErrorResponse("Not Found", http.StatusNotFound, rw) return } - delete(items, id) + delete(store, id) })) } diff --git a/internal/netbirdutil/group.go b/internal/netbirdutil/group.go new file mode 100644 index 0000000..2aaaa08 --- /dev/null +++ b/internal/netbirdutil/group.go @@ -0,0 +1,42 @@ +package netbirdutil + +import ( + "context" + "fmt" + + netbird "github.com/netbirdio/netbird/shared/management/client/rest" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + nbv1alpha1 "github.com/netbirdio/kubernetes-operator/api/v1alpha1" +) + +func GetGroupIDs(ctx context.Context, k8sClient client.Client, nbClient *netbird.Client, refs []nbv1alpha1.ResourceReference, namespace string) ([]string, error) { + groupIDs := []string{} + for _, ref := range refs { + switch { + case ref.ID != nil: + _, err := nbClient.Groups.Get(ctx, *ref.ID) + if err != nil { + return nil, err + } + groupIDs = append(groupIDs, *ref.ID) + case ref.LocalRef != nil: + group := nbv1alpha1.Group{ + ObjectMeta: metav1.ObjectMeta{ + Name: ref.LocalRef.Name, + Namespace: namespace, + }, + } + err := k8sClient.Get(ctx, client.ObjectKeyFromObject(&group), &group) + if err != nil { + return nil, err + } + if group.Status.GroupID == "" { + return nil, fmt.Errorf("group %s in groups list is not ready", group.Name) + } + groupIDs = append(groupIDs, group.Status.GroupID) + } + } + return groupIDs, nil +} diff --git a/internal/netbirdutil/zone.go b/internal/netbirdutil/zone.go new file mode 100644 index 0000000..48bee15 --- /dev/null +++ b/internal/netbirdutil/zone.go @@ -0,0 +1,24 @@ +package netbirdutil + +import ( + "context" + "fmt" + "slices" + + netbird "github.com/netbirdio/netbird/shared/management/client/rest" + "github.com/netbirdio/netbird/shared/management/http/api" +) + +func GetDNSZoneByName(ctx context.Context, nbClient *netbird.Client, name string) (api.Zone, error) { + resp, err := nbClient.DNSZones.ListZones(ctx) + if err != nil { + return api.Zone{}, err + } + zoneIdx := slices.IndexFunc(resp, func(zone api.Zone) bool { + return zone.Name == name + }) + if zoneIdx == -1 { + return api.Zone{}, fmt.Errorf("zone with name %s cannot be found", name) + } + return resp[zoneIdx], nil +} diff --git a/pkg/applyconfigurations/api/v1alpha1/crossnamespacereference.go b/pkg/applyconfigurations/api/v1alpha1/crossnamespacereference.go new file mode 100644 index 0000000..4b11320 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/crossnamespacereference.go @@ -0,0 +1,34 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +// CrossNamespaceReferenceApplyConfiguration represents a declarative configuration of the CrossNamespaceReference type for use +// with apply. +type CrossNamespaceReferenceApplyConfiguration struct { + // Name of the referent. + Name *string `json:"name,omitempty"` + // Namespace of the referent. + Namespace *string `json:"namespace,omitempty"` +} + +// CrossNamespaceReferenceApplyConfiguration constructs a declarative configuration of the CrossNamespaceReference type for use with +// apply. +func CrossNamespaceReference() *CrossNamespaceReferenceApplyConfiguration { + return &CrossNamespaceReferenceApplyConfiguration{} +} + +// 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 *CrossNamespaceReferenceApplyConfiguration) WithName(value string) *CrossNamespaceReferenceApplyConfiguration { + b.Name = &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 *CrossNamespaceReferenceApplyConfiguration) WithNamespace(value string) *CrossNamespaceReferenceApplyConfiguration { + b.Namespace = &value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/deploymentoverride.go b/pkg/applyconfigurations/api/v1alpha1/deploymentoverride.go new file mode 100644 index 0000000..e6980c9 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/deploymentoverride.go @@ -0,0 +1,70 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" +) + +// DeploymentOverrideApplyConfiguration represents a declarative configuration of the DeploymentOverride type for use +// with apply. +type DeploymentOverrideApplyConfiguration struct { + // Labels that will be added to the deployment. + Labels map[string]string `json:"labels,omitempty"` + // Annotations that will be added to the deployment. + Annotations map[string]string `json:"annotations,omitempty"` + // Replicas set for the deployment. + Replicas *int32 `json:"replicas,omitempty"` + // PodTemplate overrides the deployment pod template. + PodTemplate *v1.PodTemplateSpec `json:"podTemplate,omitempty"` +} + +// DeploymentOverrideApplyConfiguration constructs a declarative configuration of the DeploymentOverride type for use with +// apply. +func DeploymentOverride() *DeploymentOverrideApplyConfiguration { + return &DeploymentOverrideApplyConfiguration{} +} + +// 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 *DeploymentOverrideApplyConfiguration) WithLabels(entries map[string]string) *DeploymentOverrideApplyConfiguration { + if b.Labels == nil && len(entries) > 0 { + b.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.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 *DeploymentOverrideApplyConfiguration) WithAnnotations(entries map[string]string) *DeploymentOverrideApplyConfiguration { + if b.Annotations == nil && len(entries) > 0 { + b.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Annotations[k] = v + } + return b +} + +// WithReplicas sets the Replicas 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 Replicas field is set to the value of the last call. +func (b *DeploymentOverrideApplyConfiguration) WithReplicas(value int32) *DeploymentOverrideApplyConfiguration { + b.Replicas = &value + return b +} + +// WithPodTemplate sets the PodTemplate 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 PodTemplate field is set to the value of the last call. +func (b *DeploymentOverrideApplyConfiguration) WithPodTemplate(value v1.PodTemplateSpec) *DeploymentOverrideApplyConfiguration { + b.PodTemplate = &value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/dnszonereference.go b/pkg/applyconfigurations/api/v1alpha1/dnszonereference.go new file mode 100644 index 0000000..4b11ea1 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/dnszonereference.go @@ -0,0 +1,26 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +// DNSZoneReferenceApplyConfiguration represents a declarative configuration of the DNSZoneReference type for use +// with apply. +// +// DNSZoneReference references a Netbird DNS zone by domain name. +type DNSZoneReferenceApplyConfiguration struct { + // Name is the domain name of an existing Netbird DNS zone, e.g. "example.com". + Name *string `json:"name,omitempty"` +} + +// DNSZoneReferenceApplyConfiguration constructs a declarative configuration of the DNSZoneReference type for use with +// apply. +func DNSZoneReference() *DNSZoneReferenceApplyConfiguration { + return &DNSZoneReferenceApplyConfiguration{} +} + +// 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 *DNSZoneReferenceApplyConfiguration) WithName(value string) *DNSZoneReferenceApplyConfiguration { + b.Name = &value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/networkresource.go b/pkg/applyconfigurations/api/v1alpha1/networkresource.go new file mode 100644 index 0000000..912d320 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/networkresource.go @@ -0,0 +1,229 @@ +// 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" +) + +// NetworkResourceApplyConfiguration represents a declarative configuration of the NetworkResource type for use +// with apply. +// +// NetworkResource is the Schema for the networkresources API. +type NetworkResourceApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *NetworkResourceSpecApplyConfiguration `json:"spec,omitempty"` + Status *NetworkResourceStatusApplyConfiguration `json:"status,omitempty"` +} + +// NetworkResource constructs a declarative configuration of the NetworkResource type for use with +// apply. +func NetworkResource(name, namespace string) *NetworkResourceApplyConfiguration { + b := &NetworkResourceApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("NetworkResource") + b.WithAPIVersion("netbird.io/v1alpha1") + return b +} + +func (b NetworkResourceApplyConfiguration) 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 *NetworkResourceApplyConfiguration) WithKind(value string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithAPIVersion(value string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithName(value string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithGenerateName(value string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithNamespace(value string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithUID(value types.UID) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithResourceVersion(value string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithGeneration(value int64) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithCreationTimestamp(value metav1.Time) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithLabels(entries map[string]string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithAnnotations(entries map[string]string) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithFinalizers(values ...string) *NetworkResourceApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *NetworkResourceApplyConfiguration) 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 *NetworkResourceApplyConfiguration) WithSpec(value *NetworkResourceSpecApplyConfiguration) *NetworkResourceApplyConfiguration { + 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 *NetworkResourceApplyConfiguration) WithStatus(value *NetworkResourceStatusApplyConfiguration) *NetworkResourceApplyConfiguration { + b.Status = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *NetworkResourceApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *NetworkResourceApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *NetworkResourceApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *NetworkResourceApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/applyconfigurations/api/v1alpha1/networkresourcespec.go b/pkg/applyconfigurations/api/v1alpha1/networkresourcespec.go new file mode 100644 index 0000000..656fcfd --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/networkresourcespec.go @@ -0,0 +1,55 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" +) + +// NetworkResourceSpecApplyConfiguration represents a declarative configuration of the NetworkResourceSpec type for use +// with apply. +// +// NetworkResourceSpec defines the desired state of NetworkResource. +type NetworkResourceSpecApplyConfiguration struct { + // NetworkRouterRef is a reference to the network and router where the resource will be created. + NetworkRouterRef *CrossNamespaceReferenceApplyConfiguration `json:"networkRouterRef,omitempty"` + // ServiceRef is a reference to the service to expose in the Network. + ServiceRef *v1.LocalObjectReference `json:"serviceRef,omitempty"` + // Groups are references to groups that the resource will be a part of. + Groups []ResourceReferenceApplyConfiguration `json:"groups,omitempty"` +} + +// NetworkResourceSpecApplyConfiguration constructs a declarative configuration of the NetworkResourceSpec type for use with +// apply. +func NetworkResourceSpec() *NetworkResourceSpecApplyConfiguration { + return &NetworkResourceSpecApplyConfiguration{} +} + +// WithNetworkRouterRef sets the NetworkRouterRef field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the NetworkRouterRef field is set to the value of the last call. +func (b *NetworkResourceSpecApplyConfiguration) WithNetworkRouterRef(value *CrossNamespaceReferenceApplyConfiguration) *NetworkResourceSpecApplyConfiguration { + b.NetworkRouterRef = value + return b +} + +// WithServiceRef sets the ServiceRef 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 ServiceRef field is set to the value of the last call. +func (b *NetworkResourceSpecApplyConfiguration) WithServiceRef(value v1.LocalObjectReference) *NetworkResourceSpecApplyConfiguration { + b.ServiceRef = &value + return b +} + +// WithGroups adds the given value to the Groups 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 Groups field. +func (b *NetworkResourceSpecApplyConfiguration) WithGroups(values ...*ResourceReferenceApplyConfiguration) *NetworkResourceSpecApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithGroups") + } + b.Groups = append(b.Groups, *values[i]) + } + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/networkresourcestatus.go b/pkg/applyconfigurations/api/v1alpha1/networkresourcestatus.go new file mode 100644 index 0000000..8404af7 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/networkresourcestatus.go @@ -0,0 +1,85 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// NetworkResourceStatusApplyConfiguration represents a declarative configuration of the NetworkResourceStatus type for use +// with apply. +// +// NetworkResourceStatus defines the observed state of NetworkResource. +type NetworkResourceStatusApplyConfiguration struct { + // ObservedGeneration is the last reconciled generation. + ObservedGeneration *int64 `json:"observedGeneration,omitempty"` + // Conditions holds the conditions for the NetworkResource. + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` + // NetworkID is the id of the network the resource is created in. + NetworkID *string `json:"networkID,omitempty"` + // ResourceID is the id of the created resource. + ResourceID *string `json:"resourceID,omitempty"` + // DNSZoneID is the id of the zone the DNS record is created in. + DNSZoneID *string `json:"dnsZoneID,omitempty"` + // DNSRecordID is the id of the created DNS record. + DNSRecordID *string `json:"dnsRecordID,omitempty"` +} + +// NetworkResourceStatusApplyConfiguration constructs a declarative configuration of the NetworkResourceStatus type for use with +// apply. +func NetworkResourceStatus() *NetworkResourceStatusApplyConfiguration { + return &NetworkResourceStatusApplyConfiguration{} +} + +// 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 *NetworkResourceStatusApplyConfiguration) WithObservedGeneration(value int64) *NetworkResourceStatusApplyConfiguration { + 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 *NetworkResourceStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *NetworkResourceStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} + +// WithNetworkID sets the NetworkID 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 NetworkID field is set to the value of the last call. +func (b *NetworkResourceStatusApplyConfiguration) WithNetworkID(value string) *NetworkResourceStatusApplyConfiguration { + b.NetworkID = &value + return b +} + +// WithResourceID sets the ResourceID 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 ResourceID field is set to the value of the last call. +func (b *NetworkResourceStatusApplyConfiguration) WithResourceID(value string) *NetworkResourceStatusApplyConfiguration { + b.ResourceID = &value + return b +} + +// WithDNSZoneID sets the DNSZoneID 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 DNSZoneID field is set to the value of the last call. +func (b *NetworkResourceStatusApplyConfiguration) WithDNSZoneID(value string) *NetworkResourceStatusApplyConfiguration { + b.DNSZoneID = &value + return b +} + +// WithDNSRecordID sets the DNSRecordID 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 DNSRecordID field is set to the value of the last call. +func (b *NetworkResourceStatusApplyConfiguration) WithDNSRecordID(value string) *NetworkResourceStatusApplyConfiguration { + b.DNSRecordID = &value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/networkrouter.go b/pkg/applyconfigurations/api/v1alpha1/networkrouter.go new file mode 100644 index 0000000..c2c6dd7 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/networkrouter.go @@ -0,0 +1,229 @@ +// 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" +) + +// NetworkRouterApplyConfiguration represents a declarative configuration of the NetworkRouter type for use +// with apply. +// +// NetworkRouter is the Schema for the networkrouters API. +type NetworkRouterApplyConfiguration struct { + v1.TypeMetaApplyConfiguration `json:",inline"` + *v1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` + Spec *NetworkRouterSpecApplyConfiguration `json:"spec,omitempty"` + Status *NetworkRouterStatusApplyConfiguration `json:"status,omitempty"` +} + +// NetworkRouter constructs a declarative configuration of the NetworkRouter type for use with +// apply. +func NetworkRouter(name, namespace string) *NetworkRouterApplyConfiguration { + b := &NetworkRouterApplyConfiguration{} + b.WithName(name) + b.WithNamespace(namespace) + b.WithKind("NetworkRouter") + b.WithAPIVersion("netbird.io/v1alpha1") + return b +} + +func (b NetworkRouterApplyConfiguration) 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 *NetworkRouterApplyConfiguration) WithKind(value string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithAPIVersion(value string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithName(value string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithGenerateName(value string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithNamespace(value string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithUID(value types.UID) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithResourceVersion(value string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithGeneration(value int64) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithCreationTimestamp(value metav1.Time) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithDeletionTimestamp(value metav1.Time) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithLabels(entries map[string]string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithAnnotations(entries map[string]string) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithOwnerReferences(values ...*v1.OwnerReferenceApplyConfiguration) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithFinalizers(values ...string) *NetworkRouterApplyConfiguration { + b.ensureObjectMetaApplyConfigurationExists() + for i := range values { + b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) + } + return b +} + +func (b *NetworkRouterApplyConfiguration) 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 *NetworkRouterApplyConfiguration) WithSpec(value *NetworkRouterSpecApplyConfiguration) *NetworkRouterApplyConfiguration { + 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 *NetworkRouterApplyConfiguration) WithStatus(value *NetworkRouterStatusApplyConfiguration) *NetworkRouterApplyConfiguration { + b.Status = value + return b +} + +// GetKind retrieves the value of the Kind field in the declarative configuration. +func (b *NetworkRouterApplyConfiguration) GetKind() *string { + return b.TypeMetaApplyConfiguration.Kind +} + +// GetAPIVersion retrieves the value of the APIVersion field in the declarative configuration. +func (b *NetworkRouterApplyConfiguration) GetAPIVersion() *string { + return b.TypeMetaApplyConfiguration.APIVersion +} + +// GetName retrieves the value of the Name field in the declarative configuration. +func (b *NetworkRouterApplyConfiguration) GetName() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Name +} + +// GetNamespace retrieves the value of the Namespace field in the declarative configuration. +func (b *NetworkRouterApplyConfiguration) GetNamespace() *string { + b.ensureObjectMetaApplyConfigurationExists() + return b.ObjectMetaApplyConfiguration.Namespace +} diff --git a/pkg/applyconfigurations/api/v1alpha1/networkrouterspec.go b/pkg/applyconfigurations/api/v1alpha1/networkrouterspec.go new file mode 100644 index 0000000..6704347 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/networkrouterspec.go @@ -0,0 +1,36 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +// NetworkRouterSpecApplyConfiguration represents a declarative configuration of the NetworkRouterSpec type for use +// with apply. +// +// NetworkRouterSpec defines the desired state of NetworkRouter. +type NetworkRouterSpecApplyConfiguration struct { + // DNSZoneRef is a reference to the DNS zone used to create records for resources. + DNSZoneRef *DNSZoneReferenceApplyConfiguration `json:"dnsZoneRef,omitempty"` + // WorkloadOverride contains configuration that will override the default workload. + WorkloadOverride *WorkloadOverrideApplyConfiguration `json:"workloadOverride,omitempty"` +} + +// NetworkRouterSpecApplyConfiguration constructs a declarative configuration of the NetworkRouterSpec type for use with +// apply. +func NetworkRouterSpec() *NetworkRouterSpecApplyConfiguration { + return &NetworkRouterSpecApplyConfiguration{} +} + +// WithDNSZoneRef sets the DNSZoneRef 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 DNSZoneRef field is set to the value of the last call. +func (b *NetworkRouterSpecApplyConfiguration) WithDNSZoneRef(value *DNSZoneReferenceApplyConfiguration) *NetworkRouterSpecApplyConfiguration { + b.DNSZoneRef = value + return b +} + +// WithWorkloadOverride sets the WorkloadOverride 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 WorkloadOverride field is set to the value of the last call. +func (b *NetworkRouterSpecApplyConfiguration) WithWorkloadOverride(value *WorkloadOverrideApplyConfiguration) *NetworkRouterSpecApplyConfiguration { + b.WorkloadOverride = value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/networkrouterstatus.go b/pkg/applyconfigurations/api/v1alpha1/networkrouterstatus.go new file mode 100644 index 0000000..b7cc453 --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/networkrouterstatus.go @@ -0,0 +1,65 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/client-go/applyconfigurations/meta/v1" +) + +// NetworkRouterStatusApplyConfiguration represents a declarative configuration of the NetworkRouterStatus type for use +// with apply. +// +// NetworkRouterStatus defines the observed state of NetworkRouter. +type NetworkRouterStatusApplyConfiguration struct { + // ObservedGeneration is the last reconciled generation. + ObservedGeneration *int64 `json:"observedGeneration,omitempty"` + // Conditions holds the conditions for the NetworkRouter. + Conditions []v1.ConditionApplyConfiguration `json:"conditions,omitempty"` + // RoutingPeerID is the id of the created routing peer. + RoutingPeerID *string `json:"routingPeerID,omitempty"` + // NetworkID is the id of the network the routing peer was created in. + NetworkID *string `json:"networkID,omitempty"` +} + +// NetworkRouterStatusApplyConfiguration constructs a declarative configuration of the NetworkRouterStatus type for use with +// apply. +func NetworkRouterStatus() *NetworkRouterStatusApplyConfiguration { + return &NetworkRouterStatusApplyConfiguration{} +} + +// 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 *NetworkRouterStatusApplyConfiguration) WithObservedGeneration(value int64) *NetworkRouterStatusApplyConfiguration { + 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 *NetworkRouterStatusApplyConfiguration) WithConditions(values ...*v1.ConditionApplyConfiguration) *NetworkRouterStatusApplyConfiguration { + for i := range values { + if values[i] == nil { + panic("nil value passed to WithConditions") + } + b.Conditions = append(b.Conditions, *values[i]) + } + return b +} + +// WithRoutingPeerID sets the RoutingPeerID 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 RoutingPeerID field is set to the value of the last call. +func (b *NetworkRouterStatusApplyConfiguration) WithRoutingPeerID(value string) *NetworkRouterStatusApplyConfiguration { + b.RoutingPeerID = &value + return b +} + +// WithNetworkID sets the NetworkID 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 NetworkID field is set to the value of the last call. +func (b *NetworkRouterStatusApplyConfiguration) WithNetworkID(value string) *NetworkRouterStatusApplyConfiguration { + b.NetworkID = &value + return b +} diff --git a/pkg/applyconfigurations/api/v1alpha1/resourcereference.go b/pkg/applyconfigurations/api/v1alpha1/resourcereference.go index 3ac7078..cb63d5a 100644 --- a/pkg/applyconfigurations/api/v1alpha1/resourcereference.go +++ b/pkg/applyconfigurations/api/v1alpha1/resourcereference.go @@ -9,9 +9,9 @@ import ( // ResourceReferenceApplyConfiguration represents a declarative configuration of the ResourceReference type for use // with apply. type ResourceReferenceApplyConfiguration struct { - // id of the resource in the Netbird API. + // ID is the id of a resource in the Netbird API. ID *string `json:"id,omitempty"` - // local reference to the object in the same namespace. + // LocalReference is a reference to a object in the same namespace. LocalRef *v1.LocalObjectReference `json:"localRef,omitempty"` } diff --git a/pkg/applyconfigurations/api/v1alpha1/setupkeyspec.go b/pkg/applyconfigurations/api/v1alpha1/setupkeyspec.go index 66457b4..c1c1785 100644 --- a/pkg/applyconfigurations/api/v1alpha1/setupkeyspec.go +++ b/pkg/applyconfigurations/api/v1alpha1/setupkeyspec.go @@ -11,6 +11,8 @@ import ( // // SetupKeySpec defines the desired state of SetupKey. type SetupKeySpecApplyConfiguration struct { + // Name of the setup key. + Name *string `json:"name,omitempty"` // Ephemeral decides if peers added with the key are ephemeral or not. Ephemeral *bool `json:"ephemeral,omitempty"` // Duration sets how long the setup key is valid for. @@ -25,6 +27,14 @@ func SetupKeySpec() *SetupKeySpecApplyConfiguration { return &SetupKeySpecApplyConfiguration{} } +// 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 *SetupKeySpecApplyConfiguration) WithName(value string) *SetupKeySpecApplyConfiguration { + b.Name = &value + return b +} + // WithEphemeral sets the Ephemeral 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 Ephemeral field is set to the value of the last call. diff --git a/pkg/applyconfigurations/api/v1alpha1/workloadoverride.go b/pkg/applyconfigurations/api/v1alpha1/workloadoverride.go new file mode 100644 index 0000000..66135da --- /dev/null +++ b/pkg/applyconfigurations/api/v1alpha1/workloadoverride.go @@ -0,0 +1,70 @@ +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1 "k8s.io/api/core/v1" +) + +// WorkloadOverrideApplyConfiguration represents a declarative configuration of the WorkloadOverride type for use +// with apply. +type WorkloadOverrideApplyConfiguration struct { + // Labels that will be added. + Labels map[string]string `json:"labels,omitempty"` + // Annotations that will be added. + Annotations map[string]string `json:"annotations,omitempty"` + // Replicas sets the amount of client replicas. + Replicas *int32 `json:"replicas,omitempty"` + // PodTemplate overrides the pod template. + PodTemplate *v1.PodTemplateSpec `json:"podTemplate,omitempty"` +} + +// WorkloadOverrideApplyConfiguration constructs a declarative configuration of the WorkloadOverride type for use with +// apply. +func WorkloadOverride() *WorkloadOverrideApplyConfiguration { + return &WorkloadOverrideApplyConfiguration{} +} + +// 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 *WorkloadOverrideApplyConfiguration) WithLabels(entries map[string]string) *WorkloadOverrideApplyConfiguration { + if b.Labels == nil && len(entries) > 0 { + b.Labels = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.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 *WorkloadOverrideApplyConfiguration) WithAnnotations(entries map[string]string) *WorkloadOverrideApplyConfiguration { + if b.Annotations == nil && len(entries) > 0 { + b.Annotations = make(map[string]string, len(entries)) + } + for k, v := range entries { + b.Annotations[k] = v + } + return b +} + +// WithReplicas sets the Replicas 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 Replicas field is set to the value of the last call. +func (b *WorkloadOverrideApplyConfiguration) WithReplicas(value int32) *WorkloadOverrideApplyConfiguration { + b.Replicas = &value + return b +} + +// WithPodTemplate sets the PodTemplate 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 PodTemplate field is set to the value of the last call. +func (b *WorkloadOverrideApplyConfiguration) WithPodTemplate(value v1.PodTemplateSpec) *WorkloadOverrideApplyConfiguration { + b.PodTemplate = &value + return b +} diff --git a/pkg/applyconfigurations/utils.go b/pkg/applyconfigurations/utils.go index f596e4e..b3eb325 100644 --- a/pkg/applyconfigurations/utils.go +++ b/pkg/applyconfigurations/utils.go @@ -16,12 +16,28 @@ import ( func ForKind(kind schema.GroupVersionKind) interface{} { switch kind { // Group=netbird.io, Version=v1alpha1 + case v1alpha1.SchemeGroupVersion.WithKind("CrossNamespaceReference"): + return &apiv1alpha1.CrossNamespaceReferenceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("DNSZoneReference"): + return &apiv1alpha1.DNSZoneReferenceApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("Group"): return &apiv1alpha1.GroupApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("GroupSpec"): return &apiv1alpha1.GroupSpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("GroupStatus"): return &apiv1alpha1.GroupStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("NetworkResource"): + return &apiv1alpha1.NetworkResourceApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("NetworkResourceSpec"): + return &apiv1alpha1.NetworkResourceSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("NetworkResourceStatus"): + return &apiv1alpha1.NetworkResourceStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("NetworkRouter"): + return &apiv1alpha1.NetworkRouterApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("NetworkRouterSpec"): + return &apiv1alpha1.NetworkRouterSpecApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("NetworkRouterStatus"): + return &apiv1alpha1.NetworkRouterStatusApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("ResourceReference"): return &apiv1alpha1.ResourceReferenceApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SetupKey"): @@ -30,6 +46,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} { return &apiv1alpha1.SetupKeySpecApplyConfiguration{} case v1alpha1.SchemeGroupVersion.WithKind("SetupKeyStatus"): return &apiv1alpha1.SetupKeyStatusApplyConfiguration{} + case v1alpha1.SchemeGroupVersion.WithKind("WorkloadOverride"): + return &apiv1alpha1.WorkloadOverrideApplyConfiguration{} } return nil