utils.rs 4.3 KB
Newer Older
1
// SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2
3
4
5
6
7
8
9
10
// SPDX-License-Identifier: Apache-2.0

use anyhow::Result;
use k8s_openapi::api::discovery::v1::EndpointSlice;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};

/// Hash a pod name to get a consistent instance ID
pub fn hash_pod_name(pod_name: &str) -> u64 {
11
12
    // Clear top 11 bits to ensure it can be safely rounded to IEEE-754 f64
    const INSTANCE_ID_MASK: u64 = 0x001F_FFFF_FFFF_FFFFu64;
13
14
    let mut hasher = DefaultHasher::new();
    pod_name.hash(&mut hasher);
15
    hasher.finish() & INSTANCE_ID_MASK
16
17
18
}

/// Extract endpoint information from an EndpointSlice
19
20
/// Returns (instance_id, pod_name) tuples for ready endpoints
pub(super) fn extract_endpoint_info(slice: &EndpointSlice) -> Vec<(u64, String)> {
21
    let mut result = Vec::new();
22

23
    for endpoint in &slice.endpoints {
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
        let is_ready = endpoint
            .conditions
            .as_ref()
            .and_then(|c| c.ready)
            .unwrap_or(false);

        if !is_ready {
            continue;
        }

        let pod_name = match endpoint.target_ref.as_ref() {
            Some(target_ref) => target_ref.name.as_deref().unwrap_or(""),
            None => continue,
        };

        if pod_name.is_empty() {
            continue;
        }

        let instance_id = hash_pod_name(pod_name);

45
        result.push((instance_id, pod_name.to_string()));
46
47
48
49
50
51
52
53
54
55
    }

    result
}

/// Pod information extracted from environment
#[derive(Debug, Clone)]
pub(super) struct PodInfo {
    pub pod_name: String,
    pub pod_namespace: String,
56
    pub pod_uid: String,
57
58
59
60
61
    pub system_port: u16,
}

impl PodInfo {
    /// Discover pod information from environment variables
62
63
64
65
66
    ///
    /// Required environment variables:
    /// - `POD_NAME`: Name of the pod (required)
    /// - `POD_UID`: UID of the pod (required for CR owner reference)
    /// - `POD_NAMESPACE`: Namespace of the pod (defaults to "default")
67
68
69
70
    pub fn from_env() -> Result<Self> {
        let pod_name = std::env::var("POD_NAME")
            .map_err(|_| anyhow::anyhow!("POD_NAME environment variable not set"))?;

71
72
73
        let pod_uid = std::env::var("POD_UID")
            .map_err(|_| anyhow::anyhow!("POD_UID environment variable not set"))?;

74
75
76
77
78
79
80
81
82
83
84
85
        let pod_namespace = std::env::var("POD_NAMESPACE").unwrap_or_else(|_| {
            tracing::warn!("POD_NAMESPACE not set, defaulting to 'default'");
            "default".to_string()
        });

        // Read system server port from config
        let config = crate::config::RuntimeConfig::from_settings().unwrap_or_default();
        let system_port = config.system_port as u16;

        Ok(Self {
            pod_name,
            pod_namespace,
86
            pod_uid,
87
88
89
90
            system_port,
        })
    }
}
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_hash_json_serialization_roundtrip() {
        // Verify that JSON serialization/deserialization preserves exact values
        let pod_names = [
            "worker-0",
            "worker-99999",
            "deployment-with-hash-suffix-a1b2c3d4e5f6",
            "fake-name-1-0-worker-nrdfv",
        ];

        for pod_name in &pod_names {
            let original_hash = hash_pod_name(pod_name);
            let json = serde_json::to_string(&original_hash).unwrap();
            let deserialized_hash: u64 = serde_json::from_str(&json).unwrap();

            assert_eq!(
                original_hash, deserialized_hash,
                "JSON roundtrip changed hash value for pod_name={:?}: {} -> {} (json: {})",
                pod_name, original_hash, deserialized_hash, json
            );
        }
    }

    #[test]
    fn test_hash_in_struct_serialization() {
        // Test serialization when the hash is embedded in a struct
        #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
        struct WorkerInfo {
            instance_id: u64,
            name: String,
        }

        let pod_name = "fake-name-1-0-worker-nrdfv";
        let info = WorkerInfo {
            instance_id: hash_pod_name(pod_name),
            name: pod_name.to_string(),
        };

        let json = serde_json::to_string(&info).unwrap();
        let deserialized: WorkerInfo = serde_json::from_str(&json).unwrap();

        assert_eq!(info, deserialized);
    }
}