Files
spinifex/tests/integration/keypair_test.go
T
joshsiv-mulga 48b5f737e2 feat(integration): daemon-lite harness for key/tags/routetable/vpc
Adds DaemonLite, an in-process stand-in for a live spinifex daemon that
subscribes the real key, tags, route table, and VPC/subnet/SG/IGW service
implementations directly to their NATS subjects, backed by memory and
embedded-JetStream stores. Ports TestKeyPairs, TestTagManagement,
TestRouteTableValidation, and TestReplaceRouteConvergence from
tests/e2e/single so the integration tier drives genuine daemon-side
business logic instead of StubSubject canned replies.

vpcd (OVN) is out of scope, so its "vpc.create-sg"/"vpc.delete-sg"
acknowledgements are stubbed with a fixed success reply — the SG record
itself is still written by the real service impl before that event fires,
so the stub only unblocks the synchronous round-trip rather than
replacing tested behavior.
2026-07-20 12:04:58 +10:00

85 lines
2.9 KiB
Go

//go:build integration
package integration
import (
"crypto/rand"
"crypto/rsa"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/ec2"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/ssh"
)
// TestKeyPairs is ported from tests/e2e/single/keypair_test.go runKeyPairs,
// driving the REAL key/service_impl.go over the harness's DaemonLite instead
// of a live spinifex daemon. Exercises CreateKeyPair / ImportKeyPair /
// DescribeKeyPairs / DeleteKeyPair with the same assertions as the E2E
// source: both keys visible after import, only the primary key remains after
// delete.
func TestKeyPairs(t *testing.T) {
gw := StartGateway(t)
StartDaemonLite(t, gw)
ec2Cli := gw.EC2Client(t)
const key1 = "test-key-1"
const key2 = "test-key-2"
createOut, err := ec2Cli.CreateKeyPair(&ec2.CreateKeyPairInput{KeyName: aws.String(key1)})
require.NoError(t, err, "create-key-pair %s", key1)
require.NotEmpty(t, aws.StringValue(createOut.KeyMaterial), "CreateKeyPair PEM empty")
require.Contains(t, aws.StringValue(createOut.KeyMaterial), "-----BEGIN",
"PEM must contain -----BEGIN (got prefix %q)", firstN(aws.StringValue(createOut.KeyMaterial), 32))
pubMaterial := generateImportPubKey(t)
_, err = ec2Cli.ImportKeyPair(&ec2.ImportKeyPairInput{
KeyName: aws.String(key2),
PublicKeyMaterial: pubMaterial,
})
require.NoError(t, err, "import-key-pair %s", key2)
listed := describeKeyNames(t, ec2Cli)
assert.Contains(t, listed, key1, "describe-key-pairs missing %s (got %v)", key1, listed)
assert.Contains(t, listed, key2, "describe-key-pairs missing %s (got %v)", key2, listed)
_, err = ec2Cli.DeleteKeyPair(&ec2.DeleteKeyPairInput{KeyName: aws.String(key2)})
require.NoError(t, err, "delete-key-pair %s", key2)
remaining := describeKeyNames(t, ec2Cli)
assert.Contains(t, remaining, key1, "describe-key-pairs lost %s after deleting %s", key1, key2)
assert.NotContains(t, remaining, key2, "describe-key-pairs still lists %s after delete", key2)
}
// generateImportPubKey returns an OpenSSH-formatted public key (matching the
// `ssh-keygen -t rsa` output the E2E source's import-key-pair step feeds in).
// 2048-bit to match the E2E key length.
func generateImportPubKey(t *testing.T) []byte {
t.Helper()
priv, err := rsa.GenerateKey(rand.Reader, 2048)
require.NoError(t, err, "generate RSA key")
pub, err := ssh.NewPublicKey(&priv.PublicKey)
require.NoError(t, err, "ssh.NewPublicKey")
return ssh.MarshalAuthorizedKey(pub)
}
func describeKeyNames(t *testing.T, ec2Cli *ec2.EC2) []string {
t.Helper()
out, err := ec2Cli.DescribeKeyPairs(&ec2.DescribeKeyPairsInput{})
require.NoError(t, err, "describe-key-pairs")
names := make([]string, 0, len(out.KeyPairs))
for _, kp := range out.KeyPairs {
names = append(names, aws.StringValue(kp.KeyName))
}
return names
}
func firstN(s string, n int) string {
if len(s) <= n {
return s
}
return s[:n]
}