Files
spinifex/tests/e2e/harness/multinode_guest_ssh.go
T
joshsiv-mulga c9a4e904a8 test(e2e): fresh-install reachability baselines + drop qemu hostfwd fallback
Add baseline tests that confirm two fresh-install regressions the existing
suite masks (it authorizes tcp/22 on the default SG before every SSH probe):

  - default SG default-deny gates external reach of instances in the
    default subnet (SG, not routing)
  - a newly-created public subnet never gets its OVN IGW egress LRP, so it
    has no route to the VPC IGW despite MapPublicIpOnLaunch

Plus a positive test: instances sharing the default SG can communicate via
ICMP (same-host single-node, cross-host multinode).

Single-node baselines run after TestClusterStatsCLI (0-VM gate); multinode
after TestMultinodePreflight (fixture init). Instances are self-cleaning so
they don't perturb the singleton-VM ordering.

Remove the qemu hostfwd SSH fallback from the harness (InstancePublicSSHHost,
GuestSSHEndpoint) - it bypasses the OVN datapath and masks these regressions.
Endpoints now t.Fatal when no public IP is assigned.
2026-06-04 19:56:02 +10:00

73 lines
2.3 KiB
Go

//go:build e2e
package harness
import (
"context"
"fmt"
"os/exec"
"strconv"
"strings"
"testing"
"time"
"github.com/aws/aws-sdk-go/aws"
)
// GuestSSHEndpoint resolves (host, 22) for SSH-ing into a guest VM via its
// public IP.
//
// There is deliberately NO qemu-hostfwd fallback (see InstancePublicSSHHost):
// hostfwd bypasses the OVN datapath, so reaching a guest through it would mask
// the networking regressions these tests exist to catch. A running instance
// with no public IP is a hard failure.
func GuestSSHEndpoint(t *testing.T, c *AWSClient, cluster *Cluster, instanceID string, opts ...PollOpt) (host string, port int) {
t.Helper()
_ = cluster // retained for signature stability; node hostfwd lookup removed
inst := WaitForInstanceState(t, c, instanceID, "running", opts...)
pub := aws.StringValue(inst.PublicIpAddress)
if pub == "" {
t.Fatalf("GuestSSHEndpoint: instance %s has no public IP; "+
"qemu-hostfwd fallback is disabled (it bypasses the OVN datapath)", instanceID)
}
return pub, 22
}
// GuestSSHReady polls SSH into the named instance until a probe command
// succeeds. Pemfile is the test-scoped private key written by EnsureKeyPair;
// user defaults to ec2-user (the cloud-init account that spinifex injects
// regardless of distro family — see handlers/ec2/instance/service_impl.go).
func GuestSSHReady(t *testing.T, host string, port int, user, pemfile string, opts ...PollOpt) {
t.Helper()
cfg := applyOpts(pollCfg{timeout: 60 * time.Second, interval: 1 * time.Second}, opts...)
target := fmt.Sprintf("%s@%s", user, host)
EventuallyErr(t, func() error {
ctx, cancel := context.WithTimeout(context.Background(), cfg.interval+2*time.Second)
defer cancel()
args := []string{
"-i", pemfile,
"-p", strconv.Itoa(port),
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "ConnectTimeout=2",
"-o", "BatchMode=yes",
"-o", "LogLevel=ERROR",
target,
"echo ready",
}
if err := runSSH(ctx, args); err != nil {
return fmt.Errorf("ssh %s:%d: %w", host, port, err)
}
return nil
}, cfg.timeout, cfg.interval)
}
func runSSH(ctx context.Context, args []string) error {
out, err := exec.CommandContext(ctx, "ssh", args...).CombinedOutput()
if err != nil {
return fmt.Errorf("%w: %s", err, strings.TrimSpace(string(out)))
}
return nil
}