Class SshGameHost
- Namespace
- Valheim.Testing.GameSessions
- Assembly
- Valheim.Testing.GameSessions.dll
A machine reached through OpenSSH (ssh from PATH; on Windows the inbox client, or Git for Windows' client when the runner itself is in an SSH session) with key or agent authentication
only: BatchMode is always on, so ssh never prompts, and nothing here accepts a password. The destination is user@host,
ssh://user@host:port or an ssh-config alias; the host key must already be known (or pass StrictHostKeyChecking=accept-new).
A bash host needs a POSIX login shell; a Windows host's pwsh works under any login shell, including Windows' cmd.exe.
ssh exit 255 without the host's exit report is a transport failure.
public sealed class SshGameHost : ScriptedGameHost, IGameHost
- Inheritance
-
objectSshGameHost
- Implements
- Inherited Members
Constructors
SshGameHost(string, string, HostShell, int, IEnumerable<string>?, TimeSpan?, string)
public SshGameHost(string name, string destination, HostShell shell, int port = 0, IEnumerable<string>? sshOptions = null, TimeSpan? connectTimeout = null, string sshExecutable = "ssh")
Parameters
namestringdestinationstringuser@host,ssh://user@host:portor an ssh-config alias. Never a password.shellHostShellportintThe ssh port; 0 leaves it to the destination or the ssh config.
sshOptionsIEnumerable<string>Extra
-ooptions asName=value, for exampleIdentityFile=/path/key; the value is taken literally and may contain spaces, quotes and backslashes (User=Some Name,IdentityFile=C:\Users\Some Name.ssh\key). ssh reads a-ovalue like a config line, so such a value is passed asName="value", escaped the way ssh reads it. The value must not be empty or contain a control character such as a tab or a line break. The space-separatedName valueform is not accepted. A command (ProxyCommand=ssh -W %h:%p jump) is passed as written: ssh runs it with the user's shell. RemoteCommand is refused.connectTimeoutTimeSpan?ssh's ConnectTimeout; 10 s by default.
sshExecutablestringThe OpenSSH client. The default is
sshfrom PATH, which on Windows is the inbox client and reaches keys held by the Windows ssh-agent service. When the runner itself is inside an SSH session on Windows, the default is instead Git for Windows' matching ssh/scp pair, because the inbox ssh can hang there when a .NET process sends its script through redirected stdin. Git's msys ssh cannot open the named pipe that carries a forwarded agent in a Win32-OpenSSH session, so a host reached from such a session needs a key file (IdentityFile=). Anything but the bare default name, a path above all, is an explicit choice and always wins. On Windowsssh,ssh.exeand any capitalisation of either are the default, so forcing the inbox client from inside an SSH session takes its full path. A run's provenance records which client was chosen and why.
Properties
ConnectTimeout
public TimeSpan ConnectTimeout { get; }
Property Value
- TimeSpan
Destination
public string Destination { get; }
Property Value
- string
Kind
public override GameHostKind Kind { get; }
Property Value
Port
public int Port { get; }
Property Value
- int
Methods
OpenCliTunnelAsync(int, TimeSpan, int, CancellationToken)
Forwards a local loopback port to the host's loopback-only ValheimCLI port (ssh -N -L 127.0.0.1:local:127.0.0.1:host
with ExitOnForwardFailure). localPort 0 picks a free port; a port already in use is refused before ssh
starts. Ready means a local connection succeeded within readyTimeout: ssh listens here. Whether the host's
port answers is the next check, made through the tunnel. Neither end ever listens on a non-loopback address: ssh would also
open the LocalForward, RemoteForward and DynamicForward entries an ssh config gives this host (ClearAllForwardings would
clear the tunnel's own forward too), so a host whose effective config (ssh -G) has any is refused.
The free-port check and ssh's own bind are separate steps: another process can take the port in between. ssh then fails
to bind and exits (ExitOnForwardFailure), which fails the tunnel, but a readiness probe made before ssh has exited could
reach that other listener. Pass a localPort reserved for tests on a shared machine.
public override Task<CliTunnel> OpenCliTunnelAsync(int hostPort, TimeSpan readyTimeout, int localPort = 0, CancellationToken cancellation = default)
Parameters
hostPortintreadyTimeoutTimeSpanlocalPortintcancellationCancellationToken
Returns
- Task<CliTunnel>