test(connlib): ensure portal init doesn't interrupt data plane (#5899)

The connection to the portal could be interrupted at any point, most
notably when it is being re-deployed. Doing so results in a new `init`
message being pushed to all clients and gateways. This must not
interrupt the data plane.

To ensure this, we add a new `ReconnectPortal` transition to
`tunnel_test` where we simulate receiving a new `init` message with the
same values as we already have locally, i.e. same set of relays and
resources.

This resolves an existing TODO where the logic of performing
non-destructive updates to resources in `set_resources` wasn't tested.
This commit is contained in:
Thomas Eizinger
2024-07-19 19:28:44 +10:00
committed by GitHub
parent bf693ad83f
commit 1bac0e0f0e
3 changed files with 27 additions and 0 deletions

View File

@@ -156,6 +156,7 @@ impl ReferenceStateMachine for ReferenceState {
question_mark_wildcard_dns_resource(sample::select(state.portal.all_sites())),
],
)
.with(1, Just(Transition::ReconnectPortal))
.with_if_not_empty(
10,
state.client.inner().ipv4_cidr_resource_dsts(),
@@ -372,6 +373,9 @@ impl ReferenceStateMachine for ReferenceState {
.client
.exec_mut(|client| client.connected_dns_resources.clear());
}
Transition::ReconnectPortal => {
// Reconnecting to the portal should have no noticeable impact on the data plane.
}
};
state
@@ -557,6 +561,7 @@ impl ReferenceStateMachine for ReferenceState {
!is_assigned_ip4 && !is_assigned_ip6 && !is_previous_port
}
Transition::ReconnectPortal => true,
}
}
}

View File

@@ -256,6 +256,25 @@ impl StateMachineTest for TunnelTest {
.set_resources(ref_state.client.inner().all_resources());
});
}
Transition::ReconnectPortal => {
let ipv4 = state.client.inner().sut.tunnel_ip4().unwrap();
let ipv6 = state.client.inner().sut.tunnel_ip6().unwrap();
let upstream_dns = ref_state.client.inner().upstream_dns_resolvers.clone();
let relays = HashSet::from_iter(map_explode(state.relays.iter(), "client"));
let all_resources = ref_state.client.inner().all_resources();
// Simulate receiving `init`.
state.client.exec_mut(|c| {
let _ = c.sut.update_interface_config(Interface {
ipv4,
ipv6,
upstream_dns,
});
c.sut
.update_relays(HashSet::default(), relays, ref_state.now);
c.sut.set_resources(all_resources);
});
}
};
state.advance(ref_state, &mut buffered_transmits);
assert!(buffered_transmits.is_empty()); // Sanity check to ensure we handled all packets.

View File

@@ -83,6 +83,9 @@ pub(crate) enum Transition {
ip6: Option<Ipv6Addr>,
port: u16,
},
/// Reconnect to the portal.
ReconnectPortal,
}
pub(crate) fn ping_random_ip<I>(