minio/internal/event/name.go
Krishnan Parthasarathi 7926401cbd
ilm: Handle DeleteAllVersions action differently for DEL markers (#19481)
i.e., this rule element doesn't apply to DEL markers.

This is a breaking change to how ExpiredObejctDeleteAllVersions
functions today. This is necessary to avoid the following highly probable
footgun scenario in the future.

Scenario:
The user uses tags-based filtering to select an object's time to live(TTL). 
The application sometimes deletes objects, too, making its latest
version a DEL marker. The previous implementation skipped tag-based filters
if the newest version was DEL marker, voiding the tag-based TTL. The user is
surprised to find objects that have expired sooner than expected.

* Add DelMarkerExpiration action

This ILM action removes all versions of an object if its
the latest version is a DEL marker.

```xml
<DelMarkerObjectExpiration>
    <Days> 10 </Days>
</DelMarkerObjectExpiration>
```

1. Applies only to objects whose,
  • The latest version is a DEL marker.
  • satisfies the number of days criteria
2. Deletes all versions of this object
3. Associated rule can't have tag-based filtering

Includes,
- New bucket event type for deletion due to DelMarkerExpiration
2024-04-30 18:11:10 -07:00

366 lines
10 KiB
Go

// Copyright (c) 2015-2021 MinIO, Inc.
//
// This file is part of MinIO Object Storage stack
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package event
import (
"encoding/json"
"encoding/xml"
)
// Name - event type enum.
// Refer http://docs.aws.amazon.com/AmazonS3/latest/dev/NotificationHowTo.html#notification-how-to-event-types-and-destinations
// for most basic values we have since extend this and its not really much applicable other than a reference point.
// "s3:Replication:OperationCompletedReplication" is a MinIO extension.
type Name int
// Values of event Name
const (
// Single event types (does not require expansion)
ObjectAccessedGet Name = 1 + iota
ObjectAccessedGetRetention
ObjectAccessedGetLegalHold
ObjectAccessedHead
ObjectAccessedAttributes
ObjectCreatedCompleteMultipartUpload
ObjectCreatedCopy
ObjectCreatedPost
ObjectCreatedPut
ObjectCreatedPutRetention
ObjectCreatedPutLegalHold
ObjectCreatedPutTagging
ObjectCreatedDeleteTagging
ObjectRemovedDelete
ObjectRemovedDeleteMarkerCreated
ObjectRemovedDeleteAllVersions
ObjectRemovedNoOP
BucketCreated
BucketRemoved
ObjectReplicationFailed
ObjectReplicationComplete
ObjectReplicationMissedThreshold
ObjectReplicationReplicatedAfterThreshold
ObjectReplicationNotTracked
ObjectRestorePost
ObjectRestoreCompleted
ObjectTransitionFailed
ObjectTransitionComplete
ObjectManyVersions
ObjectLargeVersions
PrefixManyFolders
ILMDelMarkerExpirationDelete
objectSingleTypesEnd
// Start Compound types that require expansion:
ObjectAccessedAll
ObjectCreatedAll
ObjectRemovedAll
ObjectReplicationAll
ObjectRestoreAll
ObjectTransitionAll
ObjectScannerAll
Everything
)
// The number of single names should not exceed 64.
// This will break masking. Use bit 63 as extension.
var _ = uint64(1 << objectSingleTypesEnd)
// Expand - returns expanded values of abbreviated event type.
func (name Name) Expand() []Name {
switch name {
case ObjectAccessedAll:
return []Name{
ObjectAccessedGet, ObjectAccessedHead,
ObjectAccessedGetRetention, ObjectAccessedGetLegalHold, ObjectAccessedAttributes,
}
case ObjectCreatedAll:
return []Name{
ObjectCreatedCompleteMultipartUpload, ObjectCreatedCopy,
ObjectCreatedPost, ObjectCreatedPut,
ObjectCreatedPutRetention, ObjectCreatedPutLegalHold,
ObjectCreatedPutTagging, ObjectCreatedDeleteTagging,
}
case ObjectRemovedAll:
return []Name{
ObjectRemovedDelete,
ObjectRemovedDeleteMarkerCreated,
ObjectRemovedNoOP,
ObjectRemovedDeleteAllVersions,
}
case ObjectReplicationAll:
return []Name{
ObjectReplicationFailed,
ObjectReplicationComplete,
ObjectReplicationNotTracked,
ObjectReplicationMissedThreshold,
ObjectReplicationReplicatedAfterThreshold,
}
case ObjectRestoreAll:
return []Name{
ObjectRestorePost,
ObjectRestoreCompleted,
}
case ObjectTransitionAll:
return []Name{
ObjectTransitionFailed,
ObjectTransitionComplete,
}
case ObjectScannerAll:
return []Name{
ObjectManyVersions,
ObjectLargeVersions,
PrefixManyFolders,
}
case Everything:
res := make([]Name, objectSingleTypesEnd-1)
for i := range res {
res[i] = Name(i + 1)
}
return res
default:
return []Name{name}
}
}
// Mask returns the type as mask.
// Compound "All" types are expanded.
func (name Name) Mask() uint64 {
if name < objectSingleTypesEnd {
return 1 << (name - 1)
}
var mask uint64
for _, n := range name.Expand() {
mask |= 1 << (n - 1)
}
return mask
}
// String - returns string representation of event type.
func (name Name) String() string {
switch name {
case BucketCreated:
return "s3:BucketCreated:*"
case BucketRemoved:
return "s3:BucketRemoved:*"
case ObjectAccessedAll:
return "s3:ObjectAccessed:*"
case ObjectAccessedGet:
return "s3:ObjectAccessed:Get"
case ObjectAccessedGetRetention:
return "s3:ObjectAccessed:GetRetention"
case ObjectAccessedGetLegalHold:
return "s3:ObjectAccessed:GetLegalHold"
case ObjectAccessedHead:
return "s3:ObjectAccessed:Head"
case ObjectAccessedAttributes:
return "s3:ObjectAccessed:Attributes"
case ObjectCreatedAll:
return "s3:ObjectCreated:*"
case ObjectCreatedCompleteMultipartUpload:
return "s3:ObjectCreated:CompleteMultipartUpload"
case ObjectCreatedCopy:
return "s3:ObjectCreated:Copy"
case ObjectCreatedPost:
return "s3:ObjectCreated:Post"
case ObjectCreatedPut:
return "s3:ObjectCreated:Put"
case ObjectCreatedPutTagging:
return "s3:ObjectCreated:PutTagging"
case ObjectCreatedDeleteTagging:
return "s3:ObjectCreated:DeleteTagging"
case ObjectCreatedPutRetention:
return "s3:ObjectCreated:PutRetention"
case ObjectCreatedPutLegalHold:
return "s3:ObjectCreated:PutLegalHold"
case ObjectRemovedAll:
return "s3:ObjectRemoved:*"
case ObjectRemovedDelete:
return "s3:ObjectRemoved:Delete"
case ObjectRemovedDeleteMarkerCreated:
return "s3:ObjectRemoved:DeleteMarkerCreated"
case ObjectRemovedNoOP:
return "s3:ObjectRemoved:NoOP"
case ObjectRemovedDeleteAllVersions:
return "s3:ObjectRemoved:DeleteAllVersions"
case ILMDelMarkerExpirationDelete:
return "s3:LifecycleDelMarkerExpiration:Delete"
case ObjectReplicationAll:
return "s3:Replication:*"
case ObjectReplicationFailed:
return "s3:Replication:OperationFailedReplication"
case ObjectReplicationComplete:
return "s3:Replication:OperationCompletedReplication"
case ObjectReplicationNotTracked:
return "s3:Replication:OperationNotTracked"
case ObjectReplicationMissedThreshold:
return "s3:Replication:OperationMissedThreshold"
case ObjectReplicationReplicatedAfterThreshold:
return "s3:Replication:OperationReplicatedAfterThreshold"
case ObjectRestoreAll:
return "s3:ObjectRestore:*"
case ObjectRestorePost:
return "s3:ObjectRestore:Post"
case ObjectRestoreCompleted:
return "s3:ObjectRestore:Completed"
case ObjectTransitionAll:
return "s3:ObjectTransition:*"
case ObjectTransitionFailed:
return "s3:ObjectTransition:Failed"
case ObjectTransitionComplete:
return "s3:ObjectTransition:Complete"
case ObjectManyVersions:
return "s3:Scanner:ManyVersions"
case ObjectLargeVersions:
return "s3:Scanner:LargeVersions"
case PrefixManyFolders:
return "s3:Scanner:BigPrefix"
}
return ""
}
// MarshalXML - encodes to XML data.
func (name Name) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
return e.EncodeElement(name.String(), start)
}
// UnmarshalXML - decodes XML data.
func (name *Name) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
var s string
if err := d.DecodeElement(&s, &start); err != nil {
return err
}
eventName, err := ParseName(s)
if err != nil {
return err
}
*name = eventName
return nil
}
// MarshalJSON - encodes to JSON data.
func (name Name) MarshalJSON() ([]byte, error) {
return json.Marshal(name.String())
}
// UnmarshalJSON - decodes JSON data.
func (name *Name) UnmarshalJSON(data []byte) error {
var s string
if err := json.Unmarshal(data, &s); err != nil {
return err
}
eventName, err := ParseName(s)
if err != nil {
return err
}
*name = eventName
return nil
}
// ParseName - parses string to Name.
func ParseName(s string) (Name, error) {
switch s {
case "s3:BucketCreated:*":
return BucketCreated, nil
case "s3:BucketRemoved:*":
return BucketRemoved, nil
case "s3:ObjectAccessed:*":
return ObjectAccessedAll, nil
case "s3:ObjectAccessed:Get":
return ObjectAccessedGet, nil
case "s3:ObjectAccessed:GetRetention":
return ObjectAccessedGetRetention, nil
case "s3:ObjectAccessed:GetLegalHold":
return ObjectAccessedGetLegalHold, nil
case "s3:ObjectAccessed:Head":
return ObjectAccessedHead, nil
case "s3:ObjectAccessed:Attributes":
return ObjectAccessedAttributes, nil
case "s3:ObjectCreated:*":
return ObjectCreatedAll, nil
case "s3:ObjectCreated:CompleteMultipartUpload":
return ObjectCreatedCompleteMultipartUpload, nil
case "s3:ObjectCreated:Copy":
return ObjectCreatedCopy, nil
case "s3:ObjectCreated:Post":
return ObjectCreatedPost, nil
case "s3:ObjectCreated:Put":
return ObjectCreatedPut, nil
case "s3:ObjectCreated:PutRetention":
return ObjectCreatedPutRetention, nil
case "s3:ObjectCreated:PutLegalHold":
return ObjectCreatedPutLegalHold, nil
case "s3:ObjectCreated:PutTagging":
return ObjectCreatedPutTagging, nil
case "s3:ObjectCreated:DeleteTagging":
return ObjectCreatedDeleteTagging, nil
case "s3:ObjectRemoved:*":
return ObjectRemovedAll, nil
case "s3:ObjectRemoved:Delete":
return ObjectRemovedDelete, nil
case "s3:ObjectRemoved:DeleteMarkerCreated":
return ObjectRemovedDeleteMarkerCreated, nil
case "s3:ObjectRemoved:NoOP":
return ObjectRemovedNoOP, nil
case "s3:ObjectRemoved:DeleteAllVersions":
return ObjectRemovedDeleteAllVersions, nil
case "s3:LifecycleDelMarkerExpiration:Delete":
return ILMDelMarkerExpirationDelete, nil
case "s3:Replication:*":
return ObjectReplicationAll, nil
case "s3:Replication:OperationFailedReplication":
return ObjectReplicationFailed, nil
case "s3:Replication:OperationCompletedReplication":
return ObjectReplicationComplete, nil
case "s3:Replication:OperationMissedThreshold":
return ObjectReplicationMissedThreshold, nil
case "s3:Replication:OperationReplicatedAfterThreshold":
return ObjectReplicationReplicatedAfterThreshold, nil
case "s3:Replication:OperationNotTracked":
return ObjectReplicationNotTracked, nil
case "s3:ObjectRestore:*":
return ObjectRestoreAll, nil
case "s3:ObjectRestore:Post":
return ObjectRestorePost, nil
case "s3:ObjectRestore:Completed":
return ObjectRestoreCompleted, nil
case "s3:ObjectTransition:Failed":
return ObjectTransitionFailed, nil
case "s3:ObjectTransition:Complete":
return ObjectTransitionComplete, nil
case "s3:ObjectTransition:*":
return ObjectTransitionAll, nil
case "s3:Scanner:ManyVersions":
return ObjectManyVersions, nil
case "s3:Scanner:LargeVersions":
return ObjectLargeVersions, nil
case "s3:Scanner:BigPrefix":
return PrefixManyFolders, nil
default:
return 0, &ErrInvalidEventName{s}
}
}