fix: validate CORS state in replication status

Count only valid live CORS states in per-site summaries. Treat baselines and tombstones as absent, and diagnose malformed payloads or missing source timestamps.\n\nRefs: #77

Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
Feng Ruohang
2026-09-01 23:16:04 +08:00
parent 938603458d
commit f3438b2602
3 changed files with 49 additions and 21 deletions
+33 -5
View File
@@ -634,7 +634,7 @@ func testSiteReplicationStatusCountsCorsPerSite(obj ObjectLayer, _ string, bucke
globalSiteReplicationSys.Unlock()
}()
check := func(name string, wantLocal, wantRemote int) {
check := func(name string, wantLocal, wantRemote int, wantMismatch, wantReplicated bool) {
t.Helper()
status, err := globalSiteReplicationSys.siteReplicationStatus(ctx, obj, madmin.SRStatusOptions{Buckets: true})
if err != nil {
@@ -646,23 +646,51 @@ func testSiteReplicationStatusCountsCorsPerSite(obj ObjectLayer, _ string, bucke
if got := status.StatsSummary[remoteID].TotalCorsConfigCount; got != wantRemote {
t.Fatalf("%s: remote TotalCorsConfigCount = %d, want %d", name, got, wantRemote)
}
for _, id := range []string{localID, remoteID} {
bucketStatus := status.BucketStats[bucket][id]
wantSet := wantLocal != 0
if id == remoteID {
wantSet = wantRemote != 0
}
if bucketStatus.HasCorsCfgSet != wantSet {
t.Fatalf("%s: %s HasCorsCfgSet = %v, want %v", name, id, bucketStatus.HasCorsCfgSet, wantSet)
}
if bucketStatus.CorsCfgMismatch != wantMismatch {
t.Fatalf("%s: %s CorsCfgMismatch = %v, want %v", name, id, bucketStatus.CorsCfgMismatch, wantMismatch)
}
gotReplicated := status.StatsSummary[id].ReplicatedCorsConfig != 0
if gotReplicated != wantReplicated {
t.Fatalf("%s: %s ReplicatedCorsConfig = %d, want replicated %v", name, id, status.StatsSummary[id].ReplicatedCorsConfig, wantReplicated)
}
}
}
check("neither site", 0, 0)
check("neither site", 0, 0, false, false)
t1 := meta.Created.Add(time.Second)
if err = globalSiteReplicationSys.PeerBucketCorsConfigHandler(ctx, bucket, &encoded, t1); err != nil {
t.Fatal(err)
}
check("local site only", 1, 0)
check("local site only", 1, 0, true, false)
t2 := t1.Add(time.Second)
if err = globalSiteReplicationSys.PeerBucketCorsConfigHandler(ctx, bucket, nil, t2); err != nil {
t.Fatal(err)
}
remoteInfo.Buckets[bucket] = madmin.SRBucketInfo{
Bucket: bucket, CreatedAt: meta.Created, CorsConfig: &encoded,
}
check("live remote without timestamp", 0, 0, true, false)
invalidXML := base64.StdEncoding.EncodeToString([]byte(`not xml`))
remoteInfo.Buckets[bucket] = madmin.SRBucketInfo{
Bucket: bucket, CreatedAt: meta.Created, CorsConfig: &invalidXML, CorsConfigUpdatedAt: t2,
}
check("invalid remote XML", 0, 0, true, false)
remoteInfo.Buckets[bucket] = madmin.SRBucketInfo{
Bucket: bucket, CreatedAt: meta.Created, CorsConfig: &encoded, CorsConfigUpdatedAt: t2,
}
check("remote site only", 0, 1)
check("remote site only", 0, 1, true, false)
t3 := t2.Add(time.Second)
if err = globalSiteReplicationSys.PeerBucketCorsConfigHandler(ctx, bucket, &encoded, t3); err != nil {
@@ -671,7 +699,7 @@ func testSiteReplicationStatusCountsCorsPerSite(obj ObjectLayer, _ string, bucke
remoteInfo.Buckets[bucket] = madmin.SRBucketInfo{
Bucket: bucket, CreatedAt: meta.Created, CorsConfig: &encoded, CorsConfigUpdatedAt: t3,
}
check("both sites", 1, 1)
check("both sites", 1, 1, false, true)
}
func TestCORSReplicationStateOrdering(t *testing.T) {
+10 -9
View File
@@ -93,15 +93,16 @@ func testSiteReplicationStatusAccountsPerSiteAndSurvivesMalformedConfig(obj Obje
CreatedAt: localMeta.Created,
},
remoteBucket: {
Bucket: remoteBucket,
CreatedAt: remoteBucketMeta.Created,
Tags: encode(tagXML),
Versioning: encode(versioningXML),
ObjectLockConfig: encode(objectLockXML),
SSEConfig: encode(sseXML),
QuotaConfig: encode(quotaJSON),
Policy: remotePolicy,
CorsConfig: encode([]byte(testSiteReplicationCORSDoc)),
Bucket: remoteBucket,
CreatedAt: remoteBucketMeta.Created,
Tags: encode(tagXML),
Versioning: encode(versioningXML),
ObjectLockConfig: encode(objectLockXML),
SSEConfig: encode(sseXML),
QuotaConfig: encode(quotaJSON),
Policy: remotePolicy,
CorsConfig: encode([]byte(testSiteReplicationCORSDoc)),
CorsConfigUpdatedAt: remoteBucketMeta.Created,
},
},
}
+6 -7
View File
@@ -3557,13 +3557,12 @@ func (c *SiteReplicationSys) siteReplicationStatus(ctx context.Context, objAPI O
logInvalid("sse", err)
}
}
if s.CorsConfig != nil {
if _, err := decodeCORSReplicationPayload(s.CorsConfig); err == nil {
validCorsCfg[i] = true
corsCfgCount++
} else {
logInvalid("cors", err)
}
corsState, err := corsReplicationStateFromInfo(s.SRBucketInfo)
if err != nil {
logInvalid("cors", err)
} else if corsState.kind == corsReplicationLive {
validCorsCfg[i] = true
corsCfgCount++
}
ss, ok := info.StatsSummary[s.DeploymentID]
if !ok {