Skip to content

Commit

Permalink
Core: add Offline flag to virtual hosts
Browse files Browse the repository at this point in the history
- when offline, simulate failures for incoming requests
  • Loading branch information
KrzysFR committed Feb 2, 2024
1 parent cb09701 commit 4098d88
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 3 deletions.
24 changes: 24 additions & 0 deletions Doxense.Core.Testing.Common/Network/VirtualNetworkMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,30 @@ public VirtualNetworkMap(VirtualNetworkTopology topology, VirtualNetworkTopology
var (local, remote) = FindNetworkPath(host, hostOrAddress);
if (local != null && remote != null)
{
// either we are offline, or the remote host is offline (or both)
if (local.Type != VirtualNetworkType.Loopback)
{
if (this.Host.Offline)
{ // We are offline!

//TODO: maybe this would be a different error? if the local host has no online network adapter, the error may be different

// => for now, simply simulate a generic connection timeout
return VirtualDeadHttpClientHandler.SimulateConnectFailure($"Local virtual host '{this.Host.Id}' is currently marked as offline and cannot send any request to remote host {host.Id}.");
}

if (host.Offline)
{ // the host is offline (rebooting? disconnected from ethernet/wifi?)

//TODO: depending on the situation, we should either simulate a name resolution failure, OR a tcp connect timeout:
// - if the DNS entry for the host is statically assigned, OR the caller still as the IP in cache from an earlier query, it would attempt to connect with the remote host, and fail with a timeout.
// - if the host use DHCP, and/or has a very short TTL, and/or use WINS, then the caller would fail with a name resolution error.

// => for now, simply assume that the DNS is static and/or already cached, and simulate a socket connection timeout (ie: the host was alive at some point and now suddenly became offline)
return VirtualDeadHttpClientHandler.SimulateConnectFailure($"Remote virtual host '{host.Id}' is currently marked as offline and will not respond to any request.");
}
}

//TODO: check si les deux hosts veulent se parler quand meme!
var handler = host.FindHandler(remote, port);
if (handler != null)
Expand Down
30 changes: 27 additions & 3 deletions Doxense.Core.Testing.Common/Network/VirtualNetworkTopology.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ namespace Doxense.Networking
using System.Threading.Tasks;
using Doxense.Diagnostics.Contracts;
using Doxense.IO.Hashing;
using Doxense.Text;
using Doxense.Threading;

public class VirtualNetworkTopology : IVirtualNetworkTopology
Expand Down Expand Up @@ -101,11 +100,23 @@ public sealed class SimulatedHost : IVirtualNetworkHost

public bool Passthrough { get; }

/// <summary>Flag that is <see langword="true"/> when this host is 'offline' and should not respond to any external request.</summary>
public bool Offline { get; private set; }

/// <summary>Map of all handlers attached to each network location</summary>
/// <remarks>The key is the network location id, and the value is the map of the ports that are bound: <c>Location => (Port => Handler)</c></remarks>
public Dictionary<string, Dictionary<int, Func<HttpMessageHandler>>> Handlers { get; } = new(StringComparer.Ordinal);

public SimulatedHost(SimulatedNetworkAdapter[] adapters, string id, string hostName, string fqdn, string[] aliases, IPAddress[] addresses, bool passthrough)
/// <summary>Create a new simulated host</summary>
/// <param name="adapters">List of the network adapters that are available for this host (note: must include at least one adapter for 'localhost')</param>
/// <param name="id">Unique id of this host</param>
/// <param name="hostName">Primary host name (ex: "pc042")</param>
/// <param name="fqdn">Primary fully qualified domain name (ex: "pc042.acme.local")</param>
/// <param name="aliases">Optional list of aliases (including short names or other fqdn)</param>
/// <param name="addresses">List of IP addresses owned by this host</param>
/// <param name="passthrough">If true, this host represents an actual physical host, accessible on the network by the testing framework, and all requests will be forwarded to this physical host (instead of being simulated).</param>
/// <param name="offline">If true, this host starts in "offline" mode, and will not respond to requests.</param>
public SimulatedHost(SimulatedNetworkAdapter[] adapters, string id, string hostName, string fqdn, string[] aliases, IPAddress[] addresses, bool passthrough, bool offline)
{
Contract.Debug.Requires(adapters != null && adapters.Length != 0 && id != null && hostName != null && fqdn != null && aliases != null && addresses != null);
this.Adapters = adapters;
Expand All @@ -117,6 +128,17 @@ public SimulatedHost(SimulatedNetworkAdapter[] adapters, string id, string hostN
this.Aliases = aliases;
this.Addresses = addresses;
this.Passthrough = passthrough;
this.Offline = offline;
}

/// <summary>Change the <see cref="Offline">offline state</see> of this host</summary>
/// <param name="offline">Mark the host as offline if <see langword="true"/>, or back online if <see langword="false"/>.</param>
/// <remarks>If the host is offline, all simulated requests will start to fail</remarks>
public void SetOffline(bool offline)
{
//TODO: maybe add a parameter to specify which kind of "offline" fault the host should simulate: powered-down? ethernet is off? currently rebooting but not yet ready? some big crash?
this.Offline = offline;
//TODO: maybe have a way to "cancel" any in-flight requests to this host?
}

/// <summary>Bind a "virtual socket" on the specified port</summary>
Expand Down Expand Up @@ -518,6 +540,8 @@ public SimulatedHost RegisterHost(SimulatedNetwork location, string id, VirtualH
var netMask = IPAddress.Parse("255.0.0.0"); //HACKHACK: BUGBUG: must parse from the IP range!
var prefixLen = 8; //HACKHACK: BUGBUG: must parse from the IP range!

var offline = identity.StartAsOffline;

var adapters = new List<SimulatedNetworkAdapter>();

using (this.Lock.GetWriteLock())
Expand Down Expand Up @@ -556,7 +580,7 @@ public SimulatedHost RegisterHost(SimulatedNetwork location, string id, VirtualH
});
}

var host = new SimulatedHost(adapters.ToArray(), id, hostName, fqdn, aliases, addresses, identity.PassthroughToPhysicalNetwork);
var host = new SimulatedHost(adapters.ToArray(), id, hostName, fqdn, aliases, addresses, identity.PassthroughToPhysicalNetwork, offline);
this.HostsById.Add(host.Id, host);
foreach (var key in host.GetHostKeys())
{
Expand Down
11 changes: 11 additions & 0 deletions Doxense.Networking.Http/Networking/INetworkMap.cs
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ public sealed record VirtualHostIdentity
/// <summary>If <see langword="true"/>, this is an actual physical device on the network, and requests will be sent over the network. If <see langword="false"/>, this is a virtualized host.</summary>
public bool PassthroughToPhysicalNetwork { get; set; }

/// <summary>If <see langword="true"/>, the host should start in the 'offline' state.</summary>
public bool StartAsOffline { get; set; }

}

/// <summary>Représente un "LAN" où tous les hosts se voient entre eux directement</summary>
Expand Down Expand Up @@ -267,6 +270,14 @@ public interface IVirtualNetworkHost : IEquatable<IVirtualNetworkHost>

Func<HttpMessageHandler>? FindHandler(IVirtualNetworkLocation location, int port);

/// <summary>Flag that is <see langword="true"/> when this host is 'offline' and should not respond to any external request.</summary>
bool Offline { get; }

/// <summary>Change the <see cref="Offline">offline state</see> of this host</summary>
/// <param name="offline">Mark the host as offline if <see langword="true"/>, or back online if <see langword="false"/>.</param>
/// <remarks>If the host is offline, all simulated requests will start to fail</remarks>
void SetOffline(bool offline);

}

[PublicAPI]
Expand Down

0 comments on commit 4098d88

Please sign in to comment.