Files
2026-09-30 11:48:27 +10:00

105 lines
3.6 KiB
Go

//go:build e2e
// Cluster-discovery helpers that memoize EC2 catalog lookups on the Fixture.
// First call pays the API cost; later callers in the same process hit the cache.
package harness
import (
"fmt"
"strings"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
)
// DiscoverDefaultAZ returns the first AZ reported as "available" by
// DescribeAvailabilityZones. Memoized per fixture.
func DiscoverDefaultAZ(t *testing.T, fx *Fixture) string {
t.Helper()
az, err := fx.ensureOnce(t, "discover:default-az", func() (string, func() error, error) {
out, derr := fx.EC2.DescribeAvailabilityZones(&ec2.DescribeAvailabilityZonesInput{})
if derr != nil {
return "", nil, fmt.Errorf("DescribeAvailabilityZones: %w", derr)
}
if len(out.AvailabilityZones) == 0 {
return "", nil, fmt.Errorf("no availability zones returned")
}
name := aws.StringValue(out.AvailabilityZones[0].ZoneName)
state := aws.StringValue(out.AvailabilityZones[0].State)
if state != "available" {
return "", nil, fmt.Errorf("AZ %s state %q (want available)", name, state)
}
return name, nil, nil
})
if err != nil {
t.Fatalf("DiscoverDefaultAZ: %v", err)
}
return az
}
// DiscoverNanoInstanceType returns the first instance type whose name contains
// "nano" along with the architecture reported by its ProcessorInfo. Memoized
// per fixture. Mirrors the picker logic from the bash run-e2e.sh driver.
func DiscoverNanoInstanceType(t *testing.T, fx *Fixture) (instanceType, arch string) {
t.Helper()
combined, err := fx.ensureOnce(t, "discover:nano-instance-type", func() (string, func() error, error) {
out, derr := fx.EC2.DescribeInstanceTypes(&ec2.DescribeInstanceTypesInput{})
if derr != nil {
return "", nil, fmt.Errorf("DescribeInstanceTypes: %w", derr)
}
for _, it := range out.InstanceTypes {
name := aws.StringValue(it.InstanceType)
if !strings.Contains(name, "nano") {
continue
}
if it.ProcessorInfo == nil || len(it.ProcessorInfo.SupportedArchitectures) == 0 {
continue
}
a := aws.StringValue(it.ProcessorInfo.SupportedArchitectures[0])
// Pack into a single memo value (string) — split below.
return name + "|" + a, nil, nil
}
return "", nil, fmt.Errorf("no nano instance type available (saw %d types)", len(out.InstanceTypes))
})
if err != nil {
t.Fatalf("DiscoverNanoInstanceType: %v", err)
}
instanceType, arch, ok := strings.Cut(combined, "|")
if !ok {
t.Fatalf("DiscoverNanoInstanceType: malformed memo value %q", combined)
}
return instanceType, arch
}
// DiscoverUbuntuAMI returns the AMI ID for the architecture-appropriate Ubuntu
// gold image. Tries ubuntu-26.04 first, falls back to ubuntu-24.04.
// Routes through EnsureAMI so the state-available poll and memoization apply.
func DiscoverUbuntuAMI(t *testing.T, fx *Fixture, arch string) string {
t.Helper()
if arch == "" {
t.Fatalf("DiscoverUbuntuAMI: arch required")
}
id, err := fx.ensureOnce(t, "discover:ubuntu-ami:"+arch, func() (string, func() error, error) {
candidates := []string{
"ami-ubuntu-26.04-" + arch,
"ami-ubuntu-24.04-" + arch,
}
for _, name := range candidates {
out, derr := fx.EC2.DescribeImages(&ec2.DescribeImagesInput{
Filters: []*ec2.Filter{
{Name: aws.String("name"), Values: []*string{aws.String(name)}},
},
})
if derr == nil && len(out.Images) > 0 {
return aws.StringValue(out.Images[0].ImageId), nil, nil
}
}
return "", nil, fmt.Errorf("no Ubuntu AMI found (tried: %v)", candidates)
})
if err != nil {
t.Fatalf("DiscoverUbuntuAMI: %v", err)
}
return EnsureAMI(t, fx, AMISource{Existing: id})
}