replication: default tag timestamps in CopyObject call (#20891)

If object is uploaded with tags, the internal tagging-timestamp tracked
for replication will be missing. Default to ModTime in such cases to
allow tags to be synced correctly.

Also fixing a regression in fetching tags and tag comparison
This commit is contained in:
Poorna 2025-02-04 00:35:55 -08:00 committed by GitHub
parent f4fd4ea66d
commit 64a8f2e554
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -958,7 +958,11 @@ func getReplicationAction(oi1 ObjectInfo, oi2 minio.ObjectInfo, opType replicati
} }
t, _ := tags.ParseObjectTags(oi1.UserTags) t, _ := tags.ParseObjectTags(oi1.UserTags)
if (oi2.UserTagCount > 0 && !reflect.DeepEqual(oi2.UserTags, t.ToMap())) || (oi2.UserTagCount != len(t.ToMap())) { oi2Map := make(map[string]string)
for k, v := range oi2.UserTags {
oi2Map[k] = v
}
if (oi2.UserTagCount > 0 && !reflect.DeepEqual(oi2Map, t.ToMap())) || (oi2.UserTagCount != len(t.ToMap())) {
return replicateMetadata return replicateMetadata
} }
@ -1447,13 +1451,14 @@ func (ri ReplicateObjectInfo) replicateAll(ctx context.Context, objectAPI Object
} }
rinfo.Duration = time.Since(startTime) rinfo.Duration = time.Since(startTime)
}() }()
sOpts := minio.StatObjectOptions{
oi, cerr := tgt.StatObject(ctx, tgt.Bucket, object, minio.StatObjectOptions{
VersionID: objInfo.VersionID, VersionID: objInfo.VersionID,
Internal: minio.AdvancedGetOptions{ Internal: minio.AdvancedGetOptions{
ReplicationProxyRequest: "false", ReplicationProxyRequest: "false",
}, },
}) }
sOpts.Set(xhttp.AmzTagDirective, "ACCESS")
oi, cerr := tgt.StatObject(ctx, tgt.Bucket, object, sOpts)
if cerr == nil { if cerr == nil {
rAction = getReplicationAction(objInfo, oi, ri.OpType) rAction = getReplicationAction(objInfo, oi, ri.OpType)
rinfo.ReplicationStatus = replication.Completed rinfo.ReplicationStatus = replication.Completed
@ -1538,19 +1543,30 @@ applyAction:
ReplicationRequest: true, // always set this to distinguish between `mc mirror` replication and serverside ReplicationRequest: true, // always set this to distinguish between `mc mirror` replication and serverside
}, },
} }
if tagTmStr, ok := objInfo.UserDefined[ReservedMetadataPrefixLower+TaggingTimestamp]; ok { // default timestamps to ModTime unless present in metadata
lkMap := caseInsensitiveMap(objInfo.UserDefined)
if _, ok := lkMap.Lookup(xhttp.AmzObjectLockLegalHold); ok {
dstOpts.Internal.LegalholdTimestamp = objInfo.ModTime
}
if _, ok := lkMap.Lookup(xhttp.AmzObjectLockRetainUntilDate); ok {
dstOpts.Internal.RetentionTimestamp = objInfo.ModTime
}
if objInfo.UserTags != "" {
dstOpts.Internal.TaggingTimestamp = objInfo.ModTime
}
if tagTmStr, ok := lkMap.Lookup(ReservedMetadataPrefixLower + TaggingTimestamp); ok {
ondiskTimestamp, err := time.Parse(time.RFC3339, tagTmStr) ondiskTimestamp, err := time.Parse(time.RFC3339, tagTmStr)
if err == nil { if err == nil {
dstOpts.Internal.TaggingTimestamp = ondiskTimestamp dstOpts.Internal.TaggingTimestamp = ondiskTimestamp
} }
} }
if retTmStr, ok := objInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockRetentionTimestamp]; ok { if retTmStr, ok := lkMap.Lookup(ReservedMetadataPrefixLower + ObjectLockRetentionTimestamp); ok {
ondiskTimestamp, err := time.Parse(time.RFC3339, retTmStr) ondiskTimestamp, err := time.Parse(time.RFC3339, retTmStr)
if err == nil { if err == nil {
dstOpts.Internal.RetentionTimestamp = ondiskTimestamp dstOpts.Internal.RetentionTimestamp = ondiskTimestamp
} }
} }
if lholdTmStr, ok := objInfo.UserDefined[ReservedMetadataPrefixLower+ObjectLockLegalHoldTimestamp]; ok { if lholdTmStr, ok := lkMap.Lookup(ReservedMetadataPrefixLower + ObjectLockLegalHoldTimestamp); ok {
ondiskTimestamp, err := time.Parse(time.RFC3339, lholdTmStr) ondiskTimestamp, err := time.Parse(time.RFC3339, lholdTmStr)
if err == nil { if err == nil {
dstOpts.Internal.LegalholdTimestamp = ondiskTimestamp dstOpts.Internal.LegalholdTimestamp = ondiskTimestamp