mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
871513dc6c
This reverts commit b4701c78de.
131 lines
5.0 KiB
Go
131 lines
5.0 KiB
Go
//go:build integration
|
|
|
|
package integration
|
|
|
|
import (
|
|
"bytes"
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net/http"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/aws/aws-sdk-go/aws"
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
|
"github.com/aws/aws-sdk-go/aws/credentials"
|
|
"github.com/aws/aws-sdk-go/aws/session"
|
|
"github.com/aws/aws-sdk-go/service/s3"
|
|
testpredastore "github.com/mulgadc/spinifex/tests/fixtures/predastore"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
// TestPredastoreObjectLifecycle ports
|
|
// tests/e2e/single/predastore_test.go's runPredastoreObjectLifecycle: it
|
|
// verifies predastore's user-visible object contract end to end — an object
|
|
// is readable and intact after PutObject, absent after DeleteObject (both
|
|
// GET and ListObjectsV2 stop returning it), and the store keeps serving
|
|
// fresh writes once the delete has landed — against the real, in-process
|
|
// predastore daemon (testpredastore.Start), not a tofu-provisioned cluster.
|
|
// Not t.Parallel(): testpredastore.Start calls t.Setenv on whichever test
|
|
// first starts the shared fixture daemon in this binary, and t.Setenv panics
|
|
// if combined with t.Parallel on that same test.
|
|
func TestPredastoreObjectLifecycle(t *testing.T) {
|
|
fixture := testpredastore.Start(t)
|
|
cli := predastoreS3Client(t, fixture)
|
|
|
|
bucket := fmt.Sprintf("predastore-lifecycle-%d", time.Now().UnixNano())
|
|
_, err := cli.CreateBucket(&s3.CreateBucketInput{Bucket: aws.String(bucket)})
|
|
require.NoError(t, err, "create bucket %s", bucket)
|
|
t.Cleanup(func() {
|
|
_, _ = cli.DeleteBucket(&s3.DeleteBucketInput{Bucket: aws.String(bucket)})
|
|
})
|
|
|
|
const key = "lifecycle/object"
|
|
payload := bytes.Repeat([]byte("p"), 256<<10) // 256 KiB
|
|
|
|
// Write, then confirm the object is readable and byte-identical.
|
|
_, err = cli.PutObject(&s3.PutObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key),
|
|
Body: bytes.NewReader(payload),
|
|
})
|
|
require.NoError(t, err, "put %s", key)
|
|
got, err := predastoreGetBytes(t, cli, bucket, key)
|
|
require.NoError(t, err, "get %s after put", key)
|
|
require.True(t, bytes.Equal(got, payload), "get %s after put returned %d bytes, want %d", key, len(got), len(payload))
|
|
|
|
// Delete, then confirm the object is gone from both GET and List.
|
|
_, err = cli.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)})
|
|
require.NoError(t, err, "delete %s", key)
|
|
|
|
_, err = cli.GetObject(&s3.GetObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)})
|
|
require.True(t, isPredastoreNoSuchKey(err), "get %s after delete: want NoSuchKey, got %v", key, err)
|
|
|
|
list, err := cli.ListObjectsV2(&s3.ListObjectsV2Input{Bucket: aws.String(bucket), Prefix: aws.String(key)})
|
|
require.NoError(t, err, "list after delete")
|
|
require.Lenf(t, list.Contents, 0, "list after delete returned %d objects, want 0", len(list.Contents))
|
|
|
|
// The store must keep serving once the delete has landed: a fresh write
|
|
// round-trips, proving the delete did not wedge the bucket.
|
|
const key2 = "lifecycle/after-delete"
|
|
t.Cleanup(func() {
|
|
_, _ = cli.DeleteObject(&s3.DeleteObjectInput{Bucket: aws.String(bucket), Key: aws.String(key2)})
|
|
})
|
|
_, err = cli.PutObject(&s3.PutObjectInput{
|
|
Bucket: aws.String(bucket),
|
|
Key: aws.String(key2),
|
|
Body: bytes.NewReader(payload),
|
|
})
|
|
require.NoError(t, err, "put %s after delete", key2)
|
|
_, err = predastoreGetBytes(t, cli, bucket, key2)
|
|
require.NoError(t, err, "get %s after delete", key2)
|
|
}
|
|
|
|
// predastoreS3Client builds an aws-sdk-go v1 S3 client against the shared
|
|
// predastore fixture daemon. TLS verification is skipped: the fixture serves
|
|
// the S3 API under a self-signed certificate it generates for itself (see
|
|
// testpredastore.Start), which no trust store knows about.
|
|
func predastoreS3Client(t *testing.T, fixture *testpredastore.Fixture) *s3.S3 {
|
|
t.Helper()
|
|
httpClient := &http.Client{
|
|
Transport: &http.Transport{
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec // self-signed cert generated by this same fixture, localhost-only
|
|
},
|
|
}
|
|
sess, err := session.NewSession(&aws.Config{
|
|
Region: aws.String(fixture.Region),
|
|
Endpoint: aws.String("https://" + fixture.Host),
|
|
Credentials: credentials.NewStaticCredentials(fixture.AccessKey, fixture.SecretKey, ""),
|
|
S3ForcePathStyle: aws.Bool(true),
|
|
HTTPClient: httpClient,
|
|
})
|
|
require.NoError(t, err, "predastore s3 session")
|
|
return s3.New(sess)
|
|
}
|
|
|
|
// predastoreGetBytes fetches an object and returns its full body.
|
|
func predastoreGetBytes(t *testing.T, cli *s3.S3, bucket, key string) ([]byte, error) {
|
|
t.Helper()
|
|
out, err := cli.GetObject(&s3.GetObjectInput{Bucket: aws.String(bucket), Key: aws.String(key)})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer func() { _ = out.Body.Close() }()
|
|
buf := new(bytes.Buffer)
|
|
if _, err := buf.ReadFrom(out.Body); err != nil {
|
|
return nil, err
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|
|
|
|
// isPredastoreNoSuchKey reports whether err is the S3 NoSuchKey error
|
|
// predastore returns for a GET on a deleted or missing key.
|
|
func isPredastoreNoSuchKey(err error) bool {
|
|
var aerr awserr.Error
|
|
if errors.As(err, &aerr) {
|
|
return aerr.Code() == s3.ErrCodeNoSuchKey
|
|
}
|
|
return false
|
|
}
|