Skip to content

Commit 136f2a8

Browse files
Bring dev launch to parity with legacy (#2319)
This PR implements the following requirements specified in the test plan > **When I start rover dev with a supergraph config and --graph-ref** > > If the subgraphs compose > * I should see “starting a session with the ‘<subgraph name>’' subgraph” for each subgraph in the config > * I should see “composing supergraph with Federation < federation version >” with the version of federation in the supergraph config > * If `--federation-version` is specified, this is the value that appears > * If no federation version is specified, the latest Fed2 version should be used > * If a Fed1 version is specified with Fed2 subgraphs, composition should fail specifying that this was the reason >* If the supergraph does not compose > * I should see an error explaining why the supergraph composition failed Have included screenshots below for comparison purposes **Old dev launch** ![Screenshot 2024-12-20 at 11 13 43](https://github.com/user-attachments/assets/1c2e46e0-6d73-4760-80d6-af1d960c66da) **New dev launch** ![Screenshot 2024-12-20 at 11 14 41](https://github.com/user-attachments/assets/c13eb8fc-569a-41af-ba1b-5f087b969715) **Specifying a Federation Version** ![Screenshot 2024-12-20 at 11 15 49](https://github.com/user-attachments/assets/539e01c7-324a-431d-a1c5-86e7498a5a1c) **Specifying a Fed 1 Version with Fed 2 Subgraphs** ![Screenshot 2024-12-20 at 11 17 08](https://github.com/user-attachments/assets/40a37813-029d-4195-a5ea-c0e6ab45431f) **Supergraph doesn't compose** ![Screenshot 2024-12-20 at 11 17 51](https://github.com/user-attachments/assets/29836f06-2bea-4004-aa29-60029f7f9768)
1 parent 5e97e38 commit 136f2a8

File tree

10 files changed

+152
-81
lines changed

10 files changed

+152
-81
lines changed

src/command/dev/next/mod.rs

+30-7
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
use std::io::stdin;
44

55
use anyhow::anyhow;
6+
use apollo_federation_types::config::FederationVersion::LatestFedTwo;
67
use apollo_federation_types::config::RouterVersion;
78
use camino::Utf8PathBuf;
89
use futures::StreamExt;
910
use houston::{Config, Profile};
1011
use router::{install::InstallRouter, run::RunRouter, watchers::file::FileWatcher};
1112
use rover_client::operations::config::who_am_i::WhoAmI;
13+
use rover_std::{infoln, warnln};
1214

15+
use self::router::config::{RouterAddress, RunRouterConfig};
1316
use crate::{
1417
command::Dev,
1518
composition::{
@@ -30,8 +33,6 @@ use crate::{
3033
RoverError, RoverOutput, RoverResult,
3134
};
3235

33-
use self::router::config::{RouterAddress, RunRouterConfig};
34-
3536
mod router;
3637

3738
impl Dev {
@@ -65,6 +66,9 @@ impl Dev {
6566

6667
let profile = &self.opts.plugin_opts.profile;
6768
let graph_ref = &self.opts.supergraph_opts.graph_ref;
69+
if let Some(graph_ref) = graph_ref {
70+
eprintln!("retrieving subgraphs remotely from {graph_ref}")
71+
}
6872
let supergraph_config_path = &self.opts.supergraph_opts.clone().supergraph_config_path;
6973

7074
let service = client_config.get_authenticated_client(profile)?.service()?;
@@ -90,7 +94,11 @@ impl Dev {
9094
.resolve_federation_version(
9195
&client_config,
9296
make_fetch_remote_subgraph,
93-
self.opts.supergraph_opts.federation_version.clone(),
97+
self.opts
98+
.supergraph_opts
99+
.federation_version
100+
.clone()
101+
.or(Some(LatestFedTwo)),
94102
)
95103
.await?
96104
.install_supergraph_binary(
@@ -100,6 +108,7 @@ impl Dev {
100108
skip_update,
101109
)
102110
.await?;
111+
103112
let composition_success = composition_pipeline
104113
.compose(&exec_command_impl, &read_file_impl, &write_file_impl, None)
105114
.await?;
@@ -126,7 +135,12 @@ impl Dev {
126135

127136
let composition_messages = composition_runner.run();
128137

129-
let mut run_router = RunRouter::default()
138+
eprintln!(
139+
"composing supergraph with Federation {}",
140+
composition_pipeline.state.supergraph_binary.version()
141+
);
142+
143+
let run_router = RunRouter::default()
130144
.install::<InstallRouter>(
131145
router_version,
132146
client_config.clone(),
@@ -138,7 +152,9 @@ impl Dev {
138152
.load_config(&read_file_impl, router_address, router_config_path)
139153
.await?
140154
.load_remote_config(service, graph_ref.clone(), Some(credential))
141-
.await
155+
.await;
156+
let router_address = run_router.state.config.address().clone();
157+
let mut run_router = run_router
142158
.run(
143159
FsWriteFile::default(),
144160
TokioSpawn::default(),
@@ -150,6 +166,12 @@ impl Dev {
150166
.watch_for_changes(write_file_impl, composition_messages)
151167
.await;
152168

169+
warnln!(
170+
"Do not run this command in production! It is intended for local development only."
171+
);
172+
173+
infoln!("your supergraph is running! head to {router_address} to query your supergraph");
174+
153175
loop {
154176
tokio::select! {
155177
_ = tokio::signal::ctrl_c() => {
@@ -160,7 +182,9 @@ impl Dev {
160182
Some(router_log) = run_router.router_logs().next() => {
161183
match router_log {
162184
Ok(router_log) => {
163-
eprintln!("{}", router_log);
185+
if !router_log.to_string().is_empty() {
186+
eprintln!("{}", router_log);
187+
}
164188
}
165189
Err(err) => {
166190
tracing::error!("{:?}", err);
@@ -170,7 +194,6 @@ impl Dev {
170194
else => break,
171195
}
172196
}
173-
174197
Ok(RoverOutput::EmptySuccess)
175198
}
176199
}

src/command/dev/next/router/config/mod.rs

+19-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::fmt::{Display, Formatter};
12
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
23

34
use buildstructor::buildstructor;
@@ -6,12 +7,11 @@ use http::Uri;
67
use rover_std::{Fs, RoverStdError};
78
use thiserror::Error;
89

9-
use crate::utils::effect::read_file::ReadFile;
10-
1110
use self::{
1211
parser::{ParseRouterConfigError, RouterConfigParser},
1312
state::{RunRouterConfigDefault, RunRouterConfigFinal, RunRouterConfigReadConfig},
1413
};
14+
use crate::utils::effect::read_file::ReadFile;
1515

1616
mod parser;
1717
pub mod remote;
@@ -54,6 +54,19 @@ impl RouterAddress {
5454
}
5555
}
5656

57+
impl Display for RouterAddress {
58+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
59+
let host = self
60+
.host
61+
.to_string()
62+
.replace("127.0.0.1", "localhost")
63+
.replace("0.0.0.0", "localhost")
64+
.replace("[::]", "localhost")
65+
.replace("[::1]", "localhost");
66+
write!(f, "http://{}:{}", host, self.port)
67+
}
68+
}
69+
5770
impl Default for RouterAddress {
5871
fn default() -> Self {
5972
RouterAddress {
@@ -178,6 +191,10 @@ impl RunRouterConfig<RunRouterConfigFinal> {
178191
&self.state.health_check_endpoint
179192
}
180193

194+
pub fn raw_config(&self) -> String {
195+
self.state.raw_config.clone()
196+
}
197+
181198
#[allow(unused)]
182199
pub fn router_config(&self) -> RouterConfig {
183200
RouterConfig(self.state.raw_config.to_string())

src/command/dev/next/router/config/parser.rs

+5-6
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ impl<'a> RouterConfigParser<'a> {
8484
.and_then(|addr_and_port| Some(Uri::from_str(addr_and_port)))
8585
// See https://www.apollographql.com/docs/graphos/routing/self-hosted/health-checks for
8686
// defaults
87-
.unwrap_or(Uri::from_str("127.0.0.1:8088"))
87+
.unwrap_or(Uri::from_str("http://127.0.0.1:8088"))
8888
.map_err(|err| ParseRouterConfigError::ListenPath {
8989
path: "health_check.listen",
9090
source: err,
@@ -161,8 +161,8 @@ health_check:
161161
);
162162
let config_yaml = serde_yaml::from_str(&config_yaml_str)?;
163163
let router_config = RouterConfigParser { yaml: &config_yaml };
164-
let health_check = router_config.health_check();
165-
assert_that!(health_check).is_equal_to(is_health_check_enabled);
164+
let health_check = router_config.health_check_endpoint();
165+
assert_that!(health_check.is_ok()).is_equal_to(is_health_check_enabled);
166166
Ok(())
167167
}
168168

@@ -174,9 +174,8 @@ health_check:
174174
};
175175
let config_yaml = serde_yaml::from_str(&config_yaml_str)?;
176176
let router_config = RouterConfigParser { yaml: &config_yaml };
177-
let health_check = router_config.health_check();
178-
assert_that!(health_check).is_false();
179-
Ok(())
177+
let health_check = router_config.health_check_endpoint();
178+
assert_that!(health_check).is_equal_to(Ok(Uri::from_str("http://127.0.0.1:8088/health")?))
180179
}
181180

182181
#[rstest]

src/command/dev/next/router/run.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,19 @@ use rover_client::{
1111
operations::config::who_am_i::{RegistryIdentity, WhoAmIError, WhoAmIRequest},
1212
shared::GraphRef,
1313
};
14-
use rover_std::RoverStdError;
14+
use rover_std::{infoln, RoverStdError};
1515
use tokio::{process::Child, time::sleep};
1616
use tokio_stream::wrappers::UnboundedReceiverStream;
1717
use tower::{Service, ServiceExt};
1818
use tracing::info;
1919

20+
use super::{
21+
binary::{RouterLog, RunRouterBinary, RunRouterBinaryError},
22+
config::{remote::RemoteRouterConfig, ReadRouterConfigError, RouterAddress, RunRouterConfig},
23+
hot_reload::{HotReloadEvent, HotReloadWatcher, RouterUpdateEvent},
24+
install::{InstallRouter, InstallRouterError},
25+
watchers::router_config::RouterConfigWatcher,
26+
};
2027
use crate::{
2128
command::dev::next::FileWatcher,
2229
composition::events::CompositionEvent,
@@ -33,16 +40,8 @@ use crate::{
3340
},
3441
};
3542

36-
use super::{
37-
binary::{RouterLog, RunRouterBinary, RunRouterBinaryError},
38-
config::{remote::RemoteRouterConfig, ReadRouterConfigError, RouterAddress, RunRouterConfig},
39-
hot_reload::{HotReloadEvent, HotReloadWatcher, RouterUpdateEvent},
40-
install::{InstallRouter, InstallRouterError},
41-
watchers::router_config::RouterConfigWatcher,
42-
};
43-
4443
pub struct RunRouter<S> {
45-
state: S,
44+
pub(crate) state: S,
4645
}
4746

4847
impl Default for RunRouter<state::Install> {
@@ -86,6 +85,12 @@ impl RunRouter<state::LoadLocalConfig> {
8685
.with_address(router_address)
8786
.with_config(read_file_impl, config_path.as_ref())
8887
.await?;
88+
if let Some(config_path) = config_path.clone() {
89+
infoln!(
90+
"Watching {} for changes",
91+
config_path.as_std_path().display()
92+
);
93+
}
8994
Ok(RunRouter {
9095
state: state::LoadRemoteConfig {
9196
binary: self.state.binary,
@@ -158,6 +163,7 @@ impl RunRouter<state::Run> {
158163
.call(
159164
WriteFileRequest::builder()
160165
.path(hot_reload_config_path.clone())
166+
.contents(Vec::from(self.state.config.raw_config()))
161167
.build(),
162168
)
163169
.await

src/composition/pipeline.rs

+35-15
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,6 @@ use rover_client::shared::GraphRef;
66
use tempfile::tempdir;
77
use tower::MakeService;
88

9-
use crate::{
10-
options::{LicenseAccepter, ProfileOpt},
11-
utils::{
12-
client::StudioClientConfig,
13-
effect::{
14-
exec::ExecCommand, install::InstallBinary, introspect::IntrospectSubgraph,
15-
read_file::ReadFile, read_stdin::ReadStdin, write_file::WriteFile,
16-
},
17-
parsers::FileDescriptorType,
18-
},
19-
};
20-
219
use super::{
2210
runner::{CompositionRunner, Runner},
2311
supergraph::{
@@ -32,6 +20,18 @@ use super::{
3220
},
3321
CompositionError, CompositionSuccess,
3422
};
23+
use crate::composition::pipeline::CompositionPipelineError::FederationOneWithFederationTwoSubgraphs;
24+
use crate::{
25+
options::{LicenseAccepter, ProfileOpt},
26+
utils::{
27+
client::StudioClientConfig,
28+
effect::{
29+
exec::ExecCommand, install::InstallBinary, introspect::IntrospectSubgraph,
30+
read_file::ReadFile, read_stdin::ReadStdin, write_file::WriteFile,
31+
},
32+
parsers::FileDescriptorType,
33+
},
34+
};
3535

3636
#[derive(thiserror::Error, Debug)]
3737
pub enum CompositionPipelineError {
@@ -52,10 +52,12 @@ pub enum CompositionPipelineError {
5252
},
5353
#[error("Failed to install the supergraph binary.\n{}", .0)]
5454
InstallSupergraph(#[from] InstallSupergraphError),
55+
#[error("Federation 1 version specified, but supergraph schema includes Federation 2 subgraphs: {0:?}")]
56+
FederationOneWithFederationTwoSubgraphs(Vec<String>),
5557
}
5658

5759
pub struct CompositionPipeline<State> {
58-
state: State,
60+
pub(crate) state: State,
5961
}
6062

6163
impl Default for CompositionPipeline<state::Init> {
@@ -98,10 +100,12 @@ impl CompositionPipeline<state::Init> {
98100
}
99101
FileDescriptorType::Stdin => None,
100102
});
103+
eprintln!("merging supergraph schema files");
101104
let resolver = SupergraphConfigResolver::default()
102105
.load_remote_subgraphs(fetch_remote_subgraphs_factory, graph_ref.as_ref())
103106
.await?
104107
.load_from_file_descriptor(read_stdin_impl, supergraph_yaml.as_ref())?;
108+
eprintln!("supergraph config loaded successfully");
105109
Ok(CompositionPipeline {
106110
state: state::ResolveFederationVersion {
107111
resolver,
@@ -134,11 +138,27 @@ impl CompositionPipeline<state::ResolveFederationVersion> {
134138
&SubgraphPrompt::default(),
135139
)
136140
.await?;
137-
let federation_version = federation_version.unwrap_or_else(|| {
141+
let fed_two_subgraphs = fully_resolved_supergraph_config
142+
.subgraphs()
143+
.iter()
144+
.filter_map(|(name, subgraph)| {
145+
if subgraph.is_fed_two {
146+
Some(name.clone())
147+
} else {
148+
None
149+
}
150+
})
151+
.collect::<Vec<String>>();
152+
let federation_version = if let Some(fed_version) = federation_version {
153+
if !fed_two_subgraphs.is_empty() && fed_version.is_fed_one() {
154+
return Err(FederationOneWithFederationTwoSubgraphs(fed_two_subgraphs));
155+
}
156+
fed_version
157+
} else {
138158
fully_resolved_supergraph_config
139159
.federation_version()
140160
.clone()
141-
});
161+
};
142162
Ok(CompositionPipeline {
143163
state: state::InstallSupergraph {
144164
resolver: self.state.resolver,

src/composition/supergraph/config/federation.rs

+5-7
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,8 @@ use std::marker::PhantomData;
66
use apollo_federation_types::config::{FederationVersion, SupergraphConfig};
77
use derive_getters::Getters;
88

9-
use crate::command::supergraph::compose::do_compose::SupergraphComposeOpts;
10-
119
use super::full::FullyResolvedSubgraph;
10+
use crate::command::supergraph::compose::do_compose::SupergraphComposeOpts;
1211

1312
mod state {
1413
#[derive(Clone, Debug)]
@@ -166,9 +165,8 @@ mod tests {
166165
use apollo_federation_types::config::{FederationVersion, SubgraphConfig, SupergraphConfig};
167166
use speculoos::prelude::*;
168167

169-
use crate::composition::supergraph::config::{full::FullyResolvedSubgraph, scenario::*};
170-
171168
use super::FederationVersionResolverFromSupergraphConfig;
169+
use crate::composition::supergraph::config::{full::FullyResolvedSubgraph, scenario::*};
172170

173171
/// Test showing that federation version is selected from the user-specified fed version
174172
/// over local supergraph config or resolved subgraphs
@@ -189,7 +187,7 @@ mod tests {
189187
let supergraph_config =
190188
SupergraphConfig::new(unresolved_subgraphs, Some(FederationVersion::LatestFedOne));
191189

192-
let resolved_subgraphs = vec![(
190+
let resolved_subgraphs = [(
193191
subgraph_name.to_string(),
194192
FullyResolvedSubgraph::builder()
195193
.schema(subgraph_scenario.sdl)
@@ -222,7 +220,7 @@ mod tests {
222220
let supergraph_config =
223221
SupergraphConfig::new(unresolved_subgraphs, Some(FederationVersion::LatestFedTwo));
224222

225-
let resolved_subgraphs = vec![(
223+
let resolved_subgraphs = [(
226224
subgraph_name.to_string(),
227225
FullyResolvedSubgraph::builder()
228226
.schema(subgraph_scenario.sdl)
@@ -254,7 +252,7 @@ mod tests {
254252
let federation_version_resolver = FederationVersionResolverFromSupergraphConfig::default();
255253
let supergraph_config = SupergraphConfig::new(unresolved_subgraphs, None);
256254

257-
let resolved_subgraphs = vec![(
255+
let resolved_subgraphs = [(
258256
subgraph_name.to_string(),
259257
FullyResolvedSubgraph::builder()
260258
.schema(subgraph_scenario.sdl)

0 commit comments

Comments
 (0)