From 40ff43355c1f4f270d7ae6a3a773cc5f9df1a38c Mon Sep 17 00:00:00 2001 From: Evgenii Baidakov Date: Wed, 14 May 2025 12:14:34 +0400 Subject: [PATCH] *: Store lock meta in single attribute Closes #1159. Signed-off-by: Evgenii Baidakov --- api/layer/system_object.go | 38 +++++++++++++++++++++++++++++--------- api/s3headers/headers.go | 12 +++++++++--- docs/attribute_mapping.md | 15 +++++---------- 3 files changed, 43 insertions(+), 22 deletions(-) diff --git a/api/layer/system_object.go b/api/layer/system_object.go index 301c2102..82fa2e98 100644 --- a/api/layer/system_object.go +++ b/api/layer/system_object.go @@ -108,8 +108,7 @@ func (n *layer) getLockDataFromObjects(ctx context.Context, bkt *data.BucketInfo object.AttributeFilePath, object.AttributeTimestamp, object.AttributeExpirationEpoch, - s3headers.AttributeComplianceMode, - s3headers.AttributeRetentionUntilMode, + s3headers.AttributeLockMeta, } opts client.SearchObjectsOptions @@ -165,12 +164,21 @@ func (n *layer) getLockDataFromObjects(ctx context.Context, bkt *data.BucketInfo } } - psr.IsComplianceMode = item.Attributes[3] == "true" + if item.Attributes[3] != "" { + fields := make(map[string]string) + if err = json.Unmarshal([]byte(item.Attributes[3]), &fields); err != nil { + return nil, fmt.Errorf("unmarshal retention fields: %w", err) + } - if item.Attributes[4] != "" { - psr.RetentionUntilMode, err = time.Parse(time.RFC3339, item.Attributes[4]) - if err != nil { - return nil, fmt.Errorf("parse retention until attribute: %w", err) + psr.IsComplianceMode = fields[s3headers.FieldComplianceMode] == "true" + + if fields[s3headers.FieldRetentionUntilMode] != "" { + ts, err := strconv.ParseInt(fields[s3headers.FieldRetentionUntilMode], 10, 64) + if err != nil { + return nil, fmt.Errorf("invalid retention until time: %w", err) + } + + psr.RetentionUntilMode = time.Unix(ts, 0).UTC() } } @@ -375,11 +383,23 @@ func (n *layer) attributesFromLock(ctx context.Context, lock *data.ObjectLock) ( return nil, fmt.Errorf("fetch time to epoch: %w", err) } - result[s3headers.AttributeRetentionUntilMode] = lock.Retention.Until.UTC().Format(time.RFC3339) + var ( + retention = make(map[string]string, 2) + attributePayload []byte + ) + + retention[s3headers.FieldRetentionUntilMode] = strconv.FormatInt(lock.Retention.Until.UTC().Unix(), 10) if lock.Retention.IsCompliance { - result[s3headers.AttributeComplianceMode] = "true" + retention[s3headers.FieldComplianceMode] = "true" } + + attributePayload, err = json.Marshal(retention) + if err != nil { + return nil, fmt.Errorf("marshal attribute: %w", err) + } + + result[s3headers.AttributeLockMeta] = string(attributePayload) } if lock.LegalHold != nil && lock.LegalHold.Enabled { diff --git a/api/s3headers/headers.go b/api/s3headers/headers.go index 317b7b30..248a4d29 100644 --- a/api/s3headers/headers.go +++ b/api/s3headers/headers.go @@ -60,15 +60,16 @@ const ( bucketSettingsPrefix = attributePrefix + "BucketSettings-" // BucketSettingsVersioning contains versioning setting for bucket. - BucketSettingsVersioning = bucketSettingsPrefix + "Versioning" - AttributeComplianceMode = bucketSettingsPrefix + "ComplianceMode" - AttributeRetentionUntilMode = bucketSettingsPrefix + "RetentionUntil" + BucketSettingsVersioning = bucketSettingsPrefix + "Versioning" // BucketSettingsMetaVersion contains version of bucket settings file. BucketSettingsMetaVersion = bucketSettingsPrefix + "MetaVersion" AttributeObjectVersion = attributePrefix + "ObjectVersion" AttributeObjectNonce = "__NEOFS__NONCE" + // Result: S3-Lock-Meta. + AttributeLockMeta = attributePrefix + "Lock-Meta" + NeoFSSystemMetadataPrefix = attributePrefix + "Meta-" // Result: S3-Meta-Algorithm. AttributeEncryptionAlgorithm = NeoFSSystemMetadataPrefix + "Algorithm" @@ -79,3 +80,8 @@ const ( AttributeDeleteMarker = NeoFSSystemMetadataPrefix + "DeleteMarker" UploadCompletedParts = NeoFSSystemMetadataPrefix + "Completed-Parts" ) + +const ( + FieldComplianceMode = "ComplianceMode" + FieldRetentionUntilMode = "RetentionUntil" +) diff --git a/docs/attribute_mapping.md b/docs/attribute_mapping.md index 4a2171c9..5024df7d 100644 --- a/docs/attribute_mapping.md +++ b/docs/attribute_mapping.md @@ -170,17 +170,11 @@ Specifies the version of the bucket settings object's file structure If bucket versioning is enabled, this attribute indicates which version the object belongs to. -### `S3-BucketSettings-ComplianceMode` +### `S3-Lock-Meta` -Indicates whether the object is under a compliance-mode retention lock. - -**Possible values:** - -- `true` - -### `S3-BucketSettings-RetentionUntil` - -Contains the retention expiration timestamp in `time.RFC3339` format. +Contains JSON encoded lock metadata in the next fields: +- `ComplianceMode`. Indicates whether the object is under a compliance-mode retention lock. +- `RetentionUntil`. Contains the retention expiration timestamp. ### S3-Algorithm @@ -235,6 +229,7 @@ contents for NeoFS lock objects. - `S3-MetaType: lock` - `S3-ObjectVersion` - `S3-Meta-VersioningState` +- `S3-Lock-Meta` ### Tags Object