Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
5810658
refactor: actor-based dfx start
Sep 22, 2020
93270d1
remove dead code
Sep 25, 2020
1ee99b2
extract methods; remove dead code; reorder, pass &Path not PathBuf
Sep 25, 2020
b30f386
remove unused filed ProxyConfig.client_api_port
Sep 26, 2020
8c365f0
Move ProxyConfig fields into replica webserver coordinator config (do…
Sep 26, 2020
0ac8f04
pass PathBuf as &Path
Sep 26, 2020
35323ff
omg wip
Sep 29, 2020
9030384
Merge remote-tracking branch 'origin/master' into es-1020-merge-20201005
Oct 5, 2020
6650dfb
disable test in progress
Oct 5, 2020
115f6a3
format
Oct 5, 2020
c7b96d9
checkpoint - e2e replica restart tests passes
Oct 6, 2020
388d7bb
cleanup
Oct 6, 2020
c51c59d
Merge remote-tracking branch 'origin/master' into es-1020-dfx-start-a…
Oct 6, 2020
5ec77fd
cleanup
Oct 7, 2020
2b2ab33
oh boy. in a nix shell, ps has different output/options for darwin/l…
Oct 7, 2020
87230b1
try to see what blessings linux ps has to offer ffs
Oct 7, 2020
3c93d76
no man;
Oct 7, 2020
c09fa7c
error output so helpful
Oct 7, 2020
e075847
use pattern from assertion;
Oct 7, 2020
088248d
ps greppppppp
Oct 7, 2020
24aed0b
simpler
Oct 7, 2020
109453a
egrep with :space: ?
Oct 7, 2020
538ba6a
try grep with :space:
Oct 7, 2020
350c3d4
awk?
Oct 7, 2020
5ded166
i guess we do have awk
Oct 7, 2020
a297b35
jobs?
Oct 7, 2020
3f33009
ps aux, anything?
Oct 7, 2020
1950a38
force fail to see output
Oct 7, 2020
cf3e3d3
export
Oct 7, 2020
9bbc394
ps -T
Oct 7, 2020
384aae7
remove ps -T
Oct 7, 2020
d5072ff
checkpoint (no segv on exit)
Oct 7, 2020
5a01336
cleanup of webserver cleanup
Oct 7, 2020
0bd5aba
comment out automatic stop
Oct 7, 2020
3fcf0fe
wait for dfx ping, not process, and remove sleep
Oct 8, 2020
3927de4
Merge remote-tracking branch 'origin/master' into es-1020-dfx-start-a…
Oct 8, 2020
cc5f722
cleanup
Oct 8, 2020
5cd0dab
remove hotwatch dependency
Oct 8, 2020
1a094d1
reorder exec for clarity
Oct 8, 2020
dc074d5
write_pid fn
Oct 9, 2020
6f521e5
move start_shutdown_controller to its own function; other refinements
Oct 9, 2020
d67e138
Use [:space:] because I think \s is not working
Oct 9, 2020
37e2470
failing as expected
Oct 9, 2020
556bc57
clean up e2e test a bit
Oct 9, 2020
95cd392
explain why we shut down the web server
Oct 9, 2020
69a3b55
Merge remote-tracking branch 'origin/master' into es-1020-dfx-start-a…
Oct 13, 2020
4fe0660
Merge remote-tracking branch 'origin/master' into es-1020-dfx-start-a…
Oct 14, 2020
f80e64c
"Empty" commit to trigger Mergify check
Oct 14, 2020
f39033c
Undo extra line in e2e/bats/start.bash
Oct 14, 2020
043754b
Merge remote-tracking branch 'origin/master' into es-1020-dfx-start-a…
Oct 19, 2020
49844a3
ps issue fixed?
Oct 19, 2020
63a372d
Merge branch 'master' into es-1020-dfx-start-actors
Oct 20, 2020
f2cce31
use ps -x flag to match processes with no terminal
pikajude Oct 21, 2020
0f44679
see what ps x outputs
Oct 21, 2020
030db00
clean up after ps x fix
Oct 21, 2020
989887e
Merge remote-tracking branch 'origin/master' into es-1020-dfx-start-a…
Oct 21, 2020
a207af6
Merge branch 'master' into es-1020-dfx-start-actors
Oct 26, 2020
0e6a050
note about non-robustness of using ps.
Oct 26, 2020
a680e7e
client -> replica
Oct 26, 2020
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
86 changes: 0 additions & 86 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 48 additions & 0 deletions e2e/bats/start.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#!/usr/bin/env bats

load utils/_

setup() {
# We want to work from a temporary directory, different for every test.
cd $(mktemp -d -t dfx-e2e-XXXXXXXX)

dfx_new hello
}

teardown() {
dfx_stop
}

@test "dfx restarts the replica" {
[ "$USE_IC_REF" ] && skip "skip for ic-ref"

dfx_start

install_asset greet
assert_command dfx deploy
assert_command dfx canister call hello greet '("Alpha")'
assert_eq '("Hello, Alpha!")'

# ps "isn't a robust solution": https://dfinity.atlassian.net/browse/OPS-166
#
# I guess we could make dfx write the replica pid somewhere.
#
# Anyway, if this test ends up sometimes killing a replica other than
# the one created by dfx for this test, then some other test might fail.
#
# Also, this does not work on linux:
# ps -o "ppid, pid, comm"
REPLICA_PID=$(ps x | grep [/[:space:]]replica | awk '{print $1}')

echo "replica pid is $REPLICA_PID"

kill -KILL $REPLICA_PID
assert_process_exits $REPLICA_PID 15s

timeout 15s sh -c \
'until dfx ping; do echo waiting for replica to restart; sleep 1; done' \
|| (echo "replica did not restart" && ps aux && exit 1)

assert_command dfx canister call hello greet '("Omega")'
assert_eq '("Hello, Omega!")'
}
4 changes: 2 additions & 2 deletions e2e/bats/utils/assertions.bash
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ assert_process_exits() {

# Asserts that `dfx start` and `replica` are no longer running
assert_no_dfx_start_or_replica_processes() {
! ( ps | grep "[/\s]dfx start" )
! ( ps | grep "[/\s]replica" )
! ( ps | grep "[/[:space:]]dfx start" )
! ( ps | grep "[/[:space:]]replica" )
}

assert_file_eventually_exists() {
Expand Down
3 changes: 1 addition & 2 deletions src/dfx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ base64 = "0.11.0"
candid = "0.6.7"
chrono = "0.4.9"
clap = "2.33.0"
ctrlc = { version = "3.1.6", features = [ "termination" ] }
console = "0.7.7"
crossbeam = "0.7.3"
ctrlc = { version = "3.1.6", features = [ "termination" ] }
delay = "0.3.0"
dialoguer = "0.6.2"
erased-serde = "0.3.10"
Expand All @@ -37,7 +37,6 @@ indicatif = "0.13.0"
lazy-init = "0.3.0"
lazy_static = "1.4.0"
libflate = "0.1.27"
hotwatch = "0.4.3"
humanize-rs = "0.1.5"
mime = "0.3.16"
mockall = "0.6.0"
Expand Down
1 change: 1 addition & 0 deletions src/dfx/src/actors.rs
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
pub mod replica;
pub mod replica_webserver_coordinator;
pub mod shutdown_controller;
25 changes: 16 additions & 9 deletions src/dfx/src/actors/replica.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,15 +226,22 @@ fn wait_for_child_or_receiver(
receiver: &Receiver<()>,
) -> ChildOrReceiver {
loop {
// Ping-pong between waiting 100 msec for a receiver signal, and check if
// the child quit.
if let Ok(()) = receiver.recv_timeout(std::time::Duration::from_millis(100)) {
return ChildOrReceiver::Receiver;
}

if let Ok(Some(_)) = child.try_wait() {
return ChildOrReceiver::Child;
}
// Check if either the child exited or a shutdown has been requested.
// These can happen in either order in response to Ctrl-C, so increase the chance
// to notice a shutdown request even if the replica exited quickly.
let child_try_wait = child.try_wait();
let receiver_signalled = receiver.recv_timeout(std::time::Duration::from_millis(100));

match (receiver_signalled, child_try_wait) {
(Ok(()), _) => {
// Prefer to indicate the shutdown request
return ChildOrReceiver::Receiver;
}
(Err(_), Ok(Some(_))) => {
return ChildOrReceiver::Child;
}
_ => {}
};
}
}

Expand Down
130 changes: 130 additions & 0 deletions src/dfx/src/actors/replica_webserver_coordinator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use crate::actors::replica::signals::outbound::ReplicaReadySignal;
use crate::actors::replica::signals::PortReadySubscribe;
use crate::actors::replica::Replica;
use crate::actors::shutdown_controller::signals::outbound::Shutdown;
use crate::actors::shutdown_controller::signals::ShutdownSubscribe;
use crate::actors::shutdown_controller::ShutdownController;
use crate::lib::error::DfxResult;
use crate::lib::network::network_descriptor::NetworkDescriptor;
use crate::lib::webserver::run_webserver;
use actix::clock::{delay_for, Duration};
use actix::fut::wrap_future;
use actix::{Actor, Addr, AsyncContext, Context, Handler, ResponseFuture};
use actix_server::Server;
use futures::future;
use futures::future::FutureExt;
use slog::{debug, error, info, Logger};
use std::net::SocketAddr;
use std::path::PathBuf;

pub struct Config {
pub logger: Option<Logger>,
pub replica_addr: Addr<Replica>,
pub shutdown_controller: Addr<ShutdownController>,
pub bind: SocketAddr,
pub serve_dir: PathBuf,
pub providers: Vec<url::Url>,
pub build_output_root: PathBuf,
pub network_descriptor: NetworkDescriptor,
}

///
/// The ReplicaWebserverCoordinator runs a webserver for the replica.
///
/// If the replica restarts, it will start a new webserver for the new replica.
pub struct ReplicaWebserverCoordinator {
logger: Logger,
config: Config,
server: Option<Server>,
}

impl ReplicaWebserverCoordinator {
pub fn new(config: Config) -> Self {
let logger =
(config.logger.clone()).unwrap_or_else(|| Logger::root(slog::Discard, slog::o!()));
ReplicaWebserverCoordinator {
config,
logger,
server: None,
}
}

fn start_server(&self, port: u16) -> DfxResult<Server> {
let mut providers = self.config.providers.clone();

let ic_replica_bind_addr = "http://localhost:".to_owned() + port.to_string().as_str();
let ic_replica_bind_addr = ic_replica_bind_addr.as_str();
let replica_api_uri =
url::Url::parse(ic_replica_bind_addr).expect("Failed to parse replica ingress url.");
providers.push(replica_api_uri);
info!(
self.logger,
"Starting webserver on port {} for replica at {:?}", port, ic_replica_bind_addr
);

run_webserver(
self.logger.clone(),
self.config.build_output_root.clone(),
self.config.network_descriptor.clone(),
self.config.bind,
providers,
self.config.serve_dir.clone(),
)
}
}

impl Actor for ReplicaWebserverCoordinator {
type Context = Context<Self>;

fn started(&mut self, ctx: &mut Self::Context) {
self.config
.replica_addr
.do_send(PortReadySubscribe(ctx.address().recipient()));
self.config
.shutdown_controller
.do_send(ShutdownSubscribe(ctx.address().recipient::<Shutdown>()));
}
}

impl Handler<ReplicaReadySignal> for ReplicaWebserverCoordinator {
type Result = ();

fn handle(&mut self, msg: ReplicaReadySignal, ctx: &mut Self::Context) {
debug!(self.logger, "replica ready on {}", msg.port);

if let Some(server) = &self.server {
ctx.wait(wrap_future(server.stop(true)));
self.server = None;
ctx.address().do_send(msg);
} else {
match self.start_server(msg.port) {
Ok(server) => {
self.server = Some(server);
}
Err(e) => {
error!(
self.logger,
"Unable to start webserver on port {}: {}", msg.port, e
);
ctx.wait(wrap_future(delay_for(Duration::from_secs(2))));
ctx.address().do_send(msg);
}
}
}
}
}

impl Handler<Shutdown> for ReplicaWebserverCoordinator {
type Result = ResponseFuture<Result<(), ()>>;

fn handle(&mut self, _msg: Shutdown, _ctx: &mut Self::Context) -> Self::Result {
if let Some(server) = self.server.take() {
// We stop the webserver before shutting down because
// if we don't, the process will segfault
// while dropping actix stuff after main() returns.
Box::pin(server.stop(true).map(Ok))
} else {
Box::pin(future::ok(()))
}
}
}
2 changes: 1 addition & 1 deletion src/dfx/src/commands/replica.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ fn get_config(env: &dyn Environment, args: &ArgMatches<'_>) -> DfxResult<Replica
let state_dir = temp_dir.join("state/replicated_state");
std::fs::create_dir_all(&state_dir)?;

let mut replica_config = ReplicaConfig::new(env.get_state_dir());
let mut replica_config = ReplicaConfig::new(&env.get_state_dir());
replica_config.http_handler = http_handler;
replica_config.scheduler = scheduler;
Ok(replica_config)
Expand Down
Loading