diff --git a/api-bucket-lifecycle.go b/api-bucket-lifecycle.go index fec5cece5..465fc151f 100644 --- a/api-bucket-lifecycle.go +++ b/api-bucket-lifecycle.go @@ -41,23 +41,28 @@ func (c *Client) SetBucketLifecycle(ctx context.Context, bucketName string, conf if config.Empty() { return c.removeBucketLifecycle(ctx, bucketName) } - + expAfterRepl := config.ExpireAfterReplication + config.ExpireAfterReplication = "" buf, err := xml.Marshal(config) if err != nil { return err } // Save the updated lifecycle. - return c.putBucketLifecycle(ctx, bucketName, buf) + return c.putBucketLifecycle(ctx, bucketName, buf, expAfterRepl) } // Saves a new bucket lifecycle. -func (c *Client) putBucketLifecycle(ctx context.Context, bucketName string, buf []byte) error { +func (c *Client) putBucketLifecycle(ctx context.Context, bucketName string, buf []byte, expAfterRepl string) error { // Get resources properly escaped and lined up before // using them in http request. urlValues := make(url.Values) urlValues.Set("lifecycle", "") - + var cheaders http.Header + if expAfterRepl != "" { + cheaders = make(http.Header) + cheaders.Set(minioLifecycleExpiryAfterReplication, expAfterRepl) + } // Content-length is mandatory for put lifecycle request reqMetadata := requestMetadata{ bucketName: bucketName, @@ -65,6 +70,7 @@ func (c *Client) putBucketLifecycle(ctx context.Context, bucketName string, buf contentBody: bytes.NewReader(buf), contentLength: int64(len(buf)), contentMD5Base64: sumMD5Base64(buf), + customHeader: cheaders, } // Execute PUT to upload a new bucket lifecycle. @@ -114,7 +120,7 @@ func (c *Client) GetBucketLifecycleWithInfo(ctx context.Context, bucketName stri return nil, time.Time{}, err } - bucketLifecycle, updatedAt, err := c.getBucketLifecycle(ctx, bucketName) + bucketLifecycle, updatedAt, expAfterRepl, err := c.getBucketLifecycle(ctx, bucketName) if err != nil { return nil, time.Time{}, err } @@ -123,11 +129,12 @@ func (c *Client) GetBucketLifecycleWithInfo(ctx context.Context, bucketName stri if err = xml.Unmarshal(bucketLifecycle, config); err != nil { return nil, time.Time{}, err } + config.ExpireAfterReplication = expAfterRepl return config, updatedAt, nil } // Request server for current bucket lifecycle. -func (c *Client) getBucketLifecycle(ctx context.Context, bucketName string) ([]byte, time.Time, error) { +func (c *Client) getBucketLifecycle(ctx context.Context, bucketName string) ([]byte, time.Time, string, error) { // Get resources properly escaped and lined up before // using them in http request. urlValues := make(url.Values) @@ -142,18 +149,18 @@ func (c *Client) getBucketLifecycle(ctx context.Context, bucketName string) ([]b defer closeResponse(resp) if err != nil { - return nil, time.Time{}, err + return nil, time.Time{}, "", err } if resp != nil { if resp.StatusCode != http.StatusOK { - return nil, time.Time{}, httpRespToErrorResponse(resp, bucketName, "") + return nil, time.Time{}, "", httpRespToErrorResponse(resp, bucketName, "") } } lcBytes, err := io.ReadAll(resp.Body) if err != nil { - return nil, time.Time{}, err + return nil, time.Time{}, "", err } const minIOLifecycleCfgUpdatedAt = "X-Minio-LifecycleConfig-UpdatedAt" @@ -161,9 +168,9 @@ func (c *Client) getBucketLifecycle(ctx context.Context, bucketName string) ([]b if timeStr := resp.Header.Get(minIOLifecycleCfgUpdatedAt); timeStr != "" { updatedAt, err = time.Parse(iso8601DateFormat, timeStr) if err != nil { - return nil, time.Time{}, err + return nil, time.Time{}, "", err } } - - return lcBytes, updatedAt, nil + expAfterRepl := resp.Header.Get(minioLifecycleExpiryAfterReplication) + return lcBytes, updatedAt, expAfterRepl, nil } diff --git a/api-put-object.go b/api-put-object.go index dbb476a97..aefd636c0 100644 --- a/api-put-object.go +++ b/api-put-object.go @@ -45,6 +45,8 @@ const ( ReplicationStatusFailed ReplicationStatus = "FAILED" // ReplicationStatusReplica indicates object is a replica of a source ReplicationStatusReplica ReplicationStatus = "REPLICA" + // ReplicationStatusReplicaEdge indicates object is a replica of a edge source + ReplicationStatusReplicaEdge ReplicationStatus = "REPLICA-EDGE" ) // Empty returns true if no replication status set. diff --git a/constants.go b/constants.go index 4099a37f9..d536428c7 100644 --- a/constants.go +++ b/constants.go @@ -127,4 +127,7 @@ const ( minioTgtReplicationReady = "X-Minio-Replication-Ready" // Header asks if delete marker replication request can be sent by source now. isMinioTgtReplicationReady = "X-Minio-Check-Replication-Ready" + + // Header indicating if ilm expiry ignores replication status + minioLifecycleExpiryAfterReplication = "X-Minio-Lifecycle-Expiry-After-Replication" ) diff --git a/pkg/lifecycle/lifecycle.go b/pkg/lifecycle/lifecycle.go index e706b57de..6a86bccb5 100644 --- a/pkg/lifecycle/lifecycle.go +++ b/pkg/lifecycle/lifecycle.go @@ -496,8 +496,9 @@ type Rule struct { // Configuration is a collection of Rule objects. type Configuration struct { - XMLName xml.Name `xml:"LifecycleConfiguration,omitempty" json:"-"` - Rules []Rule `xml:"Rule"` + XMLName xml.Name `xml:"LifecycleConfiguration,omitempty" json:"-"` + Rules []Rule `xml:"Rule"` + ExpireAfterReplication string `xml:"-" json:"-"` } // Empty check if lifecycle configuration is empty