Verified Commit bac464d6 authored by Bohdan Parkhomchuk's avatar Bohdan Parkhomchuk 💬 Committed by GitLab
Browse files

fix(config): use with_list_parse_key for env parsing

parent 290d3e95
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@ ENV GKG_VERSION=$GKG_VERSION
RUN cargo build --release --package gkg-server && \
    cp target/release/gkg-server /gkg-server

FROM registry.access.redhat.com/ubi10/ubi-micro:10.1
FROM registry.access.redhat.com/ubi10/ubi-minimal:10.1

COPY --from=builder /gkg-server /usr/local/bin/gkg-server

+3 −2
Original line number Diff line number Diff line
@@ -69,5 +69,6 @@ parser-core = { workspace = true }
internment = { workspace = true }
smallvec = { workspace = true }

[lints]
workspace = true
[lints.rust]
# workspace uses `forbid`, downgraded to `deny` so tests can manipulate env vars
unsafe_code = "deny"
+74 −0
Original line number Diff line number Diff line
@@ -61,6 +61,7 @@ impl AppConfig {
                    .prefix_separator("_")
                    .separator("__")
                    .list_separator(",")
                    .with_list_parse_key("health_check.services")
                    .try_parsing(true),
            )
            .build()
@@ -89,3 +90,76 @@ pub enum ConfigError {
    #[error("GKG_JWT_SECRET is required")]
    MissingJwtSecret,
}

#[cfg(test)]
#[allow(unsafe_code)]
mod tests {
    use super::*;
    use serial_test::serial;

    /// Reproduces the crash seen when deploying with GKG_ prefixed env vars.
    /// `try_parsing(true)` + `list_separator(",")` without `with_list_parse_key`
    /// wraps every value in a sequence, causing "invalid type: sequence, expected
    /// a string" errors for plain string fields.
    #[test]
    #[serial]
    fn env_string_fields_not_parsed_as_lists() {
        let vars = [
            ("GKG_NATS__URL", "gkg-nats:4222"),
            ("GKG_GRAPH__URL", "http://clickhouse:8123"),
            ("GKG_GRAPH__DATABASE", "gkg"),
            ("GKG_GRAPH__USERNAME", "default"),
            ("GKG_GRAPH__PASSWORD", "supersecret"),
            ("GKG_METRICS__OTLP_ENDPOINT", "http://gkg-obs-alloy:4317"),
        ];

        // SAFETY: tests run serially via #[serial], no concurrent env access
        unsafe {
            for (k, v) in &vars {
                std::env::set_var(k, v);
            }
        }

        let result = AppConfig::load();

        unsafe {
            for (k, _) in &vars {
                std::env::remove_var(k);
            }
        }

        let config = result.expect("AppConfig::load should not fail for plain string env vars");
        assert_eq!(config.nats.url, "gkg-nats:4222");
        assert_eq!(config.graph.password.as_deref(), Some("supersecret"));
        assert_eq!(config.metrics.otlp_endpoint, "http://gkg-obs-alloy:4317");
    }

    #[test]
    #[serial]
    fn health_check_services_parsed_as_list() {
        let vars = [(
            "GKG_HEALTH_CHECK__SERVICES",
            "siphon-consumer,nats,gkg-indexer",
        )];

        unsafe {
            for (k, v) in &vars {
                std::env::set_var(k, v);
            }
        }

        let result = AppConfig::load();

        unsafe {
            for (k, _) in &vars {
                std::env::remove_var(k);
            }
        }

        let config = result.expect("AppConfig::load should parse health_check.services as a list");
        assert_eq!(
            config.health_check.services,
            vec!["siphon-consumer", "nats", "gkg-indexer"],
        );
    }
}
+1 −1
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ else
fi

docker build --platform "$DOCKER_PLATFORM" -t "$IMAGE_TAG" -f - "$CONTEXT_DIR" <<'EOF'
FROM registry.access.redhat.com/ubi9/ubi-micro:latest
FROM registry.access.redhat.com/ubi10/ubi-minimal:latest
COPY gkg-server /usr/local/bin/gkg-server
ENTRYPOINT ["gkg-server"]
EOF