diff --git a/.changesets/fix_license_handling.md b/.changesets/fix_license_handling.md new file mode 100644 index 0000000000..86a2f25863 --- /dev/null +++ b/.changesets/fix_license_handling.md @@ -0,0 +1,7 @@ +### Fix: Proper handling of licenses in a warning state + +We allowed licenses in a warning state to bypass enforcement because we weren't returning an error, only the limits. This was happening, I think, because there's middleware handling expired licenses but not licenses in a warning state. So, we assumed that there'd be same kind of handling for licenses in a warning state. Alas, there's not. + +We now error out if there are restricted features in use. + +By [@aaronarinder](https://github.com/aaronarinder) in https://github.com/apollographql/router/pull/8768 diff --git a/apollo-router/src/state_machine.rs b/apollo-router/src/state_machine.rs index 882f55ca05..af5c1ce5e8 100644 --- a/apollo-router/src/state_machine.rs +++ b/apollo-router/src/state_machine.rs @@ -354,13 +354,15 @@ impl State { LicenseState::LicensedWarn { limits } => { if report.uses_restricted_features() { tracing::error!( - "License has expired. The Router will soon stop serving requests. In order to enable these features for a self-hosted instance of Apollo Router, the Router must be connected to a graph in GraphOS that provides an active license for the following features:\n\n{}\n\nSee {LICENSE_EXPIRED_URL} for more information.", + "License violation, the router is using features not available for your license:\n\n{}\n\nThe license warning period has started. The Router will stop serving requests after the license expires. See {LICENSE_EXPIRED_URL} for more information.", report ); - limits + return Err(ApolloRouterError::LicenseViolation( + report.restricted_features_in_use(), + )); } else { - tracing::error!( - "License has expired. The Router will soon stop serving requests. In order to enable these features for a self-hosted instance of Apollo Router, the Router must be connected to a graph in GraphOS that provides an active license for the following features:\n\n{:?}\n\nSee {LICENSE_EXPIRED_URL} for more information.", + tracing::warn!( + "License warning period has started. The Router will stop serving requests after the license expires. In order to continue using these features for a self-hosted instance of Apollo Router, the Router must be connected to a graph in GraphOS that provides an active license for the following features:\n\n{:?}\n\nSee {LICENSE_EXPIRED_URL} for more information.", // The report does not contain any features because they are contained within the allowedFeatures claim, // therefore we output all of the allowed features that the user's license enables them to use. license.get_allowed_features() @@ -1146,8 +1148,10 @@ mod tests { #[case] config: Arc, #[case] allowed_features: Vec, ) { - let router_factory = create_mock_router_configurator(1); - let (server_factory, shutdown_receivers) = create_mock_server_factory(1); + // errors happen before this would be hit; so, 0, but we still need to pass _something_ to + // execute() + let router_factory = create_mock_router_configurator(0); + let (server_factory, shutdown_receivers) = create_mock_server_factory(0); assert_matches!( execute( @@ -1166,9 +1170,11 @@ mod tests { ]) ) .await, - Ok(()) + // this is where the real test happens; we expect a license violation if we're using + // features that aren't being paid for + Err(ApolloRouterError::LicenseViolation(_)) ); - assert_eq!(shutdown_receivers.0.lock().len(), 1); + assert_eq!(shutdown_receivers.0.lock().len(), 0); } #[test(tokio::test)] diff --git a/apollo-router/tests/integration/allowed_features.rs b/apollo-router/tests/integration/allowed_features.rs index 4f82cda0b3..88c3f52e04 100644 --- a/apollo-router/tests/integration/allowed_features.rs +++ b/apollo-router/tests/integration/allowed_features.rs @@ -650,6 +650,9 @@ async fn feature_violation_when_license_past_warn_at_but_not_expired_allowed_fea .build() .await; + router.replace_config_string("http://localhost:{{PRODUCTS_PORT}}", "localhost:4001"); + router.replace_config_string("http://localhost:{{ACCOUNTS_PORT}}", "localhost:4002"); + router.start().await; router .assert_error_log_contained(LICENSE_ALLOWED_FEATURES_DOES_NOT_INCLUDE_FEATURE_MSG)