diff --git a/cmd/spinifex/cmd/admin_acm.go b/cmd/spinifex/cmd/admin_acm.go index a2aaa16ba..a026a7cd1 100644 --- a/cmd/spinifex/cmd/admin_acm.go +++ b/cmd/spinifex/cmd/admin_acm.go @@ -9,7 +9,7 @@ import ( "time" "github.com/mulgadc/spinifex/spinifex/admin" - handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" + acmdomain "github.com/mulgadc/spinifex/spinifex/domains/acm" "github.com/mulgadc/spinifex/spinifex/utils" "github.com/spf13/cobra" ) @@ -86,9 +86,9 @@ func runCertForceRenew(cmd *cobra.Command, _ []string) { defer cancel() fmt.Printf("Force-renewing %s (account %s)...\n", certArn, accountID) - out, err := utils.NATSRequest[handlers_acm.ForceRenewCertificateOutput]( + out, err := utils.NATSRequest[acmdomain.ForceRenewCertificateOutput]( ctx, nc, "acm.ForceRenewCertificate", - &handlers_acm.ForceRenewCertificateInput{CertificateArn: certArn}, + &acmdomain.ForceRenewCertificateInput{CertificateArn: certArn}, 30*time.Second, accountID, ) if err != nil { @@ -144,7 +144,7 @@ func runCertCreateTenantCA(cmd *cobra.Command, _ []string) { fmt.Println("Tenant CA already exists — reporting current state (use --regenerate to replace it):") } - ca, err := handlers_acm.LoadOrCreateTenantCA(certPath, keyPath, domains) + ca, err := acmdomain.LoadOrCreateTenantCA(certPath, keyPath, domains) if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) diff --git a/spinifex/daemon/daemon.go b/spinifex/daemon/daemon.go index ec8789070..633a925b4 100644 --- a/spinifex/daemon/daemon.go +++ b/spinifex/daemon/daemon.go @@ -38,13 +38,13 @@ import ( "github.com/mulgadc/spinifex/spinifex/admin" "github.com/mulgadc/spinifex/spinifex/bootstrap/config" "github.com/mulgadc/spinifex/spinifex/bootstrap/preflight" - "github.com/mulgadc/spinifex/spinifex/foundation/state/clustersize" + acmdomain "github.com/mulgadc/spinifex/spinifex/domains/acm" "github.com/mulgadc/spinifex/spinifex/domains/ec2/instancetypes" handlers_ecr "github.com/mulgadc/spinifex/spinifex/domains/ecr" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" + "github.com/mulgadc/spinifex/spinifex/foundation/state/clustersize" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvutil" "github.com/mulgadc/spinifex/spinifex/foundation/telemetry" - handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" handlers_bedrock "github.com/mulgadc/spinifex/spinifex/handlers/bedrock" handlers_dns "github.com/mulgadc/spinifex/spinifex/handlers/dns" handlers_ec2_account "github.com/mulgadc/spinifex/spinifex/handlers/ec2/account" @@ -168,8 +168,8 @@ type Daemon struct { rdsReconciler *handlers_rds.Reconciler bedrockService *handlers_bedrock.Service bedrockReaper *handlers_bedrock.Reaper - acmService *handlers_acm.ACMServiceImpl - acmRenewalWorker *handlers_acm.Worker + acmService *acmdomain.ACMServiceImpl + acmRenewalWorker *acmdomain.Worker ochreVectorService handlers_ochrevector.VectorService ochreAppliance *handlers_ochrevector.Appliance ochreBackupService *handlers_ochrevector.BackupService @@ -2099,12 +2099,12 @@ func (d *Daemon) startCluster() error { // backoff — the key file can legitimately not be written yet during a // concurrent boot — but a master key that never arrives fails startCluster // after the retry window instead of leaving acmService permanently nil. - d.acmService, err = initServiceWithRetry("ACM service", func() (*handlers_acm.ACMServiceImpl, error) { + d.acmService, err = initServiceWithRetry("ACM service", func() (*acmdomain.ACMServiceImpl, error) { masterKey, mkErr := masterkey.ReadShared(filepath.Join(filepath.Dir(d.configPath), "master.key")) if mkErr != nil { return nil, fmt.Errorf("load ACM master key: %w", mkErr) } - return handlers_acm.NewACMServiceImplWithNATS(d.ctx, d.config, d.natsConn, masterKey) + return acmdomain.NewACMServiceImplWithNATS(d.ctx, d.config, d.natsConn, masterKey) }) if err != nil { return fmt.Errorf("failed to initialize ACM service: %w", err) @@ -2134,7 +2134,7 @@ func (d *Daemon) startCluster() error { // operator can tell at a glance, from daemon startup logs alone, which one // they are in. configDir := filepath.Dir(d.configPath) - tenantCA, tenantCAErr := handlers_acm.LoadTenantCA(admin.TenantCACertPath(configDir), admin.TenantCAKeyPath(configDir)) + tenantCA, tenantCAErr := acmdomain.LoadTenantCA(admin.TenantCACertPath(configDir), admin.TenantCAKeyPath(configDir)) if tenantCAErr != nil { slog.Warn("ACM: tenant private CA not found; PRIVATE_CA certificate requests will fail until one is created", "err", tenantCAErr) @@ -2150,7 +2150,7 @@ func (d *Daemon) startCluster() error { // tenant CA loaded later (or never, on a public-certificates-only // deployment) is not a startup dependency — the worker just finds // nothing to renew until one is wired. - d.acmRenewalWorker = handlers_acm.NewWorker(d.acmService, d.node) + d.acmRenewalWorker = acmdomain.NewWorker(d.acmService, d.node) d.shutdownWg.Go(func() { defer func() { if r := recover(); r != nil { diff --git a/spinifex/gateway/acm/authz.go b/spinifex/domains/acm/awsapi/authz.go similarity index 99% rename from spinifex/gateway/acm/authz.go rename to spinifex/domains/acm/awsapi/authz.go index b8cf4c651..d30e96c46 100644 --- a/spinifex/gateway/acm/authz.go +++ b/spinifex/domains/acm/awsapi/authz.go @@ -1,4 +1,4 @@ -package gateway_acm +package awsapi import ( "errors" diff --git a/spinifex/gateway/acm/authz_test.go b/spinifex/domains/acm/awsapi/authz_test.go similarity index 88% rename from spinifex/gateway/acm/authz_test.go rename to spinifex/domains/acm/awsapi/authz_test.go index be701db17..10b4e46c3 100644 --- a/spinifex/gateway/acm/authz_test.go +++ b/spinifex/domains/acm/awsapi/authz_test.go @@ -1,10 +1,10 @@ -package gateway_acm_test +package awsapi_test import ( "testing" + awsapi "github.com/mulgadc/spinifex/spinifex/domains/acm/awsapi" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - gateway_acm "github.com/mulgadc/spinifex/spinifex/gateway/acm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -20,7 +20,7 @@ func certARN(id string) string { func resolve(t *testing.T, action, body string) []string { t.Helper() - resources, err := gateway_acm.ResourceARNs(action, testRegion, testAccountID, []byte(body)) + resources, err := awsapi.ResourceARNs(action, testRegion, testAccountID, []byte(body)) require.NoError(t, err) return resources } @@ -92,7 +92,7 @@ func TestResourceARNsUnparseableBody(t *testing.T) { // A body carrying two spellings of one field is rejected: the gate and the // handler would otherwise name different certificates. func TestResourceARNsAmbiguousBody(t *testing.T) { - _, err := gateway_acm.ResourceARNs("DeleteCertificate", testRegion, testAccountID, + _, err := awsapi.ResourceARNs("DeleteCertificate", testRegion, testAccountID, []byte(`{"CertificateArn":"`+certARN("aaaa-1111")+`","certificateArn":"`+certARN("bbbb-2222")+`"}`)) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidParameterValue, err.Error()) @@ -111,7 +111,7 @@ func TestResourceARNsIdentifierIsAValue(t *testing.T) { // An action absent from the dispatch table cannot reach the resolver, but if one // ever did it fails closed rather than authorizing account-wide. func TestResourceARNsUnknownAction(t *testing.T) { - _, err := gateway_acm.ResourceARNs("BogusAction", testRegion, testAccountID, []byte(`{}`)) + _, err := awsapi.ResourceARNs("BogusAction", testRegion, testAccountID, []byte(`{}`)) require.Error(t, err) assert.Equal(t, awserrors.ErrorInvalidAction, err.Error()) } @@ -119,15 +119,15 @@ func TestResourceARNsUnknownAction(t *testing.T) { // Without a region or an account there is no ARN to build, and every action // falls back to the account-wide resource. func TestResourceARNsWithoutAnAnchor(t *testing.T) { - for _, action := range gateway_acm.ScopedActions() { - resources, err := gateway_acm.ResourceARNs(action, "", "", []byte(`{"CertificateArn":"`+certARN("aaaa-1111")+`"}`)) + for _, action := range awsapi.ScopedActions() { + resources, err := awsapi.ResourceARNs(action, "", "", []byte(`{"CertificateArn":"`+certARN("aaaa-1111")+`"}`)) require.NoError(t, err) assert.Equal(t, []string{"*"}, resources, "action %q", action) } } func TestHasScope(t *testing.T) { - assert.True(t, gateway_acm.HasScope("DeleteCertificate")) - assert.False(t, gateway_acm.HasScope("BogusAction")) - assert.Len(t, gateway_acm.ScopedActions(), 9) + assert.True(t, awsapi.HasScope("DeleteCertificate")) + assert.False(t, awsapi.HasScope("BogusAction")) + assert.Len(t, awsapi.ScopedActions(), 9) } diff --git a/spinifex/gateway/acm/certificates.go b/spinifex/domains/acm/awsapi/certificates.go similarity index 89% rename from spinifex/gateway/acm/certificates.go rename to spinifex/domains/acm/awsapi/certificates.go index 99375168b..14cd78bb1 100644 --- a/spinifex/gateway/acm/certificates.go +++ b/spinifex/domains/acm/awsapi/certificates.go @@ -1,4 +1,4 @@ -package gateway_acm +package awsapi import ( "context" @@ -14,6 +14,15 @@ import ( // natsTimeout bounds the gateway's wait for a daemon-side ACM response. const natsTimeout = 30 * time.Second +// RequestCertificate — CertificateManager.RequestCertificate. +func RequestCertificate(ctx context.Context, natsConn *nats.Conn, accountID string, body []byte) (*acm.RequestCertificateOutput, error) { + input := new(acm.RequestCertificateInput) + if err := unmarshalIfBody(body, input); err != nil { + return nil, errors.New(awserrors.ErrorInvalidParameterValue) + } + return utils.NATSRequest[acm.RequestCertificateOutput](ctx, natsConn, "acm.RequestCertificate", input, natsTimeout, accountID) +} + // ImportCertificate — CertificateManager.ImportCertificate. func ImportCertificate(ctx context.Context, natsConn *nats.Conn, accountID string, body []byte) (*acm.ImportCertificateOutput, error) { input := new(acm.ImportCertificateInput) diff --git a/spinifex/gateway/acm/certificates_test.go b/spinifex/domains/acm/awsapi/certificates_test.go similarity index 99% rename from spinifex/gateway/acm/certificates_test.go rename to spinifex/domains/acm/awsapi/certificates_test.go index b9d659049..985ce24f9 100644 --- a/spinifex/gateway/acm/certificates_test.go +++ b/spinifex/domains/acm/awsapi/certificates_test.go @@ -1,12 +1,12 @@ -package gateway_acm +package awsapi import ( "context" "net/http/httptest" "testing" - "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/internal/testkit" + "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/spinifex/gateway/acm/handler.go b/spinifex/domains/acm/awsapi/handler.go similarity index 83% rename from spinifex/gateway/acm/handler.go rename to spinifex/domains/acm/awsapi/handler.go index c91338a0a..e8b73e916 100644 --- a/spinifex/gateway/acm/handler.go +++ b/spinifex/domains/acm/awsapi/handler.go @@ -1,7 +1,7 @@ -// Package gateway_acm is the HTTP-side glue between awsgw and the ACM handlers. -// ACM speaks AWS JSON 1.1 (X-Amz-Target dispatch); errors are handled centrally -// by the shared gateway ErrorHandler. -package gateway_acm +// Package awsapi adapts ACM's AWS JSON 1.1 actions to the ACM domain's +// NATS-backed service contract. Generic gateway routing and authorization stay +// at the ingress boundary. +package awsapi import ( "encoding/json" diff --git a/spinifex/handlers/acm/getcertificate_test.go b/spinifex/domains/acm/getcertificate_test.go similarity index 99% rename from spinifex/handlers/acm/getcertificate_test.go rename to spinifex/domains/acm/getcertificate_test.go index 1efa245e0..7f7a0d7fc 100644 --- a/spinifex/handlers/acm/getcertificate_test.go +++ b/spinifex/domains/acm/getcertificate_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "context" diff --git a/spinifex/handlers/acm/privateca.go b/spinifex/domains/acm/privateca.go similarity index 99% rename from spinifex/handlers/acm/privateca.go rename to spinifex/domains/acm/privateca.go index 04fb73f9a..e77bd1d68 100644 --- a/spinifex/handlers/acm/privateca.go +++ b/spinifex/domains/acm/privateca.go @@ -1,4 +1,5 @@ -package handlers_acm +// Package acm owns ACM certificate state, issuance, renewal and AWS action adapters. +package acm import ( "crypto/rand" diff --git a/spinifex/handlers/acm/privateca_test.go b/spinifex/domains/acm/privateca_test.go similarity index 99% rename from spinifex/handlers/acm/privateca_test.go rename to spinifex/domains/acm/privateca_test.go index 38a29e4ad..e523a4c93 100644 --- a/spinifex/handlers/acm/privateca_test.go +++ b/spinifex/domains/acm/privateca_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "crypto/rand" diff --git a/spinifex/handlers/acm/renewal.go b/spinifex/domains/acm/renewal.go similarity index 99% rename from spinifex/handlers/acm/renewal.go rename to spinifex/domains/acm/renewal.go index 50790ce40..6f2b11d0f 100644 --- a/spinifex/handlers/acm/renewal.go +++ b/spinifex/domains/acm/renewal.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm // renewal.go implements the PRIVATE_CA renewal worker described in the ACM // design doc's "Renewal" section: a scan-and-lease worker, sharing the diff --git a/spinifex/handlers/acm/renewal_test.go b/spinifex/domains/acm/renewal_test.go similarity index 99% rename from spinifex/handlers/acm/renewal_test.go rename to spinifex/domains/acm/renewal_test.go index 3f56e7f75..05356f1fb 100644 --- a/spinifex/handlers/acm/renewal_test.go +++ b/spinifex/domains/acm/renewal_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "context" diff --git a/spinifex/handlers/acm/service_impl.go b/spinifex/domains/acm/service_impl.go similarity index 99% rename from spinifex/handlers/acm/service_impl.go rename to spinifex/domains/acm/service_impl.go index a55dce9cd..c7bbbdd6d 100644 --- a/spinifex/handlers/acm/service_impl.go +++ b/spinifex/domains/acm/service_impl.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "context" @@ -32,7 +32,7 @@ const ( // CertAuthority issues leaf certificates from the tenant private CA and // authorizes domains against the CA's own x509 name constraints. Satisfied by -// *TenantCA (handlers/acm/privateca.go), which is implemented separately; +// *TenantCA (privateca.go), which is implemented separately; // defined here as a narrow interface so this package builds and is testable // with a fake independently of that file. type CertAuthority interface { diff --git a/spinifex/handlers/acm/service_impl_test.go b/spinifex/domains/acm/service_impl_test.go similarity index 99% rename from spinifex/handlers/acm/service_impl_test.go rename to spinifex/domains/acm/service_impl_test.go index 6ad3a5ab5..336d64fd4 100644 --- a/spinifex/handlers/acm/service_impl_test.go +++ b/spinifex/domains/acm/service_impl_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "context" @@ -20,10 +20,10 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/private/protocol/json/jsonutil" "github.com/aws/aws-sdk-go/service/acm" + "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/foundation/aws/arn" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" - "github.com/mulgadc/spinifex/internal/testkit" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/spinifex/handlers/acm/store.go b/spinifex/domains/acm/store.go similarity index 99% rename from spinifex/handlers/acm/store.go rename to spinifex/domains/acm/store.go index ae1fa9a06..6052929bb 100644 --- a/spinifex/handlers/acm/store.go +++ b/spinifex/domains/acm/store.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "context" @@ -12,9 +12,9 @@ import ( "time" "github.com/mulgadc/spinifex/spinifex/foundation/aws/arn" - handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvutil" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/nats-io/nats.go" "github.com/nats-io/nats.go/jetstream" ) diff --git a/spinifex/handlers/acm/store_test.go b/spinifex/domains/acm/store_test.go similarity index 99% rename from spinifex/handlers/acm/store_test.go rename to spinifex/domains/acm/store_test.go index 378e40563..a3b645b7a 100644 --- a/spinifex/handlers/acm/store_test.go +++ b/spinifex/domains/acm/store_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "encoding/json" @@ -7,8 +7,8 @@ import ( "testing" "time" - handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/mulgadc/spinifex/internal/testkit" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/nats-io/nats.go/jetstream" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/spinifex/handlers/acm/tags_test.go b/spinifex/domains/acm/tags_test.go similarity index 99% rename from spinifex/handlers/acm/tags_test.go rename to spinifex/domains/acm/tags_test.go index 38034ec73..63633e7e6 100644 --- a/spinifex/handlers/acm/tags_test.go +++ b/spinifex/domains/acm/tags_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "context" diff --git a/spinifex/handlers/acm/validation_mode_test.go b/spinifex/domains/acm/validation_mode_test.go similarity index 99% rename from spinifex/handlers/acm/validation_mode_test.go rename to spinifex/domains/acm/validation_mode_test.go index a39cbaa98..5f8e9afa9 100644 --- a/spinifex/handlers/acm/validation_mode_test.go +++ b/spinifex/domains/acm/validation_mode_test.go @@ -1,4 +1,4 @@ -package handlers_acm +package acm import ( "testing" diff --git a/spinifex/gateway/acm.go b/spinifex/gateway/acm.go index eada44b38..a5a234ac2 100644 --- a/spinifex/gateway/acm.go +++ b/spinifex/gateway/acm.go @@ -2,65 +2,47 @@ package gateway import ( "context" - "encoding/json" "errors" "log/slog" "net/http" "strings" - "time" - "github.com/aws/aws-sdk-go/service/acm" + acmawsapi "github.com/mulgadc/spinifex/spinifex/domains/acm/awsapi" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - gateway_acm "github.com/mulgadc/spinifex/spinifex/gateway/acm" - "github.com/mulgadc/spinifex/spinifex/utils" ) -// acmNATSTimeout bounds the gateway's wait for a daemon-side ACM response. -// RequestCertificate itself returns immediately (issuance is asynchronous), -// so this is a network/queueing budget, not an issuance one. -const acmNATSTimeout = 30 * time.Second - -// acmHandler invokes a per-action ACM gateway function. +// acmHandler invokes a per-action ACM domain adapter after generic gateway +// authentication and authorization have completed. type acmHandler func(ctx context.Context, gw *GatewayConfig, accountID string, body []byte) (any, error) // acmActions maps the action suffix of X-Amz-Target (CertificateManager.) to its handler. var acmActions = map[string]acmHandler{ "ImportCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.ImportCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.ImportCertificate(ctx, gw.NATSConn, acct, b) }, - // RequestCertificate calls the daemon over NATS directly (rather than - // through package gateway_acm, which does not yet have a helper for it) — - // the same "acm.RequestCertificate" subject and request/response shape - // utils.NATSRequest uses everywhere else in this table. "RequestCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - input := new(acm.RequestCertificateInput) - if len(b) > 0 { - if err := json.Unmarshal(b, input); err != nil { - return nil, errors.New(awserrors.ErrorInvalidParameterValue) - } - } - return utils.NATSRequest[acm.RequestCertificateOutput](ctx, gw.NATSConn, "acm.RequestCertificate", input, acmNATSTimeout, acct) + return acmawsapi.RequestCertificate(ctx, gw.NATSConn, acct, b) }, "DescribeCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.DescribeCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.DescribeCertificate(ctx, gw.NATSConn, acct, b) }, "GetCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.GetCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.GetCertificate(ctx, gw.NATSConn, acct, b) }, "ListCertificates": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.ListCertificates(ctx, gw.NATSConn, acct, b) + return acmawsapi.ListCertificates(ctx, gw.NATSConn, acct, b) }, "DeleteCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.DeleteCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.DeleteCertificate(ctx, gw.NATSConn, acct, b) }, "ListTagsForCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.ListTagsForCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.ListTagsForCertificate(ctx, gw.NATSConn, acct, b) }, "AddTagsToCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.AddTagsToCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.AddTagsToCertificate(ctx, gw.NATSConn, acct, b) }, "RemoveTagsFromCertificate": func(ctx context.Context, gw *GatewayConfig, acct string, b []byte) (any, error) { - return gateway_acm.RemoveTagsFromCertificate(ctx, gw.NATSConn, acct, b) + return acmawsapi.RemoveTagsFromCertificate(ctx, gw.NATSConn, acct, b) }, } @@ -103,7 +85,7 @@ func (gw *GatewayConfig) ACM_Request(w http.ResponseWriter, r *http.Request) err return err } - resources, err := gateway_acm.ResourceARNs(action, gw.Region, accountID, body) + resources, err := acmawsapi.ResourceARNs(action, gw.Region, accountID, body) if err != nil { return err } @@ -120,6 +102,6 @@ func (gw *GatewayConfig) ACM_Request(w http.ResponseWriter, r *http.Request) err return err } - gateway_acm.WriteJSONResponse(w, output) + acmawsapi.WriteJSONResponse(w, output) return nil } diff --git a/spinifex/gateway/acm_authz_completeness_test.go b/spinifex/gateway/acm_authz_completeness_test.go index e9517e581..9b2dd18af 100644 --- a/spinifex/gateway/acm_authz_completeness_test.go +++ b/spinifex/gateway/acm_authz_completeness_test.go @@ -6,7 +6,7 @@ package gateway import ( "testing" - gateway_acm "github.com/mulgadc/spinifex/spinifex/gateway/acm" + acmawsapi "github.com/mulgadc/spinifex/spinifex/domains/acm/awsapi" "github.com/stretchr/testify/assert" ) @@ -15,13 +15,13 @@ import ( // behind by a deleted or renamed action fails too. func TestACMScopeTableIsExhaustive(t *testing.T) { for action := range acmActions { - assert.True(t, gateway_acm.HasScope(action), - "acm action %q has no resource scope entry: add one to acmScopes in gateway/acm/authz.go", action) + assert.True(t, acmawsapi.HasScope(action), + "acm action %q has no resource scope entry: add one to domains/acm/awsapi/authz.go", action) } - for _, action := range gateway_acm.ScopedActions() { + for _, action := range acmawsapi.ScopedActions() { _, ok := acmActions[action] assert.True(t, ok, - "acmScopes has an entry for %q, which the dispatch table does not serve: remove it from gateway/acm/authz.go", action) + "acmScopes has an entry for %q, which the dispatch table does not serve: remove it from domains/acm/awsapi/authz.go", action) } } diff --git a/spinifex/gateway/acm_authz_test.go b/spinifex/gateway/acm_authz_test.go index 71d5e3b6b..3cf9fffbf 100644 --- a/spinifex/gateway/acm_authz_test.go +++ b/spinifex/gateway/acm_authz_test.go @@ -10,8 +10,8 @@ import ( "testing" "github.com/mulgadc/bluebottle/pkg/sigv4" + acmawsapi "github.com/mulgadc/spinifex/spinifex/domains/acm/awsapi" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - gateway_acm "github.com/mulgadc/spinifex/spinifex/gateway/acm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -125,7 +125,7 @@ func TestACMRequest_ListIsAccountLevel(t *testing.T) { func TestACMRequest_WildcardPolicyStillPermitsEveryAction(t *testing.T) { gw := scopedPolicyGateway(statement("Allow", "acm:*", "*")) - for _, action := range gateway_acm.ScopedActions() { + for _, action := range acmawsapi.ScopedActions() { t.Run(action, func(t *testing.T) { assertPermitted(t, dispatchACM(t, gw, action, `{"CertificateArn":"`+acmCertARN("aaaa-1111")+`"}`)) }) diff --git a/spinifex/handlers/elbv2/cert_inuse_test.go b/spinifex/handlers/elbv2/cert_inuse_test.go index 41ddd25b2..e50b49683 100644 --- a/spinifex/handlers/elbv2/cert_inuse_test.go +++ b/spinifex/handlers/elbv2/cert_inuse_test.go @@ -15,9 +15,9 @@ import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/service/acm" "github.com/aws/aws-sdk-go/service/elbv2" - handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" - handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/mulgadc/spinifex/internal/testkit" + acmdomain "github.com/mulgadc/spinifex/spinifex/domains/acm" + handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -343,7 +343,7 @@ func TestReconcileCertInUseIndex_RestoresRenewalFanOut(t *testing.T) { elbv2Svc, err := NewELBv2ServiceImplWithNATS(nil, nc, masterKey) require.NoError(t, err) - acmSvc, err := handlers_acm.NewACMServiceImplWithNATS(context.Background(), nil, nc, masterKey) + acmSvc, err := acmdomain.NewACMServiceImplWithNATS(context.Background(), nil, nc, masterKey) require.NoError(t, err) acmSvc.CertMaterialUpdated = elbv2Svc.UpdateStoredConfigForCert @@ -443,7 +443,7 @@ func TestACMReimport_FansOutToInUseLoadBalancer(t *testing.T) { elbv2Svc, err := NewELBv2ServiceImplWithNATS(nil, nc, masterKey) require.NoError(t, err) - acmSvc, err := handlers_acm.NewACMServiceImplWithNATS(context.Background(), nil, nc, masterKey) + acmSvc, err := acmdomain.NewACMServiceImplWithNATS(context.Background(), nil, nc, masterKey) require.NoError(t, err) // Mirrors the daemon.go wiring between the two services. acmSvc.CertMaterialUpdated = elbv2Svc.UpdateStoredConfigForCert diff --git a/spinifex/handlers/elbv2/haproxy_certs_test.go b/spinifex/handlers/elbv2/haproxy_certs_test.go index 2f3549299..388e0aa9f 100644 --- a/spinifex/handlers/elbv2/haproxy_certs_test.go +++ b/spinifex/handlers/elbv2/haproxy_certs_test.go @@ -13,11 +13,11 @@ import ( "testing" "github.com/aws/aws-sdk-go/aws" + "github.com/mulgadc/spinifex/internal/testkit" + acmdomain "github.com/mulgadc/spinifex/spinifex/domains/acm" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" "github.com/mulgadc/spinifex/spinifex/lbagent" - "github.com/mulgadc/spinifex/internal/testkit" "github.com/mulgadc/spinifex/spinifex/utils" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -105,7 +105,7 @@ func TestConfigCertHash_ChangesOnRotation(t *testing.T) { func putTestCert(t *testing.T, svc *ELBv2ServiceImpl, arn, account, leaf, chain, key string) { t.Helper() - require.NoError(t, svc.acmStore.PutCert(t.Context(), &handlers_acm.CertRecord{ + require.NoError(t, svc.acmStore.PutCert(t.Context(), &acmdomain.CertRecord{ CertificateArn: arn, AccountID: account, Certificate: leaf, @@ -155,7 +155,7 @@ func TestResolveCertPEM_CrossesACMServiceKeyBoundary(t *testing.T) { // ACM-service-style writer: its own Store over the same bucket, wired with // the same deployment key rather than svc's own acmStore. - acmStore, err := handlers_acm.NewStore(t.Context(), nc, masterKey) + acmStore, err := acmdomain.NewStore(t.Context(), nc, masterKey) require.NoError(t, err) // A realistic EC private key PEM, not a placeholder — a non-PEM value @@ -168,7 +168,7 @@ func TestResolveCertPEM_CrossesACMServiceKeyBoundary(t *testing.T) { keyPEM := string(pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})) arn := "arn:aws:acm:ap-southeast-2:123456789012:certificate/cross-boundary" - require.NoError(t, acmStore.PutCert(t.Context(), &handlers_acm.CertRecord{ + require.NoError(t, acmStore.PutCert(t.Context(), &acmdomain.CertRecord{ CertificateArn: arn, AccountID: testAccountID, Certificate: "LEAF", diff --git a/spinifex/handlers/elbv2/service_impl.go b/spinifex/handlers/elbv2/service_impl.go index 2685bab2d..0bef5fbdf 100644 --- a/spinifex/handlers/elbv2/service_impl.go +++ b/spinifex/handlers/elbv2/service_impl.go @@ -20,11 +20,11 @@ import ( "github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/elbv2" "github.com/mulgadc/spinifex/spinifex/bootstrap/config" + acmdomain "github.com/mulgadc/spinifex/spinifex/domains/acm" resourcearn "github.com/mulgadc/spinifex/spinifex/foundation/aws/arn" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" "github.com/mulgadc/spinifex/spinifex/foundation/aws/tags" "github.com/mulgadc/spinifex/spinifex/foundation/state/kvstore" - handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" handlers_dns "github.com/mulgadc/spinifex/spinifex/handlers/dns" handlers_ec2_vpc "github.com/mulgadc/spinifex/spinifex/handlers/ec2/vpc" handlers_iam "github.com/mulgadc/spinifex/spinifex/handlers/iam" @@ -103,7 +103,7 @@ var _ ELBv2Service = (*ELBv2ServiceImpl)(nil) type ELBv2ServiceImpl struct { config *config.Config store *Store - acmStore *handlers_acm.Store // resolves listener cert ARNs → PEM; nil-safe (HTTPS unavailable when nil) + acmStore *acmdomain.Store // resolves listener cert ARNs → PEM; nil-safe (HTTPS unavailable when nil) nc *nats.Conn // NATS connection for JetStream KV store VPCService *handlers_ec2_vpc.VPCServiceImpl // nil-safe: ENI ops skipped when nil (e.g. in tests) InstanceLauncher SystemInstanceLauncher // nil-safe: system VM ops skipped when nil @@ -173,7 +173,7 @@ func NewELBv2ServiceImplWithNATS(cfg *config.Config, nc *nats.Conn, masterKey [] // found" everywhere it is consulted (resolveCertPEM, validateListenerCerts), // so a construction failure here must fail the whole service rather than // silently degrade every HTTPS listener. - acmStore, acmErr := handlers_acm.NewStore(ctx, nc, masterKey) + acmStore, acmErr := acmdomain.NewStore(ctx, nc, masterKey) if acmErr != nil { cancel() return nil, fmt.Errorf("failed to create ELBv2 ACM store: %w", acmErr) diff --git a/tests/integration/cross_tenant_test.go b/tests/integration/cross_tenant_test.go index 8add9dc76..2454e3873 100644 --- a/tests/integration/cross_tenant_test.go +++ b/tests/integration/cross_tenant_test.go @@ -18,9 +18,9 @@ import ( awscreds "github.com/aws/aws-sdk-go/aws/credentials" v4 "github.com/aws/aws-sdk-go/aws/signer/v4" "github.com/aws/aws-sdk-go/service/iam" + acmawsapi "github.com/mulgadc/spinifex/spinifex/domains/acm/awsapi" awsapi "github.com/mulgadc/spinifex/spinifex/domains/ecr/awsapi" "github.com/mulgadc/spinifex/spinifex/foundation/aws/errors" - gateway_acm "github.com/mulgadc/spinifex/spinifex/gateway/acm" gateway_ecs "github.com/mulgadc/spinifex/spinifex/gateway/ecs" gateway_eks "github.com/mulgadc/spinifex/spinifex/gateway/eks" gateway_elbv2 "github.com/mulgadc/spinifex/spinifex/gateway/elbv2" @@ -329,7 +329,7 @@ func sameHelper(casedAction string) string { func scopedServices() []scopedService { return []scopedService{ - {name: "acm", actions: gateway_acm.ScopedActions(), uncased: map[string]string{ + {name: "acm", actions: acmawsapi.ScopedActions(), uncased: map[string]string{ "GetCertificate": sameHelper("DescribeCertificate"), "ListTagsForCertificate": sameHelper("DescribeCertificate"), "AddTagsToCertificate": sameHelper("DescribeCertificate"), diff --git a/tests/integration/service_daemon_lite.go b/tests/integration/service_daemon_lite.go index c865ac1ae..4133b5ed0 100644 --- a/tests/integration/service_daemon_lite.go +++ b/tests/integration/service_daemon_lite.go @@ -7,7 +7,7 @@ import ( "testing" "github.com/mulgadc/spinifex/spinifex/bootstrap/config" - handlers_acm "github.com/mulgadc/spinifex/spinifex/handlers/acm" + acmdomain "github.com/mulgadc/spinifex/spinifex/domains/acm" handlers_ecs "github.com/mulgadc/spinifex/spinifex/handlers/ecs" handlers_eks "github.com/mulgadc/spinifex/spinifex/handlers/eks" handlers_elbv2 "github.com/mulgadc/spinifex/spinifex/handlers/elbv2" @@ -26,12 +26,12 @@ func StartServiceDaemonLite(t *testing.T, gw *Gateway) { masterKey, err := handlers_iam.GenerateMasterKey() require.NoError(t, err) - acm, err := handlers_acm.NewACMServiceImplWithNATS(t.Context(), cfg, nc, masterKey) + acm, err := acmdomain.NewACMServiceImplWithNATS(t.Context(), cfg, nc, masterKey) require.NoError(t, err, "construct ACM service") // Without a tenant CA no mode can issue, so every RequestCertificate would // be refused. The generated requests name example.com domains. caDir := t.TempDir() - acm.TenantCA, err = handlers_acm.LoadOrCreateTenantCA(filepath.Join(caDir, "ca.pem"), filepath.Join(caDir, "ca.key"), []string{"example.com"}) + acm.TenantCA, err = acmdomain.LoadOrCreateTenantCA(filepath.Join(caDir, "ca.pem"), filepath.Join(caDir, "ca.key"), []string{"example.com"}) require.NoError(t, err, "create ACM tenant CA") subscribeServiceMethods(t, nc, "acm", acm)