Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(server): Use global config from file if provided #2458

Merged
merged 41 commits into from
Sep 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
dbc71f3
wip
TBS1996 Sep 3, 2023
a320a33
Merge branch 'master' into tor/static_config
TBS1996 Sep 3, 2023
dc8fe7a
wip
TBS1996 Sep 3, 2023
d631912
wip
TBS1996 Sep 4, 2023
c125ec5
wip
TBS1996 Sep 4, 2023
aed5e6e
wip
TBS1996 Sep 4, 2023
153c9f1
wip
TBS1996 Sep 4, 2023
9367f89
take relay modes into account
TBS1996 Sep 5, 2023
9068be4
merge
TBS1996 Sep 5, 2023
1a43645
Merge branch 'master' into tor/static_config
TBS1996 Sep 5, 2023
3776ec6
wip
TBS1996 Sep 5, 2023
2d56213
Merge branch 'tor/static_config' of https://github.com/getsentry/rela…
TBS1996 Sep 5, 2023
761198e
simplify match arms
TBS1996 Sep 6, 2023
c3221b7
move match to spawnhandler
TBS1996 Sep 6, 2023
8fa4521
ref
TBS1996 Sep 6, 2023
387c246
nit
TBS1996 Sep 6, 2023
0fadcea
self request
TBS1996 Sep 6, 2023
749306a
Merge branch 'master' into tor/static_config
TBS1996 Sep 6, 2023
5281b67
ref match
TBS1996 Sep 6, 2023
68b0bbf
move static config load to service
TBS1996 Sep 6, 2023
383d850
Merge branch 'tor/static_config' of https://github.com/getsentry/rela…
TBS1996 Sep 6, 2023
67d3f73
revert sleephandle logic
TBS1996 Sep 6, 2023
43e4b4c
Merge branch 'master' into tor/static_config
TBS1996 Sep 6, 2023
e9b7272
Merge branch 'tor/static_config' of https://github.com/getsentry/rela…
TBS1996 Sep 6, 2023
5e94b76
move globalconfig load
TBS1996 Sep 7, 2023
9e0f3e3
Merge branch 'master' into tor/static_config
TBS1996 Sep 7, 2023
4f87968
add tests
iker-barriocanal Sep 7, 2023
da63e18
undo rename to prevent bad error grouping
iker-barriocanal Sep 7, 2023
a5e9c03
Merge branch 'master' into tor/static_config
iker-barriocanal Sep 7, 2023
a7030d4
update docstrings
iker-barriocanal Sep 7, 2023
a5c0224
update changelog
TBS1996 Sep 11, 2023
a6efd91
Merge branch 'master' into tor/static_config
TBS1996 Sep 11, 2023
99f2acd
address some feedback
iker-barriocanal Sep 11, 2023
20a593e
wip
TBS1996 Sep 12, 2023
17eba98
Merge branch 'tor/static_config' of https://github.com/getsentry/rela…
TBS1996 Sep 12, 2023
a6ada9f
wip
TBS1996 Sep 12, 2023
a69eb2f
wip
TBS1996 Sep 12, 2023
a4f5a14
t->T
TBS1996 Sep 12, 2023
c0e767d
remove test feat/dep
TBS1996 Sep 12, 2023
c9f0ef9
Merge branch 'master' into tor/static_config
TBS1996 Sep 12, 2023
c39f12c
Merge branch 'master' into tor/static_config
TBS1996 Sep 12, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

**Features**:

- Add `MeasurementsConfig` to `GlobalConfig` and implement merging logic with project config. ([#2415](https://github.com/getsentry/relay/pull/2415))
- Add `view_names` to `AppContext` ([#2344](https://github.com/getsentry/relay/pull/2344))
- Tag keys in error events and transaction events can now be up to `200` ASCII characters long. Before, tag keys were limited to 32 characters. ([#2453](https://github.com/getsentry/relay/pull/2453))
- The Crons monitor check-in APIs have learned to accept JSON via POST. This allows for monitor upserts by specifying the `monitor_config` in the JSON body. ([#2448](https://github.com/getsentry/relay/pull/2448))
Expand All @@ -20,6 +19,8 @@

**Internal**:

- Use static global configuration if file is provided and not in managed mode. ([#2458](https://github.com/getsentry/relay/pull/2458))
- Add `MeasurementsConfig` to `GlobalConfig` and implement merging logic with project config. ([#2415](https://github.com/getsentry/relay/pull/2415))
- Support ingestion of custom metrics when the `organizations:custom-metrics` feature flag is enabled. ([#2443](https://github.com/getsentry/relay/pull/2443))
- Merge span metrics and standalone spans extraction options. ([#2447](https://github.com/getsentry/relay/pull/2447))
- Support parsing aggregated metric buckets directly from statsd payloads. ([#2468](https://github.com/getsentry/relay/pull/2468), [#2472](https://github.com/getsentry/relay/pull/2472))
Expand Down
6 changes: 4 additions & 2 deletions relay-config/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1458,9 +1458,11 @@ impl Config {
/// Regenerates the relay credentials.
///
/// This also writes the credentials back to the file.
pub fn regenerate_credentials(&mut self) -> anyhow::Result<()> {
pub fn regenerate_credentials(&mut self, save: bool) -> anyhow::Result<()> {
let creds = Credentials::generate();
creds.save(&self.path)?;
if save {
creds.save(&self.path)?;
}
self.credentials = Some(creds);
Ok(())
}
Expand Down
21 changes: 21 additions & 0 deletions relay-dynamic-config/src/global.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use std::fs::File;
use std::io::BufReader;
use std::path::Path;

use relay_event_normalization::MeasurementsConfig;
use serde::{Deserialize, Serialize};

Expand All @@ -13,6 +17,23 @@ pub struct GlobalConfig {
pub measurements: Option<MeasurementsConfig>,
}

impl GlobalConfig {
/// Loads the [`GlobalConfig`] from a file if it's provided.
jan-auer marked this conversation as resolved.
Show resolved Hide resolved
///
/// The folder_path argument should be the path to the folder where the relay config and
/// credentials are stored.
pub fn load(folder_path: &Path) -> anyhow::Result<Option<Self>> {
let path = folder_path.join("global_config.json");

if path.exists() {
let file = BufReader::new(File::open(path)?);
Ok(Some(serde_json::from_reader(file)?))
} else {
Ok(None)
}
}
}

#[cfg(test)]
mod tests {

Expand Down
5 changes: 3 additions & 2 deletions relay-server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,13 @@ license-file = "../LICENSE"
publish = false

[features]
default = []
dashboard = [
"axum/ws",
"dep:rust-embed",
"dep:mime_guess"
"dep:mime_guess",
"relay-log/dashboard",
]
default = []
processing = [
"dep:minidump",
"dep:symbolic-common",
Expand Down
102 changes: 85 additions & 17 deletions relay-server/src/actors/global_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use std::borrow::Cow;
use std::sync::Arc;

use relay_config::Config;
use relay_config::RelayMode;
use relay_dynamic_config::GlobalConfig;
use relay_statsd::metric;
use relay_system::{Addr, AsyncResponse, Controller, FromMessage, Interface, Service};
Expand Down Expand Up @@ -151,8 +152,9 @@ pub struct GlobalConfigService {
impl GlobalConfigService {
/// Creates a new [`GlobalConfigService`].
pub fn new(config: Arc<Config>, upstream: Addr<UpstreamRelay>) -> Self {
let (global_config_watch, _) = watch::channel(Arc::default());
let (internal_tx, internal_rx) = mpsc::channel(1);
let (global_config_watch, _) = watch::channel(Arc::default());

Self {
config,
global_config_watch,
Expand Down Expand Up @@ -188,7 +190,8 @@ impl GlobalConfigService {
///
/// We check if we have credentials before sending,
/// otherwise we would log an [`UpstreamRequestError::NoCredentials`] error.
fn update_global_config(&mut self) {
fn request_global_config(&mut self) {
// Disable upstream requests timer until we receive result of query.
self.fetch_handle.reset();

let upstream_relay = self.upstream.clone();
Expand Down Expand Up @@ -240,6 +243,7 @@ impl GlobalConfigService {
),
}

// Enable upstream requests timer for global configs.
self.schedule_fetch();
}

Expand All @@ -257,23 +261,34 @@ impl Service for GlobalConfigService {
let mut shutdown_handle = Controller::shutdown_handle();

relay_log::info!("global config service starting");

if self.config.has_credentials() {
iker-barriocanal marked this conversation as resolved.
Show resolved Hide resolved
// NOTE(iker): if this first request fails it's possible the default
// global config is forwarded. This is not ideal, but we accept it
// for now.
self.update_global_config();
if self.config.relay_mode() == RelayMode::Managed {
relay_log::info!("serving global configs fetched from upstream");
self.request_global_config();
} else {
// NOTE(iker): not making a request results in the sleep handler
// not being reset, so no new requests are made.
relay_log::info!("fetching global configs disabled: no credentials configured");
}
match GlobalConfig::load(self.config.path()) {
Ok(Some(from_file)) => {
relay_log::info!("serving static global config loaded from file");
self.global_config_watch.send(Arc::new(from_file)).ok();
}
Ok(None) => {
relay_log::info!(
"serving default global configs due to lacking static global config file"
);
}
Err(e) => {
relay_log::error!("failed to load global config from file: {}", e);
relay_log::info!(
"serving default global configs due to failure to load global config from file"
);
}
}
};

loop {
tokio::select! {
biased;

() = &mut self.fetch_handle => self.update_global_config(),
() = &mut self.fetch_handle => self.request_global_config(),
Copy link
Contributor

Choose a reason for hiding this comment

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

In the static mode we actually never get this fetch_handle trigger? Is it right?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes, that's right. The fetch handle is only scheduled when handling a request, and a request is only made in managed modes; thus, in static mode, this fetch handle is never resolved.

Some(result) = self.internal_rx.recv() => self.handle_result(result),
Some(message) = rx.recv() => self.handle_message(message),
_ = shutdown_handle.notified() => self.handle_shutdown(),
Expand All @@ -291,7 +306,7 @@ mod tests {
use std::sync::Arc;
use std::time::Duration;

use relay_config::Config;
use relay_config::{Config, RelayMode};
use relay_system::{Controller, Service, ShutdownMode};
use relay_test::mock_service;

Expand All @@ -313,14 +328,67 @@ mod tests {
});

Controller::start(Duration::from_secs(1));
let config = Arc::<Config>::default();
let service = GlobalConfigService::new(config.clone(), upstream).start();
let mut config = Config::default();
config.regenerate_credentials(false).unwrap();
let fetch_interval = config.global_config_fetch_interval();

let service = GlobalConfigService::new(Arc::new(config), upstream).start();

assert!(service.send(Get).await.is_ok());

Controller::shutdown(ShutdownMode::Immediate);
tokio::time::sleep(config.global_config_fetch_interval() * 2).await;
tokio::time::sleep(fetch_interval * 2).await;

assert!(service.send(Get).await.is_ok());
}

#[tokio::test]
#[should_panic]
async fn managed_relay_makes_upstream_request() {
iker-barriocanal marked this conversation as resolved.
Show resolved Hide resolved
relay_test::setup();
tokio::time::pause();

let (upstream, handle) = mock_service("upstream", (), |(), _| {
panic!();
Copy link
Member

Choose a reason for hiding this comment

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

Instead of #[should_panic], which is intended to be used for expected panics, we should rather make sure the upstream service returns a default config.

Copy link
Contributor

Choose a reason for hiding this comment

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

IIUC, you suggest returning a response as if it were the actual upstream service. To actually return a global config we need to do an async operation in the closure, currently unstable in Rust. I think the panic makes the test easy to understand, and removes the complexity of dealing with mock_service. Maybe I'm missing something?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm here with @jan-auer . The thing is with unit tests, it can also panic for completely different reason when something changes in the code, and therefore the tests will be testing completely wrong thing.

It's better to make sure the behaviour is correct, but checking the status of some operation, like in this case that resulting config is default

Copy link
Member

Choose a reason for hiding this comment

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

@TBS1996 and I spent some time on this today. In order to move forward with this PR, let's keep the should_panic for now. He'll link a follow-up issue here to clean this up.

We'll implement some utilities to make mocking upstream requests easier. The boilerplate needed to achieve this now for a single test is too complex.

});

let mut config = Config::from_json_value(serde_json::json!({
"relay": {
"mode": RelayMode::Managed
}
}))
.unwrap();
config.regenerate_credentials(false).unwrap();

let fetch_interval = config.global_config_fetch_interval();
let service = GlobalConfigService::new(Arc::new(config), upstream).start();
service.send(Get).await.unwrap();

tokio::time::sleep(fetch_interval * 2).await;
handle.await.unwrap();
}

#[tokio::test]
async fn proxy_relay_does_not_make_upstream_request() {
relay_test::setup();
tokio::time::pause();

let (upstream, _) = mock_service("upstream", (), |(), _| {
panic!("upstream should not be called outside of managed mode");
});

let config = Config::from_json_value(serde_json::json!({
"relay": {
"mode": RelayMode::Proxy
}
}))
.unwrap();

let fetch_interval = config.global_config_fetch_interval();

let service = GlobalConfigService::new(Arc::new(config), upstream).start();
service.send(Get).await.unwrap();

tokio::time::sleep(fetch_interval * 2).await;
}
}
Loading