Skip to content

feat(k8s): add kubernetes metrics source#66

Open
zeldanut wants to merge 2 commits intomasterfrom
feature/LOG-23416
Open

feat(k8s): add kubernetes metrics source#66
zeldanut wants to merge 2 commits intomasterfrom
feature/LOG-23416

Conversation

@zeldanut
Copy link
Copy Markdown
Contributor

@zeldanut zeldanut commented Mar 12, 2026

fix(headers): remote task custom headers additions

allow for the passing of extra headers through an env var to downstream
requests for remote task executions including tap & metric reporting

ref: LOG-23416


feat(k8s): add kubernetes metrics source

port of mezmo agent module that allows for the scraping of kubernetes
metrics-server api and the format created is specific to the format
mezmo expects for the kubernetes enrichment feature

ref: LOG-23416

@zeldanut zeldanut force-pushed the feature/LOG-23416 branch from a1ac081 to d2ddea9 Compare March 17, 2026 16:31
@zeldanut zeldanut marked this pull request as ready for review March 17, 2026 21:21
@zeldanut zeldanut force-pushed the feature/LOG-23416 branch from d2ddea9 to f1a6f57 Compare April 1, 2026 11:26
zeldanut added 2 commits April 2, 2026 08:12
port of mezmo agent module that allows for the scraping of kubernetes
metrics-server api and the format created is specific to the format
mezmo expects for the kubernetes enrichment feature

ref: LOG-23416
allow for the passing of extra headers through an env var to downstream
requests for remote task executions including tap & metric reporting

ref: LOG-23416
@zeldanut zeldanut force-pushed the feature/LOG-23416 branch from f1a6f57 to b6b3906 Compare April 2, 2026 12:13

if let Some(container_image) = c.image.clone() {
if let Some((name, tag)) = container_image.split_once(':') {
image = name.to_string();
Copy link
Copy Markdown
Contributor

@kristof-mattei kristof-mattei Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit/correctness:

Going from &str to String works with to_string(), but ideally we'd use to_owned(). to_string() goes via std::fmt::Display, and is used to create a user-suitable representation of the receiver.

It happens to be that for String to_string() yields the same as to_owned() but semantically they're different.

let mut last_state = String::new();
let mut last_reason = String::new();

let mut cpu_limit = None;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer not using mutable variables as it makes it harder to reason about the code.

last_started = None;
}

if let Some(resources) = c.resources.as_ref() {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using mutable you can write this as follows:

        let extract = |map: Option<_>| {
            map.map(|m: &BTreeMap<_, _>| {
                (
                    m.get("cpu")
                        .and_then(|q: &Quantity| convert_cpu_usage_to_milli(q.0.as_str())),
                    m.get("memory")
                        .and_then(|q: &Quantity| convert_memory_usage_to_bytes(q.0.as_str())),
                )
            })
            .unwrap_or_default()
        };

        let (cpu_limit, memory_limit, cpu_request, memory_request) = c
            .resources
            .as_ref()
            .map(|resources| {
                let (cpu_limit, memory_limit) = extract(resources.limits.as_ref());
                let (cpu_request, memory_request) = extract(resources.requests.as_ref());
                (cpu_limit, memory_limit, cpu_request, memory_request)
            })
            .unwrap_or_default();

self.controller_pods_total += 1;
}

pub const fn copy_stats(&mut self, value: &ControllerStats) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thoughts on implementing AddAssign?

return None;
}

let parsed_value: f64 = value.parse().unwrap_or(0f64);
Copy link
Copy Markdown
Contributor

@kristof-mattei kristof-mattei Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HEre we can also get rid of the let mut denominator:

let denominator = match unit.as_str() {
    "m" => {
        1.0
    },
    "u" => {
        1000.0
    },
    "n" => {
        1000000.0
    },
    &_ => {
        error!("Unknown CPU unit");
        return None;
    }
}

return Err(MetricsPublishingError::AuthNotSetError);
};

if let Some(extra) = env::var("MEZMO_REMOTE_TASK_EXTRA_HEADERS")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make headers already a HeaderMap here? I understand this is a more foundational change. String is case-sensitive, HeaderMap's HeaderName is case-insensitive (as per the HTTP spec).

Regardless of that:

I'd like to suggest the following change:

        let mut headers = env::var("MEZMO_REMOTE_TASK_EXTRA_HEADERS")
            .ok()
            .and_then(|v| serde_json::from_str::<HashMap<String, String>>(&v).ok())
            .unwrap_or_default();

        // Option 1: if `MEZMO_REMOTE_TASK_EXTRA_HEADERS` provides `Authorization:`, DON'T overwrite
        {
            // note that if we'd switch to hashbrown, we can delay the `to_owned()` until it's actually needed to be created. 
            headers
                .entry("Authorization".to_owned())
                .or_insert_with(|| format!("Token {token}"));
        }

        // Option 2: if we want our `Authorization:` to ALWAYS win:
        {
            headers.insert("Authorization".into(), format!("Token {token}"));
        }

HeaderValue::from_str(&format!("Token {auth_token}")).unwrap(),
);
for (k, v) in extra_headers {
if let (Ok(name), Ok(value)) = (
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want log something here? This quietly eats any headers that cannot be represented as HeaderName / HeaderValue.

return Err(MetricsPublishingError::AuthNotSetError);
};

if let Some(extra) = env::var("MEZMO_REMOTE_TASK_EXTRA_HEADERS")
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, if we don't expect the value of MEZMO_REMOTE_TASK_EXTRA_HEADERS to change during the runtime of the application, we might want to use a LazyLock to create the hashmap once, and then clone it each time this call happens. That way we skip over the deserialization step.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants