This commit is contained in:
parent
3f6536bc87
commit
6de9fb1ebc
@ -173,6 +173,9 @@ func newConfigManager(root string, options ConfigLoaderOptions, security ConfigS
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := ValidateRuntimeSecrets(vault, runtime); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
snapshots, err := NewSnapshotStore(runtime, options.Validation)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -204,6 +207,9 @@ func (m *ConfigManager) Load(ctx context.Context) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ValidateRuntimeSecrets(m.vault, runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := m.snapshots.Publish(runtime); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -139,6 +139,55 @@ func TestConfigLoaderRejectsMissingPartitionAndGenerationMismatch(t *testing.T)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManagerRejectsUnavailableRuntimeSecretsAtStartup(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
runtime := validMySQLRuntimeConfig(t, root)
|
||||
otherVault, err := credential.NewVault(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foreignCiphertext, err := otherVault.Encrypt("foreign-database-secret")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
runtime.Root.Database.Password.Ciphertext = foreignCiphertext
|
||||
writeRuntimeFixture(t, root, runtime)
|
||||
|
||||
_, err = NewConfigManager(root, ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}})
|
||||
if !errors.Is(err, ErrSecretUnavailable) {
|
||||
t.Fatalf("manager construction error = %v, want unavailable secret", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManagerLoadRejectsUnavailableRuntimeSecretsWithoutChangingSnapshot(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
options := ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}}
|
||||
writeRuntimeFixture(t, root, validMySQLRuntimeConfig(t, root))
|
||||
manager, err := NewConfigManager(root, options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
current := manager.Snapshot()
|
||||
otherVault, err := credential.NewVault(t.TempDir())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
foreignCiphertext, err := otherVault.Encrypt("foreign-database-secret")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
current.Root.Database.Password.Ciphertext = foreignCiphertext
|
||||
writeRuntimeFixture(t, root, current)
|
||||
|
||||
if err := manager.Load(context.Background()); !errors.Is(err, ErrSecretUnavailable) {
|
||||
t.Fatalf("load error = %v, want unavailable secret", err)
|
||||
}
|
||||
loaded := manager.Snapshot()
|
||||
if loaded.Root.Database.Password.Ciphertext == foreignCiphertext {
|
||||
t.Fatal("rejected load changed the active snapshot")
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManagerApplyPreflightsSnapshotBeforePersisting(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeRuntimeFixture(t, root, validRuntimeConfig())
|
||||
|
||||
@ -116,7 +116,10 @@ func Current() *Service {
|
||||
return current.value
|
||||
}
|
||||
|
||||
func (s *Service) Preview(_ context.Context, source config.DatabaseConfig, currentGeneration uint64, targetName string) (Record, string, error) {
|
||||
func (s *Service) Preview(ctx context.Context, source config.DatabaseConfig, currentGeneration uint64, targetName string) (Record, string, error) {
|
||||
if err := contextError(ctx); err != nil {
|
||||
return Record{}, "", err
|
||||
}
|
||||
if s == nil || currentGeneration == 0 {
|
||||
return Record{}, "", ErrInvalidRequest
|
||||
}
|
||||
@ -139,6 +142,9 @@ func (s *Service) Preview(_ context.Context, source config.DatabaseConfig, curre
|
||||
if err != nil {
|
||||
return Record{}, "", ErrInvalidRequest
|
||||
}
|
||||
if err := contextError(ctx); err != nil {
|
||||
return Record{}, "", err
|
||||
}
|
||||
now := s.currentTime()
|
||||
item := &record{Record: Record{ID: id, Status: StatusPreviewed, Source: plan.Source, Target: plan.Target, CurrentGeneration: currentGeneration, CreatedAt: now, UpdatedAt: now}, tokenHash: sha256.Sum256([]byte(token)), expiresAt: now.Add(previewLifetime), sourceConfig: plan.sourceConfig, targetConfig: plan.targetConfig}
|
||||
s.mu.Lock()
|
||||
|
||||
@ -81,6 +81,21 @@ func TestPreviewRejectsUnsafeTargetName(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestPreviewHonorsCanceledContextWithoutCreatingRecord(t *testing.T) {
|
||||
service := NewService(testRunner{})
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
if _, _, err := service.Preview(ctx, testDatabase(t), 1, "smail_next"); !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("canceled preview returned %v, want context canceled", err)
|
||||
}
|
||||
service.mu.RLock()
|
||||
defer service.mu.RUnlock()
|
||||
if len(service.records) != 0 {
|
||||
t.Fatalf("canceled preview created %d records", len(service.records))
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailedExecutionKeepsSafeState(t *testing.T) {
|
||||
service := NewService(nil)
|
||||
record, token, err := service.Preview(context.Background(), testDatabase(t), 1, "smail_next")
|
||||
|
||||
Loading…
Reference in New Issue
Block a user