diff --git a/tests/e2e/single/image_test.go b/tests/e2e/single/image_test.go deleted file mode 100644 index 7d061ba26..000000000 --- a/tests/e2e/single/image_test.go +++ /dev/null @@ -1,37 +0,0 @@ -//go:build e2e - -package single - -import ( - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/service/ec2" - "github.com/mulgadc/spinifex/tests/e2e/harness" - "github.com/stretchr/testify/require" -) - -// runImage rediscovers the architecture-appropriate Ubuntu AMI that -// bootstrap-install.sh imported from the pre-staged gold-image file -// (~/images/ubuntu-{26,24}.04.img → `spx admin images import --file`) and -// stashes the ID on the fixture for Phase 5+. Maps to run-e2e.sh ~233–255. -func runImage(t *testing.T, fix *Fixture) { - harness.Phase(t, "Single — Image Management") - - // Resolve the gold-image AMI via the cluster discovery helper. The first - // caller pays the DescribeImages + state-poll cost; this and every other - // AMI consumer hit the memoized ID. - amiID := needAMI(t, fix) - require.NotEmpty(t, amiID, "needAMI returned empty AMI ID") - _, arch := needInstanceTypeArch(t, fix) - harness.Detail(t, "ami", amiID, "arch", arch) - - harness.Step(t, "describe-images by AMI ID (verify round-trip)") - byID, err := fix.AWS.EC2.DescribeImages(&ec2.DescribeImagesInput{ - ImageIds: []*string{aws.String(amiID)}, - }) - require.NoErrorf(t, err, "describe-images %s", amiID) - require.Lenf(t, byID.Images, 1, "expected exactly 1 image for %s, got %d", amiID, len(byID.Images)) - require.Equal(t, amiID, aws.StringValue(byID.Images[0].ImageId), "round-trip AMI ID mismatch") - require.NotEmpty(t, aws.StringValue(byID.Images[0].Name), "lookup-by-ID returned empty Name") -} diff --git a/tests/e2e/single/tests_test.go b/tests/e2e/single/tests_test.go index 48d932858..566538f67 100644 --- a/tests/e2e/single/tests_test.go +++ b/tests/e2e/single/tests_test.go @@ -25,11 +25,6 @@ func TestDiscovery(t *testing.T) { runDiscovery(t, requireSingleNodeFixture(t)) } -func TestImage(t *testing.T) { - t.Parallel() - runImage(t, requireSingleNodeFixture(t)) -} - func TestAccountScoping(t *testing.T) { t.Parallel() runAccountScoping(t, requireSingleNodeFixture(t)) diff --git a/tests/integration/image_daemon_lite.go b/tests/integration/image_daemon_lite.go new file mode 100644 index 000000000..e564962f9 --- /dev/null +++ b/tests/integration/image_daemon_lite.go @@ -0,0 +1,41 @@ +//go:build integration + +package integration + +import ( + "testing" + + handlers_ec2_image "github.com/mulgadc/spinifex/spinifex/handlers/ec2/image" + "github.com/mulgadc/spinifex/spinifex/objectstore" + "github.com/nats-io/nats.go" +) + +// testImageBucket is the bucket name StartImageDaemonLite's ImageServiceImpl +// stores AMI config.json objects under. Backed by a MemoryObjectStore (see +// below), so — like testPredastoreBucket in daemon_lite.go — the name is +// never resolved against a real Predastore. +const testImageBucket = "integration-test-images" + +// StartImageDaemonLite subscribes a real handlers_ec2_image.ImageServiceImpl +// — the same production code a live daemon runs (daemon/daemon_handlers_image.go) +// — to the ec2.DescribeImages/ec2.RegisterImage subjects, backed by a memory +// object store rather than a real Predastore. The store itself is returned +// too, so a test can seed fixture data (e.g. snapshot metadata RegisterImage +// requires) directly, without a round-trip through NATS. +// +// Deliberately separate from StartDaemonLite (like StartVolumeDaemonLite): +// only a test that actually exercises image registration/lookup should wire +// it. CreateImage/CopyImage/DeregisterImage/*ImageAttribute are not wired — +// no ported test needs them yet. +func StartImageDaemonLite(t *testing.T, gw *Gateway) (*handlers_ec2_image.ImageServiceImpl, objectstore.ObjectStore) { + t.Helper() + + store := objectstore.NewMemoryObjectStore() + svc := handlers_ec2_image.NewImageServiceImplWithStore(store, testImageBucket) + + nc := gw.NATSConn + sub(t, nc, "ec2.DescribeImages", func(m *nats.Msg) { dispatch(m, svc.DescribeImages) }) + sub(t, nc, "ec2.RegisterImage", func(m *nats.Msg) { dispatch(m, svc.RegisterImage) }) + + return svc, store +} diff --git a/tests/integration/image_test.go b/tests/integration/image_test.go new file mode 100644 index 000000000..5712bfff7 --- /dev/null +++ b/tests/integration/image_test.go @@ -0,0 +1,56 @@ +//go:build integration + +package integration + +import ( + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/service/ec2" + handlers_ec2_snapshot "github.com/mulgadc/spinifex/spinifex/handlers/ec2/snapshot" + "github.com/stretchr/testify/require" +) + +// TestImage ports tests/e2e/single/image_test.go's runImage describe-images +// round-trip. The live test resolved a pre-staged gold-image AMI via +// needAMI (bootstrap-install.sh's `spx admin images import`); this tier has +// no such bootstrap, so it registers its own AMI against a hand-seeded +// snapshot via the real RegisterImage path instead — same production +// ImageServiceImpl.DescribeImages contract, without depending on a live +// cluster's pre-imported gold image. +func TestImage(t *testing.T) { + t.Parallel() + + gw := StartGateway(t) + _, store := StartImageDaemonLite(t, gw) + ec2Cli := gw.EC2Client(t) + + const snapshotID = "snap-integration-test-image" + require.NoError(t, handlers_ec2_snapshot.WriteSnapshotConfig(store, testImageBucket, snapshotID, &handlers_ec2_snapshot.SnapshotConfig{ + SnapshotID: snapshotID, + VolumeID: "vol-integration-test-image", + VolumeSize: 8, + State: "completed", + OwnerID: gw.AccountID, + }), "seed snapshot metadata") + + registerOut, err := ec2Cli.RegisterImage(&ec2.RegisterImageInput{ + Name: aws.String("integration-test-ami"), + RootDeviceName: aws.String("/dev/sda1"), + BlockDeviceMappings: []*ec2.BlockDeviceMapping{{ + DeviceName: aws.String("/dev/sda1"), + Ebs: &ec2.EbsBlockDevice{SnapshotId: aws.String(snapshotID)}, + }}, + }) + require.NoError(t, err, "register-image") + amiID := aws.StringValue(registerOut.ImageId) + require.NotEmpty(t, amiID, "register-image returned empty ImageId") + + byID, err := ec2Cli.DescribeImages(&ec2.DescribeImagesInput{ + ImageIds: []*string{aws.String(amiID)}, + }) + require.NoErrorf(t, err, "describe-images %s", amiID) + require.Lenf(t, byID.Images, 1, "expected exactly 1 image for %s, got %d", amiID, len(byID.Images)) + require.Equal(t, amiID, aws.StringValue(byID.Images[0].ImageId), "round-trip AMI ID mismatch") + require.NotEmpty(t, aws.StringValue(byID.Images[0].Name), "lookup-by-ID returned empty Name") +}