bindy/reconcilers/bind9instance/
cluster_helpers.rs

1// Copyright (c) 2025 Erick Bourgeois, firestoned
2// SPDX-License-Identifier: MIT
3
4//! Cluster integration helpers for `Bind9Instance` resources.
5//!
6//! This module handles fetching cluster information and building cluster
7//! references for instances.
8
9#[allow(clippy::wildcard_imports)]
10use super::types::*;
11
12use crate::constants::API_GROUP_VERSION;
13
14pub(super) async fn fetch_cluster_info(
15    client: &Client,
16    namespace: &str,
17    instance: &Bind9Instance,
18) -> (
19    Option<Bind9Cluster>,
20    Option<crate::crd::ClusterBind9Provider>,
21) {
22    // First, try to get cluster from ownerReferences (preferred)
23    if let Some(owner_refs) = &instance.metadata.owner_references {
24        for owner_ref in owner_refs {
25            // Check if owner is a Bind9Cluster
26            if owner_ref.kind == "Bind9Cluster" && owner_ref.api_version == API_GROUP_VERSION {
27                let cluster_api: Api<Bind9Cluster> = Api::namespaced(client.clone(), namespace);
28                if let Ok(cluster) = cluster_api.get(&owner_ref.name).await {
29                    debug!(
30                        "Found cluster from ownerReference: Bind9Cluster/{}",
31                        owner_ref.name
32                    );
33                    return (Some(cluster), None);
34                }
35            }
36            // Check if owner is a ClusterBind9Provider
37            else if owner_ref.kind == "ClusterBind9Provider"
38                && owner_ref.api_version == API_GROUP_VERSION
39            {
40                let provider_api: Api<crate::crd::ClusterBind9Provider> = Api::all(client.clone());
41                if let Ok(provider) = provider_api.get(&owner_ref.name).await {
42                    debug!(
43                        "Found cluster from ownerReference: ClusterBind9Provider/{}",
44                        owner_ref.name
45                    );
46                    return (None, Some(provider));
47                }
48            }
49        }
50    }
51
52    // Fallback: Use spec.clusterRef for backward compatibility with manually-created instances
53    if !instance.spec.cluster_ref.is_empty() {
54        debug!(
55            "No ownerReference found, falling back to spec.clusterRef: {}",
56            instance.spec.cluster_ref
57        );
58
59        // Try namespace-scoped cluster first
60        let cluster_api: Api<Bind9Cluster> = Api::namespaced(client.clone(), namespace);
61        let cluster = cluster_api.get(&instance.spec.cluster_ref).await.ok();
62
63        // If not found, try cluster-scoped provider
64        let cluster_provider = if cluster.is_none() {
65            let provider_api: Api<crate::crd::ClusterBind9Provider> = Api::all(client.clone());
66            provider_api.get(&instance.spec.cluster_ref).await.ok()
67        } else {
68            None
69        };
70
71        return (cluster, cluster_provider);
72    }
73
74    debug!("Instance has neither ownerReferences nor spec.clusterRef");
75    (None, None)
76}
77
78/// Creates a `ClusterReference` from either a `Bind9Cluster` or `ClusterBind9Provider`.
79///
80/// This helper function creates a full Kubernetes object reference with kind, apiVersion,
81/// name, and namespace (for namespace-scoped clusters). This enables proper object
82/// references and provides backward compatibility with `spec.clusterRef` (string name).
83///
84/// # Arguments
85///
86/// * `cluster` - Optional namespace-scoped `Bind9Cluster`
87/// * `cluster_provider` - Optional cluster-scoped `ClusterBind9Provider`
88///
89/// # Returns
90///
91/// * `Some(ClusterReference)` - If either cluster or `cluster_provider` is provided
92/// * `None` - If neither is provided
93pub(super) fn build_cluster_reference(
94    cluster: Option<&Bind9Cluster>,
95    cluster_provider: Option<&crate::crd::ClusterBind9Provider>,
96) -> Option<ClusterReference> {
97    if let Some(c) = cluster {
98        Some(ClusterReference {
99            api_version: API_GROUP_VERSION.to_string(),
100            kind: "Bind9Cluster".to_string(),
101            name: c.name_any(),
102            namespace: c.namespace(),
103        })
104    } else {
105        cluster_provider.map(|cp| ClusterReference {
106            api_version: API_GROUP_VERSION.to_string(),
107            kind: "ClusterBind9Provider".to_string(),
108            name: cp.name_any(),
109            namespace: None, // Cluster-scoped resource has no namespace
110        })
111    }
112}
113
114#[cfg(test)]
115#[path = "cluster_helpers_tests.rs"]
116mod cluster_helpers_tests;