Files
Josh Siviour 71301983bd feat: add read-only EC2 platform inventory for account termination (#1212)
* feat: add read-only EC2 platform inventory for account termination

* fix: drop placement-group member instance IDs from the platform inventory reply
2026-10-16 12:05:32 +10:00

112 lines
3.1 KiB
Go

// Package readonlystore wraps an object store or a JetStream KV bucket so that
// any write fails the test. It exists to prove a code path is read-only, which
// an assertion on the final store contents cannot: a write that is undone
// before the check would pass it.
package readonlystore
import (
"context"
"errors"
"sync"
"testing"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/service/s3"
"github.com/mulgadc/spinifex/spinifex/providers/objectstore"
"github.com/nats-io/nats.go/jetstream"
)
// ErrWrite is returned by every write after the test has been failed.
var ErrWrite = errors.New("write to a read-only store")
// ObjectStore fails the test on any write and records every key it reads.
type ObjectStore struct {
objectstore.ObjectStore
t testing.TB
mu sync.Mutex
gets []string
}
var _ objectstore.ObjectStore = (*ObjectStore)(nil)
// NewObjectStore wraps inner for t.
func NewObjectStore(t testing.TB, inner objectstore.ObjectStore) *ObjectStore {
return &ObjectStore{ObjectStore: inner, t: t}
}
func (s *ObjectStore) GetObject(ctx context.Context, in *s3.GetObjectInput) (*s3.GetObjectOutput, error) {
s.mu.Lock()
s.gets = append(s.gets, aws.StringValue(in.Key))
s.mu.Unlock()
return s.ObjectStore.GetObject(ctx, in)
}
// Gets returns every key read so far.
func (s *ObjectStore) Gets() []string {
s.mu.Lock()
defer s.mu.Unlock()
return append([]string(nil), s.gets...)
}
func (s *ObjectStore) PutObject(_ context.Context, in *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
s.t.Errorf("read-only store: PutObject %s", aws.StringValue(in.Key))
return nil, ErrWrite
}
func (s *ObjectStore) DeleteObject(_ context.Context, in *s3.DeleteObjectInput) (*s3.DeleteObjectOutput, error) {
s.t.Errorf("read-only store: DeleteObject %s", aws.StringValue(in.Key))
return nil, ErrWrite
}
func (s *ObjectStore) EnsureBucket(_ context.Context, bucket string) error {
s.t.Errorf("read-only store: EnsureBucket %s", bucket)
return ErrWrite
}
// KV fails the test on any write to the wrapped bucket.
type KV struct {
jetstream.KeyValue
t testing.TB
}
// NewKV wraps inner for t.
func NewKV(t testing.TB, inner jetstream.KeyValue) *KV {
return &KV{KeyValue: inner, t: t}
}
func (k *KV) write(op, key string) error {
k.t.Errorf("read-only KV %s: %s %s", k.Bucket(), op, key)
return ErrWrite
}
func (k *KV) Put(_ context.Context, key string, _ []byte) (uint64, error) {
return 0, k.write("Put", key)
}
func (k *KV) PutString(_ context.Context, key string, _ string) (uint64, error) {
return 0, k.write("PutString", key)
}
func (k *KV) Create(_ context.Context, key string, _ []byte, _ ...jetstream.KVCreateOpt) (uint64, error) {
return 0, k.write("Create", key)
}
func (k *KV) Update(_ context.Context, key string, _ []byte, _ uint64) (uint64, error) {
return 0, k.write("Update", key)
}
func (k *KV) Delete(_ context.Context, key string, _ ...jetstream.KVDeleteOpt) error {
return k.write("Delete", key)
}
func (k *KV) Purge(_ context.Context, key string, _ ...jetstream.KVDeleteOpt) error {
return k.write("Purge", key)
}
func (k *KV) PurgeDeletes(_ context.Context, _ ...jetstream.KVPurgeOpt) error {
return k.write("PurgeDeletes", "")
}