This commit is contained in:
parent
6daec2fe3e
commit
0fd576a680
@ -528,7 +528,8 @@ func (m *ConfigManager) PreviewRollback(ctx context.Context, generation uint64,
|
||||
return nil, err
|
||||
}
|
||||
m.mu.RLock()
|
||||
currentGeneration := m.current.Generation
|
||||
current := cloneRuntimeConfig(m.current)
|
||||
currentGeneration := current.Generation
|
||||
pending := m.pending != nil
|
||||
m.mu.RUnlock()
|
||||
if pending || generation >= currentGeneration {
|
||||
@ -547,11 +548,22 @@ func (m *ConfigManager) PreviewRollback(ctx context.Context, generation uint64,
|
||||
if err := validateRollbackSSL(m.loader.root, backup); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changed := make(map[Section]bool)
|
||||
for _, change := range AuditChanges(current, backup) {
|
||||
section, _, _ := strings.Cut(change.Field, ".")
|
||||
changed[Section(section)] = true
|
||||
}
|
||||
changedSections := make([]Section, 0, len(changed))
|
||||
for _, section := range []Section{SectionRoot, SectionSMTP, SectionEmail, SectionSSL, SectionPlugins} {
|
||||
if changed[section] {
|
||||
changedSections = append(changedSections, section)
|
||||
}
|
||||
}
|
||||
return &RollbackPreview{
|
||||
CurrentGeneration: currentGeneration,
|
||||
TargetGeneration: generation,
|
||||
CandidateGeneration: currentGeneration + 1,
|
||||
ChangedSections: RequiredPartitionSections(),
|
||||
ChangedSections: changedSections,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
@ -1720,6 +1721,71 @@ func TestConfigManagerRollbackTicketIsBoundAndSingleUse(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManagerRollbackPreviewChangedSections(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
sections []Section
|
||||
mutate bool
|
||||
want []Section
|
||||
}{
|
||||
{name: "root_only", sections: []Section{SectionRoot}, mutate: true, want: []Section{SectionRoot}},
|
||||
{name: "email_only", sections: []Section{SectionEmail}, mutate: true, want: []Section{SectionEmail}},
|
||||
{name: "stable_order", sections: []Section{SectionEmail, SectionRoot}, mutate: true, want: []Section{SectionRoot, SectionEmail}},
|
||||
{name: "metadata_only", sections: []Section{SectionEmail}, want: []Section{}},
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
writeRuntimeFixture(t, root, validRuntimeConfig())
|
||||
options := ConfigLoaderOptions{Validation: RuntimeValidationOptions{HasSuperAdmin: true, Production: true}}
|
||||
manager, err := NewConfigManager(root, options)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, section := range test.sections {
|
||||
current := manager.Snapshot()
|
||||
var value any
|
||||
switch section {
|
||||
case SectionRoot:
|
||||
if test.mutate {
|
||||
current.Root.LogLevel = "warn"
|
||||
}
|
||||
value = current.Root
|
||||
case SectionEmail:
|
||||
if test.mutate {
|
||||
current.Email.Policy.MaxMessageBytes = 8 << 20
|
||||
}
|
||||
value = current.Email
|
||||
}
|
||||
payload, err := json.Marshal(value)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
updated, err := manager.Update(context.Background(), UpdateRequest{
|
||||
Section: section, ExpectedGeneration: current.Generation,
|
||||
Payload: payload, Actor: authorizedConfigActor(),
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := manager.Apply(context.Background(), updated.Generation); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
preview, err := manager.PreviewRollback(context.Background(), 1, authorizedConfigActor())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !slices.Equal(preview.ChangedSections, test.want) {
|
||||
t.Fatalf("changed sections = %v, want %v", preview.ChangedSections, test.want)
|
||||
}
|
||||
if preview.ChangedSections == nil {
|
||||
t.Fatal("changed sections must be an array, not null")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigManagerRollbackRequiresStepUpButNotRecoveryCodeForSuperAdmin(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
current := validRuntimeConfig()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user