Files
minio/docs/investigations/r6/review_probe_test.go.txt
T
Feng Ruohang 0c61128d23 fix(replication): retry marker purges through persisted MRF
(cherry picked from commit cf381a7151ef25fc95ace5fedcd767fa19410de2)
Signed-off-by: Feng Ruohang <rh@vonng.com>
2026-09-16 00:37:55 +08:00

69 lines
3.8 KiB
Plaintext

package cmd
import (
"fmt"
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"github.com/minio/minio-go/v7"
"github.com/minio/minio/internal/bucket/replication"
xhttp "github.com/minio/minio/internal/http"
)
func TestReviewR6LegacyPurgeRetries(t *testing.T) {
var deletes atomic.Int32
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodHead {
w.Header().Set(xhttp.AmzDeleteMarker, "true")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
deletes.Add(1)
w.WriteHeader(http.StatusNoContent)
}))
defer server.Close()
client, err := minio.New(strings.TrimPrefix(server.URL, "http://"), &minio.Options{Region: "us-east-1", MaxRetries: 1})
if err != nil {
t.Fatal(err)
}
old := globalBucketTargetSys
globalBucketTargetSys = &BucketTargetSys{hc: map[string]epHealth{client.EndpointURL().Host: {Online: true}}}
defer func() { globalBucketTargetSys = old }()
d := DeletedObjectReplicationInfo{Bucket: "source", DeletedObject: DeletedObject{ObjectName: "marker", DeleteMarker: true, DeleteMarkerVersionID: mustGetUUID()}}
d.ReplicationState.Targets = map[string]replication.StatusType{"arn1": replication.Completed}
d.ReplicationState.PurgeTargets = map[string]VersionPurgeStatusType{"arn1": replication.VersionPurgePending}
result := replicateDeleteToTarget(t.Context(), d, &TargetClient{Client: client, ARN: "arn1", Bucket: "target"})
t.Logf("remote DELETE calls=%d, creation=%s, purge=%s", deletes.Load(), result.ReplicationStatus, result.VersionPurgeStatus)
if deletes.Load() != 1 || result.VersionPurgeStatus != replication.VersionPurgeComplete {
t.Error("pending legacy purge was not delivered")
}
}
func TestReviewR6PurgeExits(t *testing.T) {
for _, tc := range []struct{name string; creation replication.StatusType; purge VersionPurgeStatusType; head int; existing bool; want VersionPurgeStatusType; wantCalls int; wantReset bool}{
{"head_forbidden", replication.Pending, replication.VersionPurgePending, 403, false, replication.VersionPurgeFailed, 1, false},
{"failed_resync", replication.Completed, replication.VersionPurgePending, 405, true, replication.VersionPurgeFailed, 2, false},
{"already_purged", replication.Pending, replication.VersionPurgeComplete, 405, false, replication.VersionPurgeComplete, 0, false},
} {
t.Run(tc.name, func(t *testing.T){
var calls atomic.Int32
server:=httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter,r *http.Request){
calls.Add(1)
if r.Method==http.MethodHead {w.Header().Set(xhttp.AmzDeleteMarker,"true"); w.WriteHeader(tc.head);return}
w.WriteHeader(403);fmt.Fprint(w,`<Error><Code>AccessDenied</Code></Error>`)
}))
defer server.Close()
client,err:=minio.New(strings.TrimPrefix(server.URL,"http://"),&minio.Options{Region:"us-east-1",MaxRetries:1});if err!=nil{t.Fatal(err)}
old:=globalBucketTargetSys;globalBucketTargetSys=&BucketTargetSys{hc:map[string]epHealth{client.EndpointURL().Host:{Online:true}}};defer func(){globalBucketTargetSys=old}()
d:=DeletedObjectReplicationInfo{Bucket:"source",DeletedObject:DeletedObject{ObjectName:"marker",DeleteMarker:true,DeleteMarkerVersionID:mustGetUUID()}}
d.ReplicationState.Targets=map[string]replication.StatusType{"arn1":tc.creation}
d.ReplicationState.PurgeTargets=map[string]VersionPurgeStatusType{"arn1":tc.purge}
if tc.existing {d.OpType=replication.ExistingObjectReplicationType}
got:=replicateDeleteToTarget(t.Context(),d,&TargetClient{Client:client,ARN:"arn1",Bucket:"target",ResetID:"new-reset"})
t.Logf("calls=%d, creation=%s, purge=%s, reset=%q",calls.Load(),got.ReplicationStatus,got.VersionPurgeStatus,got.ResyncTimestamp)
if got.ReplicationStatus!=tc.creation || got.VersionPurgeStatus!=tc.want || int(calls.Load())!=tc.wantCalls || (got.ResyncTimestamp!="")!=tc.wantReset {t.Errorf("operation classified inconsistently: %+v",got)}
})
}
}