From d38a419f98f80d70d42d9341826df0bfff170fca Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 11:07:33 -0400 Subject: [PATCH 01/22] chore(vdev): refactor to use std docker compose vs custom env files --- scripts/e2e/opentelemetry-logs/compose.yaml | 5 + scripts/integration/amqp/compose.yaml | 5 + scripts/integration/aws/compose.yaml | 5 + scripts/integration/azure/compose.yaml | 5 + scripts/integration/clickhouse/compose.yaml | 5 + scripts/integration/databend/compose.yaml | 5 + .../integration/datadog-agent/compose.yaml | 5 + .../integration/datadog-traces/compose.yaml | 5 + scripts/integration/dnstap/compose.yaml | 5 + .../integration/elasticsearch/compose.yaml | 5 + scripts/integration/eventstoredb/compose.yaml | 5 + scripts/integration/gcp/compose.yaml | 5 + scripts/integration/greptimedb/compose.yaml | 5 + scripts/integration/http-client/compose.yaml | 5 + scripts/integration/humio/compose.yaml | 5 + scripts/integration/influxdb/compose.yaml | 5 + scripts/integration/kafka/compose.yaml | 5 + scripts/integration/logstash/compose.yaml | 5 + scripts/integration/loki/compose.yaml | 5 + scripts/integration/mongodb/compose.yaml | 5 + scripts/integration/nats/compose.yaml | 5 + .../integration/opentelemetry/compose.yaml | 5 + scripts/integration/postgres/compose.yaml | 5 + scripts/integration/prometheus/compose.yaml | 5 + scripts/integration/pulsar/compose.yaml | 5 + scripts/integration/redis/compose.yaml | 5 + scripts/integration/shutdown/compose.yaml | 5 + scripts/integration/splunk/compose.yaml | 5 + scripts/integration/webhdfs/compose.yaml | 5 + vdev/src/commands/compose_tests/show.rs | 58 ++++++++- vdev/src/commands/compose_tests/stop.rs | 50 +++++++- vdev/src/commands/compose_tests/test.rs | 46 ++++++- vdev/src/testing/integration.rs | 118 +++++++++--------- vdev/src/testing/mod.rs | 1 - vdev/src/testing/state.rs | 93 -------------- 35 files changed, 341 insertions(+), 170 deletions(-) delete mode 100644 vdev/src/testing/state.rs diff --git a/scripts/e2e/opentelemetry-logs/compose.yaml b/scripts/e2e/opentelemetry-logs/compose.yaml index 03d23d5246ffb..4dd094820006a 100644 --- a/scripts/e2e/opentelemetry-logs/compose.yaml +++ b/scripts/e2e/opentelemetry-logs/compose.yaml @@ -81,3 +81,8 @@ services: volumes: vector_target: external: true + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/amqp/compose.yaml b/scripts/integration/amqp/compose.yaml index a60a27155df0b..c6561ae225008 100644 --- a/scripts/integration/amqp/compose.yaml +++ b/scripts/integration/amqp/compose.yaml @@ -13,3 +13,8 @@ services: - RABBITMQ_SSL_FAIL_IF_NO_PEER_CERT=false volumes: - ../../..:/code + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/aws/compose.yaml b/scripts/integration/aws/compose.yaml index 55e926dcdc1af..44fe67d7e9445 100644 --- a/scripts/integration/aws/compose.yaml +++ b/scripts/integration/aws/compose.yaml @@ -12,3 +12,8 @@ services: volumes: - $DOCKER_SOCKET:/var/run/docker.sock - $HOME/.aws/:/home/.aws/ + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/azure/compose.yaml b/scripts/integration/azure/compose.yaml index 6c7b00e37d6e1..933a72d235a24 100644 --- a/scripts/integration/azure/compose.yaml +++ b/scripts/integration/azure/compose.yaml @@ -6,3 +6,8 @@ services: command: azurite --blobHost 0.0.0.0 --loose volumes: - /var/run:/var/run + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/clickhouse/compose.yaml b/scripts/integration/clickhouse/compose.yaml index 62f8a90a543c2..fd823897ec4c2 100644 --- a/scripts/integration/clickhouse/compose.yaml +++ b/scripts/integration/clickhouse/compose.yaml @@ -3,3 +3,8 @@ version: '3' services: clickhouse: image: docker.io/clickhouse/clickhouse-server:${CONFIG_VERSION} + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/databend/compose.yaml b/scripts/integration/databend/compose.yaml index 65ed7a9c809d2..5304afc507960 100644 --- a/scripts/integration/databend/compose.yaml +++ b/scripts/integration/databend/compose.yaml @@ -19,3 +19,8 @@ services: - minio healthcheck: test: "curl -f localhost:8080/v1/health || exit 1" + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/datadog-agent/compose.yaml b/scripts/integration/datadog-agent/compose.yaml index 1b0b0a5a0fff6..0fe91c2d44c49 100644 --- a/scripts/integration/datadog-agent/compose.yaml +++ b/scripts/integration/datadog-agent/compose.yaml @@ -27,3 +27,8 @@ services: - DD_CMD_PORT=5002 - DD_USE_DOGSTATSD=false - DD_HOSTNAME=datadog-trace-agent + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/datadog-traces/compose.yaml b/scripts/integration/datadog-traces/compose.yaml index 8ae4c0dd6c026..f94ee0b53d089 100644 --- a/scripts/integration/datadog-traces/compose.yaml +++ b/scripts/integration/datadog-traces/compose.yaml @@ -29,3 +29,8 @@ services: - DD_APM_MAX_MEMORY=0 - DD_APM_MAX_CPU_PERCENT=0 - DD_HOSTNAME=datadog-trace-agent-to-vector + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/dnstap/compose.yaml b/scripts/integration/dnstap/compose.yaml index ae5f973aff541..729ae6590aa96 100644 --- a/scripts/integration/dnstap/compose.yaml +++ b/scripts/integration/dnstap/compose.yaml @@ -13,3 +13,8 @@ services: volumes: dnstap-sockets: {} + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/elasticsearch/compose.yaml b/scripts/integration/elasticsearch/compose.yaml index cdaf5e2b1d64c..6db0746fe8b10 100644 --- a/scripts/integration/elasticsearch/compose.yaml +++ b/scripts/integration/elasticsearch/compose.yaml @@ -25,3 +25,8 @@ services: - ES_JAVA_OPTS=-Xms400m -Xmx400m volumes: - ../../../tests/data/ca:/usr/share/elasticsearch/config/certs:ro + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/eventstoredb/compose.yaml b/scripts/integration/eventstoredb/compose.yaml index 324bfeeb84c54..eb0ae7b945650 100644 --- a/scripts/integration/eventstoredb/compose.yaml +++ b/scripts/integration/eventstoredb/compose.yaml @@ -6,3 +6,8 @@ services: command: --insecure --stats-period-sec=1 volumes: - ../../../tests/data:/etc/vector:ro + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/gcp/compose.yaml b/scripts/integration/gcp/compose.yaml index b9e6c8a917d16..f317c23a5604c 100644 --- a/scripts/integration/gcp/compose.yaml +++ b/scripts/integration/gcp/compose.yaml @@ -15,3 +15,8 @@ services: command: - -p - /public.pem + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/greptimedb/compose.yaml b/scripts/integration/greptimedb/compose.yaml index 7cd131240b49d..02a68364e7a31 100644 --- a/scripts/integration/greptimedb/compose.yaml +++ b/scripts/integration/greptimedb/compose.yaml @@ -6,3 +6,8 @@ services: command: "standalone start --http-addr=0.0.0.0:4000 --rpc-addr=0.0.0.0:4001" healthcheck: test: "curl -f localhost:4000/health || exit 1" + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/http-client/compose.yaml b/scripts/integration/http-client/compose.yaml index a1723c93d8d1a..11ad2bc76591a 100644 --- a/scripts/integration/http-client/compose.yaml +++ b/scripts/integration/http-client/compose.yaml @@ -29,3 +29,8 @@ services: - ../../../tests/data/http-client/serve:/data - ../../../tests/data/ca/intermediate_server/certs/dufs-https-chain.cert.pem:/certs/ca.cert.pem - ../../../tests/data/ca/intermediate_server/private/dufs-https.key.pem:/certs/ca.key.pem + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/humio/compose.yaml b/scripts/integration/humio/compose.yaml index a4b310c9f9557..bc1045d98445d 100644 --- a/scripts/integration/humio/compose.yaml +++ b/scripts/integration/humio/compose.yaml @@ -3,3 +3,8 @@ version: '3' services: humio: image: docker.io/humio/humio-single-node-demo:${CONFIG_VERSION} + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/influxdb/compose.yaml b/scripts/integration/influxdb/compose.yaml index 9202280345c19..7499564740491 100644 --- a/scripts/integration/influxdb/compose.yaml +++ b/scripts/integration/influxdb/compose.yaml @@ -19,3 +19,8 @@ services: command: influxd --reporting-disabled environment: - INFLUXDB_REPORTING_DISABLED=true + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/kafka/compose.yaml b/scripts/integration/kafka/compose.yaml index 4fb8fb45f2efe..0437b8b866f7a 100644 --- a/scripts/integration/kafka/compose.yaml +++ b/scripts/integration/kafka/compose.yaml @@ -34,3 +34,8 @@ services: - ../../../tests/data/ca/intermediate_server/private/kafka.pass:/etc/kafka/secrets/kafka.pass:ro - ../../../tests/data/ca/intermediate_server/private/kafka.p12:/etc/kafka/secrets/kafka.p12:ro - ../../../tests/data/kafka_server_jaas.conf:/etc/kafka/kafka_server_jaas.conf + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/logstash/compose.yaml b/scripts/integration/logstash/compose.yaml index 9d1e16f2bee19..1e7a17918435b 100644 --- a/scripts/integration/logstash/compose.yaml +++ b/scripts/integration/logstash/compose.yaml @@ -12,3 +12,8 @@ services: - /dev/null:/usr/share/logstash/pipeline/logstash.yml - ../../../tests/data/host.docker.internal.crt:/tmp/logstash.crt - ../../../tests/data/logstash/logstash.conf:/usr/share/logstash/pipeline/logstash.conf + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/loki/compose.yaml b/scripts/integration/loki/compose.yaml index bca8cd4a458e2..4350e809f8961 100644 --- a/scripts/integration/loki/compose.yaml +++ b/scripts/integration/loki/compose.yaml @@ -4,3 +4,8 @@ services: loki: image: docker.io/grafana/loki:${CONFIG_VERSION} command: -config.file=/etc/loki/local-config.yaml -auth.enabled=true + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/mongodb/compose.yaml b/scripts/integration/mongodb/compose.yaml index b76d008005193..f7cd8b6517471 100644 --- a/scripts/integration/mongodb/compose.yaml +++ b/scripts/integration/mongodb/compose.yaml @@ -30,3 +30,8 @@ services: - MONGODB_INITIAL_PRIMARY_PORT_NUMBER=27017 - MONGODB_INITIAL_PRIMARY_ROOT_PASSWORD=toor - MONGODB_REPLICA_SET_KEY=vector + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/nats/compose.yaml b/scripts/integration/nats/compose.yaml index 9be71dc529380..24a0495772ba6 100644 --- a/scripts/integration/nats/compose.yaml +++ b/scripts/integration/nats/compose.yaml @@ -50,3 +50,8 @@ services: - /usr/share/nats/config/nats-jetstream.conf volumes: - ../../../tests/data/nats:/usr/share/nats/config + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/opentelemetry/compose.yaml b/scripts/integration/opentelemetry/compose.yaml index e4b100750f085..b85cc417cedaf 100644 --- a/scripts/integration/opentelemetry/compose.yaml +++ b/scripts/integration/opentelemetry/compose.yaml @@ -5,3 +5,8 @@ services: image: docker.io/otel/opentelemetry-collector-contrib:${CONFIG_VERSION} volumes: - ../../../tests/data/opentelemetry/config.yaml:/etc/otelcol-contrib/config.yaml + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/postgres/compose.yaml b/scripts/integration/postgres/compose.yaml index ab73d2bbc2303..007d058786881 100644 --- a/scripts/integration/postgres/compose.yaml +++ b/scripts/integration/postgres/compose.yaml @@ -14,3 +14,8 @@ services: volumes: socket: {} + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/prometheus/compose.yaml b/scripts/integration/prometheus/compose.yaml index e41592c28eccf..9f618f8f521f0 100644 --- a/scripts/integration/prometheus/compose.yaml +++ b/scripts/integration/prometheus/compose.yaml @@ -21,3 +21,8 @@ services: command: --config.file=/etc/vector/prometheus.yaml volumes: - ../../../tests/data:/etc/vector:ro + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/pulsar/compose.yaml b/scripts/integration/pulsar/compose.yaml index 0e963cd2e0bd1..1e4742c8fc3fe 100644 --- a/scripts/integration/pulsar/compose.yaml +++ b/scripts/integration/pulsar/compose.yaml @@ -16,3 +16,8 @@ services: - ../../../tests/data/ca/intermediate_server/private/pulsar.key.pem:/etc/pulsar/certs/pulsar.key.pem:ro - ../../../tests/data//ca/intermediate_server/certs/pulsar.cert.pem:/etc/pulsar/certs/pulsar.cert.pem:ro - ../../../tests/data/ca/intermediate_server/certs/ca-chain.cert.pem:/etc/pulsar/certs/ca-chain.cert.pem:ro + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/redis/compose.yaml b/scripts/integration/redis/compose.yaml index 1399a12b2a655..4f0a909a6e455 100644 --- a/scripts/integration/redis/compose.yaml +++ b/scripts/integration/redis/compose.yaml @@ -24,3 +24,8 @@ services: echo "sentinel failover-timeout vector 5000" >> /etc/sentinel.conf && echo "sentinel parallel-syncs vector 1" >> /etc/sentinel.conf && redis-sentinel /etc/sentinel.conf' + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/shutdown/compose.yaml b/scripts/integration/shutdown/compose.yaml index 4fb8fb45f2efe..0437b8b866f7a 100644 --- a/scripts/integration/shutdown/compose.yaml +++ b/scripts/integration/shutdown/compose.yaml @@ -34,3 +34,8 @@ services: - ../../../tests/data/ca/intermediate_server/private/kafka.pass:/etc/kafka/secrets/kafka.pass:ro - ../../../tests/data/ca/intermediate_server/private/kafka.p12:/etc/kafka/secrets/kafka.p12:ro - ../../../tests/data/kafka_server_jaas.conf:/etc/kafka/kafka_server_jaas.conf + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/splunk/compose.yaml b/scripts/integration/splunk/compose.yaml index f42364adb2418..cfae5c07b545d 100644 --- a/scripts/integration/splunk/compose.yaml +++ b/scripts/integration/splunk/compose.yaml @@ -13,3 +13,8 @@ services: - 8000:8000 - 8088:8088 - 8089:8089 + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/scripts/integration/webhdfs/compose.yaml b/scripts/integration/webhdfs/compose.yaml index 9585927e84a10..082816fd01e1f 100644 --- a/scripts/integration/webhdfs/compose.yaml +++ b/scripts/integration/webhdfs/compose.yaml @@ -30,3 +30,8 @@ services: interval: 5s timeout: 5s retries: 3 + +networks: + default: + name: ${VECTOR_NETWORK} + external: true diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 4991b72b04881..30528e5c3a983 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -1,8 +1,9 @@ -use anyhow::Result; +use anyhow::{Context, Result}; +use std::{collections::HashMap, process::Command}; use crate::{ environment::Environment, - testing::{config::ComposeTestConfig, state}, + testing::{config::ComposeTestConfig, docker::CONTAINER_TOOL}, }; pub fn exec(integration: Option<&String>, path: &str) -> Result<()> { @@ -23,14 +24,16 @@ pub fn exec_environments_only(integration: &str, path: &str) -> Result<()> { fn show_all(path: &str) -> Result<()> { let entries = ComposeTestConfig::collect_all(path)?; + let active_projects = get_active_projects()?; + let width = entries .keys() .fold(16, |width, entry| width.max(entry.len())); println!("{:width$} Environment Name(s)", "Integration Name"); println!("{:width$} -------------------", "----------------"); for (integration, config) in entries { - let envs_dir = state::EnvsDir::new(&integration); - let active_env = envs_dir.active()?; + let prefix = format!("vector-{}-{}-", path, integration); + let active_env = find_active_environment(&active_projects, &prefix, &config); let environments = config .environments() .keys() @@ -44,8 +47,9 @@ fn show_all(path: &str) -> Result<()> { fn show_one(integration: &str, path: &str) -> Result<()> { let (_test_dir, config) = ComposeTestConfig::load(path, integration)?; - let envs_dir = state::EnvsDir::new(integration); - let active_env = envs_dir.active()?; + let active_projects = get_active_projects()?; + let prefix = format!("vector-{}-{}-", path, integration); + let active_env = find_active_environment(&active_projects, &prefix, &config); if let Some(args) = &config.args { println!("Test args: {}", args.join(" ")); @@ -109,3 +113,45 @@ fn format(active_env: Option<&String>, environment: &str) -> String { _ => environment.into(), } } + +fn get_active_projects() -> Result> { + let output = Command::new(CONTAINER_TOOL.clone()) + .args(["compose", "ls", "--format", "json"]) + .output() + .with_context(|| "Failed to list compose projects")?; + + if !output.status.success() { + return Ok(HashMap::new()); + } + + let projects: Vec = serde_json::from_slice(&output.stdout) + .with_context(|| "Failed to parse docker compose ls output")?; + + let mut map = HashMap::new(); + for project in projects { + if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { + map.insert(name.to_string(), true); + } + } + + Ok(map) +} + +fn find_active_environment( + active_projects: &HashMap, + prefix: &str, + config: &ComposeTestConfig, +) -> Option { + for (project_name, _) in active_projects { + if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Some(env_name.to_string()); + } + } + } + } + None +} diff --git a/vdev/src/commands/compose_tests/stop.rs b/vdev/src/commands/compose_tests/stop.rs index 20849a2ecd6a2..7f8d74d92dd24 100644 --- a/vdev/src/commands/compose_tests/stop.rs +++ b/vdev/src/commands/compose_tests/stop.rs @@ -1,8 +1,10 @@ -use anyhow::Result; +use anyhow::{Context, Result}; +use std::process::Command; use crate::testing::{ + config::ComposeTestConfig, + docker::CONTAINER_TOOL, integration::{ComposeTest, ComposeTestLocalConfig}, - state::EnvsDir, }; pub(crate) fn exec( @@ -10,10 +12,50 @@ pub(crate) fn exec( test_name: &str, all_features: bool, ) -> Result<()> { - if let Some(active) = EnvsDir::new(test_name).active()? { - ComposeTest::generate(local_config, test_name, active, all_features, 0)?.stop() + // Find which environment is running by checking docker compose ls + let active_environment = find_active_environment(local_config, test_name)?; + + if let Some(environment) = active_environment { + ComposeTest::generate(local_config, test_name, environment, all_features, 0)?.stop() } else { println!("No environment for {test_name} is active."); Ok(()) } } + +fn find_active_environment( + local_config: ComposeTestLocalConfig, + test_name: &str, +) -> Result> { + let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, test_name)?; + let prefix = format!("vector-{}-{}-", local_config.directory, test_name); + + let output = Command::new(CONTAINER_TOOL.clone()) + .args(["compose", "ls", "--format", "json"]) + .output() + .with_context(|| "Failed to list compose projects")?; + + if !output.status.success() { + return Ok(None); + } + + let projects: Vec = serde_json::from_slice(&output.stdout) + .with_context(|| "Failed to parse docker compose ls output")?; + + // Find a project that matches our naming pattern + for project in projects { + if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { + if let Some(sanitized_env_name) = name.strip_prefix(&prefix) { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Ok(Some(env_name.to_string())); + } + } + } + } + } + + Ok(None) +} diff --git a/vdev/src/commands/compose_tests/test.rs b/vdev/src/commands/compose_tests/test.rs index e2a24e50943a1..9221d8639ef2a 100644 --- a/vdev/src/commands/compose_tests/test.rs +++ b/vdev/src/commands/compose_tests/test.rs @@ -1,11 +1,11 @@ -use std::iter::once; +use std::{iter::once, process::Command}; -use anyhow::{Result, bail}; +use anyhow::{Context, Result, bail}; use crate::testing::{ config::ComposeTestConfig, + docker::CONTAINER_TOOL, integration::{ComposeTest, ComposeTestLocalConfig}, - state::EnvsDir, }; pub fn exec( @@ -19,8 +19,8 @@ pub fn exec( let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, integration)?; let envs = config.environments(); - let active = EnvsDir::new(integration).active()?; - debug!("Active environment: {environment:#?}"); + let active = find_active_environment(local_config, integration, &config)?; + debug!("Active environment: {active:#?}"); let environments: Box> = match (environment, &active) { (Some(environment), Some(active)) if environment != active => { @@ -37,3 +37,39 @@ pub fn exec( } Ok(()) } + +fn find_active_environment( + local_config: ComposeTestLocalConfig, + integration: &str, + config: &ComposeTestConfig, +) -> Result> { + let prefix = format!("vector-{}-{}-", local_config.directory, integration); + + let output = Command::new(CONTAINER_TOOL.clone()) + .args(["compose", "ls", "--format", "json"]) + .output() + .with_context(|| "Failed to list compose projects")?; + + if !output.status.success() { + return Ok(None); + } + + let projects: Vec = serde_json::from_slice(&output.stdout) + .with_context(|| "Failed to parse docker compose ls output")?; + + for project in projects { + if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { + if let Some(sanitized_env_name) = name.strip_prefix(&prefix) { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Ok(Some(env_name.to_string())); + } + } + } + } + } + + Ok(None) +} diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index 47a16b51d3d10..4ae426774db43 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -1,20 +1,15 @@ use std::{ - collections::BTreeMap, - fs, path::{Path, PathBuf}, process::Command, }; use anyhow::{Context, Result, bail}; -use serde_yaml::Value; -use tempfile::{Builder, NamedTempFile}; use super::{ config::{ ComposeConfig, ComposeTestConfig, E2E_TESTS_DIR, INTEGRATION_TESTS_DIR, RustToolchainConfig, }, runner::{ContainerTestRunner as _, IntegrationTestRunner, TestRunner as _}, - state::EnvsDir, }; use crate::{ app::CommandExt as _, @@ -69,7 +64,6 @@ pub(crate) struct ComposeTest { test_name: String, environment: String, config: ComposeTestConfig, - envs_dir: EnvsDir, runner: IntegrationTestRunner, compose: Option, env_config: Environment, @@ -88,7 +82,6 @@ impl ComposeTest { let test_name: String = test_name.into(); let environment = environment.into(); let (test_dir, config) = ComposeTestConfig::load(local_config.directory, &test_name)?; - let envs_dir = EnvsDir::new(&test_name); let Some(mut env_config) = config.environments().get(&environment).cloned() else { bail!("Could not find environment named {environment:?}"); }; @@ -112,7 +105,6 @@ impl ComposeTest { test_name, environment, config, - envs_dir, runner, compose, env_config: rename_environment_keys(&env_config), @@ -123,11 +115,48 @@ impl ComposeTest { Ok(compose_test) } + fn project_name(&self) -> String { + // Docker Compose project names must consist only of lowercase alphanumeric characters, + // hyphens, and underscores. Replace any dots with hyphens. + let sanitized_env = self.environment.replace('.', "-"); + format!( + "vector-{}-{}-{}", + self.local_config.directory, self.test_name, sanitized_env + ) + } + + fn is_running(&self) -> Result { + let Some(compose) = &self.compose else { + return Ok(false); + }; + + let output = Command::new(CONTAINER_TOOL.clone()) + .args([ + "compose", + "--project-name", + &self.project_name(), + "ps", + "--format", + "json", + "--status", + "running", + ]) + .current_dir(&compose.test_dir) + .envs(compose.env.iter().filter_map(|(k, v)| v.as_ref().map(|v| (k, v)))) + .output() + .with_context(|| "Failed to check if compose environment is running")?; + + // If stdout is empty or "[]", no containers are running + Ok(!output.stdout.is_empty() + && output.stdout != b"[]\n" + && output.stdout != b"[]") + } + pub(crate) fn test(&self, extra_args: Vec) -> Result<()> { - let active = self.envs_dir.check_active(&self.environment)?; + let was_running = self.is_running()?; self.config.check_required()?; - if !active { + if !was_running { self.start()?; } @@ -181,7 +210,7 @@ impl ComposeTest { self.local_config.directory, )?; - if !active { + if !was_running { self.runner.remove()?; self.stop()?; } @@ -204,28 +233,25 @@ impl ComposeTest { if let Some(compose) = &self.compose { self.runner.ensure_network()?; - if self.envs_dir.check_active(&self.environment)? { + if self.is_running()? { bail!("environment is already up"); } - compose.start(&self.env_config)?; - - self.envs_dir.save(&self.environment, &self.env_config) - } else { - Ok(()) + let project_name = self.project_name(); + compose.start(&self.env_config, &project_name)?; } + Ok(()) } pub(crate) fn stop(&self) -> Result<()> { if let Some(compose) = &self.compose { - // TODO: Is this check really needed? - if self.envs_dir.load()?.is_none() { + if !self.is_running()? { bail!("No environment for {} is up.", self.test_name); } self.runner.remove()?; - compose.stop()?; - self.envs_dir.remove()?; + let project_name = self.project_name(); + compose.stop(&project_name)?; } Ok(()) @@ -240,7 +266,6 @@ struct Compose { #[cfg_attr(target_family = "windows", allow(dead_code))] config: ComposeConfig, network: String, - temp_file: NamedTempFile, } impl Compose { @@ -253,30 +278,8 @@ impl Compose { } Ok(false) => Ok(None), Ok(true) => { - let mut config = ComposeConfig::parse(&original_path)?; - // Inject the networks block - config.networks.insert( - "default".to_string(), - BTreeMap::from_iter([ - ("name".to_string(), Value::String(network.clone())), - ("external".to_string(), Value::Bool(true)), - ]), - ); - - // Create a named tempfile, there may be resource leakage here in case of SIGINT - // Tried tempfile::tempfile() but this returns a File object without a usable path - // https://docs.rs/tempfile/latest/tempfile/#resource-leaking - let temp_file = Builder::new() - .prefix("compose-temp-") - .suffix(".yaml") - .tempfile_in(&test_dir) - .with_context(|| "Failed to create temporary compose file")?; - - fs::write( - temp_file.path(), - serde_yaml::to_string(&config) - .with_context(|| "Failed to serialize modified compose.yaml")?, - )?; + // Parse config only for unix volume permission checking + let config = ComposeConfig::parse(&original_path)?; Ok(Some(Self { original_path, @@ -284,25 +287,25 @@ impl Compose { env, config, network, - temp_file, })) } } } - fn start(&self, environment: &Environment) -> Result<()> { + fn start(&self, environment: &Environment, project_name: &str) -> Result<()> { #[cfg(unix)] unix::prepare_compose_volumes(&self.config, &self.test_dir, environment)?; - self.run("Starting", &["up", "--detach"], Some(environment)) + self.run("Starting", &["up", "--detach"], Some(environment), project_name) } - fn stop(&self) -> Result<()> { + fn stop(&self, project_name: &str) -> Result<()> { // The config settings are not needed when stopping a compose setup. self.run( "Stopping", &["down", "--timeout", "0", "--volumes", "--remove-orphans"], None, + project_name, ) } @@ -311,21 +314,14 @@ impl Compose { action: &str, args: &[&'static str], environment: Option<&Environment>, + project_name: &str, ) -> Result<()> { let mut command = Command::new(CONTAINER_TOOL.clone()); command.arg("compose"); - // When the integration test environment is already active, the tempfile path does not - // exist because `Compose::new()` has not been called. In this case, the `stop` command - // needs to use the calculated path from the integration name instead of the nonexistent - // tempfile path. This is because `stop` doesn't go through the same logic as `start` - // and doesn't create a new tempfile before calling docker compose. - // If stop command needs to use some of the injected bits then we need to rebuild it + command.arg("--project-name"); + command.arg(project_name); command.arg("--file"); - if self.temp_file.path().exists() { - command.arg(self.temp_file.path()); - } else { - command.arg(&self.original_path); - } + command.arg(&self.original_path); command.args(args); diff --git a/vdev/src/testing/mod.rs b/vdev/src/testing/mod.rs index a899e52804387..a5ee65f2b589b 100644 --- a/vdev/src/testing/mod.rs +++ b/vdev/src/testing/mod.rs @@ -3,4 +3,3 @@ pub mod config; pub mod docker; pub mod integration; pub mod runner; -pub mod state; diff --git a/vdev/src/testing/state.rs b/vdev/src/testing/state.rs deleted file mode 100644 index b97e0aa8c9858..0000000000000 --- a/vdev/src/testing/state.rs +++ /dev/null @@ -1,93 +0,0 @@ -use std::{ - fs, - io::ErrorKind, - path::{Path, PathBuf}, - sync::LazyLock, -}; - -use anyhow::{Context, Result, anyhow}; -use serde::{Deserialize, Serialize}; - -use crate::{environment::Environment, platform, util}; - -static DATA_DIR: LazyLock = LazyLock::new(|| { - [platform::data_dir(), Path::new("integration")] - .into_iter() - .collect() -}); - -#[derive(Debug)] -pub struct EnvsDir { - path: PathBuf, -} - -#[derive(Deserialize, Serialize)] -pub struct State { - pub active: String, - pub config: Environment, -} - -impl EnvsDir { - pub fn new(integration: &str) -> Self { - let config = format!("{integration}.json"); - let path = [&DATA_DIR, Path::new(&config)].iter().collect(); - Self { path } - } - - /// Check if the named environment is active. If the current config could not be loaded or a - /// different environment is active, an error is returned. - pub fn check_active(&self, name: &str) -> Result { - match self.active()? { - None => Ok(false), - Some(active) if active == name => Ok(true), - Some(active) => Err(anyhow!( - "Requested environment {name:?} does not match active one {active:?}" - )), - } - } - - /// Return the currently active environment name. - pub fn active(&self) -> Result> { - self.load().map(|state| state.map(|config| config.active)) - } - - /// Load the currently active state data. - pub fn load(&self) -> Result> { - let json = match fs::read_to_string(&self.path) { - Ok(json) => json, - Err(error) if error.kind() == ErrorKind::NotFound => return Ok(None), - Err(error) => { - return Err(error) - .context(format!("Could not read state file {}", self.path.display())); - } - }; - let state: State = serde_json::from_str(&json) - .with_context(|| format!("Could not parse state file {}", self.path.display()))?; - Ok(Some(state)) - } - - pub fn save(&self, environment: &str, config: &Environment) -> Result<()> { - let config = State { - active: environment.into(), - config: config.clone(), - }; - let path = &*DATA_DIR; - if !path.is_dir() { - fs::create_dir_all(path) - .with_context(|| format!("failed to create directory {}", path.display()))?; - } - - let config = serde_json::to_string(&config)?; - fs::write(&self.path, config) - .with_context(|| format!("failed to write file {}", self.path.display())) - } - - pub fn remove(&self) -> Result<()> { - if util::exists(&self.path)? { - fs::remove_file(&self.path) - .with_context(|| format!("failed to remove {}", self.path.display()))?; - } - - Ok(()) - } -} From 64cda48b2e0eccc1c1e821c0770960a7e0ef44d1 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 11:08:09 -0400 Subject: [PATCH 02/22] ran cargo fmt --- vdev/src/testing/integration.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index 4ae426774db43..c672797c73d59 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -142,14 +142,17 @@ impl ComposeTest { "running", ]) .current_dir(&compose.test_dir) - .envs(compose.env.iter().filter_map(|(k, v)| v.as_ref().map(|v| (k, v)))) + .envs( + compose + .env + .iter() + .filter_map(|(k, v)| v.as_ref().map(|v| (k, v))), + ) .output() .with_context(|| "Failed to check if compose environment is running")?; // If stdout is empty or "[]", no containers are running - Ok(!output.stdout.is_empty() - && output.stdout != b"[]\n" - && output.stdout != b"[]") + Ok(!output.stdout.is_empty() && output.stdout != b"[]\n" && output.stdout != b"[]") } pub(crate) fn test(&self, extra_args: Vec) -> Result<()> { @@ -296,7 +299,12 @@ impl Compose { #[cfg(unix)] unix::prepare_compose_volumes(&self.config, &self.test_dir, environment)?; - self.run("Starting", &["up", "--detach"], Some(environment), project_name) + self.run( + "Starting", + &["up", "--detach"], + Some(environment), + project_name, + ) } fn stop(&self, project_name: &str) -> Result<()> { From 91342fdc8d0aa3234bd86040829ffca009fc4a23 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 12:47:50 -0400 Subject: [PATCH 03/22] clippy fixes --- vdev/src/commands/compose_tests/show.rs | 6 +++--- vdev/src/commands/compose_tests/stop.rs | 5 ++--- vdev/src/commands/compose_tests/test.rs | 5 ++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 30528e5c3a983..3ef8d7987e44a 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -32,7 +32,7 @@ fn show_all(path: &str) -> Result<()> { println!("{:width$} Environment Name(s)", "Integration Name"); println!("{:width$} -------------------", "----------------"); for (integration, config) in entries { - let prefix = format!("vector-{}-{}-", path, integration); + let prefix = format!("vector-{path}-{integration}-"); let active_env = find_active_environment(&active_projects, &prefix, &config); let environments = config .environments() @@ -48,7 +48,7 @@ fn show_all(path: &str) -> Result<()> { fn show_one(integration: &str, path: &str) -> Result<()> { let (_test_dir, config) = ComposeTestConfig::load(path, integration)?; let active_projects = get_active_projects()?; - let prefix = format!("vector-{}-{}-", path, integration); + let prefix = format!("vector-{path}-{integration}-"); let active_env = find_active_environment(&active_projects, &prefix, &config); if let Some(args) = &config.args { @@ -142,7 +142,7 @@ fn find_active_environment( prefix: &str, config: &ComposeTestConfig, ) -> Option { - for (project_name, _) in active_projects { + for project_name in active_projects.keys() { if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { // The project name has dots replaced with hyphens, so we need to check // all environments to find a match after applying the same sanitization diff --git a/vdev/src/commands/compose_tests/stop.rs b/vdev/src/commands/compose_tests/stop.rs index 7f8d74d92dd24..8fc18bd12ef18 100644 --- a/vdev/src/commands/compose_tests/stop.rs +++ b/vdev/src/commands/compose_tests/stop.rs @@ -44,8 +44,8 @@ fn find_active_environment( // Find a project that matches our naming pattern for project in projects { - if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { - if let Some(sanitized_env_name) = name.strip_prefix(&prefix) { + if let Some(name) = project.get("Name").and_then(|n| n.as_str()) + && let Some(sanitized_env_name) = name.strip_prefix(&prefix) { // The project name has dots replaced with hyphens, so we need to check // all environments to find a match after applying the same sanitization for env_name in config.environments().keys() { @@ -54,7 +54,6 @@ fn find_active_environment( } } } - } } Ok(None) diff --git a/vdev/src/commands/compose_tests/test.rs b/vdev/src/commands/compose_tests/test.rs index 9221d8639ef2a..d95b32ccae17c 100644 --- a/vdev/src/commands/compose_tests/test.rs +++ b/vdev/src/commands/compose_tests/test.rs @@ -58,8 +58,8 @@ fn find_active_environment( .with_context(|| "Failed to parse docker compose ls output")?; for project in projects { - if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { - if let Some(sanitized_env_name) = name.strip_prefix(&prefix) { + if let Some(name) = project.get("Name").and_then(|n| n.as_str()) + && let Some(sanitized_env_name) = name.strip_prefix(&prefix) { // The project name has dots replaced with hyphens, so we need to check // all environments to find a match after applying the same sanitization for env_name in config.environments().keys() { @@ -68,7 +68,6 @@ fn find_active_environment( } } } - } } Ok(None) From 06ddd0463009374e2da6ea62e5298dff9278be78 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:02:57 -0400 Subject: [PATCH 04/22] refactor show further, to reduce duplication --- vdev/src/commands/compose_tests/show.rs | 220 +++++++++++++----------- 1 file changed, 115 insertions(+), 105 deletions(-) diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 3ef8d7987e44a..3e8b91d01d2a2 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -7,9 +7,10 @@ use crate::{ }; pub fn exec(integration: Option<&String>, path: &str) -> Result<()> { + let show = Show::new(path)?; match integration { - None => show_all(path), - Some(integration) => show_one(integration, path), + None => show.show_all(), + Some(integration) => show.show_one(integration), } } @@ -22,76 +23,127 @@ pub fn exec_environments_only(integration: &str, path: &str) -> Result<()> { Ok(()) } -fn show_all(path: &str) -> Result<()> { - let entries = ComposeTestConfig::collect_all(path)?; - let active_projects = get_active_projects()?; - - let width = entries - .keys() - .fold(16, |width, entry| width.max(entry.len())); - println!("{:width$} Environment Name(s)", "Integration Name"); - println!("{:width$} -------------------", "----------------"); - for (integration, config) in entries { - let prefix = format!("vector-{path}-{integration}-"); - let active_env = find_active_environment(&active_projects, &prefix, &config); - let environments = config - .environments() - .keys() - .map(|environment| format(active_env.as_ref(), environment)) - .collect::>() - .join(" "); - println!("{integration:width$} {environments}"); - } - Ok(()) +struct Show { + path: String, + active_projects: HashMap, } -fn show_one(integration: &str, path: &str) -> Result<()> { - let (_test_dir, config) = ComposeTestConfig::load(path, integration)?; - let active_projects = get_active_projects()?; - let prefix = format!("vector-{path}-{integration}-"); - let active_env = find_active_environment(&active_projects, &prefix, &config); - - if let Some(args) = &config.args { - println!("Test args: {}", args.join(" ")); - } else { - println!("Test args: N/A"); +impl Show { + fn new(path: &str) -> Result { + let output = Command::new(CONTAINER_TOOL.clone()) + .args(["compose", "ls", "--format", "json"]) + .output() + .with_context(|| "Failed to list compose projects")?; + + let active_projects = if output.status.success() { + let projects: Vec = serde_json::from_slice(&output.stdout) + .with_context(|| "Failed to parse docker compose ls output")?; + + let mut map = HashMap::new(); + for project in projects { + if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { + map.insert(name.to_string(), true); + } + } + map + } else { + HashMap::new() + }; + + Ok(Self { + path: path.to_string(), + active_projects, + }) } - if config.features.is_empty() { - println!("Features: N/A"); - } else { - println!("Features: {}", config.features.join(",")); - } + fn show_all(&self) -> Result<()> { + let entries = ComposeTestConfig::collect_all(&self.path)?; - println!( - "Test filter: {}", - config.test_filter.as_deref().unwrap_or("N/A") - ); - - println!("Environment:"); - print_env(" ", &config.env); - println!("Runner:"); - println!(" Environment:"); - print_env(" ", &config.runner.env); - println!(" Volumes:"); - if config.runner.volumes.is_empty() { - println!(" N/A"); - } else { - for (target, mount) in &config.runner.volumes { - println!(" {target} => {mount}"); + let width = entries + .keys() + .fold(16, |width, entry| width.max(entry.len())); + println!("{:width$} Environment Name(s)", "Integration Name"); + println!("{:width$} -------------------", "----------------"); + for (integration, config) in entries { + let prefix = format!("vector-{}-{integration}-", self.path); + let active_env = self.find_active_environment(&prefix, &config); + let environments = config + .environments() + .keys() + .map(|environment| format_env(active_env.as_ref(), environment)) + .collect::>() + .join(" "); + println!("{integration:width$} {environments}"); } + Ok(()) } - println!( - " Needs docker socket: {}", - config.runner.needs_docker_socket - ); - println!("Environments:"); - for environment in config.environments().keys() { - println!(" {}", format(active_env.as_ref(), environment)); + fn show_one(&self, integration: &str) -> Result<()> { + let (_test_dir, config) = ComposeTestConfig::load(&self.path, integration)?; + let prefix = format!("vector-{}-{integration}-", self.path); + let active_env = self.find_active_environment(&prefix, &config); + + if let Some(args) = &config.args { + println!("Test args: {}", args.join(" ")); + } else { + println!("Test args: N/A"); + } + + if config.features.is_empty() { + println!("Features: N/A"); + } else { + println!("Features: {}", config.features.join(",")); + } + + println!( + "Test filter: {}", + config.test_filter.as_deref().unwrap_or("N/A") + ); + + println!("Environment:"); + print_env(" ", &config.env); + println!("Runner:"); + println!(" Environment:"); + print_env(" ", &config.runner.env); + println!(" Volumes:"); + if config.runner.volumes.is_empty() { + println!(" N/A"); + } else { + for (target, mount) in &config.runner.volumes { + println!(" {target} => {mount}"); + } + } + println!( + " Needs docker socket: {}", + config.runner.needs_docker_socket + ); + + println!("Environments:"); + for environment in config.environments().keys() { + println!(" {}", format_env(active_env.as_ref(), environment)); + } + + Ok(()) } - Ok(()) + fn find_active_environment( + &self, + prefix: &str, + config: &ComposeTestConfig, + ) -> Option { + for project_name in self.active_projects.keys() { + if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Some(env_name.to_string()); + } + } + } + } + None + } } fn print_env(prefix: &str, environment: &Environment) { @@ -107,51 +159,9 @@ fn print_env(prefix: &str, environment: &Environment) { } } -fn format(active_env: Option<&String>, environment: &str) -> String { +fn format_env(active_env: Option<&String>, environment: &str) -> String { match active_env { Some(active) if active == environment => format!("{environment} (active)"), _ => environment.into(), } } - -fn get_active_projects() -> Result> { - let output = Command::new(CONTAINER_TOOL.clone()) - .args(["compose", "ls", "--format", "json"]) - .output() - .with_context(|| "Failed to list compose projects")?; - - if !output.status.success() { - return Ok(HashMap::new()); - } - - let projects: Vec = serde_json::from_slice(&output.stdout) - .with_context(|| "Failed to parse docker compose ls output")?; - - let mut map = HashMap::new(); - for project in projects { - if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { - map.insert(name.to_string(), true); - } - } - - Ok(map) -} - -fn find_active_environment( - active_projects: &HashMap, - prefix: &str, - config: &ComposeTestConfig, -) -> Option { - for project_name in active_projects.keys() { - if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { - // The project name has dots replaced with hyphens, so we need to check - // all environments to find a match after applying the same sanitization - for env_name in config.environments().keys() { - if env_name.replace('.', "-") == sanitized_env_name { - return Some(env_name.to_string()); - } - } - } - } - None -} From 47047feafb22d23ae4a75f90f57a983df2b64383 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:05:36 -0400 Subject: [PATCH 05/22] ran cargo fmt --- vdev/src/commands/compose_tests/show.rs | 6 +----- vdev/src/commands/compose_tests/stop.rs | 15 ++++++++------- vdev/src/commands/compose_tests/test.rs | 15 ++++++++------- 3 files changed, 17 insertions(+), 19 deletions(-) diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 3e8b91d01d2a2..1e97e4b01da88 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -126,11 +126,7 @@ impl Show { Ok(()) } - fn find_active_environment( - &self, - prefix: &str, - config: &ComposeTestConfig, - ) -> Option { + fn find_active_environment(&self, prefix: &str, config: &ComposeTestConfig) -> Option { for project_name in self.active_projects.keys() { if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { // The project name has dots replaced with hyphens, so we need to check diff --git a/vdev/src/commands/compose_tests/stop.rs b/vdev/src/commands/compose_tests/stop.rs index 8fc18bd12ef18..356b8f313b9ba 100644 --- a/vdev/src/commands/compose_tests/stop.rs +++ b/vdev/src/commands/compose_tests/stop.rs @@ -45,15 +45,16 @@ fn find_active_environment( // Find a project that matches our naming pattern for project in projects { if let Some(name) = project.get("Name").and_then(|n| n.as_str()) - && let Some(sanitized_env_name) = name.strip_prefix(&prefix) { - // The project name has dots replaced with hyphens, so we need to check - // all environments to find a match after applying the same sanitization - for env_name in config.environments().keys() { - if env_name.replace('.', "-") == sanitized_env_name { - return Ok(Some(env_name.to_string())); - } + && let Some(sanitized_env_name) = name.strip_prefix(&prefix) + { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Ok(Some(env_name.to_string())); } } + } } Ok(None) diff --git a/vdev/src/commands/compose_tests/test.rs b/vdev/src/commands/compose_tests/test.rs index d95b32ccae17c..bccd246490b72 100644 --- a/vdev/src/commands/compose_tests/test.rs +++ b/vdev/src/commands/compose_tests/test.rs @@ -59,15 +59,16 @@ fn find_active_environment( for project in projects { if let Some(name) = project.get("Name").and_then(|n| n.as_str()) - && let Some(sanitized_env_name) = name.strip_prefix(&prefix) { - // The project name has dots replaced with hyphens, so we need to check - // all environments to find a match after applying the same sanitization - for env_name in config.environments().keys() { - if env_name.replace('.', "-") == sanitized_env_name { - return Ok(Some(env_name.to_string())); - } + && let Some(sanitized_env_name) = name.strip_prefix(&prefix) + { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Ok(Some(env_name.to_string())); } } + } } Ok(None) From 2a3aa698d0561de6a0ae70e1cb70ab0aaf7e0062 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:07:29 -0400 Subject: [PATCH 06/22] show: use set, map redundant --- vdev/src/commands/compose_tests/show.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 1e97e4b01da88..6bb4025c5c434 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -1,5 +1,5 @@ use anyhow::{Context, Result}; -use std::{collections::HashMap, process::Command}; +use std::{collections::HashSet, process::Command}; use crate::{ environment::Environment, @@ -25,7 +25,7 @@ pub fn exec_environments_only(integration: &str, path: &str) -> Result<()> { struct Show { path: String, - active_projects: HashMap, + active_projects: HashSet, } impl Show { @@ -39,15 +39,14 @@ impl Show { let projects: Vec = serde_json::from_slice(&output.stdout) .with_context(|| "Failed to parse docker compose ls output")?; - let mut map = HashMap::new(); - for project in projects { - if let Some(name) = project.get("Name").and_then(|n| n.as_str()) { - map.insert(name.to_string(), true); - } - } - map + projects + .iter() + .filter_map(|project| { + project.get("Name").and_then(|n| n.as_str()).map(String::from) + }) + .collect() } else { - HashMap::new() + HashSet::new() }; Ok(Self { From 1cc9754ac0d4b6c087127714eb27172f217bebd2 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:08:36 -0400 Subject: [PATCH 07/22] add space --- vdev/src/commands/compose_tests/show.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 6bb4025c5c434..4409be6cc2e58 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -126,7 +126,7 @@ impl Show { } fn find_active_environment(&self, prefix: &str, config: &ComposeTestConfig) -> Option { - for project_name in self.active_projects.keys() { + for project_name in &self.active_projects { if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { // The project name has dots replaced with hyphens, so we need to check // all environments to find a match after applying the same sanitization @@ -143,7 +143,7 @@ impl Show { fn print_env(prefix: &str, environment: &Environment) { if environment.is_empty() { - println!("{prefix}N/A"); + println!("{prefix} N/A"); } else { for (key, value) in environment { match value { From 2902532467be38bc5786f88da1f5939f485b934d Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:13:36 -0400 Subject: [PATCH 08/22] reuse active projects code --- .../commands/compose_tests/active_projects.rs | 55 +++++++++++++++++++ vdev/src/commands/compose_tests/mod.rs | 2 + vdev/src/commands/compose_tests/show.rs | 47 +++------------- vdev/src/commands/compose_tests/stop.rs | 47 ++-------------- vdev/src/commands/compose_tests/test.rs | 45 ++------------- 5 files changed, 75 insertions(+), 121 deletions(-) create mode 100644 vdev/src/commands/compose_tests/active_projects.rs diff --git a/vdev/src/commands/compose_tests/active_projects.rs b/vdev/src/commands/compose_tests/active_projects.rs new file mode 100644 index 0000000000000..6f182286c02fd --- /dev/null +++ b/vdev/src/commands/compose_tests/active_projects.rs @@ -0,0 +1,55 @@ +use anyhow::{Context, Result}; +use std::{collections::HashSet, process::Command}; + +use crate::testing::{config::ComposeTestConfig, docker::CONTAINER_TOOL}; + +/// Query Docker Compose for active projects +pub(super) fn load_active_projects() -> Result> { + let output = Command::new(CONTAINER_TOOL.clone()) + .args(["compose", "ls", "--format", "json"]) + .output() + .with_context(|| "Failed to list compose projects")?; + + if !output.status.success() { + return Ok(HashSet::new()); + } + + let projects: Vec = serde_json::from_slice(&output.stdout) + .with_context(|| "Failed to parse docker compose ls output")?; + + Ok(projects + .iter() + .filter_map(|project| project.get("Name").and_then(|n| n.as_str()).map(String::from)) + .collect()) +} + +/// Find the active environment for a given integration by matching Docker Compose project names +pub(super) fn find_active_environment( + active_projects: &HashSet, + prefix: &str, + config: &ComposeTestConfig, +) -> Option { + for project_name in active_projects { + if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { + // The project name has dots replaced with hyphens, so we need to check + // all environments to find a match after applying the same sanitization + for env_name in config.environments().keys() { + if env_name.replace('.', "-") == sanitized_env_name { + return Some(env_name.to_string()); + } + } + } + } + None +} + +/// Find the active environment for a given integration by querying Docker Compose +pub(super) fn find_active_environment_for_integration( + directory: &str, + integration: &str, + config: &ComposeTestConfig, +) -> Result> { + let active_projects = load_active_projects()?; + let prefix = format!("vector-{directory}-{integration}-"); + Ok(find_active_environment(&active_projects, &prefix, config)) +} diff --git a/vdev/src/commands/compose_tests/mod.rs b/vdev/src/commands/compose_tests/mod.rs index 27a033f199a12..c289e7dff3d74 100644 --- a/vdev/src/commands/compose_tests/mod.rs +++ b/vdev/src/commands/compose_tests/mod.rs @@ -1,3 +1,5 @@ +mod active_projects; + pub(crate) mod ci_paths; pub(crate) mod show; pub(crate) mod start; diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index 4409be6cc2e58..c834a6251d498 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -1,11 +1,13 @@ -use anyhow::{Context, Result}; -use std::{collections::HashSet, process::Command}; +use anyhow::Result; +use std::collections::HashSet; use crate::{ environment::Environment, - testing::{config::ComposeTestConfig, docker::CONTAINER_TOOL}, + testing::config::ComposeTestConfig, }; +use super::active_projects::{find_active_environment, load_active_projects}; + pub fn exec(integration: Option<&String>, path: &str) -> Result<()> { let show = Show::new(path)?; match integration { @@ -30,28 +32,9 @@ struct Show { impl Show { fn new(path: &str) -> Result { - let output = Command::new(CONTAINER_TOOL.clone()) - .args(["compose", "ls", "--format", "json"]) - .output() - .with_context(|| "Failed to list compose projects")?; - - let active_projects = if output.status.success() { - let projects: Vec = serde_json::from_slice(&output.stdout) - .with_context(|| "Failed to parse docker compose ls output")?; - - projects - .iter() - .filter_map(|project| { - project.get("Name").and_then(|n| n.as_str()).map(String::from) - }) - .collect() - } else { - HashSet::new() - }; - Ok(Self { path: path.to_string(), - active_projects, + active_projects: load_active_projects()?, }) } @@ -65,7 +48,7 @@ impl Show { println!("{:width$} -------------------", "----------------"); for (integration, config) in entries { let prefix = format!("vector-{}-{integration}-", self.path); - let active_env = self.find_active_environment(&prefix, &config); + let active_env = find_active_environment(&self.active_projects, &prefix, &config); let environments = config .environments() .keys() @@ -80,7 +63,7 @@ impl Show { fn show_one(&self, integration: &str) -> Result<()> { let (_test_dir, config) = ComposeTestConfig::load(&self.path, integration)?; let prefix = format!("vector-{}-{integration}-", self.path); - let active_env = self.find_active_environment(&prefix, &config); + let active_env = find_active_environment(&self.active_projects, &prefix, &config); if let Some(args) = &config.args { println!("Test args: {}", args.join(" ")); @@ -125,20 +108,6 @@ impl Show { Ok(()) } - fn find_active_environment(&self, prefix: &str, config: &ComposeTestConfig) -> Option { - for project_name in &self.active_projects { - if let Some(sanitized_env_name) = project_name.strip_prefix(prefix) { - // The project name has dots replaced with hyphens, so we need to check - // all environments to find a match after applying the same sanitization - for env_name in config.environments().keys() { - if env_name.replace('.', "-") == sanitized_env_name { - return Some(env_name.to_string()); - } - } - } - } - None - } } fn print_env(prefix: &str, environment: &Environment) { diff --git a/vdev/src/commands/compose_tests/stop.rs b/vdev/src/commands/compose_tests/stop.rs index 356b8f313b9ba..12dec8dc7bca0 100644 --- a/vdev/src/commands/compose_tests/stop.rs +++ b/vdev/src/commands/compose_tests/stop.rs @@ -1,19 +1,19 @@ -use anyhow::{Context, Result}; -use std::process::Command; +use anyhow::Result; use crate::testing::{ config::ComposeTestConfig, - docker::CONTAINER_TOOL, integration::{ComposeTest, ComposeTestLocalConfig}, }; +use super::active_projects::find_active_environment_for_integration; + pub(crate) fn exec( local_config: ComposeTestLocalConfig, test_name: &str, all_features: bool, ) -> Result<()> { - // Find which environment is running by checking docker compose ls - let active_environment = find_active_environment(local_config, test_name)?; + let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, test_name)?; + let active_environment = find_active_environment_for_integration(local_config.directory, test_name, &config)?; if let Some(environment) = active_environment { ComposeTest::generate(local_config, test_name, environment, all_features, 0)?.stop() @@ -22,40 +22,3 @@ pub(crate) fn exec( Ok(()) } } - -fn find_active_environment( - local_config: ComposeTestLocalConfig, - test_name: &str, -) -> Result> { - let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, test_name)?; - let prefix = format!("vector-{}-{}-", local_config.directory, test_name); - - let output = Command::new(CONTAINER_TOOL.clone()) - .args(["compose", "ls", "--format", "json"]) - .output() - .with_context(|| "Failed to list compose projects")?; - - if !output.status.success() { - return Ok(None); - } - - let projects: Vec = serde_json::from_slice(&output.stdout) - .with_context(|| "Failed to parse docker compose ls output")?; - - // Find a project that matches our naming pattern - for project in projects { - if let Some(name) = project.get("Name").and_then(|n| n.as_str()) - && let Some(sanitized_env_name) = name.strip_prefix(&prefix) - { - // The project name has dots replaced with hyphens, so we need to check - // all environments to find a match after applying the same sanitization - for env_name in config.environments().keys() { - if env_name.replace('.', "-") == sanitized_env_name { - return Ok(Some(env_name.to_string())); - } - } - } - } - - Ok(None) -} diff --git a/vdev/src/commands/compose_tests/test.rs b/vdev/src/commands/compose_tests/test.rs index bccd246490b72..562bb2d0ca110 100644 --- a/vdev/src/commands/compose_tests/test.rs +++ b/vdev/src/commands/compose_tests/test.rs @@ -1,13 +1,14 @@ -use std::{iter::once, process::Command}; +use std::iter::once; -use anyhow::{Context, Result, bail}; +use anyhow::{Result, bail}; use crate::testing::{ config::ComposeTestConfig, - docker::CONTAINER_TOOL, integration::{ComposeTest, ComposeTestLocalConfig}, }; +use super::active_projects::find_active_environment_for_integration; + pub fn exec( local_config: ComposeTestLocalConfig, integration: &str, @@ -19,7 +20,7 @@ pub fn exec( let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, integration)?; let envs = config.environments(); - let active = find_active_environment(local_config, integration, &config)?; + let active = find_active_environment_for_integration(local_config.directory, integration, &config)?; debug!("Active environment: {active:#?}"); let environments: Box> = match (environment, &active) { @@ -37,39 +38,3 @@ pub fn exec( } Ok(()) } - -fn find_active_environment( - local_config: ComposeTestLocalConfig, - integration: &str, - config: &ComposeTestConfig, -) -> Result> { - let prefix = format!("vector-{}-{}-", local_config.directory, integration); - - let output = Command::new(CONTAINER_TOOL.clone()) - .args(["compose", "ls", "--format", "json"]) - .output() - .with_context(|| "Failed to list compose projects")?; - - if !output.status.success() { - return Ok(None); - } - - let projects: Vec = serde_json::from_slice(&output.stdout) - .with_context(|| "Failed to parse docker compose ls output")?; - - for project in projects { - if let Some(name) = project.get("Name").and_then(|n| n.as_str()) - && let Some(sanitized_env_name) = name.strip_prefix(&prefix) - { - // The project name has dots replaced with hyphens, so we need to check - // all environments to find a match after applying the same sanitization - for env_name in config.environments().keys() { - if env_name.replace('.', "-") == sanitized_env_name { - return Ok(Some(env_name.to_string())); - } - } - } - } - - Ok(None) -} From 055feb86d981e50fa965f31cc36a18b7cebbf694 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:22:54 -0400 Subject: [PATCH 09/22] ran cargo fmt --- vdev/src/commands/compose_tests/active_projects.rs | 7 ++++++- vdev/src/commands/compose_tests/show.rs | 6 +----- vdev/src/commands/compose_tests/stop.rs | 3 ++- vdev/src/commands/compose_tests/test.rs | 3 ++- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/vdev/src/commands/compose_tests/active_projects.rs b/vdev/src/commands/compose_tests/active_projects.rs index 6f182286c02fd..b2698a6cb9266 100644 --- a/vdev/src/commands/compose_tests/active_projects.rs +++ b/vdev/src/commands/compose_tests/active_projects.rs @@ -19,7 +19,12 @@ pub(super) fn load_active_projects() -> Result> { Ok(projects .iter() - .filter_map(|project| project.get("Name").and_then(|n| n.as_str()).map(String::from)) + .filter_map(|project| { + project + .get("Name") + .and_then(|n| n.as_str()) + .map(String::from) + }) .collect()) } diff --git a/vdev/src/commands/compose_tests/show.rs b/vdev/src/commands/compose_tests/show.rs index c834a6251d498..0fcc831e6cb3b 100644 --- a/vdev/src/commands/compose_tests/show.rs +++ b/vdev/src/commands/compose_tests/show.rs @@ -1,10 +1,7 @@ use anyhow::Result; use std::collections::HashSet; -use crate::{ - environment::Environment, - testing::config::ComposeTestConfig, -}; +use crate::{environment::Environment, testing::config::ComposeTestConfig}; use super::active_projects::{find_active_environment, load_active_projects}; @@ -107,7 +104,6 @@ impl Show { Ok(()) } - } fn print_env(prefix: &str, environment: &Environment) { diff --git a/vdev/src/commands/compose_tests/stop.rs b/vdev/src/commands/compose_tests/stop.rs index 12dec8dc7bca0..7470512efb5f1 100644 --- a/vdev/src/commands/compose_tests/stop.rs +++ b/vdev/src/commands/compose_tests/stop.rs @@ -13,7 +13,8 @@ pub(crate) fn exec( all_features: bool, ) -> Result<()> { let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, test_name)?; - let active_environment = find_active_environment_for_integration(local_config.directory, test_name, &config)?; + let active_environment = + find_active_environment_for_integration(local_config.directory, test_name, &config)?; if let Some(environment) = active_environment { ComposeTest::generate(local_config, test_name, environment, all_features, 0)?.stop() diff --git a/vdev/src/commands/compose_tests/test.rs b/vdev/src/commands/compose_tests/test.rs index 562bb2d0ca110..ac98d7718bfcd 100644 --- a/vdev/src/commands/compose_tests/test.rs +++ b/vdev/src/commands/compose_tests/test.rs @@ -20,7 +20,8 @@ pub fn exec( let (_test_dir, config) = ComposeTestConfig::load(local_config.directory, integration)?; let envs = config.environments(); - let active = find_active_environment_for_integration(local_config.directory, integration, &config)?; + let active = + find_active_environment_for_integration(local_config.directory, integration, &config)?; debug!("Active environment: {active:#?}"); let environments: Box> = match (environment, &active) { From 1b599f07a2ee5e87f1fe41f2c281052b68e21b96 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 13:35:50 -0400 Subject: [PATCH 10/22] rename --- vdev/src/testing/integration.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index c672797c73d59..34369d12d39dc 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -263,7 +263,7 @@ impl ComposeTest { #[derive(Debug)] struct Compose { - original_path: PathBuf, + compose_yaml_path: PathBuf, test_dir: PathBuf, env: Environment, #[cfg_attr(target_family = "windows", allow(dead_code))] @@ -273,19 +273,18 @@ struct Compose { impl Compose { fn new(test_dir: PathBuf, env: Environment, network: String) -> Result> { - let original_path: PathBuf = [&test_dir, Path::new("compose.yaml")].iter().collect(); + let compose_yaml_path: PathBuf = [&test_dir, Path::new("compose.yaml")].iter().collect(); - match original_path.try_exists() { - Err(error) => { - Err(error).with_context(|| format!("Could not lookup {}", original_path.display())) - } + match compose_yaml_path.try_exists() { + Err(error) => Err(error) + .with_context(|| format!("Could not lookup {}", compose_yaml_path.display())), Ok(false) => Ok(None), Ok(true) => { // Parse config only for unix volume permission checking - let config = ComposeConfig::parse(&original_path)?; + let config = ComposeConfig::parse(&compose_yaml_path)?; Ok(Some(Self { - original_path, + compose_yaml_path, test_dir, env, config, @@ -329,7 +328,7 @@ impl Compose { command.arg("--project-name"); command.arg(project_name); command.arg("--file"); - command.arg(&self.original_path); + command.arg(&self.compose_yaml_path); command.args(args); From dc75f7dbdd996ffaf02d18745c52f59617830df6 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 14:22:36 -0400 Subject: [PATCH 11/22] check if running again --- vdev/src/testing/integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index 34369d12d39dc..9e2652713dd1e 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -213,7 +213,7 @@ impl ComposeTest { self.local_config.directory, )?; - if !was_running { + if self.is_running()? { self.runner.remove()?; self.stop()?; } From ade71cc5d464cd5c10b16055e2cacd9dc3d30109 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 14:26:13 -0400 Subject: [PATCH 12/22] simplify map --- vdev/src/testing/integration.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index 9e2652713dd1e..e0ecfc4c17b75 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -146,7 +146,7 @@ impl ComposeTest { compose .env .iter() - .filter_map(|(k, v)| v.as_ref().map(|v| (k, v))), + .filter_map(|(k, v)| v.as_ref().map(|val| (k, val))), ) .output() .with_context(|| "Failed to check if compose environment is running")?; From a8b94af489a16062110f955dadbf68fb57da829f Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 14:40:52 -0400 Subject: [PATCH 13/22] clippy wants me to rename a perfectly named variable --- vdev/src/testing/integration.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index e0ecfc4c17b75..1fee6048e2cef 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -263,7 +263,7 @@ impl ComposeTest { #[derive(Debug)] struct Compose { - compose_yaml_path: PathBuf, + yaml_path: PathBuf, test_dir: PathBuf, env: Environment, #[cfg_attr(target_family = "windows", allow(dead_code))] @@ -273,18 +273,18 @@ struct Compose { impl Compose { fn new(test_dir: PathBuf, env: Environment, network: String) -> Result> { - let compose_yaml_path: PathBuf = [&test_dir, Path::new("compose.yaml")].iter().collect(); + let yaml_path: PathBuf = [&test_dir, Path::new("compose.yaml")].iter().collect(); - match compose_yaml_path.try_exists() { + match yaml_path.try_exists() { Err(error) => Err(error) - .with_context(|| format!("Could not lookup {}", compose_yaml_path.display())), + .with_context(|| format!("Could not lookup {}", yaml_path.display())), Ok(false) => Ok(None), Ok(true) => { // Parse config only for unix volume permission checking - let config = ComposeConfig::parse(&compose_yaml_path)?; + let config = ComposeConfig::parse(&yaml_path)?; Ok(Some(Self { - compose_yaml_path, + yaml_path, test_dir, env, config, @@ -328,7 +328,7 @@ impl Compose { command.arg("--project-name"); command.arg(project_name); command.arg("--file"); - command.arg(&self.compose_yaml_path); + command.arg(&self.yaml_path); command.args(args); From f5fbbdaa6af1a5ff7737fe7cdff7b780036c5e8a Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 9 Oct 2025 19:07:22 +0000 Subject: [PATCH 14/22] fmt --- vdev/src/testing/integration.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index 1fee6048e2cef..f22979ed57369 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -276,8 +276,9 @@ impl Compose { let yaml_path: PathBuf = [&test_dir, Path::new("compose.yaml")].iter().collect(); match yaml_path.try_exists() { - Err(error) => Err(error) - .with_context(|| format!("Could not lookup {}", yaml_path.display())), + Err(error) => { + Err(error).with_context(|| format!("Could not lookup {}", yaml_path.display())) + } Ok(false) => Ok(None), Ok(true) => { // Parse config only for unix volume permission checking From ebc52187e19e713ed23c528602331b80788e4f7c Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 16 Oct 2025 13:11:16 -0400 Subject: [PATCH 15/22] add external tag to e2e networks --- scripts/e2e/datadog-logs/compose.yaml | 1 + scripts/e2e/datadog-metrics/compose.yaml | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/e2e/datadog-logs/compose.yaml b/scripts/e2e/datadog-logs/compose.yaml index ae004588f46f3..d74dd756b5325 100644 --- a/scripts/e2e/datadog-logs/compose.yaml +++ b/scripts/e2e/datadog-logs/compose.yaml @@ -120,6 +120,7 @@ services: networks: default: name: ${VECTOR_NETWORK} + external: true volumes: log_path: {} diff --git a/scripts/e2e/datadog-metrics/compose.yaml b/scripts/e2e/datadog-metrics/compose.yaml index 80523ac8144f4..4859e43c9a5a2 100644 --- a/scripts/e2e/datadog-metrics/compose.yaml +++ b/scripts/e2e/datadog-metrics/compose.yaml @@ -79,6 +79,7 @@ services: networks: default: name: ${VECTOR_NETWORK} + external: true volumes: target: {} From b0a38560db43fb185f0ab60cc70750713a9182d6 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 16 Oct 2025 13:47:37 -0400 Subject: [PATCH 16/22] rmv network_mode --- scripts/e2e/datadog-logs/compose.yaml | 1 - scripts/e2e/datadog-metrics/compose.yaml | 1 - 2 files changed, 2 deletions(-) diff --git a/scripts/e2e/datadog-logs/compose.yaml b/scripts/e2e/datadog-logs/compose.yaml index d74dd756b5325..dfefad548ad72 100644 --- a/scripts/e2e/datadog-logs/compose.yaml +++ b/scripts/e2e/datadog-logs/compose.yaml @@ -94,7 +94,6 @@ services: environment: - FEATURES=e2e-tests-datadog working_dir: /home/vector - network_mode: host command: - "/usr/bin/vector" - "-vvv" diff --git a/scripts/e2e/datadog-metrics/compose.yaml b/scripts/e2e/datadog-metrics/compose.yaml index 4859e43c9a5a2..75a1f09dc01f5 100644 --- a/scripts/e2e/datadog-metrics/compose.yaml +++ b/scripts/e2e/datadog-metrics/compose.yaml @@ -55,7 +55,6 @@ services: environment: - FEATURES=e2e-tests-datadog working_dir: /home/vector - network_mode: host command: - "/usr/bin/vector" - "-vvv" From 5799096c18999d302e375e1d7c49aa886b0d1e81 Mon Sep 17 00:00:00 2001 From: Pavlos Rontidis Date: Thu, 16 Oct 2025 14:35:06 -0400 Subject: [PATCH 17/22] fix postgres int test too --- scripts/integration/postgres/compose.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/scripts/integration/postgres/compose.yaml b/scripts/integration/postgres/compose.yaml index 007d058786881..8c3ccb12a7913 100644 --- a/scripts/integration/postgres/compose.yaml +++ b/scripts/integration/postgres/compose.yaml @@ -13,7 +13,11 @@ services: - ../../../tests/data/ca:/certs:ro volumes: - socket: {} + # Use external volume 'postgres_socket' that's shared with the test runner + # The runner creates this volume and mounts it at /pg_socket + socket: + name: postgres_socket + external: true networks: default: From c3ba5dbe05b26129c0ec0c7ab3efd0a2ad206903 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 16 Oct 2025 15:48:08 -0400 Subject: [PATCH 18/22] Add ensure_external_volumes --- vdev/src/testing/integration.rs | 1 + vdev/src/testing/runner.rs | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index f22979ed57369..5b50931ef0c9b 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -235,6 +235,7 @@ impl ComposeTest { self.config.check_required()?; if let Some(compose) = &self.compose { self.runner.ensure_network()?; + self.runner.ensure_external_volumes()?; if self.is_running()? { bail!("environment is already up"); diff --git a/vdev/src/testing/runner.rs b/vdev/src/testing/runner.rs index a0c7d002460ea..5d71ec2adfee6 100644 --- a/vdev/src/testing/runner.rs +++ b/vdev/src/testing/runner.rs @@ -308,6 +308,29 @@ impl IntegrationTestRunner { Ok(()) } } + + pub(super) fn ensure_external_volumes(&self) -> Result<()> { + // Get list of existing volumes + let mut command = docker_command(["volume", "ls", "--format", "{{.Name}}"]); + let existing_volumes: HashSet = command + .check_output()? + .lines() + .map(String::from) + .collect(); + + // Extract volume names from self.volumes (format is "volume_name:/mount/path") + for volume_spec in &self.volumes { + if let Some((volume_name, _)) = volume_spec.split_once(':') { + // Only create named volumes (not paths like /host/path) + if !volume_name.starts_with('/') && !existing_volumes.contains(volume_name) { + docker_command(["volume", "create", volume_name]) + .wait(format!("Creating volume {volume_name}"))?; + } + } + } + + Ok(()) + } } impl ContainerTestRunner for IntegrationTestRunner { From 371593e17b6118720141e0ab4f7f58bb6b7b56a5 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 16 Oct 2025 16:01:38 -0400 Subject: [PATCH 19/22] Format --- vdev/src/testing/runner.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/vdev/src/testing/runner.rs b/vdev/src/testing/runner.rs index 5d71ec2adfee6..7fe682db4418e 100644 --- a/vdev/src/testing/runner.rs +++ b/vdev/src/testing/runner.rs @@ -312,11 +312,8 @@ impl IntegrationTestRunner { pub(super) fn ensure_external_volumes(&self) -> Result<()> { // Get list of existing volumes let mut command = docker_command(["volume", "ls", "--format", "{{.Name}}"]); - let existing_volumes: HashSet = command - .check_output()? - .lines() - .map(String::from) - .collect(); + let existing_volumes: HashSet = + command.check_output()?.lines().map(String::from).collect(); // Extract volume names from self.volumes (format is "volume_name:/mount/path") for volume_spec in &self.volumes { From 20e1e327cf0c7c8e63f207c6ee67a8e6545c7e42 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 16 Oct 2025 17:04:24 -0400 Subject: [PATCH 20/22] Fix nginx int tests --- scripts/integration/nginx/compose.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/integration/nginx/compose.yaml b/scripts/integration/nginx/compose.yaml index ddedd8eb2e05e..aad61bbc97a13 100644 --- a/scripts/integration/nginx/compose.yaml +++ b/scripts/integration/nginx/compose.yaml @@ -22,4 +22,7 @@ services: - proxy networks: + default: + name: ${VECTOR_NETWORK} + external: true proxy: {} From da33c6ebda2eae284a3cb62be5972495feea4379 Mon Sep 17 00:00:00 2001 From: Thomas Date: Thu, 16 Oct 2025 17:04:38 -0400 Subject: [PATCH 21/22] Pass environment to docker compose down --- vdev/src/testing/integration.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/vdev/src/testing/integration.rs b/vdev/src/testing/integration.rs index 5b50931ef0c9b..7256bc420e787 100644 --- a/vdev/src/testing/integration.rs +++ b/vdev/src/testing/integration.rs @@ -255,7 +255,7 @@ impl ComposeTest { self.runner.remove()?; let project_name = self.project_name(); - compose.stop(&project_name)?; + compose.stop(&self.env_config, &project_name)?; } Ok(()) @@ -308,12 +308,11 @@ impl Compose { ) } - fn stop(&self, project_name: &str) -> Result<()> { - // The config settings are not needed when stopping a compose setup. + fn stop(&self, environment: &Environment, project_name: &str) -> Result<()> { self.run( "Stopping", &["down", "--timeout", "0", "--volumes", "--remove-orphans"], - None, + Some(environment), project_name, ) } From 6df87f4a641b26bc77c566a9bb772c76dc1603d7 Mon Sep 17 00:00:00 2001 From: Thomas Date: Fri, 17 Oct 2025 09:31:36 -0400 Subject: [PATCH 22/22] Mark dnstap_dnstap-sockets as an external volume --- scripts/integration/dnstap/compose.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/integration/dnstap/compose.yaml b/scripts/integration/dnstap/compose.yaml index 729ae6590aa96..00264121cb474 100644 --- a/scripts/integration/dnstap/compose.yaml +++ b/scripts/integration/dnstap/compose.yaml @@ -12,7 +12,9 @@ services: - dnstap-sockets:/bind3/etc/bind/socket volumes: - dnstap-sockets: {} + dnstap-sockets: + name: dnstap_dnstap-sockets + external: true networks: default: