Loading Dockerfile +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading crates/gkg-server/Cargo.toml +3 −2 Original line number Diff line number Diff line Loading @@ -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" crates/gkg-server/src/config.rs +74 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ impl AppConfig { .prefix_separator("_") .separator("__") .list_separator(",") .with_list_parse_key("health_check.services") .try_parsing(true), ) .build() Loading Loading @@ -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"], ); } } scripts/build-dev.sh +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
Dockerfile +1 −1 Original line number Diff line number Diff line Loading @@ -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 Loading
crates/gkg-server/Cargo.toml +3 −2 Original line number Diff line number Diff line Loading @@ -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"
crates/gkg-server/src/config.rs +74 −0 Original line number Diff line number Diff line Loading @@ -61,6 +61,7 @@ impl AppConfig { .prefix_separator("_") .separator("__") .list_separator(",") .with_list_parse_key("health_check.services") .try_parsing(true), ) .build() Loading Loading @@ -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"], ); } }
scripts/build-dev.sh +1 −1 Original line number Diff line number Diff line Loading @@ -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