From 02e0dd0dc682e47a2debe43e49df451f4c57af3d Mon Sep 17 00:00:00 2001 From: joshsiv-mulga Date: Tue, 6 Oct 2026 11:20:26 +1000 Subject: [PATCH] refactor: move account identifiers to aws identifiers --- cmd/spinifex/cmd/admin.go | 3 +- cmd/spinifex/cmd/admin_ochre.go | 9 ++--- cmd/spinifex/cmd/admin_ochre_appliance.go | 3 +- cmd/spinifex/cmd/admin_ochre_endpoint.go | 14 +++---- cmd/spinifex/cmd/admin_ochre_pull_test.go | 6 +-- cmd/spinifex/cmd/admin_ochre_test.go | 6 +-- cmd/spinifex/cmd/admin_ochre_vector.go | 16 ++++---- .../register_imported_ami_snapshot_test.go | 8 ++-- spinifex/admin/images_promote.go | 6 +-- spinifex/admin/images_promote_test.go | 6 +-- spinifex/admin/images_remove.go | 6 +-- spinifex/daemon/daemon.go | 3 +- spinifex/daemon/daemon_handlers_vpc.go | 3 +- spinifex/daemon/daemon_handlers_vpc_test.go | 6 +-- spinifex/daemon/daemon_system_instance.go | 2 +- spinifex/daemon/ownership.go | 3 +- spinifex/domains/ec2/ebs/metadata/metadata.go | 5 +-- .../domains/ec2/ebs/metadata/metadata_test.go | 4 +- spinifex/domains/ec2/ebs/metadata/store.go | 4 +- .../aws/identifiers}/account.go | 2 +- .../aws/identifiers}/account_test.go | 2 +- spinifex/gateway/auth_test.go | 6 +-- spinifex/gateway/bedrock/access.go | 4 +- .../gateway/bedrock/access_autogrant_test.go | 6 +-- spinifex/gateway/bedrock/access_test.go | 4 +- spinifex/gateway/ec2/instance/RunInstances.go | 3 +- spinifex/gateway/ecr_principal_test.go | 8 ++-- spinifex/gateway/eks/internal_authz.go | 4 +- spinifex/gateway/eks/internal_authz_test.go | 10 ++--- spinifex/gateway/eks_authz_test.go | 8 ++-- spinifex/gateway/gateway.go | 3 +- spinifex/gateway/rds/agent_test.go | 14 +++---- spinifex/gateway/rds/authz.go | 4 +- spinifex/gateway/rds/authz_test.go | 6 +-- spinifex/gateway/rds/enginecatalog.go | 4 +- spinifex/gateway/rds_test.go | 8 ++-- spinifex/gateway/sts.go | 4 +- spinifex/gateway/sts_policy_test.go | 6 +-- spinifex/gateway/sts_request_test.go | 26 ++++++------- spinifex/handlers/bedrock/ami.go | 4 +- spinifex/handlers/bedrock/eviction_test.go | 8 ++-- spinifex/handlers/bedrock/hostport.go | 6 +-- spinifex/handlers/bedrock/launch.go | 20 +++++----- spinifex/handlers/bedrock/reaper.go | 12 +++--- spinifex/handlers/bedrock/reaper_test.go | 18 ++++----- spinifex/handlers/bedrock/resolver.go | 4 +- spinifex/handlers/bedrock/service.go | 10 ++--- spinifex/handlers/bedrock/service_test.go | 20 +++++----- spinifex/handlers/bedrock/types.go | 6 +-- spinifex/handlers/ec2/account/service_impl.go | 4 +- .../handlers/ec2/account/service_impl_test.go | 4 +- .../ec2/image/ami_source_volume_test.go | 4 +- .../handlers/ec2/image/describe_byid_test.go | 4 +- spinifex/handlers/ec2/image/service_impl.go | 12 +++--- .../handlers/ec2/image/service_impl_test.go | 10 ++--- .../ec2/instance/metadata_options_test.go | 18 ++++----- .../handlers/ec2/instance/service_impl.go | 6 +-- .../ec2/instance/service_impl_test.go | 38 +++++++++---------- .../handlers/ec2/snapshot/service_impl.go | 2 +- .../ec2/snapshot/snapshot_partition_test.go | 16 ++++---- spinifex/handlers/elbv2/lb_role_test.go | 14 +++---- spinifex/handlers/elbv2/service_impl.go | 8 ++-- spinifex/handlers/elbv2/service_impl_test.go | 6 +-- spinifex/handlers/iam/account_test.go | 14 +++---- spinifex/handlers/iam/service_impl.go | 16 ++++---- spinifex/handlers/iam/service_impl_test.go | 28 +++++++------- spinifex/handlers/ochrevector/appliance.go | 2 +- spinifex/handlers/ochrevector/hostport.go | 12 +++--- spinifex/handlers/ochrevector/identifiers.go | 4 +- .../handlers/ochrevector/identifiers_test.go | 8 ++-- spinifex/handlers/ochrevector/rds_launcher.go | 7 ++-- spinifex/handlers/quota/bedrock.go | 6 +-- spinifex/handlers/quota/bedrock_test.go | 6 +-- spinifex/handlers/quota/live_test.go | 14 ++++--- spinifex/handlers/quota/quota.go | 4 +- spinifex/handlers/quota/quota_test.go | 6 +-- spinifex/handlers/quota/reconcile.go | 6 +-- spinifex/handlers/quota/reconcile_test.go | 19 +++++----- spinifex/handlers/quota/records.go | 6 +-- spinifex/handlers/quota/records_test.go | 6 +-- spinifex/handlers/quota/storage_test.go | 8 ++-- spinifex/handlers/quota/vcpu_launch_test.go | 8 ++-- spinifex/handlers/quota/vcpu_retype_test.go | 4 +- spinifex/handlers/quota/vcpu_test.go | 6 +-- spinifex/handlers/rds/create.go | 3 +- spinifex/handlers/rds/create_test.go | 4 +- spinifex/handlers/rds/delete.go | 10 ++--- spinifex/handlers/rds/iam_test.go | 20 +++++----- spinifex/handlers/rds/launch.go | 19 +++++----- spinifex/handlers/rds/launch_test.go | 10 ++--- spinifex/handlers/rds/lifecycle.go | 5 ++- spinifex/handlers/rds/reconciler.go | 12 +++--- spinifex/handlers/rds/replace.go | 6 +-- spinifex/handlers/rds/replace_test.go | 4 +- spinifex/handlers/rds/restore.go | 8 ++-- spinifex/handlers/rds/restore_test.go | 6 +-- spinifex/handlers/rds/snapshot.go | 8 ++-- spinifex/handlers/rds/storage.go | 6 +-- spinifex/handlers/sts/assume_role.go | 4 +- spinifex/handlers/sts/assume_role_test.go | 4 +- spinifex/runtime/compute/cache/cache_test.go | 10 ++--- .../compute/vm/volume_state_account_test.go | 2 +- spinifex/runtime/roles/awsgw/awsgw.go | 3 +- tests/integration/harness.go | 10 ++--- 104 files changed, 427 insertions(+), 416 deletions(-) rename spinifex/{utils => foundation/aws/identifiers}/account.go (94%) rename spinifex/{utils => foundation/aws/identifiers}/account_test.go (97%) diff --git a/cmd/spinifex/cmd/admin.go b/cmd/spinifex/cmd/admin.go index e43ac0cad..a5420ebcc 100644 --- a/cmd/spinifex/cmd/admin.go +++ b/cmd/spinifex/cmd/admin.go @@ -46,7 +46,6 @@ import ( hostcommand "github.com/mulgadc/spinifex/spinifex/runtime/host/command" "github.com/mulgadc/spinifex/spinifex/runtime/host/dns" "github.com/mulgadc/spinifex/spinifex/services/viperblockd/vbwire" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" "github.com/pterm/pterm" @@ -889,7 +888,7 @@ func registerImportedAMISnapshot(store objectstore.ObjectStore, bucket string, a StartTime: time.Now(), Description: fmt.Sprintf("Imported AMI volume for %s", ami.Name), Encrypted: encrypted, - OwnerID: utils.GlobalAccountID, + OwnerID: awsidentifiers.GlobalAccountID, AvailabilityZone: az, } if err := ebsmetadata.NewStore(store, bucket).PutSnapshot(context.Background(), cfg); err != nil { diff --git a/cmd/spinifex/cmd/admin_ochre.go b/cmd/spinifex/cmd/admin_ochre.go index 6e798df75..2bca64dc5 100644 --- a/cmd/spinifex/cmd/admin_ochre.go +++ b/cmd/spinifex/cmd/admin_ochre.go @@ -26,7 +26,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/gateway/bedrock/hfhub" "github.com/mulgadc/spinifex/spinifex/providers/ebs" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" "github.com/pterm/pterm" @@ -641,7 +640,7 @@ func registerWeightsSnapshot(store objectstore.ObjectStore, bucket, snapshotID, StartTime: time.Now(), Description: fmt.Sprintf("Ochre weights volume %s", volumeID), Encrypted: encrypted, - OwnerID: utils.GlobalAccountID, + OwnerID: awsidentifiers.GlobalAccountID, AvailabilityZone: az, } if err := ebsmetadata.NewStore(store, bucket).PutSnapshot(context.Background(), cfg); err != nil { @@ -809,7 +808,7 @@ func materializeWeightsVolume(ctx context.Context, provider ebsprovider.EBSProvi func newWeightsSnapshotChecker(store objectstore.ObjectStore, bucket string) weightsSnapshotChecker { metadata := ebsmetadata.NewStore(store, bucket) return func(ctx context.Context, snapshotID string) (bool, error) { - if _, err := metadata.GetSnapshot(ctx, utils.GlobalAccountID, snapshotID); err != nil { + if _, err := metadata.GetSnapshot(ctx, awsidentifiers.GlobalAccountID, snapshotID); err != nil { if objectstore.IsNoSuchKeyError(err) { return false, nil } @@ -1012,7 +1011,7 @@ func resolveHFToken(ctx context.Context, cmd *cobra.Command, cfg *config.Cluster return "" } credStore := gateway_bedrock.NewCredentialStore(js, masterKey, nil) - token, ok, err := credStore.Resolve(ctx, utils.GlobalAccountID, vendorHuggingFace) + token, ok, err := credStore.Resolve(ctx, awsidentifiers.GlobalAccountID, vendorHuggingFace) if err != nil || !ok { return "" } @@ -1099,7 +1098,7 @@ func runOchreCredentialsSet(cmd *cobra.Command, _ []string) { return } if accountID == "" { - accountID = utils.GlobalAccountID + accountID = awsidentifiers.GlobalAccountID } token, err := readTokenFromStdin(os.Stdin) diff --git a/cmd/spinifex/cmd/admin_ochre_appliance.go b/cmd/spinifex/cmd/admin_ochre_appliance.go index 342ddf0e1..56807a667 100644 --- a/cmd/spinifex/cmd/admin_ochre_appliance.go +++ b/cmd/spinifex/cmd/admin_ochre_appliance.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "os" "time" @@ -62,7 +63,7 @@ var applianceTeardownFn = func(ctx context.Context, purgeMetadata bool) error { defer nc.Close() _, err = utils.NATSRequest[handlers_ochrevector.TeardownApplianceResponse](ctx, nc, handlers_ochrevector.SubjectTeardownAppliance, &handlers_ochrevector.TeardownApplianceRequest{PurgeMetadata: purgeMetadata}, - applianceTeardownTimeout, utils.GlobalAccountID) + applianceTeardownTimeout, awsidentifiers.GlobalAccountID) return err } diff --git a/cmd/spinifex/cmd/admin_ochre_endpoint.go b/cmd/spinifex/cmd/admin_ochre_endpoint.go index 110ee0132..d4d8a4006 100644 --- a/cmd/spinifex/cmd/admin_ochre_endpoint.go +++ b/cmd/spinifex/cmd/admin_ochre_endpoint.go @@ -4,12 +4,12 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "os" "strconv" "time" handlers_bedrock "github.com/mulgadc/spinifex/spinifex/handlers/bedrock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/pterm/pterm" "github.com/spf13/cobra" ) @@ -122,7 +122,7 @@ func waitForEndpointReady(ctx context.Context, svc handlers_bedrock.EndpointServ timeout time.Duration, clock endpointWaitClock) (handlers_bedrock.EndpointRecord, time.Duration, error) { start := clock.now() for { - out, err := svc.Describe(ctx, &handlers_bedrock.DescribeEndpointInput{ModelID: modelID}, utils.GlobalAccountID) + out, err := svc.Describe(ctx, &handlers_bedrock.DescribeEndpointInput{ModelID: modelID}, awsidentifiers.GlobalAccountID) if err != nil { return handlers_bedrock.EndpointRecord{}, clock.now().Sub(start), err } @@ -216,7 +216,7 @@ func reclaimRows(rec handlers_bedrock.EndpointRecord) [][2]string { // shared platform one — List itself now returns every account's records, not // just the shared platform account's. func listEndpointsOutput(ctx context.Context, svc handlers_bedrock.EndpointService) (string, error) { - out, err := svc.List(ctx, &handlers_bedrock.ListEndpointsInput{}, utils.GlobalAccountID) + out, err := svc.List(ctx, &handlers_bedrock.ListEndpointsInput{}, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -239,7 +239,7 @@ func listEndpointsOutput(ctx context.Context, svc handlers_bedrock.EndpointServi // the endpoint, then optionally wait for it. Returns the message to print. func runEnsureEndpoint(ctx context.Context, svc handlers_bedrock.EndpointService, modelID string, wait bool, timeout time.Duration, clock endpointWaitClock) (string, error) { - out, err := svc.Ensure(ctx, &handlers_bedrock.EnsureEndpointInput{ModelID: modelID}, utils.GlobalAccountID) + out, err := svc.Ensure(ctx, &handlers_bedrock.EnsureEndpointInput{ModelID: modelID}, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -305,7 +305,7 @@ func runOchreEndpointDescribe(cmd *cobra.Command, _ []string) { } defer closeFn() - out, err := svc.Describe(context.Background(), &handlers_bedrock.DescribeEndpointInput{ModelID: modelID, AccountID: accountID}, utils.GlobalAccountID) + out, err := svc.Describe(context.Background(), &handlers_bedrock.DescribeEndpointInput{ModelID: modelID, AccountID: accountID}, awsidentifiers.GlobalAccountID) if err != nil { fmt.Fprintf(os.Stderr, "%v\n", err) ochreExit(1) @@ -337,7 +337,7 @@ func runOchreEndpointList(_ *cobra.Command, _ []string) { // no record must not claim a teardown, and it points the operator at --account // so a pinned, account-scoped record they can see in 'list' is reachable. func deleteEndpointOutput(ctx context.Context, svc handlers_bedrock.EndpointService, modelID, accountID string) (string, error) { - out, err := svc.Delete(ctx, &handlers_bedrock.DeleteEndpointInput{ModelID: modelID, AccountID: accountID}, utils.GlobalAccountID) + out, err := svc.Delete(ctx, &handlers_bedrock.DeleteEndpointInput{ModelID: modelID, AccountID: accountID}, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -353,7 +353,7 @@ func deleteEndpointOutput(ctx context.Context, svc handlers_bedrock.EndpointServ // no-op message names the shared platform account rather than an empty string. func resolveAccountLabel(accountID string) string { if accountID == "" { - return utils.GlobalAccountID + " (shared platform)" + return awsidentifiers.GlobalAccountID + " (shared platform)" } return accountID } diff --git a/cmd/spinifex/cmd/admin_ochre_pull_test.go b/cmd/spinifex/cmd/admin_ochre_pull_test.go index 6ffa7dc10..507a9ffc9 100644 --- a/cmd/spinifex/cmd/admin_ochre_pull_test.go +++ b/cmd/spinifex/cmd/admin_ochre_pull_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "net/http" "net/http/httptest" @@ -22,7 +23,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/gateway/bedrock/hfhub" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -471,7 +471,7 @@ func TestResolveHFToken_FallsBackToStoredPlatformCredential(t *testing.T) { require.NoError(t, handlers_iam.SaveMasterKey(filepath.Join(baseDir, "config", "master.key"), masterKey)) credStore := gateway_bedrock.NewCredentialStore(js, masterKey, nil) - require.NoError(t, credStore.PutCredential(context.Background(), utils.GlobalAccountID, vendorHuggingFace, "hf_stored_token")) + require.NoError(t, credStore.PutCredential(context.Background(), awsidentifiers.GlobalAccountID, vendorHuggingFace, "hf_stored_token")) cmd := newOchreWeightsPullTestCmd(t, selfHostModelID, testHFRepo, "main", "", "") cfg := &config.ClusterConfig{Node: "node1", Nodes: map[string]config.Config{"node1": {BaseDir: baseDir}}} @@ -622,7 +622,7 @@ func TestRunOchreCredentialsSet_RoundTripsAndDefaultsToPlatformAccount(t *testin verifyJS := testutil.NewJetStream(t, verifyConn) credStore := gateway_bedrock.NewCredentialStore(verifyJS, masterKey, nil) - token, ok, err := credStore.Resolve(context.Background(), utils.GlobalAccountID, vendorHuggingFace) + token, ok, err := credStore.Resolve(context.Background(), awsidentifiers.GlobalAccountID, vendorHuggingFace) require.NoError(t, err) require.True(t, ok) assert.Equal(t, "hf_supersecret", token) diff --git a/cmd/spinifex/cmd/admin_ochre_test.go b/cmd/spinifex/cmd/admin_ochre_test.go index 50a177c50..926e961a5 100644 --- a/cmd/spinifex/cmd/admin_ochre_test.go +++ b/cmd/spinifex/cmd/admin_ochre_test.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "net/http" "net/http/httptest" @@ -19,7 +20,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/spf13/cobra" "github.com/stretchr/testify/assert" @@ -1095,7 +1095,7 @@ func TestRegisterWeightsSnapshot_WritesEC2ReadableMetadata(t *testing.T) { require.NoError(t, registerWeightsSnapshot(store, bucket, "snap-vol-abc", "vol-abc", 12*bytesPerGiB, "ap-southeast-2a", true)) - cfg, err := ebsmetadata.NewStore(store, bucket).GetSnapshot(context.Background(), utils.GlobalAccountID, "snap-vol-abc") + cfg, err := ebsmetadata.NewStore(store, bucket).GetSnapshot(context.Background(), awsidentifiers.GlobalAccountID, "snap-vol-abc") require.NoError(t, err) assert.Equal(t, "snap-vol-abc", cfg.SnapshotID) assert.Equal(t, "vol-abc", cfg.VolumeID) @@ -1112,7 +1112,7 @@ func TestRegisterWeightsSnapshot_UnencryptedVolume(t *testing.T) { require.NoError(t, registerWeightsSnapshot(store, "predastore", "snap-vol-plain", "vol-plain", bytesPerGiB, "ap-southeast-2a", false)) - cfg, err := ebsmetadata.NewStore(store, "predastore").GetSnapshot(context.Background(), utils.GlobalAccountID, "snap-vol-plain") + cfg, err := ebsmetadata.NewStore(store, "predastore").GetSnapshot(context.Background(), awsidentifiers.GlobalAccountID, "snap-vol-plain") require.NoError(t, err) assert.False(t, cfg.Encrypted) } diff --git a/cmd/spinifex/cmd/admin_ochre_vector.go b/cmd/spinifex/cmd/admin_ochre_vector.go index 799aa655c..cd47d2b47 100644 --- a/cmd/spinifex/cmd/admin_ochre_vector.go +++ b/cmd/spinifex/cmd/admin_ochre_vector.go @@ -192,7 +192,7 @@ var backupAccountFn = func(ctx context.Context, accountID string) (*handlers_och defer nc.Close() return utils.NATSRequest[handlers_ochrevector.BackupAccountResponse](ctx, nc, handlers_ochrevector.SubjectBackupAccount, &handlers_ochrevector.BackupAccountRequest{AccountID: accountID}, - vectorBackupRestoreTimeout, utils.GlobalAccountID) + vectorBackupRestoreTimeout, awsidentifiers.GlobalAccountID) } // restoreAccountFn indirects the NATS call so runRestoreAccount is testable @@ -205,7 +205,7 @@ var restoreAccountFn = func(ctx context.Context, accountID, objectKey string) er defer nc.Close() _, err = utils.NATSRequest[handlers_ochrevector.RestoreAccountResponse](ctx, nc, handlers_ochrevector.SubjectRestoreAccount, &handlers_ochrevector.RestoreAccountRequest{AccountID: accountID, ObjectKey: objectKey}, - vectorBackupRestoreTimeout, utils.GlobalAccountID) + vectorBackupRestoreTimeout, awsidentifiers.GlobalAccountID) return err } @@ -331,7 +331,7 @@ func runIndexCreate(ctx context.Context, svc handlers_ochrevector.VectorService, Name: name, Dimension: dimension, EmbeddingModel: model, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -340,7 +340,7 @@ func runIndexCreate(ctx context.Context, svc handlers_ochrevector.VectorService, // runIndexDelete is the testable core of 'ochre vector index delete'. func runIndexDelete(ctx context.Context, svc handlers_ochrevector.VectorService, indexID string) (string, error) { - if _, err := svc.DeleteIndex(ctx, &handlers_ochrevector.DeleteIndexRequest{IndexID: indexID}, utils.GlobalAccountID); err != nil { + if _, err := svc.DeleteIndex(ctx, &handlers_ochrevector.DeleteIndexRequest{IndexID: indexID}, awsidentifiers.GlobalAccountID); err != nil { return "", err } return fmt.Sprintf("✅ Index %s deleted.", indexID), nil @@ -349,7 +349,7 @@ func runIndexDelete(ctx context.Context, svc handlers_ochrevector.VectorService, // listIndexesOutput renders 'ochre vector index list'. Split from its Run // function so it is testable against a fake service with no NATS connection. func listIndexesOutput(ctx context.Context, svc handlers_ochrevector.VectorService) (string, error) { - out, err := svc.ListIndexes(ctx, &handlers_ochrevector.ListIndexesRequest{}, utils.GlobalAccountID) + out, err := svc.ListIndexes(ctx, &handlers_ochrevector.ListIndexesRequest{}, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -376,7 +376,7 @@ func runIngest(ctx context.Context, svc handlers_ochrevector.VectorService, inde ChunkOverlap: chunkOverlap, Metadata: meta, }, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -385,7 +385,7 @@ func runIngest(ctx context.Context, svc handlers_ochrevector.VectorService, inde // runJobDescribe is the testable core of 'ochre vector job describe'. func runJobDescribe(ctx context.Context, svc handlers_ochrevector.VectorService, jobID string) (string, error) { - out, err := svc.DescribeJob(ctx, &handlers_ochrevector.DescribeJobRequest{JobID: jobID}, utils.GlobalAccountID) + out, err := svc.DescribeJob(ctx, &handlers_ochrevector.DescribeJobRequest{JobID: jobID}, awsidentifiers.GlobalAccountID) if err != nil { return "", err } @@ -398,7 +398,7 @@ func runQuery(ctx context.Context, svc handlers_ochrevector.VectorService, index if err != nil { return "", err } - out, err := svc.Query(ctx, &handlers_ochrevector.QueryRequest{IndexID: indexID, Text: text, K: k, Filter: filter}, utils.GlobalAccountID) + out, err := svc.Query(ctx, &handlers_ochrevector.QueryRequest{IndexID: indexID, Text: text, K: k, Filter: filter}, awsidentifiers.GlobalAccountID) if err != nil { return "", err } diff --git a/cmd/spinifex/cmd/register_imported_ami_snapshot_test.go b/cmd/spinifex/cmd/register_imported_ami_snapshot_test.go index db5cea2c8..356686aad 100644 --- a/cmd/spinifex/cmd/register_imported_ami_snapshot_test.go +++ b/cmd/spinifex/cmd/register_imported_ami_snapshot_test.go @@ -2,11 +2,11 @@ package cmd import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -28,7 +28,7 @@ func TestRegisterImportedAMISnapshot_WritesEC2ReadableMetadata(t *testing.T) { require.NoError(t, registerImportedAMISnapshot(store, bucket, ami, "ap-southeast-2a", true)) - cfg, err := ebsmetadata.NewStore(store, bucket).GetSnapshot(context.Background(), utils.GlobalAccountID, "snap-ami-import01") + cfg, err := ebsmetadata.NewStore(store, bucket).GetSnapshot(context.Background(), awsidentifiers.GlobalAccountID, "snap-ami-import01") require.NoError(t, err) assert.Equal(t, "snap-ami-import01", cfg.SnapshotID) assert.Equal(t, "ami-import01", cfg.VolumeID, "the import path creates the volume under the AMI's own ID") @@ -37,7 +37,7 @@ func TestRegisterImportedAMISnapshot_WritesEC2ReadableMetadata(t *testing.T) { assert.Equal(t, "100%", cfg.Progress) assert.Equal(t, "ap-southeast-2a", cfg.AvailabilityZone) assert.True(t, cfg.Encrypted) - assert.Equal(t, utils.GlobalAccountID, cfg.OwnerID, "a system-catalog import has no tenant account to own its snapshot") + assert.Equal(t, awsidentifiers.GlobalAccountID, cfg.OwnerID, "a system-catalog import has no tenant account to own its snapshot") assert.Contains(t, cfg.Description, ami.Name) } @@ -49,7 +49,7 @@ func TestRegisterImportedAMISnapshot_UnencryptedVolume(t *testing.T) { require.NoError(t, registerImportedAMISnapshot(store, "predastore", ami, "ap-southeast-2a", false)) - cfg, err := ebsmetadata.NewStore(store, "predastore").GetSnapshot(context.Background(), utils.GlobalAccountID, "snap-ami-plain01") + cfg, err := ebsmetadata.NewStore(store, "predastore").GetSnapshot(context.Background(), awsidentifiers.GlobalAccountID, "snap-ami-plain01") require.NoError(t, err) assert.False(t, cfg.Encrypted) } diff --git a/spinifex/admin/images_promote.go b/spinifex/admin/images_promote.go index 1c539c56c..7a7f08f59 100644 --- a/spinifex/admin/images_promote.go +++ b/spinifex/admin/images_promote.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "strings" "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" ) // SystemOwnerAlias is the fixed owner alias written to AMI config on promotion. @@ -59,7 +59,7 @@ func PromoteSystemImage(store objectstore.ObjectStore, bucket string, opts Promo return nil, errors.New(awserrors.ErrorServerInternal) } - if meta.ImageOwnerAlias == "" || !utils.IsAccountID(meta.ImageOwnerAlias) { + if meta.ImageOwnerAlias == "" || !awsidentifiers.IsAccountID(meta.ImageOwnerAlias) { return nil, fmt.Errorf("%s is already a system-owned AMI (owner: %q); promotion not allowed", opts.ImageID, meta.ImageOwnerAlias) } @@ -81,7 +81,7 @@ func PromoteSystemImage(store objectstore.ObjectStore, bucket string, opts Promo return nil, errors.New(awserrors.ErrorServerInternal) } if moveSnapshot { - snap.OwnerID = utils.GlobalAccountID + snap.OwnerID = awsidentifiers.GlobalAccountID if err := metaStore.PutSnapshot(context.Background(), snap); err != nil { slog.Error("PromoteSystemImage: write snapshot document under the global account", "imageId", opts.ImageID, "snapshotId", meta.SnapshotID, "err", err) diff --git a/spinifex/admin/images_promote_test.go b/spinifex/admin/images_promote_test.go index 60984f711..a96cec960 100644 --- a/spinifex/admin/images_promote_test.go +++ b/spinifex/admin/images_promote_test.go @@ -2,6 +2,7 @@ package admin import ( "bytes" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/aws" @@ -9,7 +10,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -58,9 +58,9 @@ func TestPromoteSystemImage_MovesSnapshotToGlobalAccount(t *testing.T) { require.NoError(t, err) metaStore := ebsmetadata.NewStore(store, testRemoveBucket) - moved, err := metaStore.GetSnapshot(t.Context(), utils.GlobalAccountID, snapID) + moved, err := metaStore.GetSnapshot(t.Context(), awsidentifiers.GlobalAccountID, snapID) require.NoError(t, err) - assert.Equal(t, utils.GlobalAccountID, moved.OwnerID) + assert.Equal(t, awsidentifiers.GlobalAccountID, moved.OwnerID) assert.Equal(t, "vol-source-001", moved.VolumeID) _, err = metaStore.GetSnapshot(t.Context(), testRemoveAccountID, snapID) diff --git a/spinifex/admin/images_remove.go b/spinifex/admin/images_remove.go index b32632a80..ae795fe08 100644 --- a/spinifex/admin/images_remove.go +++ b/spinifex/admin/images_remove.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "strings" "time" @@ -13,7 +14,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" ) // RemoveImageOpts configures RemoveSystemImage. @@ -90,7 +90,7 @@ func PreviewRemoveSystemImage(store objectstore.ObjectStore, bucket, imageID str preview.Name = meta.Name preview.Owner = meta.ImageOwnerAlias preview.Created = meta.CreationDate - preview.IsSystemOwned = meta.ImageOwnerAlias != "" && !utils.IsAccountID(meta.ImageOwnerAlias) + preview.IsSystemOwned = meta.ImageOwnerAlias != "" && !awsidentifiers.IsAccountID(meta.ImageOwnerAlias) case objectstore.IsNoSuchKeyError(configErr): // Config absent — salvage candidate. Leave ConfigPresent=false. case errors.Is(configErr, ebsmetadata.ErrCorruptDocument): @@ -213,7 +213,7 @@ func RemoveSystemImage(store objectstore.ObjectStore, bucket string, opts Remove } if configErr == nil && !opts.Force { - if meta.ImageOwnerAlias != "" && utils.IsAccountID(meta.ImageOwnerAlias) { + if meta.ImageOwnerAlias != "" && awsidentifiers.IsAccountID(meta.ImageOwnerAlias) { return nil, fmt.Errorf("%s is account-owned (%s); use `aws ec2 deregister-image` followed by `aws ec2 delete-snapshot`", opts.ImageID, meta.ImageOwnerAlias) } diff --git a/spinifex/daemon/daemon.go b/spinifex/daemon/daemon.go index 4158bfcc0..5a9c6fbec 100644 --- a/spinifex/daemon/daemon.go +++ b/spinifex/daemon/daemon.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "github.com/mulgadc/spinifex/spinifex/foundation/netaddr" "log/slog" "maps" @@ -2178,7 +2179,7 @@ func (d *Daemon) startCluster() error { // (matches AWS: every account has a default VPC with IGW + default SG) if d.vpcService != nil { failedDefaultVPCs := map[string]struct{}{} - for _, accountID := range []string{utils.GlobalAccountID, admin.DefaultAccountID()} { + for _, accountID := range []string{awsidentifiers.GlobalAccountID, admin.DefaultAccountID()} { // Pass bootstrap IDs for the admin account so EnsureDefaultVPC uses // the same IDs that admin init wrote to [bootstrap] in spinifex.toml. var opts []handlers_ec2_vpc.BootstrapIDs diff --git a/spinifex/daemon/daemon_handlers_vpc.go b/spinifex/daemon/daemon_handlers_vpc.go index 7b6fbdc3a..c122a8d9f 100644 --- a/spinifex/daemon/daemon_handlers_vpc.go +++ b/spinifex/daemon/daemon_handlers_vpc.go @@ -3,6 +3,7 @@ package daemon import ( "context" "encoding/json" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "time" @@ -107,7 +108,7 @@ func (d *Daemon) handleEnsureDefaultVpc(msg *nats.Msg) string { // in skipAccounts are not touched — used to avoid attaching infrastructure to // a half-built VPC when EnsureDefaultVPC failed earlier in startup. func (d *Daemon) ensureDefaultVPCInfrastructure(skipAccounts map[string]struct{}) { - for _, accountID := range []string{utils.GlobalAccountID, admin.DefaultAccountID()} { + for _, accountID := range []string{awsidentifiers.GlobalAccountID, admin.DefaultAccountID()} { if _, skip := skipAccounts[accountID]; skip { continue } diff --git a/spinifex/daemon/daemon_handlers_vpc_test.go b/spinifex/daemon/daemon_handlers_vpc_test.go index f911db282..2931f5440 100644 --- a/spinifex/daemon/daemon_handlers_vpc_test.go +++ b/spinifex/daemon/daemon_handlers_vpc_test.go @@ -2,6 +2,7 @@ package daemon import ( "encoding/json" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "sync" "testing" "time" @@ -13,7 +14,6 @@ import ( handlers_ec2_igw "github.com/mulgadc/spinifex/spinifex/handlers/ec2/igw" handlers_ec2_routetable "github.com/mulgadc/spinifex/spinifex/handlers/ec2/routetable" handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -261,7 +261,7 @@ func TestEnsureDefaultVPCInfrastructure_SkipsHalfBuiltAccounts(t *testing.T) { daemon, _ := createDefaultVPCTestDaemon(t) adminAccount := admin.DefaultAccountID() - for _, accountID := range []string{utils.GlobalAccountID, adminAccount} { + for _, accountID := range []string{awsidentifiers.GlobalAccountID, adminAccount} { _, err := daemon.vpcService.EnsureDefaultVPC(accountID) require.NoError(t, err) } @@ -273,7 +273,7 @@ func TestEnsureDefaultVPCInfrastructure_SkipsHalfBuiltAccounts(t *testing.T) { require.NoError(t, err) assert.Empty(t, skipped.InternetGateways, "a skipped account must not have infrastructure attached") - built, err := daemon.igwService.DescribeInternetGateways(t.Context(), &ec2.DescribeInternetGatewaysInput{}, utils.GlobalAccountID) + built, err := daemon.igwService.DescribeInternetGateways(t.Context(), &ec2.DescribeInternetGatewaysInput{}, awsidentifiers.GlobalAccountID) require.NoError(t, err) assert.Len(t, built.InternetGateways, 1) diff --git a/spinifex/daemon/daemon_system_instance.go b/spinifex/daemon/daemon_system_instance.go index afd7fc979..2fccfd194 100644 --- a/spinifex/daemon/daemon_system_instance.go +++ b/spinifex/daemon/daemon_system_instance.go @@ -84,7 +84,7 @@ func (d *Daemon) LaunchSystemInstance(input *handlers_elbv2.SystemInstanceInput) if input.BootMode == sysinstance.BootAMI { return d.launchAMISystemInstance(input) } - accountID := utils.GlobalAccountID + accountID := awsidentifiers.GlobalAccountID // ENI account may differ from system account — the ENI is created under // the caller's account, so lookups/updates must use that account ID. eniAccountID := resolveENIAccount(input.AccountID, accountID) diff --git a/spinifex/daemon/ownership.go b/spinifex/daemon/ownership.go index c73ed633f..8248ca7d4 100644 --- a/spinifex/daemon/ownership.go +++ b/spinifex/daemon/ownership.go @@ -1,6 +1,7 @@ package daemon import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" @@ -14,7 +15,7 @@ func checkInstanceOwnership(nodeID string, msg *nats.Msg, instanceID, ownerAccou callerAccountID := utils.AccountIDFromMsg(msg) if ownerAccountID == "" { - if callerAccountID != utils.GlobalAccountID { + if callerAccountID != awsidentifiers.GlobalAccountID { slog.Warn("Untenanted instance access denied (not root)", "instanceId", instanceID, "callerAccount", callerAccountID) respondWithError(nodeID, msg, awserrors.ErrorInvalidInstanceIDNotFound) diff --git a/spinifex/domains/ec2/ebs/metadata/metadata.go b/spinifex/domains/ec2/ebs/metadata/metadata.go index db67efcc5..4bf7e8235 100644 --- a/spinifex/domains/ec2/ebs/metadata/metadata.go +++ b/spinifex/domains/ec2/ebs/metadata/metadata.go @@ -6,10 +6,9 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "time" - - "github.com/mulgadc/spinifex/spinifex/utils" ) // SchemaVersion is the document's field generation, and moves independently of @@ -150,7 +149,7 @@ func AMIKey(imageID string) (string, error) { return key("amis", imageID) } // untenanted document cannot be written, cannot exist, and never has to be // read. A transposed (id, account) pair fails here too. func partitionedKey(kind, accountID, id string) (string, error) { - if !utils.IsAccountID(accountID) { + if !awsidentifiers.IsAccountID(accountID) { return "", fmt.Errorf("invalid EBS metadata account ID %q", accountID) } return key(kind+"/"+accountID, id) diff --git a/spinifex/domains/ec2/ebs/metadata/metadata_test.go b/spinifex/domains/ec2/ebs/metadata/metadata_test.go index d8ac3a8b1..234454ffc 100644 --- a/spinifex/domains/ec2/ebs/metadata/metadata_test.go +++ b/spinifex/domains/ec2/ebs/metadata/metadata_test.go @@ -1,10 +1,10 @@ package ebsmetadata import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "time" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -113,7 +113,7 @@ func TestPartitionedKeysRejectNonAccountOwner(t *testing.T) { require.Error(t, err) // The system account is an ordinary account and partitions like any other. - key, err := VolumeKey(utils.GlobalAccountID, "vol-1") + key, err := VolumeKey(awsidentifiers.GlobalAccountID, "vol-1") require.NoError(t, err) assert.Equal(t, "spinifex/ebsmetadata/v2/volumes/000000000000/vol-1.json", key) } diff --git a/spinifex/domains/ec2/ebs/metadata/store.go b/spinifex/domains/ec2/ebs/metadata/store.go index 0a292969e..8437d5eb5 100644 --- a/spinifex/domains/ec2/ebs/metadata/store.go +++ b/spinifex/domains/ec2/ebs/metadata/store.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "log/slog" "sync" @@ -13,7 +14,6 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/s3" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" ) // Store persists Spinifex-owned metadata documents in the existing object @@ -192,7 +192,7 @@ func (s *Store) listSnapshotDocuments(ctx context.Context, accountID string, ski // accountPrefix rejects the same accounts the key builders do, so a listing // cannot widen to the whole tree by being handed an empty account. func accountPrefix(kindPrefix, accountID string) (string, error) { - if !utils.IsAccountID(accountID) { + if !awsidentifiers.IsAccountID(accountID) { return "", fmt.Errorf("invalid EBS metadata account ID %q", accountID) } return kindPrefix + accountID + "/", nil diff --git a/spinifex/utils/account.go b/spinifex/foundation/aws/identifiers/account.go similarity index 94% rename from spinifex/utils/account.go rename to spinifex/foundation/aws/identifiers/account.go index 507a2c14f..63a130af5 100644 --- a/spinifex/utils/account.go +++ b/spinifex/foundation/aws/identifiers/account.go @@ -1,4 +1,4 @@ -package utils +package identifiers // GlobalAccountID is the root/system account ID. const GlobalAccountID = "000000000000" diff --git a/spinifex/utils/account_test.go b/spinifex/foundation/aws/identifiers/account_test.go similarity index 97% rename from spinifex/utils/account_test.go rename to spinifex/foundation/aws/identifiers/account_test.go index 1b749d828..bcb3fb07f 100644 --- a/spinifex/utils/account_test.go +++ b/spinifex/foundation/aws/identifiers/account_test.go @@ -1,4 +1,4 @@ -package utils +package identifiers import ( "testing" diff --git a/spinifex/gateway/auth_test.go b/spinifex/gateway/auth_test.go index f10614c91..cb6287235 100644 --- a/spinifex/gateway/auth_test.go +++ b/spinifex/gateway/auth_test.go @@ -8,6 +8,7 @@ import ( "encoding/xml" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "net/http" "net/http/httptest" @@ -27,7 +28,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1367,7 +1367,7 @@ func TestCheckPolicy_RootGlobalAccount_Bypasses(t *testing.T) { AccessKeyID: testAccessKey, SecretAccessKey: encryptedSecret, UserName: "root", - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, Status: "Active", }, }, @@ -2400,7 +2400,7 @@ func TestCheckPolicy_AssumedRole_ZeroPolicyRole_DenyAll(t *testing.T) { // other session. func TestCheckPolicy_AssumedRole_SessionNamedRoot_NoBypass(t *testing.T) { cred := assumedRoleSessionCred("root", - "arn:aws:iam::"+utils.GlobalAccountID+":role/sneaky-role", utils.GlobalAccountID) + "arn:aws:iam::"+awsidentifiers.GlobalAccountID+":role/sneaky-role", awsidentifiers.GlobalAccountID) var roleResolved bool gw := newAssumedRoleEnforcementGateway(t, cred, func(_, roleName string) ([]handlers_iam.PolicyDocument, error) { roleResolved = true diff --git a/spinifex/gateway/bedrock/access.go b/spinifex/gateway/bedrock/access.go index 2a8607e7e..e57a9ea7e 100644 --- a/spinifex/gateway/bedrock/access.go +++ b/spinifex/gateway/bedrock/access.go @@ -5,10 +5,10 @@ import ( "encoding/base64" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" ) @@ -78,7 +78,7 @@ func NewModelAccessStore(js jetstream.JetStream) *ModelAccessStore { // account bypasses grants entirely, matching how handlers_quota exempts it // from every quota dimension. func (s *ModelAccessStore) Granted(ctx context.Context, accountID, modelID string) (bool, error) { - if accountID == utils.GlobalAccountID { + if accountID == awsidentifiers.GlobalAccountID { return true, nil } kv, err := s.bucket.KV(ctx) diff --git a/spinifex/gateway/bedrock/access_autogrant_test.go b/spinifex/gateway/bedrock/access_autogrant_test.go index 560848a89..5d511c81d 100644 --- a/spinifex/gateway/bedrock/access_autogrant_test.go +++ b/spinifex/gateway/bedrock/access_autogrant_test.go @@ -7,10 +7,10 @@ package gateway_bedrock import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/service/bedrock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,7 +21,7 @@ import ( type systemBypassInner struct{} func (systemBypassInner) Granted(_ context.Context, accountID, _ string) (bool, error) { - return accountID == utils.GlobalAccountID, nil + return accountID == awsidentifiers.GlobalAccountID, nil } func TestStagedOpenAccessResolver_StagedSelfHostGrantedWithoutInnerGrant(t *testing.T) { @@ -66,7 +66,7 @@ func TestStagedOpenAccessResolver_ExplicitInnerGrantStillWins(t *testing.T) { func TestStagedOpenAccessResolver_SystemAccountBypassIntact(t *testing.T) { r := NewStagedOpenAccessResolver(systemBypassInner{}) - granted, err := r.Granted(context.Background(), utils.GlobalAccountID, "nonexistent.model-v1:0") + granted, err := r.Granted(context.Background(), awsidentifiers.GlobalAccountID, "nonexistent.model-v1:0") require.NoError(t, err) assert.True(t, granted, "the inner resolver's system-account bypass must still win") } diff --git a/spinifex/gateway/bedrock/access_test.go b/spinifex/gateway/bedrock/access_test.go index f688e146a..bf9534e5c 100644 --- a/spinifex/gateway/bedrock/access_test.go +++ b/spinifex/gateway/bedrock/access_test.go @@ -3,11 +3,11 @@ package gateway_bedrock import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -156,7 +156,7 @@ func TestModelAccessStore_SystemAccountBypassesGrants(t *testing.T) { _, _, js := testutil.StartTestJetStream(t) store := NewModelAccessStore(js) - granted, err := store.Granted(context.Background(), utils.GlobalAccountID, selfHostTestModel) + granted, err := store.Granted(context.Background(), awsidentifiers.GlobalAccountID, selfHostTestModel) require.NoError(t, err) assert.True(t, granted, "the system account must bypass grants") } diff --git a/spinifex/gateway/ec2/instance/RunInstances.go b/spinifex/gateway/ec2/instance/RunInstances.go index 1d11f1db4..9b75a3fa8 100644 --- a/spinifex/gateway/ec2/instance/RunInstances.go +++ b/spinifex/gateway/ec2/instance/RunInstances.go @@ -3,6 +3,7 @@ package gateway_ec2_instance import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "slices" "strings" @@ -307,7 +308,7 @@ func lookupPlacementGroupStrategy(ctx context.Context, natsConn *nats.Conn, acco // isKnownInstanceType checks whether any daemon recognizes the given instance type. func isKnownInstanceType(ctx context.Context, natsConn *nats.Conn, instanceType string) bool { - result, err := utils.NATSRequest[ec2.DescribeInstanceTypesOutput](ctx, natsConn, "ec2.DescribeInstanceTypes", &ec2.DescribeInstanceTypesInput{}, 3*time.Second, utils.GlobalAccountID) + result, err := utils.NATSRequest[ec2.DescribeInstanceTypesOutput](ctx, natsConn, "ec2.DescribeInstanceTypes", &ec2.DescribeInstanceTypesInput{}, 3*time.Second, awsidentifiers.GlobalAccountID) if err != nil || result == nil { return false } diff --git a/spinifex/gateway/ecr_principal_test.go b/spinifex/gateway/ecr_principal_test.go index 052a353f5..facbfacaa 100644 --- a/spinifex/gateway/ecr_principal_test.go +++ b/spinifex/gateway/ecr_principal_test.go @@ -2,6 +2,7 @@ package gateway import ( "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "testing" "time" @@ -12,7 +13,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -177,15 +177,15 @@ func TestResolveECRPrincipal_LongLivedUser(t *testing.T) { func TestResolveECRPrincipal_GlobalRoot(t *testing.T) { iamSvc := newECRMockIAMService() - seedECRTestUser(iamSvc, utils.GlobalAccountID, "root", ecrPrincipalTestAKID) + seedECRTestUser(iamSvc, awsidentifiers.GlobalAccountID, "root", ecrPrincipalTestAKID) gw := &GatewayConfig{IAMService: iamSvc} claims := &ecrauth.Claims{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeUser, AccessKeyID: ecrPrincipalTestAKID, } - claims.Subject = "arn:aws:iam::" + utils.GlobalAccountID + ":root" + claims.Subject = "arn:aws:iam::" + awsidentifiers.GlobalAccountID + ":root" got, err := gw.resolveECRPrincipal(claims) require.NoError(t, err) diff --git a/spinifex/gateway/eks/internal_authz.go b/spinifex/gateway/eks/internal_authz.go index 3a203cdb3..a854949a9 100644 --- a/spinifex/gateway/eks/internal_authz.go +++ b/spinifex/gateway/eks/internal_authz.go @@ -5,12 +5,12 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "slices" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" ) @@ -84,7 +84,7 @@ func AuthorizeInternal(ctx context.Context, natsConn *nats.Conn, action string, // one — the binding to a cluster is AuthorizeInternal's, and needs NATS. func requireCPAgent(ctx context.Context, action string, caller Caller) error { if caller.PrincipalType != principalTypeAssumedRole || - caller.AccountID != utils.GlobalAccountID || + caller.AccountID != awsidentifiers.GlobalAccountID || caller.RoleName != handlers_eks.CPInstanceRoleName || caller.SessionName == "" { slog.WarnContext(ctx, "EKS: internal route rejected for non-CP-agent caller", diff --git a/spinifex/gateway/eks/internal_authz_test.go b/spinifex/gateway/eks/internal_authz_test.go index 35f9cc37a..1d09cf2de 100644 --- a/spinifex/gateway/eks/internal_authz_test.go +++ b/spinifex/gateway/eks/internal_authz_test.go @@ -1,13 +1,13 @@ package gateway_eks_test import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" gateway_eks "github.com/mulgadc/spinifex/spinifex/gateway/eks" handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" "github.com/stretchr/testify/assert" @@ -28,7 +28,7 @@ const ( // cpAgent is the principal an IMDS-credentialed control-plane VM presents. func cpAgent(instanceID string) gateway_eks.Caller { return gateway_eks.Caller{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: handlers_eks.CPInstanceRoleName, SessionName: instanceID, @@ -71,16 +71,16 @@ func TestAuthorizeInternal_RejectsNonCPPrincipals(t *testing.T) { SessionName: cpInstanceID, }}, {"system user, not a role session", gateway_eks.Caller{ - AccountID: utils.GlobalAccountID, PrincipalType: "user", SessionName: "root", + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: "user", SessionName: "root", }}, {"another role in the system account", gateway_eks.Caller{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: "spinifex-rds-instance", SessionName: cpInstanceID, }}, {"no session name", gateway_eks.Caller{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: handlers_eks.CPInstanceRoleName, }}, diff --git a/spinifex/gateway/eks_authz_test.go b/spinifex/gateway/eks_authz_test.go index 8c5b9c6e3..685600621 100644 --- a/spinifex/gateway/eks_authz_test.go +++ b/spinifex/gateway/eks_authz_test.go @@ -5,6 +5,7 @@ package gateway import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "net/http" "net/http/httptest" "net/url" @@ -16,7 +17,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" gateway_eks "github.com/mulgadc/spinifex/spinifex/gateway/eks" handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -180,13 +180,13 @@ func dispatchEKSAsCPAgent(t *testing.T, gw *GatewayConfig, path, instanceID stri roleName := handlers_eks.CPInstanceRoleName req := httptest.NewRequest(http.MethodGet, path, nil) ctx := context.WithValue(req.Context(), ctxService, "eks") - ctx = context.WithValue(ctx, ctxAccountID, utils.GlobalAccountID) + ctx = context.WithValue(ctx, ctxAccountID, awsidentifiers.GlobalAccountID) ctx = context.WithValue(ctx, ctxIdentity, instanceID) ctx = context.WithValue(ctx, ctxPrincipalType, principalTypeAssumedRole) ctx = context.WithValue(ctx, ctxUnderlyingRoleARN, - "arn:aws:iam::"+utils.GlobalAccountID+":role/"+roleName) + "arn:aws:iam::"+awsidentifiers.GlobalAccountID+":role/"+roleName) ctx = context.WithValue(ctx, ctxAssumedRoleARN, - "arn:aws:sts::"+utils.GlobalAccountID+":assumed-role/"+roleName+"/"+instanceID) + "arn:aws:sts::"+awsidentifiers.GlobalAccountID+":assumed-role/"+roleName+"/"+instanceID) return gw.EKS_Request(httptest.NewRecorder(), req.WithContext(ctx)) } diff --git a/spinifex/gateway/gateway.go b/spinifex/gateway/gateway.go index fe3202747..4afdc4816 100644 --- a/spinifex/gateway/gateway.go +++ b/spinifex/gateway/gateway.go @@ -7,6 +7,7 @@ import ( "encoding/xml" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "log/slog" "maps" @@ -777,7 +778,7 @@ func (gw *GatewayConfig) evaluatePrincipalPolicyResources( switch principal.principalType { case principalTypeUser: - if principal.identity == "root" && principal.accountID == utils.GlobalAccountID { + if principal.identity == "root" && principal.accountID == awsidentifiers.GlobalAccountID { // Global root bypass — user branch only. return nil } diff --git a/spinifex/gateway/rds/agent_test.go b/spinifex/gateway/rds/agent_test.go index bcb428164..8806bd0a5 100644 --- a/spinifex/gateway/rds/agent_test.go +++ b/spinifex/gateway/rds/agent_test.go @@ -3,6 +3,7 @@ package gateway_rds import ( "context" "encoding/json" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "testing" "time" @@ -11,7 +12,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" awsxml "github.com/mulgadc/spinifex/spinifex/foundation/aws/xml" handlers_rds "github.com/mulgadc/spinifex/spinifex/handlers/rds" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -26,7 +26,7 @@ const ( // session under the system account whose session name is its own instance ID. func agentCaller() Caller { return Caller{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID, @@ -72,12 +72,12 @@ func TestAuthorizeAgent_RejectsNonAgentCallers(t *testing.T) { name string caller Caller }{ - {"plain IAM user", Caller{AccountID: utils.GlobalAccountID, PrincipalType: "user", RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID}}, - {"root", Caller{AccountID: utils.GlobalAccountID, PrincipalType: "root", RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID}}, + {"plain IAM user", Caller{AccountID: awsidentifiers.GlobalAccountID, PrincipalType: "user", RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID}}, + {"root", Caller{AccountID: awsidentifiers.GlobalAccountID, PrincipalType: "root", RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID}}, {"customer account", Caller{AccountID: testAccountID, PrincipalType: principalTypeAssumedRole, RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID}}, - {"another role", Caller{AccountID: utils.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: "ecsInstanceRole", SessionName: testInstanceID}}, - {"unresolvable role", Caller{AccountID: utils.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: "", SessionName: testInstanceID}}, - {"no session name", Caller{AccountID: utils.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: handlers_rds.InstanceRoleName, SessionName: ""}}, + {"another role", Caller{AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: "ecsInstanceRole", SessionName: testInstanceID}}, + {"unresolvable role", Caller{AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: "", SessionName: testInstanceID}}, + {"no session name", Caller{AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: handlers_rds.InstanceRoleName, SessionName: ""}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/spinifex/gateway/rds/authz.go b/spinifex/gateway/rds/authz.go index 01d407e52..dfb77fa94 100644 --- a/spinifex/gateway/rds/authz.go +++ b/spinifex/gateway/rds/authz.go @@ -3,11 +3,11 @@ package gateway_rds import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_rds "github.com/mulgadc/spinifex/spinifex/handlers/rds" - "github.com/mulgadc/spinifex/spinifex/utils" ) // The resource a policy check evaluates against when the request names nothing @@ -51,7 +51,7 @@ func AuthorizeCaller(ctx context.Context, action string, caller Caller) error { // binding to a specific DB instance is authorizeAgent's, and needs NATS. func requireAgentPrincipal(ctx context.Context, caller Caller) error { if caller.PrincipalType != principalTypeAssumedRole || - caller.AccountID != utils.GlobalAccountID || + caller.AccountID != awsidentifiers.GlobalAccountID || caller.RoleName != handlers_rds.InstanceRoleName || caller.SessionName == "" { slog.DebugContext(ctx, "RDS: internal action rejected for non-agent caller", diff --git a/spinifex/gateway/rds/authz_test.go b/spinifex/gateway/rds/authz_test.go index c54a45379..af485a50b 100644 --- a/spinifex/gateway/rds/authz_test.go +++ b/spinifex/gateway/rds/authz_test.go @@ -1,6 +1,7 @@ package gateway_rds import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "slices" "testing" @@ -8,7 +9,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/aws/policy" handlers_rds "github.com/mulgadc/spinifex/spinifex/handlers/rds" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -46,11 +46,11 @@ func TestAuthorizeCaller_DeniesInternalActionsToCustomers(t *testing.T) { RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID, }}, {"system user rather than a session", Caller{ - AccountID: utils.GlobalAccountID, PrincipalType: "user", + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: "user", RoleName: handlers_rds.InstanceRoleName, SessionName: testInstanceID, }}, {"system session under another role", Caller{ - AccountID: utils.GlobalAccountID, PrincipalType: principalTypeAssumedRole, + AccountID: awsidentifiers.GlobalAccountID, PrincipalType: principalTypeAssumedRole, RoleName: "ecsInstanceRole", SessionName: testInstanceID, }}, } diff --git a/spinifex/gateway/rds/enginecatalog.go b/spinifex/gateway/rds/enginecatalog.go index 0a4fc4fbb..317176848 100644 --- a/spinifex/gateway/rds/enginecatalog.go +++ b/spinifex/gateway/rds/enginecatalog.go @@ -3,6 +3,7 @@ package gateway_rds import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "strconv" "strings" @@ -13,7 +14,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" gateway_ec2_instance "github.com/mulgadc/spinifex/spinifex/gateway/ec2/instance" handlers_rds "github.com/mulgadc/spinifex/spinifex/handlers/rds" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" ) @@ -146,7 +146,7 @@ func DescribeOrderableDBInstanceOptions(ctx context.Context, input *rds.Describe // collapse both onto the same answer. func clusterRunnableTypes(ctx context.Context, nc *nats.Conn, env Env) (func(string) bool, error) { out, err := gateway_ec2_instance.DescribeInstanceTypes(ctx, &ec2.DescribeInstanceTypesInput{}, - nc, env.ExpectedNodes, nil, utils.GlobalAccountID) + nc, env.ExpectedNodes, nil, awsidentifiers.GlobalAccountID) if err != nil { slog.ErrorContext(ctx, "RDS: instance-type capability probe failed", "err", err) return nil, errors.New(awserrors.ErrorServerInternal) diff --git a/spinifex/gateway/rds_test.go b/spinifex/gateway/rds_test.go index b7d88117e..b9f16f074 100644 --- a/spinifex/gateway/rds_test.go +++ b/spinifex/gateway/rds_test.go @@ -3,6 +3,7 @@ package gateway import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "net/http" "net/http/httptest" "strings" @@ -12,7 +13,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" handlers_rds "github.com/mulgadc/spinifex/spinifex/handlers/rds" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -40,13 +40,13 @@ func setupRDSUserRequest(body, accountID string) *http.Request { // whose underlying role is the DB VM instance role. func setupRDSAgentRequest(body, sessionName string) *http.Request { req := setupRDSRequest(body) - roleARN := "arn:aws:iam::" + utils.GlobalAccountID + ":role/" + handlers_rds.InstanceRoleName - ctx := context.WithValue(req.Context(), ctxAccountID, utils.GlobalAccountID) + roleARN := "arn:aws:iam::" + awsidentifiers.GlobalAccountID + ":role/" + handlers_rds.InstanceRoleName + ctx := context.WithValue(req.Context(), ctxAccountID, awsidentifiers.GlobalAccountID) ctx = context.WithValue(ctx, ctxIdentity, sessionName) ctx = context.WithValue(ctx, ctxPrincipalType, principalTypeAssumedRole) ctx = context.WithValue(ctx, ctxUnderlyingRoleARN, roleARN) ctx = context.WithValue(ctx, ctxAssumedRoleARN, - "arn:aws:sts::"+utils.GlobalAccountID+":assumed-role/"+handlers_rds.InstanceRoleName+"/"+sessionName) + "arn:aws:sts::"+awsidentifiers.GlobalAccountID+":assumed-role/"+handlers_rds.InstanceRoleName+"/"+sessionName) return req.WithContext(ctx) } diff --git a/spinifex/gateway/sts.go b/spinifex/gateway/sts.go index e6ddc0695..207b0b355 100644 --- a/spinifex/gateway/sts.go +++ b/spinifex/gateway/sts.go @@ -2,6 +2,7 @@ package gateway import ( "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "net/http" @@ -13,7 +14,6 @@ import ( gateway_sts "github.com/mulgadc/spinifex/spinifex/gateway/sts" handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" "github.com/mulgadc/spinifex/spinifex/ingress/aws/query" - "github.com/mulgadc/spinifex/spinifex/utils" ) // stsCaller bundles the SigV4-derived caller fields that any STS action may @@ -225,7 +225,7 @@ func buildCallerARN(accountID, identity, principalType, assumedRoleARN string) ( case principalTypeRoot: return spxarn.FormatIAMRoot(accountID), nil case principalTypeUser: - if identity == "root" && accountID == utils.GlobalAccountID { + if identity == "root" && accountID == awsidentifiers.GlobalAccountID { return spxarn.FormatIAMRoot(accountID), nil } if identity == "" { diff --git a/spinifex/gateway/sts_policy_test.go b/spinifex/gateway/sts_policy_test.go index 12154aab4..e8a652255 100644 --- a/spinifex/gateway/sts_policy_test.go +++ b/spinifex/gateway/sts_policy_test.go @@ -6,6 +6,7 @@ package gateway import ( "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "net/http" "net/http/httptest" @@ -17,7 +18,6 @@ import ( "github.com/aws/aws-sdk-go/service/sts" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -102,7 +102,7 @@ func stsPolicyRequest(t *testing.T, iamSvc handlers_iam.IAMService, stsSvc *flex stsSvc = deniedSTSService(t) } return stsPolicyRequestAs(t, stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "alice", principalType: principalTypeUser, accessKey: "AKIAEXAMPLE", @@ -168,7 +168,7 @@ func TestSTSRequest_AssumeRole_AssumedRoleCallerIsGated(t *testing.T) { body := "Action=AssumeRole&RoleArn=" + stsTestRoleARN + "&RoleSessionName=s1" params := func(iamSvc handlers_iam.IAMService, stsSvc *flexMockSTSService) stsRequestParams { return stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "s1", principalType: principalTypeAssumedRole, accessKey: "ASIAEXAMPLE", diff --git a/spinifex/gateway/sts_request_test.go b/spinifex/gateway/sts_request_test.go index 3503651ef..818cb30b3 100644 --- a/spinifex/gateway/sts_request_test.go +++ b/spinifex/gateway/sts_request_test.go @@ -3,6 +3,7 @@ package gateway import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "net/http" "net/http/httptest" @@ -15,7 +16,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -141,7 +141,7 @@ func TestSTSRequest_AssumeRole_Success(t *testing.T) { }, } handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "alice", principalType: principalTypeUser, accessKey: "AKIAEXAMPLE", @@ -172,7 +172,7 @@ func TestSTSRequest_GetCallerIdentity_AssumedRole(t *testing.T) { }, } handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "s1", principalType: principalTypeAssumedRole, accessKey: "ASIAEXAMPLE", @@ -197,7 +197,7 @@ func TestSTSRequest_GetCallerIdentity_AssumedRole(t *testing.T) { func TestSTSRequest_GetCallerIdentity_RootShortcircuitsIAMLookup(t *testing.T) { // Root short-circuits IAM lookup — flexMockIAMService.GetUser would cause InternalError otherwise. handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "root", principalType: principalTypeUser, accessKey: "AKIAROOT", @@ -241,7 +241,7 @@ func TestSTSRequest_GetSessionToken_Success(t *testing.T) { }, } handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "alice", principalType: principalTypeUser, accessKey: "AKIAEXAMPLE", @@ -261,7 +261,7 @@ func TestSTSRequest_GetSessionToken_Success(t *testing.T) { assert.Contains(t, xmlStr, "ASIAEXAMPLE123") // Verify forwarded identity fields and that checkPolicy did not block (STS_Request runs no checkPolicy pass). - assert.Equal(t, utils.GlobalAccountID, got.accountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, got.accountID) assert.Equal(t, "alice", got.userName) assert.Equal(t, principalTypeUser, got.principalType) assert.Equal(t, "AKIAEXAMPLE", got.accessKeyID) @@ -270,7 +270,7 @@ func TestSTSRequest_GetSessionToken_Success(t *testing.T) { func TestSTSRequest_UnknownAction(t *testing.T) { handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "root", principalType: principalTypeUser, accessKey: "AKIAROOT", @@ -288,7 +288,7 @@ func TestSTSRequest_UnknownAction(t *testing.T) { func TestSTSRequest_MissingAction(t *testing.T) { handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "root", principalType: principalTypeUser, accessKey: "AKIAROOT", @@ -312,7 +312,7 @@ func TestSTSRequest_AssumeRole_MissingRoleArn(t *testing.T) { }, } handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "alice", principalType: principalTypeUser, accessKey: "AKIAEXAMPLE", @@ -336,7 +336,7 @@ func TestSTSRequest_NilService_InternalError(t *testing.T) { gw := &GatewayConfig{DisableLogging: true, STSService: nil, IAMService: &flexMockIAMService{}} handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), ctxService, "sts") - ctx = context.WithValue(ctx, ctxAccountID, utils.GlobalAccountID) + ctx = context.WithValue(ctx, ctxAccountID, awsidentifiers.GlobalAccountID) ctx = context.WithValue(ctx, ctxIdentity, "alice") ctx = context.WithValue(ctx, ctxPrincipalType, principalTypeUser) ctx = context.WithValue(ctx, ctxAccessKey, "AKIAEXAMPLE") @@ -368,7 +368,7 @@ func TestSTSRequest_AssumeRole_NilIAMService_InternalError(t *testing.T) { gw := &GatewayConfig{DisableLogging: true, STSService: svc, IAMService: nil} handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := context.WithValue(r.Context(), ctxService, "sts") - ctx = context.WithValue(ctx, ctxAccountID, utils.GlobalAccountID) + ctx = context.WithValue(ctx, ctxAccountID, awsidentifiers.GlobalAccountID) ctx = context.WithValue(ctx, ctxIdentity, "alice") ctx = context.WithValue(ctx, ctxPrincipalType, principalTypeUser) ctx = context.WithValue(ctx, ctxAccessKey, "AKIAEXAMPLE") @@ -396,7 +396,7 @@ func TestSTSRequest_AssumeRole_ServiceError_PropagatesAccessDenied(t *testing.T) }, } handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "alice", principalType: principalTypeUser, accessKey: "AKIAEXAMPLE", @@ -423,7 +423,7 @@ func TestSTSRequest_GetCallerIdentity_User_LookupIAM(t *testing.T) { }, } handler := setupSTSRequestHandler(stsRequestParams{ - accountID: utils.GlobalAccountID, + accountID: awsidentifiers.GlobalAccountID, identity: "alice", principalType: principalTypeUser, accessKey: "AKIAEXAMPLE", diff --git a/spinifex/handlers/bedrock/ami.go b/spinifex/handlers/bedrock/ami.go index 812b57403..5aa4508a8 100644 --- a/spinifex/handlers/bedrock/ami.go +++ b/spinifex/handlers/bedrock/ami.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" awsami "github.com/mulgadc/spinifex/spinifex/foundation/aws/ami" "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" - "github.com/mulgadc/spinifex/spinifex/utils" ) // servingAMIRoleTag identifies the single vllm-serving runtime AMI, the same @@ -36,7 +36,7 @@ func resolveServingAMI(ctx context.Context, imgSvc amiResolver) (string, error) {Name: aws.String("tag:" + tags.ManagedByKey), Values: aws.StringSlice([]string{tags.ManagedByBedrock})}, {Name: aws.String("tag:" + servingAMIRoleTag), Values: aws.StringSlice([]string{"vllm-serving"})}, }, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", fmt.Errorf("bedrock: describe vllm-serving AMI: %w", err) } diff --git a/spinifex/handlers/bedrock/eviction_test.go b/spinifex/handlers/bedrock/eviction_test.go index 5f68057fa..4be309589 100644 --- a/spinifex/handlers/bedrock/eviction_test.go +++ b/spinifex/handlers/bedrock/eviction_test.go @@ -1,13 +1,13 @@ package handlers_bedrock import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "net/http" "testing" "time" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -213,7 +213,7 @@ func TestSelectEvictable_SkipsRecordCreatedPinnedViaEnsure(t *testing.T) { func seedReadyEndpoint(t *testing.T, s *Service, modelID string, readyAt time.Time) EndpointRecord { t.Helper() rec := EndpointRecord{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, ModelID: modelID, State: StateReady, NodeID: s.deps.NodeID, @@ -222,14 +222,14 @@ func seedReadyEndpoint(t *testing.T, s *Service, modelID string, readyAt time.Ti ReadyAt: readyAt, Generation: 2, } - _, err := s.store.Create(t.Context(), EndpointKey(utils.GlobalAccountID, modelID), &rec) + _, err := s.store.Create(t.Context(), EndpointKey(awsidentifiers.GlobalAccountID, modelID), &rec) require.NoError(t, err) return rec } func pinEndpoint(t *testing.T, s *Service, modelID string) { t.Helper() - key := EndpointKey(utils.GlobalAccountID, modelID) + key := EndpointKey(awsidentifiers.GlobalAccountID, modelID) rec, rev, found, err := s.store.getRevision(t.Context(), key) require.NoError(t, err) require.True(t, found) diff --git a/spinifex/handlers/bedrock/hostport.go b/spinifex/handlers/bedrock/hostport.go index c59875c2c..7c03429ca 100644 --- a/spinifex/handlers/bedrock/hostport.go +++ b/spinifex/handlers/bedrock/hostport.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "net/netip" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" - "github.com/mulgadc/spinifex/spinifex/utils" ) // The system-VPC subnet lives inside OVN on br-int, so the daemon host has no @@ -86,7 +86,7 @@ func ensureDaemonENI(ctx context.Context, vpcSvc launchVPCProvisioner, subnetID, {Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{subnetID})}, {Name: aws.String("description"), Values: aws.StringSlice([]string{desc})}, }, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return nil, fmt.Errorf("bedrock: describe daemon ENI in subnet %s: %w", subnetID, err) } @@ -118,7 +118,7 @@ func ensureDaemonENI(ctx context.Context, vpcSvc launchVPCProvisioner, subnetID, {Key: aws.String(bedrockDaemonNodeTagKey), Value: aws.String(nodeID)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return nil, fmt.Errorf("bedrock: create daemon ENI in subnet %s: %w", subnetID, err) } diff --git a/spinifex/handlers/bedrock/launch.go b/spinifex/handlers/bedrock/launch.go index ca7c6d24f..b581608be 100644 --- a/spinifex/handlers/bedrock/launch.go +++ b/spinifex/handlers/bedrock/launch.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "net" "slices" @@ -18,7 +19,6 @@ import ( gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock" "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" handlers_systemvpc "github.com/mulgadc/spinifex/spinifex/handlers/systemvpc" - "github.com/mulgadc/spinifex/spinifex/utils" ) // vllmServePort is the vLLM OpenAI-compatible server's listen port, baked @@ -211,7 +211,7 @@ func LaunchServingVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (out snapshotIDs[m.ModelID] = snapshotID } - sysRefs, err := EnsureSystemVPC(ctx, deps.SystemVPC, &deps.Config.Bedrock, utils.GlobalAccountID, region) + sysRefs, err := EnsureSystemVPC(ctx, deps.SystemVPC, &deps.Config.Bedrock, awsidentifiers.GlobalAccountID, region) if err != nil { return nil, err } @@ -272,7 +272,7 @@ func LaunchServingVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (out } volumeIDs[m.ModelID] = weightsVolumeID rollback = append(rollback, func(ctx context.Context) { - if _, delErr := deps.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{VolumeId: aws.String(weightsVolumeID)}, utils.GlobalAccountID); delErr != nil { + if _, delErr := deps.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{VolumeId: aws.String(weightsVolumeID)}, awsidentifiers.GlobalAccountID); delErr != nil { slog.WarnContext(ctx, "bedrock: rollback delete of orphaned weights volume failed", "model", m.ModelID, "volumeId", weightsVolumeID, "err", delErr) } @@ -300,7 +300,7 @@ func LaunchServingVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (out // The VM and its primary NIC live in the system account: a serving VM // has no customer owner of its own, only the accountID the endpoint // record is keyed under (a separate, KV-level concern from VM ownership). - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, SubnetID: systemSubnetID, ENIID: eni.id, ENIMac: eni.mac, @@ -326,7 +326,7 @@ func LaunchServingVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (out primaryModelID := in.Members[0].ModelID for _, m := range in.Members { weightsVolumeID := volumeIDs[m.ModelID] - attachedDevice, err := deps.Attacher.AttachVolume(ctx, utils.GlobalAccountID, instanceID, weightsVolumeID, devices[m.ModelID]) + attachedDevice, err := deps.Attacher.AttachVolume(ctx, awsidentifiers.GlobalAccountID, instanceID, weightsVolumeID, devices[m.ModelID]) if err != nil { return nil, fmt.Errorf("bedrock: attach weights volume %s to %s: %w", weightsVolumeID, instanceID, err) } @@ -373,7 +373,7 @@ func TerminateServingVM(ctx context.Context, deps LaunchDeps, rec EndpointRecord if member.WeightsVolumeID == "" { continue } - if _, err := deps.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{VolumeId: aws.String(member.WeightsVolumeID)}, utils.GlobalAccountID); err != nil && + if _, err := deps.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{VolumeId: aws.String(member.WeightsVolumeID)}, awsidentifiers.GlobalAccountID); err != nil && !awserrors.IsNotFound(err) { errs = append(errs, fmt.Errorf("delete weights volume %s for %s: %w", member.WeightsVolumeID, modelID, err)) } @@ -417,7 +417,7 @@ func createLaunchENI(ctx context.Context, vpcSvc launchVPCProvisioner, subnetID, {Key: aws.String(bedrockInstanceTagKey), Value: aws.String(modelID)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return nil, fmt.Errorf("bedrock: create ENI in subnet %s: %w", subnetID, err) } @@ -437,12 +437,12 @@ func createLaunchENI(ctx context.Context, vpcSvc launchVPCProvisioner, subnetID, // Detach comes first because a terminated VM can leave the attachment record // behind, which a plain delete rejects as InUse. func deleteLaunchENI(ctx context.Context, vpcSvc launchVPCProvisioner, eniID string) { - if err := vpcSvc.DetachENI(ctx, utils.GlobalAccountID, eniID); err != nil && !awserrors.IsNotFound(err) { + if err := vpcSvc.DetachENI(ctx, awsidentifiers.GlobalAccountID, eniID); err != nil && !awserrors.IsNotFound(err) { slog.DebugContext(ctx, "bedrock: rollback ENI detach failed", "eniId", eniID, "err", err) } if _, err := vpcSvc.DeleteNetworkInterface(ctx, &ec2.DeleteNetworkInterfaceInput{ NetworkInterfaceId: aws.String(eniID), - }, utils.GlobalAccountID); err != nil && !awserrors.IsNotFound(err) { + }, awsidentifiers.GlobalAccountID); err != nil && !awserrors.IsNotFound(err) { slog.WarnContext(ctx, "bedrock: rollback delete of orphaned ENI failed", "eniId", eniID, "err", err) } } @@ -462,7 +462,7 @@ func createWeightsVolume(ctx context.Context, volSvc launchVolumeProvisioner, az {Key: aws.String(bedrockInstanceTagKey), Value: aws.String(modelID)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", fmt.Errorf("bedrock: clone weights volume for %s from snapshot %s: %w", modelID, snapshotID, err) } diff --git a/spinifex/handlers/bedrock/reaper.go b/spinifex/handlers/bedrock/reaper.go index b7947f6d4..167566128 100644 --- a/spinifex/handlers/bedrock/reaper.go +++ b/spinifex/handlers/bedrock/reaper.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "time" @@ -11,7 +12,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" "github.com/mulgadc/spinifex/spinifex/foundation/telemetry" gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock" - "github.com/mulgadc/spinifex/spinifex/utils" ) // Sweep and lease timing. The holder refreshes well inside the bucket's TTL, @@ -127,7 +127,7 @@ func (r *Reaper) IsLeader() bool { // sweepOnce scrapes every READY endpoint once and acts on what it saw. One // endpoint's failure does not stop the pass: the others still need deciding. func (r *Reaper) sweepOnce(ctx context.Context) error { - recs, err := r.svc.store.list(ctx, utils.GlobalAccountID) + recs, err := r.svc.store.list(ctx, awsidentifiers.GlobalAccountID) if err != nil { return err } @@ -191,7 +191,7 @@ func (r *Reaper) sweepEndpoint(ctx context.Context, rec EndpointRecord) error { slog.InfoContext(ctx, "bedrock reaper: reclaiming an idle endpoint", "model", rec.ModelID, "instanceId", rec.InstanceID, "idle_ms", otelsetup.Millis(now.Sub(updated.LastActive()))) - if _, err := r.svc.Delete(ctx, &DeleteEndpointInput{ModelID: rec.ModelID}, utils.GlobalAccountID); err != nil { + if _, err := r.svc.Delete(ctx, &DeleteEndpointInput{ModelID: rec.ModelID}, awsidentifiers.GlobalAccountID); err != nil { return fmt.Errorf("reap idle endpoint: %w", err) } return nil @@ -254,13 +254,13 @@ func (r *Reaper) recordScrapeFailure(ctx context.Context, rec EndpointRecord, ca slog.ErrorContext(ctx, "bedrock reaper: endpoint unreachable; terminating and relaunching", "model", rec.ModelID, "instanceId", rec.InstanceID, "consecutiveFailures", updated.ScrapeFailures, "err", cause) - if _, err := r.svc.Delete(ctx, &DeleteEndpointInput{ModelID: rec.ModelID}, utils.GlobalAccountID); err != nil { + if _, err := r.svc.Delete(ctx, &DeleteEndpointInput{ModelID: rec.ModelID}, awsidentifiers.GlobalAccountID); err != nil { return fmt.Errorf("terminate unreachable endpoint: %w", err) } // A relaunch that cannot be admitted (the device did not come back, or // another model took it) is not a sweep failure: the endpoint is gone, which // was the point, and the next invoke will ask for it again. - if _, err := r.svc.Ensure(ctx, &EnsureEndpointInput{ModelID: rec.ModelID}, utils.GlobalAccountID); err != nil { + if _, err := r.svc.Ensure(ctx, &EnsureEndpointInput{ModelID: rec.ModelID}, awsidentifiers.GlobalAccountID); err != nil { slog.WarnContext(ctx, "bedrock reaper: relaunch of an unreachable endpoint was refused", "model", rec.ModelID, "err", err) } @@ -275,7 +275,7 @@ func (r *Reaper) persistObservation(ctx context.Context, prev, next EndpointReco prev.SuccessTotal == next.SuccessTotal && prev.ScrapeFailures == next.ScrapeFailures { return nil } - key := resolveKey(utils.GlobalAccountID, next.ModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, next.ModelID) current, rev, found, err := r.svc.store.getRevision(ctx, key) if err != nil { return err diff --git a/spinifex/handlers/bedrock/reaper_test.go b/spinifex/handlers/bedrock/reaper_test.go index f3d70a072..5555f7270 100644 --- a/spinifex/handlers/bedrock/reaper_test.go +++ b/spinifex/handlers/bedrock/reaper_test.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "net/http" "strings" @@ -14,7 +15,6 @@ import ( "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/bootstrap/config" gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -120,7 +120,7 @@ func (f *reaperFixture) ready(t *testing.T) EndpointRecord { // endpoint that has been up and quiet for a while. func (f *reaperFixture) age(t *testing.T, readyAgo, activeAgo time.Duration) EndpointRecord { t.Helper() - key := resolveKey(utils.GlobalAccountID, testModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, testModelID) rec, rev, found, err := f.store.getRevision(t.Context(), key) require.NoError(t, err) require.True(t, found) @@ -137,7 +137,7 @@ func (f *reaperFixture) age(t *testing.T, readyAgo, activeAgo time.Duration) End func (f *reaperFixture) current(t *testing.T) (EndpointRecord, bool) { t.Helper() - rec, _, found, err := f.store.getRevision(t.Context(), resolveKey(utils.GlobalAccountID, testModelID)) + rec, _, found, err := f.store.getRevision(t.Context(), resolveKey(awsidentifiers.GlobalAccountID, testModelID)) require.NoError(t, err) return rec, found } @@ -175,7 +175,7 @@ func TestReaper_NeverReapsPinnedEndpoint(t *testing.T) { f.ready(t) f.age(t, time.Hour, time.Hour) - key := resolveKey(utils.GlobalAccountID, testModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, testModelID) rec, rev, _, err := f.store.getRevision(t.Context(), key) require.NoError(t, err) rec.Pinned = true @@ -289,7 +289,7 @@ func TestReaper_PinnedEndpointNotReapedOnScrapeFailure(t *testing.T) { f := newReaperFixture(t, ReaperDeps{IdleTTL: time.Hour}) f.ready(t) - key := resolveKey(utils.GlobalAccountID, testModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, testModelID) rec, rev, found, err := f.store.getRevision(t.Context(), key) require.NoError(t, err) require.True(t, found) @@ -316,9 +316,9 @@ func TestReaper_ServiceOnlyBundleLivenessProbedNotReaped(t *testing.T) { f := newReaperFixture(t, ReaperDeps{IdleTTL: time.Nanosecond}) const embedModelID = "embed-only-bundle" - key := resolveKey(utils.GlobalAccountID, embedModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, embedModelID) rec := EndpointRecord{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, ModelID: embedModelID, State: StateReady, BaseURL: "http://10.0.0.1:8001", @@ -378,9 +378,9 @@ func TestReaper_UnchangedObservationWritesNothing(t *testing.T) { // scrapes neither, so a launch in progress cannot be torn down under it. func TestReaper_SkipsNonReadyRecords(t *testing.T) { f := newReaperFixture(t, ReaperDeps{IdleTTL: time.Nanosecond}) - key := resolveKey(utils.GlobalAccountID, testModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, testModelID) rec := EndpointRecord{ - AccountID: utils.GlobalAccountID, ModelID: testModelID, State: StateStarting, Generation: 1, + AccountID: awsidentifiers.GlobalAccountID, ModelID: testModelID, State: StateStarting, Generation: 1, } _, err := f.store.Create(t.Context(), key, &rec) require.NoError(t, err) diff --git a/spinifex/handlers/bedrock/resolver.go b/spinifex/handlers/bedrock/resolver.go index fe7878da9..653b3dc5b 100644 --- a/spinifex/handlers/bedrock/resolver.go +++ b/spinifex/handlers/bedrock/resolver.go @@ -2,12 +2,12 @@ package handlers_bedrock import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "net/http" "sync" "time" gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock" - "github.com/mulgadc/spinifex/spinifex/utils" "golang.org/x/sync/singleflight" ) @@ -159,7 +159,7 @@ func (r *DynamicEndpointResolver) Endpoint(ctx context.Context, modelID string) // empty base URL means "not resolved", which is the only outcome a cold model // can have: the launch outlives this request by design. func (r *DynamicEndpointResolver) resolve(ctx context.Context, modelID string) (string, error) { - baseURL, err := r.describeAndEnsure(ctx, utils.GlobalAccountID, modelID, false) + baseURL, err := r.describeAndEnsure(ctx, awsidentifiers.GlobalAccountID, modelID, false) if err != nil || baseURL == "" { return baseURL, err } diff --git a/spinifex/handlers/bedrock/service.go b/spinifex/handlers/bedrock/service.go index fb99fd1f2..84f7f9d32 100644 --- a/spinifex/handlers/bedrock/service.go +++ b/spinifex/handlers/bedrock/service.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "net" "net/http" @@ -15,7 +16,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" gateway_bedrock "github.com/mulgadc/spinifex/spinifex/gateway/bedrock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" ) @@ -141,12 +141,12 @@ func (s *Service) httpClient() *http.Client { return &http.Client{} } -// resolveAccountID returns accountID, or utils.GlobalAccountID for the zero +// resolveAccountID returns accountID, or awsidentifiers.GlobalAccountID for the zero // value, so every existing Ensure/Describe/Delete caller that leaves the // input's AccountID unset keeps resolving to the shared platform endpoint. func resolveAccountID(accountID string) string { if accountID == "" { - return utils.GlobalAccountID + return awsidentifiers.GlobalAccountID } return accountID } @@ -262,7 +262,7 @@ func (s *Service) Ensure(ctx context.Context, in *EnsureEndpointInput, _ string) // Delete takes the per-key mutex for the victim while Ensure holds it for // wantModelID. The two can never be the same key, so the locks are disjoint. func (s *Service) evictForCapacity(ctx context.Context, wantModelID string, minVRAMMiB int) bool { - recs, err := s.store.list(ctx, utils.GlobalAccountID) + recs, err := s.store.list(ctx, awsidentifiers.GlobalAccountID) if err != nil { slog.ErrorContext(ctx, "bedrock: list endpoints for eviction failed", "model", wantModelID, "err", err) return false @@ -284,7 +284,7 @@ func (s *Service) evictForCapacity(ctx context.Context, wantModelID string, minV slog.InfoContext(ctx, "bedrock: evicting an idle endpoint to make room", "evicting", victim.ModelID, "instanceId", victim.InstanceID, "for", wantModelID, "idleSince", victim.LastActive()) - if _, err := s.Delete(ctx, &DeleteEndpointInput{ModelID: victim.ModelID}, utils.GlobalAccountID); err != nil { + if _, err := s.Delete(ctx, &DeleteEndpointInput{ModelID: victim.ModelID}, awsidentifiers.GlobalAccountID); err != nil { slog.ErrorContext(ctx, "bedrock: eviction failed; the pending launch keeps its capacity refusal", "evicting", victim.ModelID, "for", wantModelID, "err", err) return false diff --git a/spinifex/handlers/bedrock/service_test.go b/spinifex/handlers/bedrock/service_test.go index 56d142cc7..fba456cc7 100644 --- a/spinifex/handlers/bedrock/service_test.go +++ b/spinifex/handlers/bedrock/service_test.go @@ -1,6 +1,7 @@ package handlers_bedrock import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "net/http" "sync" "testing" @@ -11,7 +12,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" "github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -209,9 +209,9 @@ func TestDelete_RefusesNonReadyState(t *testing.T) { h := newLaunchHarness() s, _ := newTestService(t, h, http.StatusOK, sufficientGPU()) - key := resolveKey(utils.GlobalAccountID, testModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, testModelID) rec := EndpointRecord{ - AccountID: utils.GlobalAccountID, ModelID: testModelID, State: StateStarting, Generation: 1, + AccountID: awsidentifiers.GlobalAccountID, ModelID: testModelID, State: StateStarting, Generation: 1, } _, err := s.store.Create(t.Context(), key, &rec) require.NoError(t, err) @@ -229,9 +229,9 @@ func TestDelete_ResumesFromDraining(t *testing.T) { h := newLaunchHarness() s, _ := newTestService(t, h, http.StatusOK, sufficientGPU()) - key := resolveKey(utils.GlobalAccountID, testModelID) + key := resolveKey(awsidentifiers.GlobalAccountID, testModelID) rec := EndpointRecord{ - AccountID: utils.GlobalAccountID, ModelID: testModelID, State: StateDraining, + AccountID: awsidentifiers.GlobalAccountID, ModelID: testModelID, State: StateDraining, InstanceID: "i-stranded", Generation: 2, } _, err := s.store.Create(t.Context(), key, &rec) @@ -246,7 +246,7 @@ func TestDelete_ResumesFromDraining(t *testing.T) { assert.Contains(t, h.launcher.terminated, "i-stranded", "the resume must still tear the VM down") } -// testAccountID is a non-Global account, distinct from utils.GlobalAccountID, +// testAccountID is a non-Global account, distinct from awsidentifiers.GlobalAccountID, // standing in for a real tenant across the account-scoping tests below. const testAccountID = "111111111111" @@ -260,11 +260,11 @@ func TestEnsure_EmptyAccountIDKeysGlobalAndUnpinned(t *testing.T) { out, err := s.Ensure(t.Context(), &EnsureEndpointInput{ModelID: testModelID}, "") require.NoError(t, err) - assert.Equal(t, utils.GlobalAccountID, out.Endpoint.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, out.Endpoint.AccountID) assert.False(t, out.Endpoint.Pinned) s.WaitLaunches() - rec, found, err := s.store.get(t.Context(), resolveKey(utils.GlobalAccountID, testModelID)) + rec, found, err := s.store.get(t.Context(), resolveKey(awsidentifiers.GlobalAccountID, testModelID)) require.NoError(t, err) require.True(t, found, "an empty AccountID must key the record under GlobalAccountID") assert.False(t, rec.Pinned) @@ -291,7 +291,7 @@ func TestEnsure_RealAccountIDKeysUnderAccountAndPersistsPinned(t *testing.T) { assert.True(t, rec.Pinned) // Nothing must have landed under the shared Global key. - _, foundGlobal, err := s.store.get(t.Context(), resolveKey(utils.GlobalAccountID, testModelID)) + _, foundGlobal, err := s.store.get(t.Context(), resolveKey(awsidentifiers.GlobalAccountID, testModelID)) require.NoError(t, err) assert.False(t, foundGlobal) } @@ -405,7 +405,7 @@ func TestList_ReturnsEndpointsAcrossAllAccountsIncludingPinned(t *testing.T) { } global, ok := byModel[testModelID] require.True(t, ok, "the shared GlobalAccountID endpoint must still list unchanged") - assert.Equal(t, utils.GlobalAccountID, global.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, global.AccountID) assert.False(t, global.Pinned) pinned, ok := byModel[pinnedModelID] diff --git a/spinifex/handlers/bedrock/types.go b/spinifex/handlers/bedrock/types.go index 312d3588b..b39d0a61d 100644 --- a/spinifex/handlers/bedrock/types.go +++ b/spinifex/handlers/bedrock/types.go @@ -15,7 +15,7 @@ const ( // current record without launching a second VM. // // AccountID scopes the endpoint to a tenant; empty resolves to -// utils.GlobalAccountID, the shared platform endpoint every caller used +// awsidentifiers.GlobalAccountID, the shared platform endpoint every caller used // before per-account endpoints existed. Pinned marks a created endpoint // exempt from idle reaping and eviction. type EnsureEndpointInput struct { @@ -29,7 +29,7 @@ type EnsureEndpointOutput struct { } // DescribeEndpointInput looks up modelID's current endpoint record. -// AccountID scopes the lookup; empty resolves to utils.GlobalAccountID. +// AccountID scopes the lookup; empty resolves to awsidentifiers.GlobalAccountID. type DescribeEndpointInput struct { ModelID string `json:"model_id"` AccountID string `json:"account_id,omitempty"` @@ -49,7 +49,7 @@ type ListEndpointsOutput struct { // DeleteEndpointInput requests a READY endpoint move to DRAINING and its VM // be torn down. Idempotent: an already-ABSENT endpoint returns success. -// AccountID scopes the target; empty resolves to utils.GlobalAccountID. +// AccountID scopes the target; empty resolves to awsidentifiers.GlobalAccountID. type DeleteEndpointInput struct { ModelID string `json:"model_id"` AccountID string `json:"account_id,omitempty"` diff --git a/spinifex/handlers/ec2/account/service_impl.go b/spinifex/handlers/ec2/account/service_impl.go index 9d7c314e8..d4f0b493f 100644 --- a/spinifex/handlers/ec2/account/service_impl.go +++ b/spinifex/handlers/ec2/account/service_impl.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/aws/aws-sdk-go/aws" @@ -12,7 +13,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/bootstrap/config" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvutil" "github.com/mulgadc/spinifex/spinifex/foundation/state/migrate" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" ) @@ -67,7 +67,7 @@ func NewAccountSettingsServiceImplWithNATS(ctx context.Context, cfg *config.Conf // Falls back to GlobalAccountID for pre-Phase-4 resources with no accountID. func settingsKey(accountID string) string { if accountID == "" { - return utils.GlobalAccountID + return awsidentifiers.GlobalAccountID } return accountID } diff --git a/spinifex/handlers/ec2/account/service_impl_test.go b/spinifex/handlers/ec2/account/service_impl_test.go index 736394d3e..3cb307429 100644 --- a/spinifex/handlers/ec2/account/service_impl_test.go +++ b/spinifex/handlers/ec2/account/service_impl_test.go @@ -2,11 +2,11 @@ package handlers_ec2_account import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/internal/testkit" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -140,6 +140,6 @@ func TestSerialConsole_MultiAccountIsolation(t *testing.T) { } func TestSettingsKey_EmptyAccountIDFallback(t *testing.T) { - assert.Equal(t, utils.GlobalAccountID, settingsKey("")) + assert.Equal(t, awsidentifiers.GlobalAccountID, settingsKey("")) assert.Equal(t, "123456789012", settingsKey("123456789012")) } diff --git a/spinifex/handlers/ec2/image/ami_source_volume_test.go b/spinifex/handlers/ec2/image/ami_source_volume_test.go index fd741f164..4d672135c 100644 --- a/spinifex/handlers/ec2/image/ami_source_volume_test.go +++ b/spinifex/handlers/ec2/image/ami_source_volume_test.go @@ -2,13 +2,13 @@ package handlers_ec2_image import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "strings" "testing" "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -93,7 +93,7 @@ func TestGetAMISourceVolumeID_ImportedSystemAMI(t *testing.T) { // registerImportedAMISnapshot writes. Deriving it here would let the // fixture follow the read path and pin nothing. require.NoError(t, svc.MetadataStore().PutSnapshot(context.Background(), ebsmetadata.Snapshot{ - SnapshotID: "snap-sysimp01", VolumeID: "vol-imported", OwnerID: utils.GlobalAccountID, + SnapshotID: "snap-sysimp01", VolumeID: "vol-imported", OwnerID: awsidentifiers.GlobalAccountID, })) got, err := svc.GetAMISourceVolumeID(context.Background(), "ami-sysimp01") diff --git a/spinifex/handlers/ec2/image/describe_byid_test.go b/spinifex/handlers/ec2/image/describe_byid_test.go index 11ad34b56..81baf74e6 100644 --- a/spinifex/handlers/ec2/image/describe_byid_test.go +++ b/spinifex/handlers/ec2/image/describe_byid_test.go @@ -3,6 +3,7 @@ package handlers_ec2_image_test import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strconv" "strings" "testing" @@ -16,7 +17,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_ec2_image "github.com/mulgadc/spinifex/spinifex/handlers/ec2/image" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -156,7 +156,7 @@ func TestDescribeImagesByID_SystemAMIIsVisibleToEveryAccount(t *testing.T) { require.NoError(t, err) require.Len(t, out.Images, 1) - assert.Equal(t, utils.GlobalAccountID, aws.StringValue(out.Images[0].OwnerId)) + assert.Equal(t, awsidentifiers.GlobalAccountID, aws.StringValue(out.Images[0].OwnerId)) assert.Equal(t, "amazon", aws.StringValue(out.Images[0].ImageOwnerAlias)) } diff --git a/spinifex/handlers/ec2/image/service_impl.go b/spinifex/handlers/ec2/image/service_impl.go index 24917ba5e..9287ed9fe 100644 --- a/spinifex/handlers/ec2/image/service_impl.go +++ b/spinifex/handlers/ec2/image/service_impl.go @@ -248,14 +248,14 @@ func (s *ImageServiceImpl) describeImagesByIDs(ctx context.Context, input *ec2.D func renderVisibleImage(amiMeta ebsmetadata.AMI, input *ec2.DescribeImagesInput, accountID string, parsedFilters map[string][]string, encryptedAtRest bool) *ec2.Image { amiOwner := amiMeta.ImageOwnerAlias - isSystemAMI := amiOwner != "" && !utils.IsAccountID(amiOwner) + isSystemAMI := amiOwner != "" && !awsidentifiers.IsAccountID(amiOwner) // Resolved up front so the owner filter below compares against the same // value the OwnerId field reports, rather than the raw alias string a // system AMI carries instead of a numeric account ID. ownerID := amiOwner if isSystemAMI { - ownerID = utils.GlobalAccountID + ownerID = awsidentifiers.GlobalAccountID } if !callerCanReadAMI(amiMeta, accountID) { @@ -619,10 +619,10 @@ func (s *ImageServiceImpl) GetAMIConfig(ctx context.Context, imageID string) (eb // drift from this predicate. An empty alias is a corrupt document rather than a // system image, so it derives nothing and the fallback cannot answer for it. func snapshotAccountForAMI(meta ebsmetadata.AMI) (accountID string, derived bool) { - if utils.IsAccountID(meta.ImageOwnerAlias) { + if awsidentifiers.IsAccountID(meta.ImageOwnerAlias) { return meta.ImageOwnerAlias, false } - return utils.GlobalAccountID, meta.ImageOwnerAlias != "" + return awsidentifiers.GlobalAccountID, meta.ImageOwnerAlias != "" } // GetAMISourceVolumeID returns the volume whose blocks imageID's snapshot @@ -745,7 +745,7 @@ func (s *ImageServiceImpl) checkAMIOwnership(meta ebsmetadata.AMI, accountID str slog.Error("checkAMIOwnership: AMI config has empty ImageOwnerAlias", "imageId", meta.ImageID) return errors.New(awserrors.ErrorServerInternal) } - if !utils.IsAccountID(owner) || owner != accountID { + if !awsidentifiers.IsAccountID(owner) || owner != accountID { return errors.New(awserrors.ErrorUnauthorizedOperation) } return nil @@ -758,7 +758,7 @@ func callerCanReadAMI(meta ebsmetadata.AMI, accountID string) bool { if owner == "" { return false } - if !utils.IsAccountID(owner) { + if !awsidentifiers.IsAccountID(owner) { return true } return owner == accountID diff --git a/spinifex/handlers/ec2/image/service_impl_test.go b/spinifex/handlers/ec2/image/service_impl_test.go index 05716f766..6222a67db 100644 --- a/spinifex/handlers/ec2/image/service_impl_test.go +++ b/spinifex/handlers/ec2/image/service_impl_test.go @@ -2,6 +2,7 @@ package handlers_ec2_image import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "io" "log/slog" "net/http" @@ -18,7 +19,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/domains/ec2/ebs/metadata" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -1095,7 +1095,7 @@ func TestDescribeImages_OwnerSelf_GlobalAccountIncludesSystemAMI(t *testing.T) { out, err := svc.DescribeImages(context.Background(), &ec2.DescribeImagesInput{ Owners: []*string{aws.String("self")}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) require.NoError(t, err) gotIDs := make([]string, len(out.Images)) @@ -1379,15 +1379,15 @@ func TestRegisterImage_EmptyCallerAccountIsRefusedAtTheBoundary(t *testing.T) { func TestRegisterImage_SystemAccountSnapshot(t *testing.T) { svc, store := setupTestImageService(t) - putTestSnapshotConfig(t, store, "snap-sys01", 8, utils.GlobalAccountID) + putTestSnapshotConfig(t, store, "snap-sys01", 8, awsidentifiers.GlobalAccountID) - out, err := svc.RegisterImage(context.Background(), validRegisterImageServiceInput("snap-sys01"), utils.GlobalAccountID) + out, err := svc.RegisterImage(context.Background(), validRegisterImageServiceInput("snap-sys01"), awsidentifiers.GlobalAccountID) require.NoError(t, err) require.NotNil(t, out.ImageId) meta, err := svc.GetAMIConfig(context.Background(), *out.ImageId) require.NoError(t, err) - assert.Equal(t, utils.GlobalAccountID, meta.ImageOwnerAlias) + assert.Equal(t, awsidentifiers.GlobalAccountID, meta.ImageOwnerAlias) } func TestRegisterImage_ArchitectureAndVirtualizationDefaults(t *testing.T) { diff --git a/spinifex/handlers/ec2/instance/metadata_options_test.go b/spinifex/handlers/ec2/instance/metadata_options_test.go index adde5ed23..caab6921c 100644 --- a/spinifex/handlers/ec2/instance/metadata_options_test.go +++ b/spinifex/handlers/ec2/instance/metadata_options_test.go @@ -2,6 +2,7 @@ package handlers_ec2_instance import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/aws" @@ -12,7 +13,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" vmmock "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm/mock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -54,7 +54,7 @@ func TestValidateMetadataOptions(t *testing.T) { // Enabling IMDSv1 on an existing instance is applied and echoed back; IMDSv1-only // guest agents can be switched on after launch without a relaunch. func TestModifyInstanceMetadataOptions_EnableV1(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID id := "i-imdsv1" v := &vm.VM{ ID: id, AccountID: owner, Status: vm.StateRunning, @@ -77,7 +77,7 @@ func TestModifyInstanceMetadataOptions_EnableV1(t *testing.T) { // An unrecognised http-tokens state is still refused, and the instance is left // untouched — no partial application of a rejected request. func TestModifyInstanceMetadataOptions_RejectUnknownTokensState(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID id := "i-imdsbad" v := &vm.VM{ ID: id, AccountID: owner, Status: vm.StateRunning, @@ -98,7 +98,7 @@ func TestModifyInstanceMetadataOptions_RejectUnknownTokensState(t *testing.T) { // allows this in any state, so it must not return IncorrectInstanceState. The // no-op http-tokens=required is accepted alongside it. func TestModifyInstanceMetadataOptions_HopLimitRunning(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID id := "i-hop-run" v := &vm.VM{ ID: id, AccountID: owner, Status: vm.StateRunning, @@ -120,7 +120,7 @@ func TestModifyInstanceMetadataOptions_HopLimitRunning(t *testing.T) { // The stopped-store fallback persists the hop-limit change too. func TestModifyInstanceMetadataOptions_HopLimitStopped(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID id := "i-hop-stop" stored := &vm.VM{ ID: id, AccountID: owner, Status: vm.StateStopped, @@ -148,7 +148,7 @@ func TestModifyInstanceMetadataOptions_NotFound(t *testing.T) { _, err := svc.ModifyInstanceMetadataOptions(context.Background(), &ec2.ModifyInstanceMetadataOptionsInput{ InstanceId: aws.String("i-ghost"), HttpPutResponseHopLimit: aws.Int64(2), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.True(t, awserrors.IsErrorCode(err, awserrors.ErrorInvalidInstanceIDNotFound), "got %v", err) } @@ -157,7 +157,7 @@ func TestModifyInstanceMetadataOptions_NotFound(t *testing.T) { // guards) returns ServerInternal rather than panicking the daemon — on both the // running and stopped paths. func TestModifyInstanceMetadataOptions_NilInstance(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID t.Run("running", func(t *testing.T) { id := "i-nil-run" @@ -186,7 +186,7 @@ func TestModifyInstanceMetadataOptions_NilInstance(t *testing.T) { // A legacy instance launched before the constant block (nil MetadataOptions) is // stamped with the full IMDSv2-only block on the first modify, not just the hop. func TestModifyInstanceMetadataOptions_LegacyNilBlockStamped(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID id := "i-legacy" v := &vm.VM{ ID: id, AccountID: owner, Status: vm.StateRunning, @@ -285,7 +285,7 @@ func TestPrepareRunInstances_RejectUnsupportedMetadataOptions(t *testing.T) { MetadataOptions: &ec2.InstanceMetadataOptionsRequest{ HttpEndpoint: aws.String(ec2.InstanceMetadataEndpointStateDisabled), }, - }, utils.GlobalAccountID, "") + }, awsidentifiers.GlobalAccountID, "") require.Error(t, err) assert.True(t, awserrors.IsErrorCode(err, awserrors.ErrorUnsupportedOperation), "got %v", err) } diff --git a/spinifex/handlers/ec2/instance/service_impl.go b/spinifex/handlers/ec2/instance/service_impl.go index db09bcca2..e91589054 100644 --- a/spinifex/handlers/ec2/instance/service_impl.go +++ b/spinifex/handlers/ec2/instance/service_impl.go @@ -662,7 +662,7 @@ func (s *InstanceServiceImpl) PrepareRunInstances(ctx context.Context, input *ec } // Caller must own the AMI or the owner alias must be non-account-ID (e.g. "self", "spinifex", ""). amiOwner := amiMeta.ImageOwnerAlias - if amiOwner != "" && amiOwner != accountID && utils.IsAccountID(amiOwner) { + if amiOwner != "" && amiOwner != accountID && awsidentifiers.IsAccountID(amiOwner) { slog.WarnContext(ctx, "PrepareRunInstances: AMI not owned by caller", "imageId", *input.ImageId, "amiOwner", amiOwner, "accountID", accountID) return nil, nil, nil, errors.New(awserrors.ErrorInvalidAMIIDNotFound) } @@ -1965,7 +1965,7 @@ var DescribeInstanceStatusValidFilters = map[string]bool{ // Pre-Phase4 instances (empty AccountID) are only visible to root (GlobalAccountID). func IsInstanceVisible(callerAccountID, ownerAccountID string) bool { if ownerAccountID == "" { - return callerAccountID == utils.GlobalAccountID + return callerAccountID == awsidentifiers.GlobalAccountID } return callerAccountID == ownerAccountID } @@ -1977,7 +1977,7 @@ func IsInstanceVisibleToCaller(accountID string, v *vm.VM) bool { if !IsInstanceVisible(accountID, v.AccountID) { return false } - return v.ManagedBy == "" || accountID == utils.GlobalAccountID + return v.ManagedBy == "" || accountID == awsidentifiers.GlobalAccountID } // instanceMatchesFilters checks whether a VM + its built ec2.Instance copy satisfy all parsed awsfilters. diff --git a/spinifex/handlers/ec2/instance/service_impl_test.go b/spinifex/handlers/ec2/instance/service_impl_test.go index 682136bbc..f997fed29 100644 --- a/spinifex/handlers/ec2/instance/service_impl_test.go +++ b/spinifex/handlers/ec2/instance/service_impl_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "sync" "sync/atomic" @@ -25,7 +26,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/runtime/compute/gpu" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" vmmock "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm/mock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -806,7 +806,7 @@ func TestDescribeInstanceTypes_CapacityFilterHitsAvailable(t *testing.T) { func TestDescribeInstances_Empty(t *testing.T) { svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{})} - out, err := svc.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{}, utils.GlobalAccountID) + out, err := svc.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{}, awsidentifiers.GlobalAccountID) require.NoError(t, err) assert.Empty(t, out.Reservations) } @@ -879,17 +879,17 @@ func TestDescribeInstances_HidesManagedSystemVMFromCustomer(t *testing.T) { func TestDescribeInstances_RootSeesManagedSystemVM(t *testing.T) { v := &vm.VM{ ID: "i-lb", - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, ManagedBy: tags.ManagedByELBv2, Reservation: &ec2.Reservation{ ReservationId: aws.String("r-lb"), - OwnerId: aws.String(utils.GlobalAccountID), + OwnerId: aws.String(awsidentifiers.GlobalAccountID), }, Instance: &ec2.Instance{InstanceId: aws.String("i-lb")}, } svc := &InstanceServiceImpl{vmMgr: mgrWith(map[string]*vm.VM{v.ID: v}), config: &config.Config{}} - out, err := svc.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{}, utils.GlobalAccountID) + out, err := svc.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{}, awsidentifiers.GlobalAccountID) require.NoError(t, err) require.Len(t, out.Reservations, 1) assert.Equal(t, "i-lb", *out.Reservations[0].Instances[0].InstanceId) @@ -922,7 +922,7 @@ func TestDescribeInstances_MalformedID(t *testing.T) { input := &ec2.DescribeInstancesInput{ InstanceIds: []*string{aws.String("not-an-id")}, } - _, err := svc.DescribeInstances(context.Background(), input, utils.GlobalAccountID) + _, err := svc.DescribeInstances(context.Background(), input, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidInstanceIDMalformed, err.Error()) } @@ -948,7 +948,7 @@ func TestDescribeInstances_FilterByInstanceID(t *testing.T) { }), config: &config.Config{}} input := &ec2.DescribeInstancesInput{InstanceIds: []*string{aws.String("i-keep")}} - out, err := svc.DescribeInstances(context.Background(), input, utils.GlobalAccountID) + out, err := svc.DescribeInstances(context.Background(), input, awsidentifiers.GlobalAccountID) require.NoError(t, err) require.Len(t, out.Reservations, 1) assert.Equal(t, "i-keep", *out.Reservations[0].Instances[0].InstanceId) @@ -968,7 +968,7 @@ func TestDescribeInstances_ReservationGrouping(t *testing.T) { } svc := &InstanceServiceImpl{vmMgr: mgrWith(instances), config: &config.Config{}} - out, err := svc.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{}, utils.GlobalAccountID) + out, err := svc.DescribeInstances(context.Background(), &ec2.DescribeInstancesInput{}, awsidentifiers.GlobalAccountID) require.NoError(t, err) require.Len(t, out.Reservations, 1) assert.Equal(t, "r-shared", aws.StringValue(out.Reservations[0].ReservationId)) @@ -990,7 +990,7 @@ func TestDescribeInstances_FilterExcludesOnlyInstance_ReturnsZeroReservations(t input := &ec2.DescribeInstancesInput{ Filters: []*ec2.Filter{{Name: aws.String("instance-type"), Values: []*string{aws.String("t3.nano")}}}, } - out, err := svc.DescribeInstances(context.Background(), input, utils.GlobalAccountID) + out, err := svc.DescribeInstances(context.Background(), input, awsidentifiers.GlobalAccountID) require.NoError(t, err) assert.Empty(t, out.Reservations, "a reservation with every instance filtered out must not appear in the response") } @@ -1010,7 +1010,7 @@ func TestDescribeInstancesFromKV_FilterExcludesOnlyInstance_ReturnsZeroReservati input := &ec2.DescribeInstancesInput{ Filters: []*ec2.Filter{{Name: aws.String("instance-type"), Values: []*string{aws.String("t3.nano")}}}, } - out, err := svc.describeInstancesFromKV(context.Background(), input, utils.GlobalAccountID, listFn, 80, "stopped", "DescribeStoppedInstances") + out, err := svc.describeInstancesFromKV(context.Background(), input, awsidentifiers.GlobalAccountID, listFn, 80, "stopped", "DescribeStoppedInstances") require.NoError(t, err) assert.Empty(t, out.Reservations, "a reservation with every instance filtered out must not appear in the response") } @@ -1035,7 +1035,7 @@ func TestDescribeInstanceAttribute_MissingAttribute(t *testing.T) { func TestDescribeInstanceAttribute_RunningInstance(t *testing.T) { id := "i-attr1" - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID v := &vm.VM{ ID: id, InstanceType: "t3.large", @@ -1128,7 +1128,7 @@ func TestDescribeInstanceAttribute_NotRunning_NoStore(t *testing.T) { func TestDescribeInstanceAttribute_FoundInStoppedStore(t *testing.T) { id := "i-stopped1" - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID store := &vmmock.StateStore{Stopped: map[string]*vm.VM{ id: {ID: id, InstanceType: "t3.medium", AccountID: owner}, }} @@ -1153,7 +1153,7 @@ func TestDescribeInstanceAttribute_NotFound(t *testing.T) { _, err := svc.DescribeInstanceAttribute(context.Background(), &ec2.DescribeInstanceAttributeInput{ InstanceId: aws.String("i-ghost"), Attribute: aws.String(ec2.InstanceAttributeNameInstanceType), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidInstanceIDNotFound, err.Error()) } @@ -1172,7 +1172,7 @@ func TestDescribeInstanceAttribute_HiddenForOtherAccount(t *testing.T) { } func TestDescribeInstanceAttribute_DisableApiTermination(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID tests := []struct { name string @@ -1236,7 +1236,7 @@ func TestDescribeStoppedInstances_NilStore(t *testing.T) { } func TestDescribeStoppedInstances_HappyPath(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID store := &vmmock.StateStore{ Stopped: map[string]*vm.VM{ "i-stop1": { @@ -1266,7 +1266,7 @@ func TestDescribeTerminatedInstances_NilStore(t *testing.T) { } func TestDescribeTerminatedInstances_HappyPath(t *testing.T) { - owner := utils.GlobalAccountID + owner := awsidentifiers.GlobalAccountID store := &vmmock.StateStore{ Terminated: map[string]*vm.VM{ "i-term1": { @@ -1346,7 +1346,7 @@ func TestIsInstanceVisible(t *testing.T) { owner string want bool }{ - {"empty owner, global caller", utils.GlobalAccountID, "", true}, + {"empty owner, global caller", awsidentifiers.GlobalAccountID, "", true}, {"empty owner, non-global caller", "111122223333", "", false}, {"matching account", "111122223333", "111122223333", true}, {"different accounts", "111122223333", "999988887777", false}, @@ -4472,7 +4472,7 @@ func runningVM(id, owner string) *vm.VM { func TestDescribeInstanceStatus_Empty(t *testing.T) { svc := instanceStatusService(t, "az-a", map[string]*vm.VM{}) - out, err := svc.DescribeInstanceStatus(context.Background(), &ec2.DescribeInstanceStatusInput{}, utils.GlobalAccountID) + out, err := svc.DescribeInstanceStatus(context.Background(), &ec2.DescribeInstanceStatusInput{}, awsidentifiers.GlobalAccountID) require.NoError(t, err) require.NotNil(t, out) assert.Empty(t, out.InstanceStatuses) @@ -4683,7 +4683,7 @@ func TestDescribeInstanceStatus_MalformedInstanceID(t *testing.T) { svc := instanceStatusService(t, "az-a", map[string]*vm.VM{}) _, err := svc.DescribeInstanceStatus(context.Background(), &ec2.DescribeInstanceStatusInput{ InstanceIds: []*string{aws.String("not-an-id")}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidInstanceIDMalformed, err.Error()) } diff --git a/spinifex/handlers/ec2/snapshot/service_impl.go b/spinifex/handlers/ec2/snapshot/service_impl.go index e8abb2d79..e590d08d7 100644 --- a/spinifex/handlers/ec2/snapshot/service_impl.go +++ b/spinifex/handlers/ec2/snapshot/service_impl.go @@ -534,7 +534,7 @@ func (s *SnapshotServiceImpl) describeSnapshotsByIDs(ctx context.Context, input // An untenanted caller has no prefix to read under; refuse here rather than // report a snapshot that may well exist as absent. The key builder owns the // wording, so the strict variant reports what the listing path reports. - if !utils.IsAccountID(accountID) { + if !awsidentifiers.IsAccountID(accountID) { _, keyErr := ebsmetadata.SnapshotKey(accountID, snapshotIDs[0]) slog.ErrorContext(ctx, "DescribeSnapshots refused a caller with no account", "accountID", accountID) if strict { diff --git a/spinifex/handlers/ec2/snapshot/snapshot_partition_test.go b/spinifex/handlers/ec2/snapshot/snapshot_partition_test.go index 2d5505c5f..a015996ec 100644 --- a/spinifex/handlers/ec2/snapshot/snapshot_partition_test.go +++ b/spinifex/handlers/ec2/snapshot/snapshot_partition_test.go @@ -7,6 +7,7 @@ package handlers_ec2_snapshot import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "testing" @@ -19,7 +20,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/providers/ebs" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -52,17 +52,17 @@ func TestDeleteSnapshot_LeavesNoDocument(t *testing.T) { func TestSnapshot_SystemAccountIsAnOrdinaryAccount(t *testing.T) { ctx := context.Background() svc, store := setupTestSnapshotService(t) - createTestVolumeForAccount(t, svc, store, "vol-sys", 8, utils.GlobalAccountID) + createTestVolumeForAccount(t, svc, store, "vol-sys", 8, awsidentifiers.GlobalAccountID) - snap, err := svc.CreateSnapshot(ctx, &ec2.CreateSnapshotInput{VolumeId: aws.String("vol-sys")}, utils.GlobalAccountID) + snap, err := svc.CreateSnapshot(ctx, &ec2.CreateSnapshotInput{VolumeId: aws.String("vol-sys")}, awsidentifiers.GlobalAccountID) require.NoError(t, err) - listed, err := svc.DescribeSnapshots(ctx, &ec2.DescribeSnapshotsInput{}, utils.GlobalAccountID) + listed, err := svc.DescribeSnapshots(ctx, &ec2.DescribeSnapshotsInput{}, awsidentifiers.GlobalAccountID) require.NoError(t, err) require.Len(t, listed.Snapshots, 1) assert.Equal(t, aws.StringValue(snap.SnapshotId), aws.StringValue(listed.Snapshots[0].SnapshotId)) - _, err = svc.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{SnapshotId: snap.SnapshotId}, utils.GlobalAccountID) + _, err = svc.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{SnapshotId: snap.SnapshotId}, awsidentifiers.GlobalAccountID) require.NoError(t, err) } @@ -257,9 +257,9 @@ func TestDescribeSnapshots_ForeignOwnerIDReadsNothing(t *testing.T) { func TestDeleteSnapshot_BlockedByACloneInAnotherAccount(t *testing.T) { ctx := context.Background() svc, store := setupTestSnapshotService(t) - createTestVolumeForAccount(t, svc, store, "vol-system", 8, utils.GlobalAccountID) + createTestVolumeForAccount(t, svc, store, "vol-system", 8, awsidentifiers.GlobalAccountID) - snap, err := svc.CreateSnapshot(ctx, &ec2.CreateSnapshotInput{VolumeId: aws.String("vol-system")}, utils.GlobalAccountID) + snap, err := svc.CreateSnapshot(ctx, &ec2.CreateSnapshotInput{VolumeId: aws.String("vol-system")}, awsidentifiers.GlobalAccountID) require.NoError(t, err) seedVolumeDocument(t, store, ebsmetadata.Volume{ @@ -268,7 +268,7 @@ func TestDeleteSnapshot_BlockedByACloneInAnotherAccount(t *testing.T) { SnapshotID: aws.StringValue(snap.SnapshotId), }) - _, err = svc.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{SnapshotId: snap.SnapshotId}, utils.GlobalAccountID) + _, err = svc.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{SnapshotId: snap.SnapshotId}, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidSnapshotInUse, err.Error()) } diff --git a/spinifex/handlers/elbv2/lb_role_test.go b/spinifex/handlers/elbv2/lb_role_test.go index dedf1b21e..15fec5b1a 100644 --- a/spinifex/handlers/elbv2/lb_role_test.go +++ b/spinifex/handlers/elbv2/lb_role_test.go @@ -1,11 +1,11 @@ package handlers_elbv2 import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" iammock "github.com/mulgadc/spinifex/spinifex/handlers/iam/mock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" ) @@ -17,8 +17,8 @@ func TestEnsureLBInstanceProfile_UsesLazyProvider(t *testing.T) { f := iammock.New() s := &ELBv2ServiceImpl{IAMProvider: func() handlers_iam.SystemInstanceRoleEnsurer { return f }} - arn := s.ensureLBInstanceProfile(utils.GlobalAccountID) - assert.Equal(t, "arn:aws:iam::"+utils.GlobalAccountID+":instance-profile/"+lbAgentSystemRoleName, arn) + arn := s.ensureLBInstanceProfile(awsidentifiers.GlobalAccountID) + assert.Equal(t, "arn:aws:iam::"+awsidentifiers.GlobalAccountID+":instance-profile/"+lbAgentSystemRoleName, arn) } // TestEnsureLBInstanceProfile_CreatesInSystemAccount asserts the role and profile @@ -30,9 +30,9 @@ func TestEnsureLBInstanceProfile_CreatesInSystemAccount(t *testing.T) { f := iammock.New() s := &ELBv2ServiceImpl{IAM: f} - s.ensureLBInstanceProfile(utils.GlobalAccountID) - assert.Equal(t, utils.GlobalAccountID, f.LastRoleAcct) - assert.Equal(t, utils.GlobalAccountID, f.LastProfileAcct) + s.ensureLBInstanceProfile(awsidentifiers.GlobalAccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, f.LastRoleAcct) + assert.Equal(t, awsidentifiers.GlobalAccountID, f.LastProfileAcct) } // TestEnsureLBInstanceProfile_NotReadyFallsBack asserts an unwired IAM (no field, @@ -41,5 +41,5 @@ func TestEnsureLBInstanceProfile_CreatesInSystemAccount(t *testing.T) { func TestEnsureLBInstanceProfile_NotReadyFallsBack(t *testing.T) { t.Parallel() s := &ELBv2ServiceImpl{IAMProvider: func() handlers_iam.SystemInstanceRoleEnsurer { return nil }} - assert.Empty(t, s.ensureLBInstanceProfile(utils.GlobalAccountID)) + assert.Empty(t, s.ensureLBInstanceProfile(awsidentifiers.GlobalAccountID)) } diff --git a/spinifex/handlers/elbv2/service_impl.go b/spinifex/handlers/elbv2/service_impl.go index a7e5a05c4..9e18c5963 100644 --- a/spinifex/handlers/elbv2/service_impl.go +++ b/spinifex/handlers/elbv2/service_impl.go @@ -503,7 +503,7 @@ func (s *ELBv2ServiceImpl) launchLBVM(ctx context.Context, lbID, scheme string, // secret rides in fw_cfg. Falls back to baked system keys when IAM is unwired. // The role lives in the system account because the LB VM (and its ENI) run // there — IMDS resolves the profile under the instance's account. - profileARN := s.ensureLBInstanceProfile(utils.GlobalAccountID) + profileARN := s.ensureLBInstanceProfile(awsidentifiers.GlobalAccountID) nics := s.buildMicrovmNICs(primaryIP, primaryMAC, subnets[0], eniIDs[0], scheme, extraENIInputs, accountID) launchInput := &SystemInstanceInput{ @@ -610,7 +610,7 @@ func (s *ELBv2ServiceImpl) RebuildSystemInstanceInput(rc RecoveryContext) (*Syst // Re-ensure the instance profile so a recovered LB VM keeps IMDS creds; the // ensure is idempotent and converges on the existing role/profile. The role // lives in the system account where the LB VM runs, not the LB owner account. - profileARN := s.ensureLBInstanceProfile(utils.GlobalAccountID) + profileARN := s.ensureLBInstanceProfile(awsidentifiers.GlobalAccountID) return &SystemInstanceInput{ InstanceType: rc.InstanceType, @@ -1047,7 +1047,7 @@ func (s *ELBv2ServiceImpl) LBAgentHeartbeat(ctx context.Context, input *LBAgentH slog.ErrorContext(ctx, "LBAgentHeartbeat: failed to get LB", "lbId", lbID, "err", err) return nil, errors.New(awserrors.ErrorServerInternal) } - if lb == nil || (lb.AccountID != accountID && accountID != utils.GlobalAccountID) { + if lb == nil || (lb.AccountID != accountID && accountID != awsidentifiers.GlobalAccountID) { // Log to distinguish a stuck-in-provisioning LB from one whose heartbeat never arrived. slog.WarnContext(ctx, "LBAgentHeartbeat: LB not found or account mismatch", "lbId", lbID, "accountId", accountID, "found", lb != nil) @@ -1106,7 +1106,7 @@ func (s *ELBv2ServiceImpl) GetLBConfig(ctx context.Context, input *GetLBConfigIn slog.ErrorContext(ctx, "GetLBConfig: failed to get LB", "lbId", lbID, "err", err) return nil, errors.New(awserrors.ErrorServerInternal) } - if lb == nil || (lb.AccountID != accountID && accountID != utils.GlobalAccountID) { + if lb == nil || (lb.AccountID != accountID && accountID != awsidentifiers.GlobalAccountID) { return nil, errors.New(awserrors.ErrorELBv2LoadBalancerNotFound) } diff --git a/spinifex/handlers/elbv2/service_impl_test.go b/spinifex/handlers/elbv2/service_impl_test.go index bbcb48d70..b9f8b043f 100644 --- a/spinifex/handlers/elbv2/service_impl_test.go +++ b/spinifex/handlers/elbv2/service_impl_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/xml" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "sort" "strings" "sync" @@ -16,7 +17,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" awsxml "github.com/mulgadc/spinifex/spinifex/foundation/aws/xml" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -2375,7 +2375,7 @@ func TestLBAgentHeartbeat_SystemAccountAllowed(t *testing.T) { out, err := svc.LBAgentHeartbeat(context.Background(), &LBAgentHeartbeatInput{ LBID: aws.String("lb-sys1"), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) require.NoError(t, err) assert.Equal(t, StateActive, *out.Status) } @@ -2471,7 +2471,7 @@ func TestGetLBConfig_SystemAccountAllowed(t *testing.T) { out, err := svc.GetLBConfig(context.Background(), &GetLBConfigInput{ LBID: aws.String("lb-syscfg1"), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) require.NoError(t, err) assert.Equal(t, "global\n log stdout\n", *out.ConfigText) } diff --git a/spinifex/handlers/iam/account_test.go b/spinifex/handlers/iam/account_test.go index 637eb1e5d..2847ea97c 100644 --- a/spinifex/handlers/iam/account_test.go +++ b/spinifex/handlers/iam/account_test.go @@ -2,13 +2,13 @@ package handlers_iam import ( "encoding/json" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "time" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/iam" "github.com/mulgadc/spinifex/internal/testkit" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -370,28 +370,28 @@ func TestSeedBootstrap_AccountScoped(t *testing.T) { err = svc.SeedBootstrap(&BootstrapData{ AccessKeyID: "AKIAROOTEXAMPLE12345", EncryptedSecret: encryptedSecret, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, }) require.NoError(t, err) // Root user stored at 000000000000:root - out, err := svc.GetUser(utils.GlobalAccountID, &iam.GetUserInput{ + out, err := svc.GetUser(awsidentifiers.GlobalAccountID, &iam.GetUserInput{ UserName: aws.String("root"), }) require.NoError(t, err) assert.Equal(t, "root", *out.User.UserName) - assert.Contains(t, *out.User.Arn, utils.GlobalAccountID) + assert.Contains(t, *out.User.Arn, awsidentifiers.GlobalAccountID) // Access key has correct AccountID ak, err := svc.LookupAccessKey("AKIAROOTEXAMPLE12345") require.NoError(t, err) - assert.Equal(t, utils.GlobalAccountID, ak.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, ak.AccountID) assert.Equal(t, "root", ak.UserName) // Global account record was created - account, err := svc.GetAccount(utils.GlobalAccountID) + account, err := svc.GetAccount(awsidentifiers.GlobalAccountID) require.NoError(t, err) - assert.Equal(t, utils.GlobalAccountID, account.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, account.AccountID) assert.Equal(t, "system", account.AccountName) assert.Equal(t, "ACTIVE", account.Status) } diff --git a/spinifex/handlers/iam/service_impl.go b/spinifex/handlers/iam/service_impl.go index 4bbeac4e9..72a0a3c0a 100644 --- a/spinifex/handlers/iam/service_impl.go +++ b/spinifex/handlers/iam/service_impl.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "maps" "net/netip" @@ -30,7 +31,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvutil" "github.com/mulgadc/spinifex/spinifex/foundation/state/migrate" - "github.com/mulgadc/spinifex/spinifex/utils" ) const ( @@ -828,7 +828,7 @@ func (s *IAMServiceImpl) SeedBootstrap(data *BootstrapData) error { ctx := context.Background() // --- Seed system account (000000000000) --- systemAccount := Account{ - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, AccountName: "system", Status: AccountStatusActive, CreatedAt: time.Now().UTC().Format(time.RFC3339), @@ -837,16 +837,16 @@ func (s *IAMServiceImpl) SeedBootstrap(data *BootstrapData) error { if err != nil { return fmt.Errorf("marshal system account: %w", err) } - if _, err := s.accountsBucket.Create(ctx, utils.GlobalAccountID, accountData); err != nil && !errors.Is(err, jetstream.ErrKeyExists) { + if _, err := s.accountsBucket.Create(ctx, awsidentifiers.GlobalAccountID, accountData); err != nil && !errors.Is(err, jetstream.ErrKeyExists) { return fmt.Errorf("seed system account: %w", err) } - kvKey := utils.GlobalAccountID + ".root" + kvKey := awsidentifiers.GlobalAccountID + ".root" rootUser := User{ UserName: "root", UserID: "AIDAAAAAAAAAAAAAAAAA", - AccountID: utils.GlobalAccountID, - ARN: arn.FormatIAMRoot(utils.GlobalAccountID), + AccountID: awsidentifiers.GlobalAccountID, + ARN: arn.FormatIAMRoot(awsidentifiers.GlobalAccountID), Path: "/", CreatedAt: time.Now().UTC().Format(time.RFC3339), AccessKeys: []string{data.AccessKeyID}, @@ -873,7 +873,7 @@ func (s *IAMServiceImpl) SeedBootstrap(data *BootstrapData) error { AccessKeyID: data.AccessKeyID, SecretAccessKey: data.EncryptedSecret, UserName: "root", - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, Status: AccessKeyStatusActive, CreatedAt: rootUser.CreatedAt, } @@ -889,7 +889,7 @@ func (s *IAMServiceImpl) SeedBootstrap(data *BootstrapData) error { } else if err != nil { return fmt.Errorf("seed root access key: %w", err) } else { - slog.Info("System root user seeded", "accountID", utils.GlobalAccountID, "accessKeyID", data.AccessKeyID) + slog.Info("System root user seeded", "accountID", awsidentifiers.GlobalAccountID, "accessKeyID", data.AccessKeyID) } // --- Seed admin account (000000000001) if present --- diff --git a/spinifex/handlers/iam/service_impl_test.go b/spinifex/handlers/iam/service_impl_test.go index 26b24e1b2..190705f26 100644 --- a/spinifex/handlers/iam/service_impl_test.go +++ b/spinifex/handlers/iam/service_impl_test.go @@ -2,6 +2,7 @@ package handlers_iam import ( "encoding/hex" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "strings" "testing" @@ -13,7 +14,6 @@ import ( "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/admin" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -21,7 +21,7 @@ import ( // Interface compliance check. var _ IAMService = (*IAMServiceImpl)(nil) -const testAccountID = utils.GlobalAccountID +const testAccountID = awsidentifiers.GlobalAccountID func setupTestIAMService(t *testing.T) *IAMServiceImpl { t.Helper() @@ -847,24 +847,24 @@ func TestSeedBootstrap(t *testing.T) { err = svc.SeedBootstrap(&BootstrapData{ AccessKeyID: "AKIAEXAMPLE123456789", EncryptedSecret: encryptedSecret, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, }) require.NoError(t, err) // Verify root user exists at account-scoped key - out, err := svc.GetUser(utils.GlobalAccountID, &iam.GetUserInput{ + out, err := svc.GetUser(awsidentifiers.GlobalAccountID, &iam.GetUserInput{ UserName: aws.String("root"), }) require.NoError(t, err) assert.Equal(t, "root", *out.User.UserName) - assert.Contains(t, *out.User.Arn, utils.GlobalAccountID) + assert.Contains(t, *out.User.Arn, awsidentifiers.GlobalAccountID) assert.Contains(t, *out.User.Arn, "root") // Verify access key exists with AccountID ak, err := svc.LookupAccessKey("AKIAEXAMPLE123456789") require.NoError(t, err) assert.Equal(t, "root", ak.UserName) - assert.Equal(t, utils.GlobalAccountID, ak.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, ak.AccountID) assert.Equal(t, "Active", ak.Status) // Verify secret is decryptable @@ -873,9 +873,9 @@ func TestSeedBootstrap(t *testing.T) { assert.Equal(t, "test-secret-key", decrypted) // Verify global account record was created - account, err := svc.GetAccount(utils.GlobalAccountID) + account, err := svc.GetAccount(awsidentifiers.GlobalAccountID) require.NoError(t, err) - assert.Equal(t, utils.GlobalAccountID, account.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, account.AccountID) assert.Equal(t, "system", account.AccountName) assert.Equal(t, "ACTIVE", account.Status) } @@ -888,7 +888,7 @@ func TestSeedBootstrap_Idempotent(t *testing.T) { data := &BootstrapData{ AccessKeyID: "AKIAEXAMPLE123456789", EncryptedSecret: encryptedSecret, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, } // First call seeds @@ -900,7 +900,7 @@ func TestSeedBootstrap_Idempotent(t *testing.T) { require.NoError(t, err) // Root user should still exist with original data - out, err := svc.GetUser(utils.GlobalAccountID, &iam.GetUserInput{ + out, err := svc.GetUser(awsidentifiers.GlobalAccountID, &iam.GetUserInput{ UserName: aws.String("root"), }) require.NoError(t, err) @@ -919,7 +919,7 @@ func TestSeedBootstrap_WithAdmin(t *testing.T) { err = svc.SeedBootstrap(&BootstrapData{ AccessKeyID: "AKIASYSTEM1234567890", EncryptedSecret: systemSecret, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, Admin: &AdminBootstrapData{ AccountID: "000000000001", AccountName: "spinifex", @@ -931,7 +931,7 @@ func TestSeedBootstrap_WithAdmin(t *testing.T) { require.NoError(t, err) // Verify system root user exists - out, err := svc.GetUser(utils.GlobalAccountID, &iam.GetUserInput{ + out, err := svc.GetUser(awsidentifiers.GlobalAccountID, &iam.GetUserInput{ UserName: aws.String("root"), }) require.NoError(t, err) @@ -987,12 +987,12 @@ func TestSeedBootstrap_AdminNil_BackwardCompat(t *testing.T) { err = svc.SeedBootstrap(&BootstrapData{ AccessKeyID: "AKIAEXAMPLE123456789", EncryptedSecret: encryptedSecret, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, }) require.NoError(t, err) // Root user should exist - out, err := svc.GetUser(utils.GlobalAccountID, &iam.GetUserInput{ + out, err := svc.GetUser(awsidentifiers.GlobalAccountID, &iam.GetUserInput{ UserName: aws.String("root"), }) require.NoError(t, err) diff --git a/spinifex/handlers/ochrevector/appliance.go b/spinifex/handlers/ochrevector/appliance.go index 38f03e486..f62ea100c 100644 --- a/spinifex/handlers/ochrevector/appliance.go +++ b/spinifex/handlers/ochrevector/appliance.go @@ -30,7 +30,7 @@ const applianceBucketHistory = 1 const appliancePostgresKey = "postgres" // ApplianceIdentifier is the appliance's reserved, deployment-wide identifier -// (D2): a system resource under utils.GlobalAccountID, never a +// (D2): a system resource under awsidentifiers.GlobalAccountID, never a // caller-supplied or generated id. const ApplianceIdentifier = "ochre-vector-pg" diff --git a/spinifex/handlers/ochrevector/hostport.go b/spinifex/handlers/ochrevector/hostport.go index b1f67c0b2..8b047776e 100644 --- a/spinifex/handlers/ochrevector/hostport.go +++ b/spinifex/handlers/ochrevector/hostport.go @@ -4,13 +4,13 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "net/netip" "strings" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" - "github.com/mulgadc/spinifex/spinifex/utils" ) // The appliance's reachable address is the customer-ENI IP RDS places in the @@ -81,7 +81,7 @@ func ensureDaemonENI(ctx context.Context, vpcSvc vpcProvisioner, subnetID, nodeI {Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{subnetID})}, {Name: aws.String("description"), Values: aws.StringSlice([]string{desc})}, }, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return nil, fmt.Errorf("ochrevector: describe daemon ENI in subnet %s: %w", subnetID, err) } @@ -123,7 +123,7 @@ func ensureDaemonENI(ctx context.Context, vpcSvc vpcProvisioner, subnetID, nodeI {Key: aws.String(ochreDaemonNodeTagKey), Value: aws.String(nodeID)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return nil, fmt.Errorf("ochrevector: create daemon ENI in subnet %s: %w", subnetID, err) } @@ -153,7 +153,7 @@ func ensureENIGroups(ctx context.Context, vpcSvc vpcProvisioner, ni *ec2.Network _, err := vpcSvc.ModifyNetworkInterfaceAttribute(ctx, &ec2.ModifyNetworkInterfaceAttributeInput{ NetworkInterfaceId: ni.NetworkInterfaceId, Groups: aws.StringSlice(groupIDs), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return fmt.Errorf("ochrevector: set daemon ENI %s security groups: %w", aws.StringValue(ni.NetworkInterfaceId), err) } @@ -201,7 +201,7 @@ func resolveApplianceTarget(ctx context.Context, deps HostPortDeps, identifier s Filters: []*ec2.Filter{ {Name: aws.String("tag:" + applianceInstanceTagKey), Values: aws.StringSlice([]string{identifier})}, }, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", "", "", nil, fmt.Errorf("ochrevector: describe appliance ENI for %s: %w", identifier, err) } @@ -238,7 +238,7 @@ func resolveApplianceTarget(ctx context.Context, deps HostPortDeps, identifier s subOut, err := deps.VPC.DescribeSubnets(ctx, &ec2.DescribeSubnetsInput{ Filters: []*ec2.Filter{{Name: aws.String("subnet-id"), Values: aws.StringSlice([]string{subnetID})}}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", "", "", nil, fmt.Errorf("ochrevector: describe subnet %s: %w", subnetID, err) } diff --git a/spinifex/handlers/ochrevector/identifiers.go b/spinifex/handlers/ochrevector/identifiers.go index 5c5f6dea2..7612840d4 100644 --- a/spinifex/handlers/ochrevector/identifiers.go +++ b/spinifex/handlers/ochrevector/identifiers.go @@ -2,10 +2,10 @@ package handlers_ochrevector import ( "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "regexp" "github.com/jackc/pgx/v5" - "github.com/mulgadc/spinifex/spinifex/utils" ) // indexIDPattern is a conservative allowlist for anything that reaches a SQL @@ -18,7 +18,7 @@ var indexIDPattern = regexp.MustCompile(`^[a-zA-Z0-9][a-zA-Z0-9_-]{0,62}$`) // AWS-style account id before it ever reaches a SQL identifier or // search_path. func validateAccountID(accountID string) error { - if !utils.IsAccountID(accountID) { + if !awsidentifiers.IsAccountID(accountID) { return fmt.Errorf("ochrevector: invalid account id %q", accountID) } return nil diff --git a/spinifex/handlers/ochrevector/identifiers_test.go b/spinifex/handlers/ochrevector/identifiers_test.go index 27f869c1b..12fed80a0 100644 --- a/spinifex/handlers/ochrevector/identifiers_test.go +++ b/spinifex/handlers/ochrevector/identifiers_test.go @@ -5,14 +5,14 @@ package handlers_ochrevector import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" ) func TestValidateAccountID(t *testing.T) { - assert.NoError(t, validateAccountID(utils.GlobalAccountID)) + assert.NoError(t, validateAccountID(awsidentifiers.GlobalAccountID)) assert.NoError(t, validateAccountID("123456789012")) assert.Error(t, validateAccountID("")) @@ -34,8 +34,8 @@ func TestValidateIndexID(t *testing.T) { } func TestSchemaAndRoleNames(t *testing.T) { - assert.Equal(t, "kb_"+utils.GlobalAccountID, schemaName(utils.GlobalAccountID)) - assert.Equal(t, "kb_"+utils.GlobalAccountID+"_role", roleName(utils.GlobalAccountID)) + assert.Equal(t, "kb_"+awsidentifiers.GlobalAccountID, schemaName(awsidentifiers.GlobalAccountID)) + assert.Equal(t, "kb_"+awsidentifiers.GlobalAccountID+"_role", roleName(awsidentifiers.GlobalAccountID)) } func TestTableAndIndexNames(t *testing.T) { diff --git a/spinifex/handlers/ochrevector/rds_launcher.go b/spinifex/handlers/ochrevector/rds_launcher.go index 17dde015b..353211976 100644 --- a/spinifex/handlers/ochrevector/rds_launcher.go +++ b/spinifex/handlers/ochrevector/rds_launcher.go @@ -3,6 +3,7 @@ package handlers_ochrevector import ( "context" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "time" @@ -99,7 +100,7 @@ func (l *rdsLauncher) create(ctx context.Context, identifier, masterUsername, ma MasterUsername: aws.String(masterUsername), MasterUserPassword: aws.String(masterPassword), } - _, err := utils.NATSRequest[rds.CreateDBInstanceOutput](ctx, l.nc, rdsCreateSubject, input, rdsCreateTimeout, utils.GlobalAccountID) + _, err := utils.NATSRequest[rds.CreateDBInstanceOutput](ctx, l.nc, rdsCreateSubject, input, rdsCreateTimeout, awsidentifiers.GlobalAccountID) if err != nil { if strings.Contains(err.Error(), awserrors.ErrorDBInstanceAlreadyExists) { return nil @@ -121,7 +122,7 @@ func (l *rdsLauncher) Delete(ctx context.Context, identifier string) error { DBInstanceIdentifier: aws.String(identifier), SkipFinalSnapshot: aws.Bool(true), } - _, err := utils.NATSRequest[rds.DeleteDBInstanceOutput](ctx, l.nc, rdsDeleteSubject, input, rdsDeleteTimeout, utils.GlobalAccountID) + _, err := utils.NATSRequest[rds.DeleteDBInstanceOutput](ctx, l.nc, rdsDeleteSubject, input, rdsDeleteTimeout, awsidentifiers.GlobalAccountID) if err != nil { if strings.Contains(err.Error(), awserrors.ErrorDBInstanceNotFound) { return nil @@ -136,7 +137,7 @@ func (l *rdsLauncher) Delete(ctx context.Context, identifier string) error { func (l *rdsLauncher) pollUntilAvailable(ctx context.Context, identifier string) (string, int, error) { for { out, err := utils.NATSRequest[rds.DescribeDBInstancesOutput](ctx, l.nc, rdsDescribeSubject, - &rds.DescribeDBInstancesInput{DBInstanceIdentifier: aws.String(identifier)}, rdsDescribeTimeout, utils.GlobalAccountID) + &rds.DescribeDBInstancesInput{DBInstanceIdentifier: aws.String(identifier)}, rdsDescribeTimeout, awsidentifiers.GlobalAccountID) if err != nil { return "", 0, fmt.Errorf("ochrevector: describe appliance db instance %s: %w", identifier, err) } diff --git a/spinifex/handlers/quota/bedrock.go b/spinifex/handlers/quota/bedrock.go index 2f1ada5ca..6dbd20e0b 100644 --- a/spinifex/handlers/quota/bedrock.go +++ b/spinifex/handlers/quota/bedrock.go @@ -4,13 +4,13 @@ import ( "context" "encoding/json" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "sync" "time" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvutil" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" "golang.org/x/time/rate" ) @@ -42,7 +42,7 @@ func (s *Service) SetBedrockUsage(r BedrockUsageReader) { // stream-fed counter catches up. A few seconds of lag behind real usage is // an accepted property of a monthly cap. func (s *Service) CheckBedrockTokens(ctx context.Context, accountID string) error { - if s == nil || !s.limits.TokensPerMonthEnabled || accountID == utils.GlobalAccountID { + if s == nil || !s.limits.TokensPerMonthEnabled || accountID == awsidentifiers.GlobalAccountID { return nil } if s.bedrockUsage == nil { @@ -66,7 +66,7 @@ func (s *Service) CheckBedrockTokens(ctx context.Context, accountID string) erro // limit is to occasionally allow a little more, never to add network latency // to every invocation for a slightly tighter cap. func (s *Service) CheckBedrockRPM(accountID string) error { - if s == nil || !s.limits.RequestsPerMinuteEnabled || accountID == utils.GlobalAccountID { + if s == nil || !s.limits.RequestsPerMinuteEnabled || accountID == awsidentifiers.GlobalAccountID { return nil } if !s.rpm.allow(accountID, s.limits.RequestsPerMinute) { diff --git a/spinifex/handlers/quota/bedrock_test.go b/spinifex/handlers/quota/bedrock_test.go index 59dd64746..ab1eb2694 100644 --- a/spinifex/handlers/quota/bedrock_test.go +++ b/spinifex/handlers/quota/bedrock_test.go @@ -4,12 +4,12 @@ import ( "context" "encoding/json" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "time" "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" toml "github.com/pelletier/go-toml/v2" "github.com/stretchr/testify/assert" @@ -62,7 +62,7 @@ func TestCheckBedrockTokens_DisabledIsNoop(t *testing.T) { func TestCheckBedrockTokens_ExemptSystemAccount(t *testing.T) { s := New(Limits{TokensPerMonthEnabled: true, TokensPerMonth: 1}, nil) s.SetBedrockUsage(stubBedrockUsageReader{tokens: 1_000_000}) - assert.NoError(t, s.CheckBedrockTokens(context.Background(), utils.GlobalAccountID)) + assert.NoError(t, s.CheckBedrockTokens(context.Background(), awsidentifiers.GlobalAccountID)) } // TestCheckBedrockTokens_NoReaderIsNoop covers a wiring gap: the dimension is @@ -103,7 +103,7 @@ func TestCheckBedrockRPM_DisabledIsNoop(t *testing.T) { func TestCheckBedrockRPM_ExemptSystemAccount(t *testing.T) { s := New(Limits{RequestsPerMinuteEnabled: true, RequestsPerMinute: 1}, nil) for range 10 { - assert.NoError(t, s.CheckBedrockRPM(utils.GlobalAccountID)) + assert.NoError(t, s.CheckBedrockRPM(awsidentifiers.GlobalAccountID)) } } diff --git a/spinifex/handlers/quota/live_test.go b/spinifex/handlers/quota/live_test.go index f9d3fb1f7..97b7d2c9e 100644 --- a/spinifex/handlers/quota/live_test.go +++ b/spinifex/handlers/quota/live_test.go @@ -2,10 +2,10 @@ package handlers_quota import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" ) // liveLimits is a representative enabled tier with distinct caps per dimension. @@ -54,20 +54,22 @@ func TestEnforceLiveExemptShortCircuits(t *testing.T) { {"vpc disabled", func() error { return disabled.EnforceVPCs(context.Background(), nil, normalAccount, 1) }}, {"subnet disabled", func() error { return disabled.EnforceSubnets(context.Background(), nil, normalAccount, 1) }}, {"eip disabled", func() error { return disabled.EnforceEIPs(context.Background(), nil, normalAccount, 1) }}, - {"vpc system account", func() error { return enabled.EnforceVPCs(context.Background(), nil, utils.GlobalAccountID, 1) }}, - {"subnet system account", func() error { return enabled.EnforceSubnets(context.Background(), nil, utils.GlobalAccountID, 1) }}, - {"eip system account", func() error { return enabled.EnforceEIPs(context.Background(), nil, utils.GlobalAccountID, 1) }}, + {"vpc system account", func() error { return enabled.EnforceVPCs(context.Background(), nil, awsidentifiers.GlobalAccountID, 1) }}, + {"subnet system account", func() error { + return enabled.EnforceSubnets(context.Background(), nil, awsidentifiers.GlobalAccountID, 1) + }}, + {"eip system account", func() error { return enabled.EnforceEIPs(context.Background(), nil, awsidentifiers.GlobalAccountID, 1) }}, {"lb disabled", func() error { return disabled.EnforceLoadBalancers(context.Background(), nil, normalAccount, 1) }}, {"lb system account", func() error { - return enabled.EnforceLoadBalancers(context.Background(), nil, utils.GlobalAccountID, 1) + return enabled.EnforceLoadBalancers(context.Background(), nil, awsidentifiers.GlobalAccountID, 1) }}, {"rds disabled", func() error { return disabled.EnforceRDSInstances(context.Background(), nil, normalAccount, 1) }}, {"rds system account", func() error { - return enabled.EnforceRDSInstances(context.Background(), nil, utils.GlobalAccountID, 1) + return enabled.EnforceRDSInstances(context.Background(), nil, awsidentifiers.GlobalAccountID, 1) }}, } for _, tc := range cases { diff --git a/spinifex/handlers/quota/quota.go b/spinifex/handlers/quota/quota.go index baea13217..e65bf616c 100644 --- a/spinifex/handlers/quota/quota.go +++ b/spinifex/handlers/quota/quota.go @@ -6,10 +6,10 @@ package handlers_quota import ( "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "time" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" ) @@ -132,5 +132,5 @@ func (s *Service) Exempt(accountID string) bool { if s == nil || !s.limits.Enabled { return true } - return accountID == utils.GlobalAccountID + return accountID == awsidentifiers.GlobalAccountID } diff --git a/spinifex/handlers/quota/quota_test.go b/spinifex/handlers/quota/quota_test.go index b4072aa67..022407f39 100644 --- a/spinifex/handlers/quota/quota_test.go +++ b/spinifex/handlers/quota/quota_test.go @@ -1,9 +1,9 @@ package handlers_quota import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" - "github.com/mulgadc/spinifex/spinifex/utils" toml "github.com/pelletier/go-toml/v2" ) @@ -73,10 +73,10 @@ func TestExempt(t *testing.T) { accountID string want bool }{ - {"system account when enabled", Limits{Enabled: true}, utils.GlobalAccountID, true}, + {"system account when enabled", Limits{Enabled: true}, awsidentifiers.GlobalAccountID, true}, {"normal account when enabled", Limits{Enabled: true}, normalAccount, false}, {"normal account when disabled", Limits{Enabled: false}, normalAccount, true}, - {"system account when disabled", Limits{Enabled: false}, utils.GlobalAccountID, true}, + {"system account when disabled", Limits{Enabled: false}, awsidentifiers.GlobalAccountID, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/spinifex/handlers/quota/reconcile.go b/spinifex/handlers/quota/reconcile.go index 10c4be5d7..48869076e 100644 --- a/spinifex/handlers/quota/reconcile.go +++ b/spinifex/handlers/quota/reconcile.go @@ -3,11 +3,11 @@ package handlers_quota import ( "context" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/domains/ec2/instancetypes" - "github.com/mulgadc/spinifex/spinifex/utils" ) // AccountLister enumerates the account IDs whose vCPU counters reconcile should @@ -37,7 +37,7 @@ func (s *Service) Reconcile(ctx context.Context, accounts AccountLister, list In } charged := make([]string, 0, len(ids)) for _, accountID := range ids { - if accountID != utils.GlobalAccountID { + if accountID != awsidentifiers.GlobalAccountID { charged = append(charged, accountID) } } @@ -75,7 +75,7 @@ func (s *Service) Reconcile(ctx context.Context, accounts AccountLister, list In // nothing indexes it by account, but it runs once per settled burst instead of // once per account per tick. func (s *Service) ReconcileAccount(ctx context.Context, accountID string, list InstanceVCPULister) error { - if s == nil || !s.limits.Enabled || accountID == utils.GlobalAccountID { + if s == nil || !s.limits.Enabled || accountID == awsidentifiers.GlobalAccountID { return nil } before := s.counterRevisions(ctx, []string{accountID}) diff --git a/spinifex/handlers/quota/reconcile_test.go b/spinifex/handlers/quota/reconcile_test.go index a683f9f18..fb0d3b225 100644 --- a/spinifex/handlers/quota/reconcile_test.go +++ b/spinifex/handlers/quota/reconcile_test.go @@ -3,9 +3,8 @@ package handlers_quota import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" - - "github.com/mulgadc/spinifex/spinifex/utils" ) // staticTotals serves a fixed per-account total as one scan, and records how @@ -81,13 +80,13 @@ func TestReconcileSkipsSystemAccount(t *testing.T) { s := newVCPUService(t, Limits{Enabled: true, VCPUs: 100}) // Park a non-zero value directly under the system key; AddVCPU would no-op it. - seedCounter(t, s, utils.GlobalAccountID, 42) + seedCounter(t, s, awsidentifiers.GlobalAccountID, 42) - if err := s.Reconcile(context.Background(), accountList(utils.GlobalAccountID, testAccount), - staticTotals(map[string]int{utils.GlobalAccountID: 99, testAccount: 2}, true, nil)); err != nil { + if err := s.Reconcile(context.Background(), accountList(awsidentifiers.GlobalAccountID, testAccount), + staticTotals(map[string]int{awsidentifiers.GlobalAccountID: 99, testAccount: 2}, true, nil)); err != nil { t.Fatalf("Reconcile: %v", err) } - assertCounter(t, s, utils.GlobalAccountID, 42) + assertCounter(t, s, awsidentifiers.GlobalAccountID, 42) assertCounter(t, s, testAccount, 2) } @@ -186,12 +185,12 @@ func TestReconcileAccountTouchesOnlyThatAccount(t *testing.T) { func TestReconcileAccountSkipsSystemAccount(t *testing.T) { s := newVCPUService(t, Limits{Enabled: true, VCPUs: 100}) - seedCounter(t, s, utils.GlobalAccountID, 42) - if err := s.ReconcileAccount(context.Background(), utils.GlobalAccountID, - staticTotals(map[string]int{utils.GlobalAccountID: 0}, true, nil)); err != nil { + seedCounter(t, s, awsidentifiers.GlobalAccountID, 42) + if err := s.ReconcileAccount(context.Background(), awsidentifiers.GlobalAccountID, + staticTotals(map[string]int{awsidentifiers.GlobalAccountID: 0}, true, nil)); err != nil { t.Fatalf("ReconcileAccount: %v", err) } - assertCounter(t, s, utils.GlobalAccountID, 42) + assertCounter(t, s, awsidentifiers.GlobalAccountID, 42) } // A disabled service never reaches the KV: both entry points are a no-op even diff --git a/spinifex/handlers/quota/records.go b/spinifex/handlers/quota/records.go index 34cd2b289..4b7b4bb7f 100644 --- a/spinifex/handlers/quota/records.go +++ b/spinifex/handlers/quota/records.go @@ -3,12 +3,12 @@ package handlers_quota import ( "context" "encoding/json" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/mulgadc/spinifex/spinifex/domains/ec2/instancetypes" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" ) // InstanceVCPULister totals the vCPUs each account currently holds, in one @@ -61,7 +61,7 @@ func RecordVCPULister(records recordSource, prefix string) InstanceVCPULister { for i := range items { record := &items[i].Value accountID := record.Metadata.AccountID - if accountID == "" || accountID == utils.GlobalAccountID { + if accountID == "" || accountID == awsidentifiers.GlobalAccountID { continue } if recordIsTerminal(record) { @@ -113,7 +113,7 @@ func AccountForRecord(value []byte) (accountID string, ok bool) { if err := json.Unmarshal(value, &record); err != nil { return "", false } - if record.Metadata.AccountID == "" || record.Metadata.AccountID == utils.GlobalAccountID { + if record.Metadata.AccountID == "" || record.Metadata.AccountID == awsidentifiers.GlobalAccountID { return "", false } return record.Metadata.AccountID, true diff --git a/spinifex/handlers/quota/records_test.go b/spinifex/handlers/quota/records_test.go index 471fc0d30..2e8ae2149 100644 --- a/spinifex/handlers/quota/records_test.go +++ b/spinifex/handlers/quota/records_test.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/mulgadc/spinifex/internal/testkit" @@ -11,7 +12,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" handlers_quota "github.com/mulgadc/spinifex/spinifex/handlers/quota" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/require" ) @@ -69,7 +69,7 @@ func TestRecordVCPULister_ChargesStoppedButNotTerminal(t *testing.T) { // rather than present as zero — the caller's account list is what zeroes it. func TestRecordVCPULister_SkipsSystemAccountAndReportsNothingAsAbsent(t *testing.T) { store := recordStore(t) - putRecord(t, store, "i-1", utils.GlobalAccountID, "m5.xlarge", vm.StateRunning) + putRecord(t, store, "i-1", awsidentifiers.GlobalAccountID, "m5.xlarge", vm.StateRunning) totals, complete, err := handlers_quota.RecordVCPULister(store, recordPrefix)(t.Context()) require.NoError(t, err) @@ -193,7 +193,7 @@ func TestAccountForRecord(t *testing.T) { }) require.NoError(t, err) system, err := json.Marshal(vm.InstanceRecord{ - Metadata: resource.Metadata{Name: "i-2", AccountID: utils.GlobalAccountID}, + Metadata: resource.Metadata{Name: "i-2", AccountID: awsidentifiers.GlobalAccountID}, }) require.NoError(t, err) unowned, err := json.Marshal(vm.InstanceRecord{Metadata: resource.Metadata{Name: "i-3"}}) diff --git a/spinifex/handlers/quota/storage_test.go b/spinifex/handlers/quota/storage_test.go index bde11ca0f..e4dca01bb 100644 --- a/spinifex/handlers/quota/storage_test.go +++ b/spinifex/handlers/quota/storage_test.go @@ -2,12 +2,12 @@ package handlers_quota import ( "context" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" ) func vol(id string, size int64) *ec2.Volume { @@ -101,9 +101,11 @@ func TestEnforceVolumeExemptShortCircuits(t *testing.T) { {"modify disabled", func() error { return disabled.EnforceVolumeModify(context.Background(), nil, normalAccount, "vol-a", 1) }}, - {"create system account", func() error { return enabled.EnforceVolumeCreate(context.Background(), nil, utils.GlobalAccountID, 1) }}, + {"create system account", func() error { + return enabled.EnforceVolumeCreate(context.Background(), nil, awsidentifiers.GlobalAccountID, 1) + }}, {"modify system account", func() error { - return enabled.EnforceVolumeModify(context.Background(), nil, utils.GlobalAccountID, "vol-a", 1) + return enabled.EnforceVolumeModify(context.Background(), nil, awsidentifiers.GlobalAccountID, "vol-a", 1) }}, } for _, tc := range cases { diff --git a/spinifex/handlers/quota/vcpu_launch_test.go b/spinifex/handlers/quota/vcpu_launch_test.go index 6a7b8dea4..5cdf56571 100644 --- a/spinifex/handlers/quota/vcpu_launch_test.go +++ b/spinifex/handlers/quota/vcpu_launch_test.go @@ -1,12 +1,12 @@ package handlers_quota import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" ) // reservationOf builds a reservation of n instances of one type, modelling what @@ -130,9 +130,11 @@ func TestLaunchExemptShortCircuits(t *testing.T) { }{ {"enforce disabled", func() error { return disabled.EnforceLaunch(t.Context(), testAccount, "t3.micro", 1000) }}, {"charge disabled", func() error { return disabled.ChargeLaunch(t.Context(), testAccount, reservationOf("t3.micro", 1000)) }}, - {"enforce system account", func() error { return enabled.EnforceLaunch(t.Context(), utils.GlobalAccountID, "t3.micro", 1000) }}, + {"enforce system account", func() error { + return enabled.EnforceLaunch(t.Context(), awsidentifiers.GlobalAccountID, "t3.micro", 1000) + }}, {"charge system account", func() error { - return enabled.ChargeLaunch(t.Context(), utils.GlobalAccountID, reservationOf("t3.micro", 1000)) + return enabled.ChargeLaunch(t.Context(), awsidentifiers.GlobalAccountID, reservationOf("t3.micro", 1000)) }}, } for _, tc := range cases { diff --git a/spinifex/handlers/quota/vcpu_retype_test.go b/spinifex/handlers/quota/vcpu_retype_test.go index 72afd8405..e6e38ac56 100644 --- a/spinifex/handlers/quota/vcpu_retype_test.go +++ b/spinifex/handlers/quota/vcpu_retype_test.go @@ -2,12 +2,12 @@ package handlers_quota import ( "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" ) // reservation builds a single reservation whose instances carry the given type @@ -153,7 +153,7 @@ func TestEnforceRetypeExemptShortCircuits(t *testing.T) { return disabled.EnforceRetype(t.Context(), failResolver, testAccount, "i-a", "m5.xlarge") }}, {"system account", func() (int, error) { - return enabled.EnforceRetype(t.Context(), failResolver, utils.GlobalAccountID, "i-a", "m5.xlarge") + return enabled.EnforceRetype(t.Context(), failResolver, awsidentifiers.GlobalAccountID, "i-a", "m5.xlarge") }}, } for _, tc := range cases { diff --git a/spinifex/handlers/quota/vcpu_test.go b/spinifex/handlers/quota/vcpu_test.go index 75846056a..9eebdd4a4 100644 --- a/spinifex/handlers/quota/vcpu_test.go +++ b/spinifex/handlers/quota/vcpu_test.go @@ -1,12 +1,12 @@ package handlers_quota import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "sync" "testing" "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" ) @@ -62,8 +62,8 @@ func TestVCPUExemptShortCircuits(t *testing.T) { }{ {"check disabled", func() error { return disabled.CheckVCPU(t.Context(), testAccount, 1000) }}, {"add disabled", func() error { return disabled.AddVCPU(t.Context(), testAccount, 1000) }}, - {"check system account", func() error { return enabled.CheckVCPU(t.Context(), utils.GlobalAccountID, 1000) }}, - {"add system account", func() error { return enabled.AddVCPU(t.Context(), utils.GlobalAccountID, 1000) }}, + {"check system account", func() error { return enabled.CheckVCPU(t.Context(), awsidentifiers.GlobalAccountID, 1000) }}, + {"add system account", func() error { return enabled.AddVCPU(t.Context(), awsidentifiers.GlobalAccountID, 1000) }}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { diff --git a/spinifex/handlers/rds/create.go b/spinifex/handlers/rds/create.go index 40301f83b..030d38f8e 100644 --- a/spinifex/handlers/rds/create.go +++ b/spinifex/handlers/rds/create.go @@ -12,7 +12,6 @@ import ( awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" ) // The first VM behind a DB instance. Replacement and recovery increment it, @@ -50,7 +49,7 @@ func (s *Service) CreateDBInstance(ctx context.Context, input *rds.CreateDBInsta } // The agent cannot bootstrap without this profile, so resolve it before the // identifier reservation or any launch side effects. - profileARN, err := ensureInstanceProfile(s.deps.IAM, utils.GlobalAccountID) + profileARN, err := ensureInstanceProfile(s.deps.IAM, awsidentifiers.GlobalAccountID) if err != nil { return nil, err } diff --git a/spinifex/handlers/rds/create_test.go b/spinifex/handlers/rds/create_test.go index c47706170..2628f1044 100644 --- a/spinifex/handlers/rds/create_test.go +++ b/spinifex/handlers/rds/create_test.go @@ -3,6 +3,7 @@ package handlers_rds import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "slices" "strings" "testing" @@ -16,7 +17,6 @@ import ( handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" iammock "github.com/mulgadc/spinifex/spinifex/handlers/iam/mock" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -282,7 +282,7 @@ func TestCreateDBInstance_ProvisionsAndRecordsTheInstance(t *testing.T) { assert.Equal(t, int64(firstVMGeneration), entry.VMGeneration) require.NotNil(t, h.launch.launcher.input) - assert.Equal(t, rdsInstanceProfileARN(utils.GlobalAccountID), h.launch.launcher.input.IamInstanceProfileArn) + assert.Equal(t, rdsInstanceProfileARN(awsidentifiers.GlobalAccountID), h.launch.launcher.input.IamInstanceProfileArn) } func TestCreateDBInstance_IAMFailurePrecedesReservationAndLaunch(t *testing.T) { diff --git a/spinifex/handlers/rds/delete.go b/spinifex/handlers/rds/delete.go index d4b410962..81c34535d 100644 --- a/spinifex/handlers/rds/delete.go +++ b/spinifex/handlers/rds/delete.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "time" @@ -14,7 +15,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" - "github.com/mulgadc/spinifex/spinifex/utils" ) var errFinalSnapshotInProgress = errors.New("rds: final DB snapshot creation is still in progress") @@ -370,7 +370,7 @@ func (s *Service) takeFinalSnapshot(ctx context.Context, kv *kvstore.Bucket, acc {Key: aws.String(rdsSnapshotAccountTagKey), Value: aws.String(accountID)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { s.rollbackFinalSnapshotReservation(ctx, kv, rec.FinalSnapshotIdentifier, rev) return fmt.Errorf("rds: take the final snapshot of %s: %w", rec.DBInstanceIdentifier, err) @@ -463,7 +463,7 @@ func (s *Service) releaseDataVolume(ctx context.Context, kv *kvstore.Bucket, acc } _, err = s.deps.Launch.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{ VolumeId: aws.String(rec.DataVolumeID), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) switch { case err == nil || awserrors.IsNotFound(err): return nil @@ -512,7 +512,7 @@ func (s *Service) snapshotsHolding(ctx context.Context, volumeID string) ([]stri Name: aws.String("volume-id"), Values: aws.StringSlice([]string{volumeID}), }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { if awserrors.IsNotFound(err) { return nil, nil @@ -543,6 +543,6 @@ func (s *Service) deleteInstanceENIs(ctx context.Context, accountID string, rec deleteLaunchENI(ctx, vpcSvc, accountID, rec.ENIID) } if rec.SystemENIID != "" { - deleteLaunchENI(ctx, vpcSvc, utils.GlobalAccountID, rec.SystemENIID) + deleteLaunchENI(ctx, vpcSvc, awsidentifiers.GlobalAccountID, rec.SystemENIID) } } diff --git a/spinifex/handlers/rds/iam_test.go b/spinifex/handlers/rds/iam_test.go index e2fba7ebd..e89fe8efe 100644 --- a/spinifex/handlers/rds/iam_test.go +++ b/spinifex/handlers/rds/iam_test.go @@ -3,13 +3,13 @@ package handlers_rds import ( "encoding/json" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "slices" "testing" "github.com/aws/aws-sdk-go/aws" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" iammock "github.com/mulgadc/spinifex/spinifex/handlers/iam/mock" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -57,12 +57,12 @@ func TestInstanceRolePolicy_GrantsOnlyTheInternalActions(t *testing.T) { func TestEnsureInstanceProfile_CreatesInSystemAccount(t *testing.T) { t.Parallel() f := iammock.New() - arn, err := ensureInstanceProfile(func() handlers_iam.SystemInstanceRoleEnsurer { return f }, utils.GlobalAccountID) + arn, err := ensureInstanceProfile(func() handlers_iam.SystemInstanceRoleEnsurer { return f }, awsidentifiers.GlobalAccountID) require.NoError(t, err) - assert.Equal(t, "arn:aws:iam::"+utils.GlobalAccountID+":instance-profile/"+InstanceRoleName, arn) - assert.Equal(t, utils.GlobalAccountID, f.LastRoleAcct) - assert.Equal(t, utils.GlobalAccountID, f.LastProfileAcct) + assert.Equal(t, "arn:aws:iam::"+awsidentifiers.GlobalAccountID+":instance-profile/"+InstanceRoleName, arn) + assert.Equal(t, awsidentifiers.GlobalAccountID, f.LastRoleAcct) + assert.Equal(t, awsidentifiers.GlobalAccountID, f.LastProfileAcct) assert.Equal(t, handlers_iam.EC2InstanceTrustPolicy, f.LastTrustDoc, "AssumeRoleForInstance only mints credentials for the EC2 trust document") } @@ -74,9 +74,9 @@ func TestEnsureInstanceProfile_IsIdempotent(t *testing.T) { f := iammock.New() provider := func() handlers_iam.SystemInstanceRoleEnsurer { return f } - first, err := ensureInstanceProfile(provider, utils.GlobalAccountID) + first, err := ensureInstanceProfile(provider, awsidentifiers.GlobalAccountID) require.NoError(t, err) - second, err := ensureInstanceProfile(provider, utils.GlobalAccountID) + second, err := ensureInstanceProfile(provider, awsidentifiers.GlobalAccountID) require.NoError(t, err) assert.Equal(t, first, second) @@ -100,7 +100,7 @@ func TestEnsureInstanceProfile_RejectsUnavailableIAM(t *testing.T) { } for name, provider := range tests { t.Run(name, func(t *testing.T) { - arn, err := ensureInstanceProfile(provider, utils.GlobalAccountID) + arn, err := ensureInstanceProfile(provider, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.Empty(t, arn) assert.Contains(t, err.Error(), "IAM") @@ -115,7 +115,7 @@ func TestEnsureInstanceProfile_PreservesEnsureFailure(t *testing.T) { f.PutRolePolicyErr = ensureErr arn, err := ensureInstanceProfile( - func() handlers_iam.SystemInstanceRoleEnsurer { return f }, utils.GlobalAccountID) + func() handlers_iam.SystemInstanceRoleEnsurer { return f }, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.ErrorIs(t, err, ensureErr) @@ -129,7 +129,7 @@ func TestEnsureInstanceProfile_RejectsEmptyARN(t *testing.T) { f.EmptyInstanceProfileARN = true arn, err := ensureInstanceProfile( - func() handlers_iam.SystemInstanceRoleEnsurer { return f }, utils.GlobalAccountID) + func() handlers_iam.SystemInstanceRoleEnsurer { return f }, awsidentifiers.GlobalAccountID) require.Error(t, err) assert.Empty(t, arn) diff --git a/spinifex/handlers/rds/launch.go b/spinifex/handlers/rds/launch.go index 7064197cb..ebf8c2445 100644 --- a/spinifex/handlers/rds/launch.go +++ b/spinifex/handlers/rds/launch.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "net" "slices" @@ -185,13 +186,13 @@ func LaunchDBInstanceVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (o return nil, err } - sysRefs, err := EnsureSystemVPC(ctx, deps.SystemVPC, &deps.Config.RDS, utils.GlobalAccountID, region) + sysRefs, err := EnsureSystemVPC(ctx, deps.SystemVPC, &deps.Config.RDS, awsidentifiers.GlobalAccountID, region) if err != nil { return nil, err } systemSubnetID := sysRefs.PrivateSubnetIDs[0] - systemSGID, err := EnsureSystemSecurityGroup(ctx, deps.VPC, utils.GlobalAccountID, region, sysRefs.VpcID) + systemSGID, err := EnsureSystemSecurityGroup(ctx, deps.VPC, awsidentifiers.GlobalAccountID, region, sysRefs.VpcID) if err != nil { return nil, err } @@ -223,13 +224,13 @@ func LaunchDBInstanceVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (o } }() - systemENI, err := createLaunchENI(ctx, deps.VPC, utils.GlobalAccountID, systemSubnetID, []string{systemSGID}, + systemENI, err := createLaunchENI(ctx, deps.VPC, awsidentifiers.GlobalAccountID, systemSubnetID, []string{systemSGID}, "RDS management NIC for "+in.DBInstanceIdentifier, in.DBInstanceIdentifier, false) if err != nil { return nil, err } rollback = append(rollback, func(ctx context.Context) { - deleteLaunchENI(ctx, deps.VPC, utils.GlobalAccountID, systemENI.id) + deleteLaunchENI(ctx, deps.VPC, awsidentifiers.GlobalAccountID, systemENI.id) }) // A replace adopts the ENI the endpoint already resolves to; only a create @@ -261,7 +262,7 @@ func LaunchDBInstanceVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (o ImageID: amiID, // The VM and its primary NIC live in the system account; the customer // ENI carries its own account so the daemon updates the right record. - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, SubnetID: systemSubnetID, ENIID: systemENI.id, ENIMac: systemENI.mac, @@ -314,7 +315,7 @@ func LaunchDBInstanceVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (o {Key: aws.String(rdsInstanceTagKey), Value: aws.String(in.DBInstanceIdentifier)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if volErr != nil { return nil, fmt.Errorf("rds: create data volume for %s: %w", in.DBInstanceIdentifier, volErr) } @@ -324,7 +325,7 @@ func LaunchDBInstanceVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (o volumeID = aws.StringValue(volume.VolumeId) volumeEncrypted = aws.BoolValue(volume.Encrypted) rollback = append(rollback, func(ctx context.Context) { - if _, delErr := deps.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{VolumeId: aws.String(volumeID)}, utils.GlobalAccountID); delErr != nil { + if _, delErr := deps.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{VolumeId: aws.String(volumeID)}, awsidentifiers.GlobalAccountID); delErr != nil { slog.WarnContext(ctx, "rds: rollback delete of orphaned data volume failed", "dbInstance", in.DBInstanceIdentifier, "volumeId", volumeID, "err", delErr) } @@ -338,7 +339,7 @@ func LaunchDBInstanceVM(ctx context.Context, deps LaunchDeps, in LaunchInput) (o } } - device, err := deps.Attacher.AttachVolume(ctx, utils.GlobalAccountID, instanceID, volumeID, dataVolumeDevice) + device, err := deps.Attacher.AttachVolume(ctx, awsidentifiers.GlobalAccountID, instanceID, volumeID, dataVolumeDevice) if err != nil { return nil, fmt.Errorf("rds: attach data volume %s to %s: %w", volumeID, instanceID, err) } @@ -532,7 +533,7 @@ func resolveEngineAMI(ctx context.Context, amiSvc launchAMIResolver, engine, ver }) } - out, err := amiSvc.DescribeImages(ctx, &ec2.DescribeImagesInput{Filters: filters}, utils.GlobalAccountID) + out, err := amiSvc.DescribeImages(ctx, &ec2.DescribeImagesInput{Filters: filters}, awsidentifiers.GlobalAccountID) if err != nil { return "", fmt.Errorf("rds: describe %s AMI: %w", engine, err) } diff --git a/spinifex/handlers/rds/launch_test.go b/spinifex/handlers/rds/launch_test.go index ec6be0bd4..f693cb211 100644 --- a/spinifex/handlers/rds/launch_test.go +++ b/spinifex/handlers/rds/launch_test.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "testing" @@ -17,7 +18,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/handlers/sysinstance" handlers_systemvpc "github.com/mulgadc/spinifex/spinifex/handlers/systemvpc" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -572,7 +572,7 @@ func TestLaunchDBInstanceVMWiresBothNICs(t *testing.T) { // The system NIC sits in the RDS system VPC's private subnet under the // system account. That is the NIC with NAT egress, which is how the in-guest // agent reaches the gateway from a customer DB subnet that has none. - assert.Equal(t, utils.GlobalAccountID, h.enis.accts[0]) + assert.Equal(t, awsidentifiers.GlobalAccountID, h.enis.accts[0]) assert.True(t, strings.HasPrefix(aws.StringValue(sysENI.SubnetId), "subnet-rdssys"), "the primary NIC must land in the RDS system VPC, got %s", aws.StringValue(sysENI.SubnetId)) // Its own ingress-free group, not the system VPC's default one — whose sole @@ -606,7 +606,7 @@ func TestLaunchDBInstanceVMWiresBothNICs(t *testing.T) { assert.Equal(t, sysinstance.BootAMI, in.BootMode) assert.Equal(t, tags.ManagedByRDS, in.ManagedBy) assert.Equal(t, testEngineAMI, in.ImageID) - assert.Equal(t, utils.GlobalAccountID, in.AccountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, in.AccountID) assert.Equal(t, aws.StringValue(sysENI.SubnetId), in.SubnetID) assert.Equal(t, out.SystemENIID, in.ENIID) require.Len(t, in.ExtraENIs, 1) @@ -718,12 +718,12 @@ func TestLaunchDBInstanceVMAttachesTheDataVolume(t *testing.T) { assert.Equal(t, "ap-southeast-2a", aws.StringValue(vol.AvailabilityZone)) assert.Equal(t, tags.ManagedByRDS, tagOf(vol.TagSpecifications, tags.ManagedByKey)) assert.Equal(t, "mydb", tagOf(vol.TagSpecifications, rdsInstanceTagKey)) - assert.Equal(t, utils.GlobalAccountID, h.volumes.accts[0], "the data volume belongs to the system account, like the VM it serves") + assert.Equal(t, awsidentifiers.GlobalAccountID, h.volumes.accts[0], "the data volume belongs to the system account, like the VM it serves") assert.Equal(t, "i-rds0001", h.attacher.instanceID) assert.Equal(t, "vol-rdsdata01", h.attacher.volumeID) assert.Equal(t, dataVolumeDevice, h.attacher.device) - assert.Equal(t, utils.GlobalAccountID, h.attacher.accountID) + assert.Equal(t, awsidentifiers.GlobalAccountID, h.attacher.accountID) assert.Equal(t, "vol-rdsdata01", out.DataVolumeID) assert.Equal(t, vm.VolumeSerial(out.DataVolumeID), out.DataVolumeSerial) diff --git a/spinifex/handlers/rds/lifecycle.go b/spinifex/handlers/rds/lifecycle.go index f944ec6a5..c29b29b1c 100644 --- a/spinifex/handlers/rds/lifecycle.go +++ b/spinifex/handlers/rds/lifecycle.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "slices" "strings" @@ -430,7 +431,7 @@ func (c *natsInstanceCommander) RebootInstance(ctx context.Context, instanceID s // ownership check on the far side compares against that same account. func (c *natsInstanceCommander) send(ctx context.Context, instanceID string, attrs ec2v1.EC2CommandAttributes) error { cmd := ec2v1.EC2InstanceCommand{ID: instanceID, Attributes: attrs} - _, err := utils.NATSRequest[struct{}](ctx, c.nc, ec2v1.InstanceCommandSubject(instanceID), cmd, c.timeout, utils.GlobalAccountID) + _, err := utils.NATSRequest[struct{}](ctx, c.nc, ec2v1.InstanceCommandSubject(instanceID), cmd, c.timeout, awsidentifiers.GlobalAccountID) if err == nil { return nil } @@ -448,6 +449,6 @@ func (c *natsInstanceCommander) send(ctx context.Context, instanceID string, att func (c *natsInstanceCommander) StartStoppedInstance(ctx context.Context, instanceID string) error { _, err := utils.NATSRequest[handlers_ec2_instance.StartStoppedInstanceOutput](ctx, c.nc, "ec2.start", - handlers_ec2_instance.StartStoppedInstanceInput{InstanceID: instanceID}, c.timeout, utils.GlobalAccountID) + handlers_ec2_instance.StartStoppedInstanceInput{InstanceID: instanceID}, c.timeout, awsidentifiers.GlobalAccountID) return err } diff --git a/spinifex/handlers/rds/reconciler.go b/spinifex/handlers/rds/reconciler.go index 960e6df6c..e7eae6867 100644 --- a/spinifex/handlers/rds/reconciler.go +++ b/spinifex/handlers/rds/reconciler.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "sync" "time" @@ -14,7 +15,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/lifecycle/reconciler" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvlease" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" ) @@ -357,7 +357,7 @@ func (r *Reconciler) remediateSystemENISG(ctx context.Context, kv *kvstore.Bucke if _, err := r.svc.deps.Launch.VPC.ModifyNetworkInterfaceAttribute(ctx, &ec2.ModifyNetworkInterfaceAttributeInput{ NetworkInterfaceId: aws.String(rec.SystemENIID), Groups: aws.StringSlice([]string{sgID}), - }, utils.GlobalAccountID); err != nil { + }, awsidentifiers.GlobalAccountID); err != nil { return false, fmt.Errorf("rds: move the system NIC of %s onto %s: %w", rec.DBInstanceIdentifier, sgID, err) } // Recorded only once vpcd has accepted the change, so a failure above is @@ -383,11 +383,11 @@ func (r *Reconciler) ensuredSystemSG(ctx context.Context) (string, error) { if deps.VPC == nil || deps.Config == nil { return "", errors.New("rds reconciler: no VPC path is configured to place system NICs on their own security group") } - refs, err := EnsureSystemVPC(ctx, deps.SystemVPC, &deps.Config.RDS, utils.GlobalAccountID, deps.Config.Region) + refs, err := EnsureSystemVPC(ctx, deps.SystemVPC, &deps.Config.RDS, awsidentifiers.GlobalAccountID, deps.Config.Region) if err != nil { return "", err } - sgID, err := EnsureSystemSecurityGroup(ctx, deps.VPC, utils.GlobalAccountID, deps.Config.Region, refs.VpcID) + sgID, err := EnsureSystemSecurityGroup(ctx, deps.VPC, awsidentifiers.GlobalAccountID, deps.Config.Region, refs.VpcID) if err != nil { return "", err } @@ -764,7 +764,7 @@ func (s *Service) findEC2SnapshotFor(ctx context.Context, accountID, dbSnapshotI Values: aws.StringSlice([]string{accountID}), }, }, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { if awserrors.IsNotFound(err) { return "", nil @@ -879,7 +879,7 @@ func NewDescribeInstanceState(describe func(*ec2.DescribeInstancesInput, string) func (d *describeInstanceState) InstanceState(_ context.Context, instanceID, _ string) (string, error) { out, err := d.describe(&ec2.DescribeInstancesInput{ InstanceIds: []*string{aws.String(instanceID)}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", err } diff --git a/spinifex/handlers/rds/replace.go b/spinifex/handlers/rds/replace.go index 6cb8e37d4..874340f77 100644 --- a/spinifex/handlers/rds/replace.go +++ b/spinifex/handlers/rds/replace.go @@ -4,10 +4,10 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - "github.com/mulgadc/spinifex/spinifex/utils" ) // The one primitive a class change, a storage grow that rides the same outage, @@ -55,7 +55,7 @@ func (s *Service) replaceInstanceVM(ctx context.Context, kv *kvstore.Bucket, acc } // Resolve the profile before stopping the engine or terminating its VM. A // profile failure must leave the current database serving. - profileARN, err := ensureInstanceProfile(s.deps.IAM, utils.GlobalAccountID) + profileARN, err := ensureInstanceProfile(s.deps.IAM, awsidentifiers.GlobalAccountID) if err != nil { return err } @@ -248,5 +248,5 @@ func (s *Service) deleteSystemENI(ctx context.Context, rec *DBInstanceRecord) { if rec.SystemENIID == "" || s.deps.Launch.VPC == nil { return } - deleteLaunchENI(ctx, s.deps.Launch.VPC, utils.GlobalAccountID, rec.SystemENIID) + deleteLaunchENI(ctx, s.deps.Launch.VPC, awsidentifiers.GlobalAccountID, rec.SystemENIID) } diff --git a/spinifex/handlers/rds/replace_test.go b/spinifex/handlers/rds/replace_test.go index d47b1a440..d14447b47 100644 --- a/spinifex/handlers/rds/replace_test.go +++ b/spinifex/handlers/rds/replace_test.go @@ -3,10 +3,10 @@ package handlers_rds import ( "context" "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "time" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -351,5 +351,5 @@ func TestReplaceInstanceVM_LaunchesWithTheInstancesOwnIdentity(t *testing.T) { assert.Equal(t, testAccountID, h.launch.launcher.input.ExtraENIs[0].AccountID) assert.NotEqual(t, testAccountID, h.launch.launcher.input.AccountID, "the VM and its management NIC live in the system account") - assert.Equal(t, rdsInstanceProfileARN(utils.GlobalAccountID), h.launch.launcher.input.IamInstanceProfileArn) + assert.Equal(t, rdsInstanceProfileARN(awsidentifiers.GlobalAccountID), h.launch.launcher.input.IamInstanceProfileArn) } diff --git a/spinifex/handlers/rds/restore.go b/spinifex/handlers/rds/restore.go index bdebfbf46..98d34693a 100644 --- a/spinifex/handlers/rds/restore.go +++ b/spinifex/handlers/rds/restore.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "strings" @@ -13,7 +14,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - "github.com/mulgadc/spinifex/spinifex/utils" ) // Builds a new DB instance on a volume created from the snapshot. It is a fresh @@ -65,7 +65,7 @@ func (s *Service) RestoreDBInstanceFromDBSnapshot(ctx context.Context, input *rd } // The agent cannot bootstrap without this profile, so resolve it before the // identifier reservation or restored-volume creation. - profileARN, err := ensureInstanceProfile(s.deps.IAM, utils.GlobalAccountID) + profileARN, err := ensureInstanceProfile(s.deps.IAM, awsidentifiers.GlobalAccountID) if err != nil { return nil, err } @@ -384,7 +384,7 @@ func (s *Service) createRestoreVolume(ctx context.Context, req *validatedCreate, {Key: aws.String(rdsInstanceTagKey), Value: aws.String(req.Identifier)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return nil, fmt.Errorf("rds: create the data volume for %s from %s: %w", req.Identifier, snapshot.DBSnapshotIdentifier, err) @@ -404,7 +404,7 @@ func (s *Service) discardRestoreVolume(ctx context.Context, volumeID string) { defer cancel() if _, err := s.deps.Launch.Volume.DeleteVolume(rbCtx, &ec2.DeleteVolumeInput{ VolumeId: aws.String(volumeID), - }, utils.GlobalAccountID); err != nil && !awserrors.IsNotFound(err) { + }, awsidentifiers.GlobalAccountID); err != nil && !awserrors.IsNotFound(err) { slog.WarnContext(rbCtx, "rds: rollback delete of a restored data volume failed", "volumeId", volumeID, "err", err) } diff --git a/spinifex/handlers/rds/restore_test.go b/spinifex/handlers/rds/restore_test.go index a4a14a76c..3245b74ac 100644 --- a/spinifex/handlers/rds/restore_test.go +++ b/spinifex/handlers/rds/restore_test.go @@ -2,6 +2,7 @@ package handlers_rds import ( "errors" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "testing" "time" @@ -10,7 +11,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -57,7 +57,7 @@ func TestRestoreDBInstanceFromDBSnapshot_BuildsANewInstanceOnTheSnapshotsData(t assert.Equal(t, tags.ManagedByRDS, tagOf(created[0].TagSpecifications, tags.ManagedByKey)) // System-owned like every RDS data volume, so the customer reaches it only // through the DB instance. - assert.Equal(t, []string{utils.GlobalAccountID}, h.launch.volumes.accts) + assert.Equal(t, []string{awsidentifiers.GlobalAccountID}, h.launch.volumes.accts) stored := h.instance(t, testRestoredID) assert.Equal(t, "vol-rdsdata01", stored.DataVolumeID) @@ -72,7 +72,7 @@ func TestRestoreDBInstanceFromDBSnapshot_BuildsANewInstanceOnTheSnapshotsData(t assert.Empty(t, stored.Bootstrap.MasterUserPassword) require.NotNil(t, h.launch.launcher.input) - assert.Equal(t, rdsInstanceProfileARN(utils.GlobalAccountID), h.launch.launcher.input.IamInstanceProfileArn) + assert.Equal(t, rdsInstanceProfileARN(awsidentifiers.GlobalAccountID), h.launch.launcher.input.IamInstanceProfileArn) } func TestRestoreDBInstanceFromDBSnapshot_IAMFailurePrecedesReservationAndVolume(t *testing.T) { diff --git a/spinifex/handlers/rds/snapshot.go b/spinifex/handlers/rds/snapshot.go index 4ecc5a01c..95edd3e2a 100644 --- a/spinifex/handlers/rds/snapshot.go +++ b/spinifex/handlers/rds/snapshot.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "slices" "strings" @@ -15,7 +16,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - "github.com/mulgadc/spinifex/spinifex/utils" ) // The EC2 snapshot surface the RDS control plane drives. A DB snapshot is an @@ -174,7 +174,7 @@ func (s *Service) snapshotDataVolume(ctx context.Context, accountID string, rec {Key: aws.String(rdsSnapshotAccountTagKey), Value: aws.String(accountID)}, }, }}, - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return "", crashConsistent, fmt.Errorf("rds: snapshot the data volume of %s: %w", rec.DBInstanceIdentifier, err) } @@ -368,7 +368,7 @@ func (s *Service) deleteEC2Snapshot(ctx context.Context, kv *kvstore.Bucket, acc } _, err := s.deps.Snapshots.DeleteSnapshot(ctx, &ec2.DeleteSnapshotInput{ SnapshotId: aws.String(rec.SnapshotID), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) switch { case err == nil || awserrors.IsNotFound(err): return nil @@ -444,7 +444,7 @@ func (s *Service) reclaimRetainedVolume(ctx context.Context, kv *kvstore.Bucket, } _, err = s.deps.Launch.Volume.DeleteVolume(ctx, &ec2.DeleteVolumeInput{ VolumeId: aws.String(retained.VolumeID), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) switch { case err == nil || awserrors.IsNotFound(err): case awserrors.IsErrorCode(err, awserrors.ErrorVolumeInUse): diff --git a/spinifex/handlers/rds/storage.go b/spinifex/handlers/rds/storage.go index b0cbb3fb9..5912a34e3 100644 --- a/spinifex/handlers/rds/storage.go +++ b/spinifex/handlers/rds/storage.go @@ -4,12 +4,12 @@ import ( "context" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - "github.com/mulgadc/spinifex/spinifex/utils" ) // ModifyVolume refuses a volume a running VM holds, so a storage grow is @@ -128,7 +128,7 @@ func (s *Service) growDataVolume(ctx context.Context, volumeID string, targetGiB if _, err := s.deps.Storage.ModifyVolume(ctx, &ec2.ModifyVolumeInput{ VolumeId: aws.String(volumeID), Size: aws.Int64(targetGiB), - }, utils.GlobalAccountID); err != nil { + }, awsidentifiers.GlobalAccountID); err != nil { return fmt.Errorf("grow the data volume %s to %d GiB: %w", volumeID, targetGiB, err) } slog.InfoContext(ctx, "rds: data volume grown", @@ -142,7 +142,7 @@ func (s *Service) growDataVolume(ctx context.Context, volumeID string, targetGiB func (s *Service) dataVolumeSize(ctx context.Context, volumeID string) (int64, error) { out, err := s.deps.Storage.DescribeVolumes(ctx, &ec2.DescribeVolumesInput{ VolumeIds: aws.StringSlice([]string{volumeID}), - }, utils.GlobalAccountID) + }, awsidentifiers.GlobalAccountID) if err != nil { return 0, fmt.Errorf("read the data volume %s: %w", volumeID, err) } diff --git a/spinifex/handlers/sts/assume_role.go b/spinifex/handlers/sts/assume_role.go index 5361b7124..e61c7d64b 100644 --- a/spinifex/handlers/sts/assume_role.go +++ b/spinifex/handlers/sts/assume_role.go @@ -10,6 +10,7 @@ import ( "encoding/json" "errors" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "log/slog" "regexp" "slices" @@ -24,7 +25,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" - "github.com/mulgadc/spinifex/spinifex/utils" ) const ( @@ -438,7 +438,7 @@ func matchAWSPrincipalEntry(clause, callerARN string) bool { if clause == globalWildcard { return true } - if utils.IsAccountID(clause) { + if awsidentifiers.IsAccountID(clause) { clause = arn.FormatIAMRoot(clause) } if clause == callerARN { diff --git a/spinifex/handlers/sts/assume_role_test.go b/spinifex/handlers/sts/assume_role_test.go index 21b50fdcf..62b4cdaf8 100644 --- a/spinifex/handlers/sts/assume_role_test.go +++ b/spinifex/handlers/sts/assume_role_test.go @@ -6,6 +6,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "strings" "testing" "time" @@ -15,13 +16,12 @@ import ( "github.com/aws/aws-sdk-go/service/sts" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) const ( - testCallerAccountID = utils.GlobalAccountID + testCallerAccountID = awsidentifiers.GlobalAccountID testCallerUserName = "alice" testCallerAccessKeyID = "AKIAIOSFODNN7EXAMPLE" testCrossAccountID = "999999999999" diff --git a/spinifex/runtime/compute/cache/cache_test.go b/spinifex/runtime/compute/cache/cache_test.go index d07dc337b..4df47f081 100644 --- a/spinifex/runtime/compute/cache/cache_test.go +++ b/spinifex/runtime/compute/cache/cache_test.go @@ -10,6 +10,7 @@ import ( "context" "encoding/json" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "sync" "testing" "time" @@ -19,7 +20,6 @@ import ( "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" instance "github.com/mulgadc/spinifex/spinifex/handlers/ec2/instance" "github.com/mulgadc/spinifex/spinifex/runtime/compute/vm" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go/jetstream" "github.com/stretchr/testify/require" "go.opentelemetry.io/otel" @@ -48,7 +48,7 @@ func newTestCache(t *testing.T, mutate ...func(*Config)) (*Cache, jetstream.KeyV Bucket: kvstore.Config{Name: bucket, History: 1}, Prefix: testPrefix, VisibleToCaller: instance.IsInstanceVisibleToCaller, - FallbackAccountID: utils.GlobalAccountID, + FallbackAccountID: awsidentifiers.GlobalAccountID, RetryInterval: 20 * time.Millisecond, } for _, m := range mutate { @@ -259,7 +259,7 @@ func TestPeriodicResync_TTLExpiredRecordRemovedWithNoWatchEvent(t *testing.T) { Bucket: kvstore.Config{Name: bucket, History: 1}, Prefix: testPrefix, VisibleToCaller: instance.IsInstanceVisibleToCaller, - FallbackAccountID: utils.GlobalAccountID, + FallbackAccountID: awsidentifiers.GlobalAccountID, RetryInterval: 20 * time.Millisecond, }) putRecord(t, kv, "i-ttl", testRecord("i-ttl", acctA, vm.StateTerminated)) @@ -614,7 +614,7 @@ func TestList_ReturnsOnlyThatAccountsInstances(t *testing.T) { func TestList_GlobalDoesNotSeeOtherAccountsInstances(t *testing.T) { c, kv, _ := newTestCache(t) putRecord(t, kv, "i-cust", testRecord("i-cust", acctA, vm.StateRunning)) - putRecord(t, kv, "i-global", testRecord("i-global", utils.GlobalAccountID, vm.StateRunning)) + putRecord(t, kv, "i-global", testRecord("i-global", awsidentifiers.GlobalAccountID, vm.StateRunning)) putRecord(t, kv, "i-legacy", testRecord("i-legacy", "", vm.StateRunning)) ctx := t.Context() @@ -626,7 +626,7 @@ func TestList_GlobalDoesNotSeeOtherAccountsInstances(t *testing.T) { return len(c.entries) == 3 }) - listGlobal, ready := c.List(ctx, utils.GlobalAccountID) + listGlobal, ready := c.List(ctx, awsidentifiers.GlobalAccountID) require.True(t, ready) require.ElementsMatch(t, []string{"i-global", "i-legacy"}, idsOf(listGlobal)) diff --git a/spinifex/runtime/compute/vm/volume_state_account_test.go b/spinifex/runtime/compute/vm/volume_state_account_test.go index b3f406659..8b1b71b6e 100644 --- a/spinifex/runtime/compute/vm/volume_state_account_test.go +++ b/spinifex/runtime/compute/vm/volume_state_account_test.go @@ -17,7 +17,7 @@ import ( // stateAccountID is the instance-owning account these tests seed. It is a real // account ID rather than a placeholder because the production key builder -// rejects anything utils.IsAccountID refuses. +// rejects anything awsidentifiers.IsAccountID refuses. const stateAccountID = "000000000042" // Volume documents are keyed under the owning account, so UpdateVolumeState's diff --git a/spinifex/runtime/roles/awsgw/awsgw.go b/spinifex/runtime/roles/awsgw/awsgw.go index b1f87dd4c..ecc71e966 100644 --- a/spinifex/runtime/roles/awsgw/awsgw.go +++ b/spinifex/runtime/roles/awsgw/awsgw.go @@ -5,6 +5,7 @@ import ( "crypto/tls" "crypto/x509" "fmt" + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "github.com/mulgadc/spinifex/spinifex/foundation/netaddr" "log/slog" "maps" @@ -540,7 +541,7 @@ func launchService(config *config.ClusterConfig) error { Bucket: kvstore.Config{Name: daemon.InstanceStateBucket, History: 1}, Prefix: daemon.InstanceRecordPrefix, VisibleToCaller: handlers_ec2_instance.IsInstanceVisibleToCaller, - FallbackAccountID: utils.GlobalAccountID, + FallbackAccountID: awsidentifiers.GlobalAccountID, }) go instanceCache.Run(janitorCtx) diff --git a/tests/integration/harness.go b/tests/integration/harness.go index e3431400f..e9671184e 100644 --- a/tests/integration/harness.go +++ b/tests/integration/harness.go @@ -20,6 +20,7 @@ package integration import ( + awsidentifiers "github.com/mulgadc/spinifex/spinifex/foundation/aws/identifiers" "encoding/json" "net/http/httptest" "testing" @@ -41,7 +42,6 @@ import ( handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" handlers_sts "github.com/mulgadc/spinifex/spinifex/handlers/sts" "github.com/mulgadc/spinifex/spinifex/providers/objectstore" - "github.com/mulgadc/spinifex/spinifex/utils" "github.com/nats-io/nats.go" "github.com/stretchr/testify/require" ) @@ -90,7 +90,7 @@ type Gateway struct { // responders. NATSConn *nats.Conn // AccountID is the account the seeded root credentials belong to - // (utils.GlobalAccountID). + // (awsidentifiers.GlobalAccountID). AccountID string // Config is the GatewayConfig SetupRoutes was built from, exposed so // tests can inspect or further wire fields StartGateway did not set. @@ -149,7 +149,7 @@ func startGateway(t *testing.T, collector *conformanceCollector, opts ...Option) require.NoError(t, iamSvc.SeedBootstrap(&handlers_iam.BootstrapData{ AccessKeyID: testAccessKeyID, EncryptedSecret: encryptedSecret, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, })) // ECR auth bridge signing key: reuses the IAM master key to encrypt the @@ -173,7 +173,7 @@ func startGateway(t *testing.T, collector *conformanceCollector, opts ...Option) // repositories must additionally call StartECRDaemonLite to subscribe a // real MetaServiceImpl or every ECR request will time out with no // responder. Blob/manifest bytes are memory-backed: no predastore. - ECRRegistry: ecrregistry.NewRegistry(objectstore.NewMemoryObjectStore(), handlers_ecr.NewNATSMetaStore(nc), utils.GlobalAccountID), + ECRRegistry: ecrregistry.NewRegistry(objectstore.NewMemoryObjectStore(), handlers_ecr.NewNATSMetaStore(nc), awsidentifiers.GlobalAccountID), ECRTokenIssuer: ecrauth.NewIssuer(signingKey, testECRAudience), ECRTokenVerifier: ecrauth.NewVerifier(verifyKeys, testECRAudience), // Ochre model access is deny-by-default, so without a grant store every @@ -198,7 +198,7 @@ func startGateway(t *testing.T, collector *conformanceCollector, opts ...Option) gw := &Gateway{ Server: srv, NATSConn: nc, - AccountID: utils.GlobalAccountID, + AccountID: awsidentifiers.GlobalAccountID, Config: cfg, }