blob: 90918d226102f764fbd48d3cd9404a20df2a054a [file]
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package ssh_test
import (
"context"
"os"
"path/filepath"
"testing"
"time"
"go.chromium.org/tast/core/internal/sshtest"
"go.chromium.org/tast/core/ssh"
"go.chromium.org/tast/core/testutil"
)
var userKey, hostKey = sshtest.MustGenerateKeys()
func TestRetry(t *testing.T) {
t.Parallel()
srv, err := sshtest.NewSSHServer(&userKey.PublicKey, hostKey, func(*sshtest.ExecReq) {})
if err != nil {
t.Fatal("Failed starting server: ", err)
}
defer srv.Close()
// Configure the server to reject the next two connections and let the client only retry once.
srv.RejectConns(2)
ctx := context.Background()
if hst, err := sshtest.ConnectToServer(ctx, srv, userKey, &ssh.Options{ConnectRetries: 1}); err == nil {
t.Error("Unexpectedly able to connect to server with inadequate retries")
hst.Close(ctx)
}
// With two retries (i.e. three attempts), the connection should be successfully established.
srv.RejectConns(2)
if hst, err := sshtest.ConnectToServer(ctx, srv, userKey, &ssh.Options{ConnectRetries: 2}); err != nil {
t.Error("Failed connecting to server despite adequate retries: ", err)
} else {
hst.Close(ctx)
}
}
func TestPing(t *testing.T) {
t.Parallel()
td := sshtest.NewTestDataConn(t)
defer td.Close()
td.Srv.AnswerPings(true)
if err := td.Hst.Ping(td.Ctx, time.Minute); err != nil {
t.Errorf("Got error when pinging host: %v", err)
}
td.Srv.AnswerPings(false)
if err := td.Hst.Ping(td.Ctx, time.Millisecond); err == nil {
t.Errorf("Didn't get expected error when pinging host with short timeout")
}
// Cancel the context to simulate it having expired.
td.Cancel()
if err := td.Hst.Ping(td.Ctx, time.Minute); err == nil {
t.Errorf("Didn't get expected error when pinging host with expired context")
}
}
func TestKeyDir(t *testing.T) {
t.Parallel()
srv, err := sshtest.NewSSHServer(&userKey.PublicKey, hostKey, nil)
if err != nil {
t.Fatal(err)
}
defer srv.Close()
keyFile, err := sshtest.WriteKey(userKey)
if err != nil {
t.Fatal(err)
}
defer os.Remove(keyFile)
td := testutil.TempDir(t)
defer os.RemoveAll(td)
if err = os.Symlink(keyFile, filepath.Join(td, "testing_rsa")); err != nil {
t.Fatal(err)
}
opt := ssh.Options{KeyDir: td}
if err = ssh.ParseTarget(srv.Addr().String(), &opt); err != nil {
t.Fatal(err)
}
hst, err := ssh.New(context.Background(), &opt)
if err != nil {
t.Fatal(err)
}
hst.Close(context.Background())
}
func TestGenerateRemoteAddress(t *testing.T) {
t.Parallel()
srv, err := sshtest.NewSSHServer(&userKey.PublicKey, hostKey, func(*sshtest.ExecReq) {})
if err != nil {
t.Fatal("Failed starting server: ", err)
}
defer srv.Close()
ctx := context.Background()
hst, err := sshtest.ConnectToServer(ctx, srv, userKey, &ssh.Options{})
if err != nil {
t.Fatal("Unexpectedly unable to connect to server: ", err)
}
defer hst.Close(ctx)
got, _ := hst.GenerateRemoteAddress(2345)
want := "127.0.0.1:2345"
if got != want {
t.Fatalf("hst.GenerateRemoteAddress(2345) = %q, want: %q", got, want)
}
}
func TestProxyCommand(t *testing.T) {
t.Parallel()
srv, err := sshtest.NewSSHServer(&userKey.PublicKey, hostKey, nil)
if err != nil {
t.Fatal("Failed starting server: ", err)
}
defer srv.Close()
ctx := context.Background()
opt := ssh.Options{ProxyCommand: "socat tcp-connect:%h:%p STDIO"}
hst, err := sshtest.ConnectToServer(ctx, srv, userKey, &opt)
if err != nil {
t.Fatal("Unexpectedly unable to connect to server: ", err)
}
defer hst.Close(ctx)
}
func TestProxyCommandNone(t *testing.T) {
t.Parallel()
srv, err := sshtest.NewSSHServer(&userKey.PublicKey, hostKey, nil)
if err != nil {
t.Fatal("Failed starting server: ", err)
}
defer srv.Close()
ctx := context.Background()
opt := ssh.Options{ProxyCommand: "none"}
hst, err := sshtest.ConnectToServer(ctx, srv, userKey, &opt)
if err != nil {
t.Fatal("Unexpectedly unable to connect to server: ", err)
}
defer hst.Close(ctx)
}