From 0c6faeea5bbc4c5a88146bcd7f52de1b2875b087 Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Tue, 2 Sep 2025 09:43:09 +0200 Subject: [PATCH 1/9] Initial changes (test still need to be adapted and some code can be simplified now). --- relay-config/src/config.rs | 30 +++++++++++++------ relay-server/src/processing/mod.rs | 2 +- relay-server/src/services/processor.rs | 2 +- .../src/services/projects/source/mod.rs | 15 +--------- relay/src/cli.rs | 4 +-- 5 files changed, 25 insertions(+), 28 deletions(-) diff --git a/relay-config/src/config.rs b/relay-config/src/config.rs index 65e53930a35..652e812c4bd 100644 --- a/relay-config/src/config.rs +++ b/relay-config/src/config.rs @@ -324,7 +324,7 @@ impl RelayInfo { } /// The operation mode of a relay. -#[derive(Clone, Copy, Debug, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] pub enum RelayMode { /// This relay acts as a proxy for all requests and events. @@ -334,12 +334,6 @@ pub enum RelayMode { /// accepted unless overridden on the file system. Proxy, - /// This relay is configured statically in the file system. - /// - /// Events are only accepted for projects configured statically in the file system. All other - /// events are rejected. If configured, PII stripping is also performed on those events. - Static, - /// Project configurations are managed by the upstream. /// /// Project configurations are always fetched from the upstream, unless they are statically @@ -348,11 +342,30 @@ pub enum RelayMode { Managed, } +impl<'de> Deserialize<'de> for RelayMode { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let s = String::deserialize(deserializer)?; + match s.as_str() { + "proxy" => Ok(RelayMode::Proxy), + "managed" => Ok(RelayMode::Managed), + "static" => Err(serde::de::Error::custom( + "Relay mode 'static' has been depreciated. Please use 'managed' or 'proxy' instead.", + )), + other => Err(serde::de::Error::unknown_variant( + other, + &["proxy", "managed"], + )), + } + } +} + impl fmt::Display for RelayMode { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { RelayMode::Proxy => write!(f, "proxy"), - RelayMode::Static => write!(f, "static"), RelayMode::Managed => write!(f, "managed"), } } @@ -417,7 +430,6 @@ impl FromStr for RelayMode { fn from_str(s: &str) -> Result { match s { "proxy" => Ok(RelayMode::Proxy), - "static" => Ok(RelayMode::Static), "managed" => Ok(RelayMode::Managed), _ => Err(ParseRelayModeError), } diff --git a/relay-server/src/processing/mod.rs b/relay-server/src/processing/mod.rs index 9fb4bbda3f4..853f1305a22 100644 --- a/relay-server/src/processing/mod.rs +++ b/relay-server/src/processing/mod.rs @@ -92,7 +92,7 @@ impl Context<'_> { use relay_config::RelayMode::*; match self.config.relay_mode() { - Proxy | Static => false, + Proxy => false, Managed => !self.project_info.has_feature(feature), } } diff --git a/relay-server/src/services/processor.rs b/relay-server/src/services/processor.rs index 2bbe72e6596..b3ba2fb2e75 100644 --- a/relay-server/src/services/processor.rs +++ b/relay-server/src/services/processor.rs @@ -827,7 +827,7 @@ fn event_type(event: &Annotated) -> Option { /// If the project config did not come from the upstream, we keep the items. fn should_filter(config: &Config, project_info: &ProjectInfo, feature: Feature) -> bool { match config.relay_mode() { - RelayMode::Proxy | RelayMode::Static => false, + RelayMode::Proxy => false, RelayMode::Managed => !project_info.has_feature(feature), } } diff --git a/relay-server/src/services/projects/source/mod.rs b/relay-server/src/services/projects/source/mod.rs index 660832f8c79..8cdd28252aa 100644 --- a/relay-server/src/services/projects/source/mod.rs +++ b/relay-server/src/services/projects/source/mod.rs @@ -37,11 +37,7 @@ impl ProjectSource { upstream_relay: Addr, #[cfg(feature = "processing")] _redis: Option, ) -> Self { - let local_source = if config.relay_mode() == RelayMode::Static { - Some(services.start(LocalProjectSourceService::new(config.clone()))) - } else { - None - }; + let local_source = None; let upstream_source = services.start(UpstreamProjectSourceService::new( config.clone(), @@ -73,15 +69,6 @@ impl ProjectSource { match self.config.relay_mode() { RelayMode::Proxy => return Ok(ProjectState::new_allowed().into()), RelayMode::Managed => (), // Proceed with loading the config from redis or upstream - RelayMode::Static => { - return Ok(match self.local_source { - Some(local) => local - .send(FetchOptionalProjectState { project_key }) - .await? - .map_or(ProjectState::Disabled.into(), Into::into), - None => ProjectState::Disabled.into(), - }); - } } #[cfg(feature = "processing")] diff --git a/relay/src/cli.rs b/relay/src/cli.rs index 85992c81e59..654c5d6c246 100644 --- a/relay/src/cli.rs +++ b/relay/src/cli.rs @@ -281,14 +281,12 @@ pub fn init_config>(config_path: P, _matches: &ArgMatches) -> Res .with_prompt("How should this relay operate?") .default(0) .item("Managed through upstream") - .item("Statically configured") .item("Proxy for all events") .interact()?; mincfg.relay.mode = match mode { 0 => RelayMode::Managed, - 1 => RelayMode::Static, - 2 => RelayMode::Proxy, + 1 => RelayMode::Proxy, _ => unreachable!(), }; From b1acee0e00e3a1c88b14a81a42dc2b79dd8b541f Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Tue, 2 Sep 2025 10:15:00 +0200 Subject: [PATCH 2/9] Remove static relay tests --- tests/integration/test_metrics.py | 54 ------------------------- tests/integration/test_query.py | 51 ----------------------- tests/integration/test_store.py | 34 ++-------------- tests/integration/test_trusted_relay.py | 44 -------------------- 4 files changed, 3 insertions(+), 180 deletions(-) diff --git a/tests/integration/test_metrics.py b/tests/integration/test_metrics.py index 133a0a68279..d20abb4a0eb 100644 --- a/tests/integration/test_metrics.py +++ b/tests/integration/test_metrics.py @@ -1343,60 +1343,6 @@ def test_limit_custom_measurements( assert metrics.keys() == expected_metrics -@pytest.mark.parametrize("has_measurements_config", [True, False]) -def test_do_not_drop_custom_measurements_in_static( - mini_sentry, - relay, - metrics_consumer, - transactions_consumer, - has_measurements_config, -): - project_id = 42 - config = mini_sentry.add_full_project_config(project_id) - - if has_measurements_config: - config["config"]["measurements"] = { - "maxCustomMeasurements": 1, - } - - metrics_consumer = metrics_consumer() - transactions_consumer = transactions_consumer() - - def configure_static_project(dir): - os.remove(dir.join("credentials.json")) - os.makedirs(dir.join("projects")) - dir.join("projects").join(f"{project_id}.json").write(json.dumps(config)) - - relay = relay( - mini_sentry, - options=TEST_CONFIG | {"relay": {"mode": "static"}}, - prepare=configure_static_project, - ) - - timestamp = datetime.now(tz=timezone.utc) - transaction = generate_transaction_item() - transaction["timestamp"] = timestamp.isoformat() - transaction["measurements"] = { - "foo": {"value": 1.2}, - "baz": {"value": 1.3}, - "bar": {"value": 1.4}, - } - - relay.send_transaction(42, transaction) - event = mini_sentry.captured_events.get(timeout=2).items[0].payload.json - - if has_measurements_config: - # With maxCustomMeasurements: 1, only 1 measurement should pass through - assert event["measurements"] == {"bar": {"value": 1.4, "unit": "none"}} - else: - # Without measurements config, all measurements should pass through - assert event["measurements"] == { - "bar": {"value": 1.4, "unit": "none"}, - "baz": {"value": 1.3, "unit": "none"}, - "foo": {"value": 1.2, "unit": "none"}, - } - - def test_generic_metric_extraction(mini_sentry, relay): PROJECT_ID = 42 mini_sentry.add_full_project_config(PROJECT_ID) diff --git a/tests/integration/test_query.py b/tests/integration/test_query.py index e3b9dd220a2..f75bad9e6dc 100644 --- a/tests/integration/test_query.py +++ b/tests/integration/test_query.py @@ -12,57 +12,6 @@ import zstandard -def test_local_project_config(mini_sentry, relay): - project_id = 42 - config = mini_sentry.basic_project_config(project_id) - relay_config = { - "relay": { - "mode": "static", - }, - "cache": { - "file_interval": 1, - "project_expiry": 1, - "project_grace_period": 0, - }, - } - relay = relay(mini_sentry, relay_config, wait_health_check=False) - relay.config_dir.mkdir("projects").join("42.json").write( - json.dumps( - { - # remove defaults to assert they work - "publicKeys": config["publicKeys"], - "config": { - "allowedDomains": ["*"], - "trustedRelays": [], - "piiConfig": {}, - }, - } - ) - ) - # get the dsn key from the config - # we need to provide it manually to Relay since it is not in the config (of MiniSentry) and - # we don't look on the file system - dsn_key = config["publicKeys"][0]["publicKey"] - - relay.wait_relay_health_check() - mini_sentry.clear_test_failures() - - relay.send_event(project_id, dsn_key=dsn_key) - event = mini_sentry.captured_events.get(timeout=1).get_event() - assert event["logentry"] == {"formatted": "Hello, World!"} - - relay.config_dir.join("projects").join("42.json").write( - json.dumps({"disabled": True, "publicKeys": config["publicKeys"]}) - ) - - time.sleep(2) - - relay.send_event(project_id, dsn_key=dsn_key) - time.sleep(0.3) - pytest.raises(HTTPError, lambda: relay.send_event(project_id, dsn_key=dsn_key)) - pytest.raises(queue.Empty, lambda: mini_sentry.captured_events.get(timeout=1)) - - @pytest.mark.parametrize("grace_period", [0, 5]) def test_project_grace_period(mini_sentry, relay, grace_period): config = mini_sentry.add_basic_project_config(42) diff --git a/tests/integration/test_store.py b/tests/integration/test_store.py index 3ec2a60756a..99d102859e4 100644 --- a/tests/integration/test_store.py +++ b/tests/integration/test_store.py @@ -174,33 +174,6 @@ def store_event(): assert event["logentry"] == {"formatted": "correct"} -def test_store_static_config(mini_sentry, relay): - from time import sleep - - project_id = 42 - project_config = mini_sentry.add_basic_project_config(project_id) - - def configure_static_project(dir): - os.remove(dir.join("credentials.json")) - os.makedirs(dir.join("projects")) - dir.join("projects").join(f"{project_id}.json").write( - json.dumps(project_config) - ) - - relay_options = {"relay": {"mode": "static"}} - relay = relay(mini_sentry, options=relay_options, prepare=configure_static_project) - - relay.send_event(project_id) - event = mini_sentry.captured_events.get(timeout=1).get_event() - assert event["logentry"] == {"formatted": "Hello, World!"} - - sleep(1) # Regression test: Relay tried to issue a request for 0 states - if not mini_sentry.test_failures.empty(): - raise AssertionError( - f"Exceptions happened in mini_sentry: {mini_sentry.format_failures()}" - ) - - def test_store_proxy_config(mini_sentry, relay): from time import sleep @@ -1036,10 +1009,9 @@ def is_live(): assert evt.wait(5) -@pytest.mark.parametrize("mode", ["static", "proxy"]) -def test_no_auth(relay, mini_sentry, mode): +def test_no_auth(relay, mini_sentry): """ - Tests that relays that run in proxy and static mode do NOT authenticate + Tests that relays that run in proxy mode do NOT authenticate """ project_id = 42 project_config = mini_sentry.add_basic_project_config(project_id) @@ -1061,7 +1033,7 @@ def configure_static_project(dir): json.dumps(project_config) ) - relay_options = {"relay": {"mode": mode}} + relay_options = {"relay": {"mode": "proxy"}} relay = relay(mini_sentry, options=relay_options, prepare=configure_static_project) relay.send_event(project_id, {"message": "123"}) diff --git a/tests/integration/test_trusted_relay.py b/tests/integration/test_trusted_relay.py index 5c2e0e19c57..26d3d41d130 100644 --- a/tests/integration/test_trusted_relay.py +++ b/tests/integration/test_trusted_relay.py @@ -251,50 +251,6 @@ def test_not_trusted_relay(mini_sentry, relay, relay_credentials): relay.send_event(project_id) -def test_static_relay(mini_sentry, relay): - """ - A static relay without credentials will automatically fail the signature check - because it does not have credentials. - """ - project_id = 42 - config = mini_sentry.add_basic_project_config(project_id) - # config for the static relay does not have the verifySignatureSetting - static_config = deepcopy(config) - - config["config"]["trustedRelaySettings"]["verifySignature"] = "enabled" - - managed_relay = relay(mini_sentry) - - def configure_static_project(dir): - os.remove(dir.join("credentials.json")) - os.makedirs(dir.join("projects")) - dir.join("projects").join(f"{project_id}.json").write(json.dumps(static_config)) - - relay_options = {"relay": {"mode": "static"}} - static_relay = relay( - managed_relay, options=relay_options, prepare=configure_static_project - ) - - # sending to static relay is fine because it does not verify signature - static_relay.send_event(project_id) - - # wait for project config and make sure that the event was discarded - outcome = mini_sentry.get_client_report(timeout=1) - assert outcome["discarded_events"] == [ - {"reason": "missing_signature", "category": "error", "quantity": 1} - ] - - # sending to managed relay directly is not because it needs the signature - # it will fail in the fast path because the project config is fetched at this point - with pytest.raises(HTTPError, match="403 Client Error"): - managed_relay.send_event(project_id) - - outcome = mini_sentry.get_client_report(timeout=1) - assert outcome["discarded_events"] == [ - {"reason": "missing_signature", "category": "error", "quantity": 1} - ] - - def test_invalid_header_value(mini_sentry, relay): """ Tests that getting a sequence in a header that cannot be represented as a UTF-8 string will From f138c85dc333d291a1d80617d0ccaa9fa8a102a0 Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Tue, 2 Sep 2025 13:42:22 +0200 Subject: [PATCH 3/9] Remove no longer used code --- .../src/services/projects/source/local.rs | 288 ------------------ .../src/services/projects/source/mod.rs | 17 -- 2 files changed, 305 deletions(-) delete mode 100644 relay-server/src/services/projects/source/local.rs diff --git a/relay-server/src/services/projects/source/local.rs b/relay-server/src/services/projects/source/local.rs deleted file mode 100644 index a288d57d052..00000000000 --- a/relay-server/src/services/projects/source/local.rs +++ /dev/null @@ -1,288 +0,0 @@ -use std::collections::HashMap; -use std::ffi::OsStr; -use std::path::Path; -use std::sync::Arc; - -use relay_base_schema::project::{ProjectId, ProjectKey}; -use relay_config::Config; -use relay_system::{AsyncResponse, FromMessage, Interface, Receiver, Sender, Service}; -use tokio::sync::mpsc; -use tokio::time::Instant; - -use crate::services::projects::project::{ParsedProjectState, ProjectState}; -use crate::services::projects::source::FetchOptionalProjectState; - -/// Service interface of the local project source. -#[derive(Debug)] -pub struct LocalProjectSource(FetchOptionalProjectState, Sender>); - -impl Interface for LocalProjectSource {} - -impl FromMessage for LocalProjectSource { - type Response = AsyncResponse>; - fn from_message( - message: FetchOptionalProjectState, - sender: Sender>, - ) -> Self { - Self(message, sender) - } -} - -/// A service which periodically loads project states from disk. -#[derive(Debug)] -pub struct LocalProjectSourceService { - config: Arc, - local_states: HashMap, -} - -impl LocalProjectSourceService { - pub fn new(config: Arc) -> Self { - Self { - config, - local_states: HashMap::new(), - } - } - - fn handle_message(&mut self, message: LocalProjectSource) { - let LocalProjectSource(message, sender) = message; - let states = self.local_states.get(&message.project_key()).cloned(); - sender.send(states); - } -} - -fn get_project_id(path: &Path) -> Option { - path.file_stem() - .and_then(OsStr::to_str) - .and_then(|stem| stem.parse().ok()) -} - -fn parse_file( - path: std::path::PathBuf, -) -> tokio::io::Result<(std::path::PathBuf, ParsedProjectState)> { - let file = std::fs::File::open(&path)?; - let reader = std::io::BufReader::new(file); - let state = serde_json::from_reader(reader)?; - Ok((path, state)) -} - -async fn load_local_states( - projects_path: &Path, -) -> tokio::io::Result> { - let mut states = HashMap::new(); - - let mut directory = match tokio::fs::read_dir(projects_path).await { - Ok(directory) => directory, - Err(error) => { - return match error.kind() { - tokio::io::ErrorKind::NotFound => Ok(states), - _ => Err(error), - }; - } - }; - - // only printed when directory even exists. - relay_log::debug!(directory = ?projects_path, "loading local states from file system"); - - while let Some(entry) = directory.next_entry().await? { - let path = entry.path(); - - let metadata = entry.metadata().await?; - if !(metadata.is_file() || metadata.is_symlink()) { - relay_log::warn!(?path, "skipping file, not a file"); - continue; - } - - if path.extension().map(|x| x != "json").unwrap_or(true) { - relay_log::warn!(?path, "skipping file, file extension must be .json"); - continue; - } - - // serde_json is not async, so spawn a blocking task here: - let (path, mut state) = tokio::task::spawn_blocking(move || parse_file(path)).await??; - - if state.info.project_id.is_none() { - if let Some(project_id) = get_project_id(&path) { - state.info.project_id = Some(project_id); - } else { - relay_log::warn!(?path, "skipping file, filename is not a valid project id"); - continue; - } - } - - // Keep a separate project state per key. - let keys = std::mem::take(&mut state.info.public_keys); - if keys.is_empty() { - relay_log::warn!( - ?path, - "skipping file, project config is missing public keys" - ); - } - - for key in keys { - let mut state = state.clone(); - state.info.public_keys = smallvec::smallvec![key.clone()]; - states.insert(key.public_key, ProjectState::from(state).sanitized()); - } - } - - Ok(states) -} - -async fn poll_local_states(path: &Path, tx: &mpsc::Sender>) { - let states = load_local_states(path).await; - match states { - Ok(states) => { - let res = tx.send(states).await; - if res.is_err() { - relay_log::error!("failed to store static project configs"); - } - } - Err(error) => relay_log::error!( - error = &error as &dyn std::error::Error, - "failed to load static project configs", - ), - }; -} - -async fn spawn_poll_local_states( - config: &Config, - tx: mpsc::Sender>, -) { - let project_path = config.project_configs_path(); - let period = config.local_cache_interval(); - - // Poll local states once before handling any message, such that the projects are - // populated. - poll_local_states(&project_path, &tx).await; - - // Start a background loop that polls periodically: - relay_system::spawn!(async move { - // To avoid running two load tasks simultaneously at startup, we delay the interval by one period: - let start_at = Instant::now() + period; - let mut ticker = tokio::time::interval_at(start_at, period); - - loop { - ticker.tick().await; - poll_local_states(&project_path, &tx).await; - } - }); -} - -impl Service for LocalProjectSourceService { - type Interface = LocalProjectSource; - - async fn run(mut self, mut rx: Receiver) { - // Use a channel with size 1. If the channel is full because the consumer does not - // collect the result, the producer will block, which is acceptable. - let (state_tx, mut state_rx) = mpsc::channel(1); - - relay_log::info!("project local cache started"); - - // Start the background task that periodically reloads projects from disk: - spawn_poll_local_states(&self.config, state_tx).await; - - loop { - tokio::select! { - biased; - Some(message) = rx.recv() => self.handle_message(message), - Some(states) = state_rx.recv() => self.local_states = states, - - else => break, - } - } - relay_log::info!("project local cache stopped"); - } -} - -/// This works only on Unix systems. -#[cfg(not(target_os = "windows"))] -#[cfg(test)] -mod tests { - use std::str::FromStr; - - use super::*; - use crate::services::projects::project::{ProjectInfo, PublicKeyConfig}; - - /// Tests that we can follow the symlinks and read the project file properly. - #[tokio::test] - async fn test_symlinked_projects() { - let temp1 = tempfile::tempdir().unwrap(); - let temp2 = tempfile::tempdir().unwrap(); - - let tmp_project_file = "111111.json"; - let project_key = ProjectKey::parse("55f6b2d962564e99832a39890ee4573e").unwrap(); - - let mut tmp_project_state = ProjectInfo::default(); - tmp_project_state.public_keys.push(PublicKeyConfig { - public_key: project_key, - numeric_id: None, - }); - - // create the project file - let project_state = serde_json::to_string(&tmp_project_state).unwrap(); - tokio::fs::write( - temp1.path().join(tmp_project_file), - project_state.as_bytes(), - ) - .await - .unwrap(); - - tokio::fs::symlink( - temp1.path().join(tmp_project_file), - temp2.path().join(tmp_project_file), - ) - .await - .unwrap(); - - let extracted_project_state = load_local_states(temp2.path()).await.unwrap(); - let project_info = extracted_project_state - .get(&project_key) - .unwrap() - .clone() - .enabled() - .unwrap(); - - assert_eq!( - project_info.project_id, - Some(ProjectId::from_str("111111").unwrap()) - ); - - assert_eq!( - project_info.public_keys.first().unwrap().public_key, - project_key, - ) - } - - #[tokio::test] - async fn test_multi_pub_static_config() { - let temp = tempfile::tempdir().unwrap(); - - let tmp_project_file = "111111.json"; - let project_key1 = ProjectKey::parse("55f6b2d962564e99832a39890ee4573e").unwrap(); - let project_key2 = ProjectKey::parse("55bbb2d96256bb9983bb39890bb457bb").unwrap(); - - let mut tmp_project_state = ProjectInfo::default(); - tmp_project_state.public_keys.extend(vec![ - PublicKeyConfig { - public_key: project_key1, - numeric_id: None, - }, - PublicKeyConfig { - public_key: project_key2, - numeric_id: None, - }, - ]); - - // create the project file - let project_state = serde_json::to_string(&tmp_project_state).unwrap(); - tokio::fs::write(temp.path().join(tmp_project_file), project_state.as_bytes()) - .await - .unwrap(); - - let extracted_project_state = load_local_states(temp.path()).await.unwrap(); - - assert_eq!(extracted_project_state.len(), 2); - assert!(extracted_project_state.contains_key(&project_key1)); - assert!(extracted_project_state.contains_key(&project_key2)); - } -} diff --git a/relay-server/src/services/projects/source/mod.rs b/relay-server/src/services/projects/source/mod.rs index 8cdd28252aa..82aee412b23 100644 --- a/relay-server/src/services/projects/source/mod.rs +++ b/relay-server/src/services/projects/source/mod.rs @@ -6,7 +6,6 @@ use relay_system::{Addr, ServiceSpawn, ServiceSpawnExt as _}; use std::convert::Infallible; use std::sync::Arc; -pub mod local; #[cfg(feature = "processing")] pub mod redis; pub mod upstream; @@ -14,7 +13,6 @@ pub mod upstream; use crate::services::projects::project::{ProjectState, Revision}; use crate::services::upstream::UpstreamRelay; -use self::local::{LocalProjectSource, LocalProjectSourceService}; #[cfg(feature = "processing")] use self::redis::RedisProjectSource; use self::upstream::{UpstreamProjectSource, UpstreamProjectSourceService}; @@ -23,7 +21,6 @@ use self::upstream::{UpstreamProjectSource, UpstreamProjectSourceService}; #[derive(Clone, Debug)] pub struct ProjectSource { config: Arc, - local_source: Option>, upstream_source: Addr, #[cfg(feature = "processing")] redis_source: Option, @@ -37,8 +34,6 @@ impl ProjectSource { upstream_relay: Addr, #[cfg(feature = "processing")] _redis: Option, ) -> Self { - let local_source = None; - let upstream_source = services.start(UpstreamProjectSourceService::new( config.clone(), upstream_relay, @@ -50,7 +45,6 @@ impl ProjectSource { Self { config, - local_source, upstream_source, #[cfg(feature = "processing")] redis_source, @@ -149,17 +143,6 @@ pub struct FetchProjectState { pub no_cache: bool, } -#[derive(Clone, Debug)] -pub struct FetchOptionalProjectState { - project_key: ProjectKey, -} - -impl FetchOptionalProjectState { - pub fn project_key(&self) -> ProjectKey { - self.project_key - } -} - /// Response indicating whether a project state needs to be updated /// or the upstream does not have a newer version. #[derive(Debug, Clone)] From 83be3c45f0144dd8d9819437a3d3101e9d115cde Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Tue, 2 Sep 2025 13:44:03 +0200 Subject: [PATCH 4/9] Typo --- relay-config/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relay-config/src/config.rs b/relay-config/src/config.rs index 652e812c4bd..68004fa8f6b 100644 --- a/relay-config/src/config.rs +++ b/relay-config/src/config.rs @@ -352,7 +352,7 @@ impl<'de> Deserialize<'de> for RelayMode { "proxy" => Ok(RelayMode::Proxy), "managed" => Ok(RelayMode::Managed), "static" => Err(serde::de::Error::custom( - "Relay mode 'static' has been depreciated. Please use 'managed' or 'proxy' instead.", + "Relay mode 'static' has been deprecated. Please use 'managed' or 'proxy' instead.", )), other => Err(serde::de::Error::unknown_variant( other, From 059d47eac9365ccbe9347950261fb4825e59a8e7 Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Tue, 2 Sep 2025 13:58:50 +0200 Subject: [PATCH 5/9] Remove unused Python imports --- tests/integration/test_metrics.py | 1 - tests/integration/test_query.py | 1 - tests/integration/test_trusted_relay.py | 4 ---- 3 files changed, 6 deletions(-) diff --git a/tests/integration/test_metrics.py b/tests/integration/test_metrics.py index d20abb4a0eb..2c20b2b0664 100644 --- a/tests/integration/test_metrics.py +++ b/tests/integration/test_metrics.py @@ -10,7 +10,6 @@ TRANSACTION_EXTRACT_MAX_SUPPORTED_VERSION, ) -import os import pytest import requests from requests.exceptions import HTTPError diff --git a/tests/integration/test_query.py b/tests/integration/test_query.py index f75bad9e6dc..9f75f7ad3e4 100644 --- a/tests/integration/test_query.py +++ b/tests/integration/test_query.py @@ -1,6 +1,5 @@ import json import math -import queue import socket import time import threading diff --git a/tests/integration/test_trusted_relay.py b/tests/integration/test_trusted_relay.py index 26d3d41d130..14f877be6ec 100644 --- a/tests/integration/test_trusted_relay.py +++ b/tests/integration/test_trusted_relay.py @@ -1,7 +1,3 @@ -import json -import os -from copy import deepcopy - import pytest from requests import HTTPError From fd3a9d98f6d12f041f7ddf3eac28dc543e9a0ec8 Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Tue, 2 Sep 2025 14:49:59 +0200 Subject: [PATCH 6/9] Update docs --- relay-server/src/services/projects/project/mod.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/relay-server/src/services/projects/project/mod.rs b/relay-server/src/services/projects/project/mod.rs index 5e233273705..bc0c859e957 100644 --- a/relay-server/src/services/projects/project/mod.rs +++ b/relay-server/src/services/projects/project/mod.rs @@ -105,8 +105,7 @@ pub struct ParsedProjectState { pub disabled: bool, /// Project info. /// - /// This contains no information when `disabled` is `true`, except for - /// public keys in static project configs (see [`crate::services::projects::source::local`]). + /// This contains no information when `disabled` is `true`. #[serde(flatten)] pub info: ProjectInfo, } @@ -119,8 +118,7 @@ pub struct LimitedParsedProjectState { pub disabled: bool, /// Limited project info for external Relays. /// - /// This contains no information when `disabled` is `true`, except for - /// public keys in static project configs (see [`crate::services::projects::source::local`]). + /// This contains no information when `disabled` is `true`. #[serde(with = "LimitedProjectInfo")] #[serde(flatten)] pub info: ProjectInfo, From 1d6eb59540ddfa244cc5f0b0de7e52a12e92e73c Mon Sep 17 00:00:00 2001 From: Tobias Wilfert <36408720+tobias-wilfert@users.noreply.github.com> Date: Thu, 4 Sep 2025 15:13:22 +0200 Subject: [PATCH 7/9] Update relay-config/src/config.rs Co-authored-by: Joris Bayer --- relay-config/src/config.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/relay-config/src/config.rs b/relay-config/src/config.rs index 68004fa8f6b..c5e16743eee 100644 --- a/relay-config/src/config.rs +++ b/relay-config/src/config.rs @@ -352,7 +352,7 @@ impl<'de> Deserialize<'de> for RelayMode { "proxy" => Ok(RelayMode::Proxy), "managed" => Ok(RelayMode::Managed), "static" => Err(serde::de::Error::custom( - "Relay mode 'static' has been deprecated. Please use 'managed' or 'proxy' instead.", + "Relay mode 'static' has been removed. Please use 'managed' or 'proxy' instead.", )), other => Err(serde::de::Error::unknown_variant( other, From 5c3a37874868535b2a8aa12909ec15bbfe46efdf Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Thu, 4 Sep 2025 15:16:46 +0200 Subject: [PATCH 8/9] Add changelog entry --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d0206b004a..263392a2149 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ **Breaking Changes**: - Removes support for the deprecated and early alpha only otel log item type. ([#5082](https://github.com/getsentry/relay/pull/5082)) -- Only check for local project configs in static mode. ([#5057](https://github.com/getsentry/relay/pull/5057)) +- Remove static mode. ([#5108](https://github.com/getsentry/relay/pull/5108)) **Features**: From 1ac9417fb30a524da549218a1727ad7f0d8efce3 Mon Sep 17 00:00:00 2001 From: tobias-wilfert Date: Mon, 8 Sep 2025 15:37:35 +0200 Subject: [PATCH 9/9] Update display impl to not refer to removed modes. --- relay-config/src/config.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/relay-config/src/config.rs b/relay-config/src/config.rs index c5e16743eee..bb977314e5c 100644 --- a/relay-config/src/config.rs +++ b/relay-config/src/config.rs @@ -415,10 +415,7 @@ pub struct ParseRelayModeError; impl fmt::Display for ParseRelayModeError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!( - f, - "Relay mode must be one of: managed, static, proxy, capture" - ) + write!(f, "Relay mode must be one of: managed or proxy") } }