mirror of
https://github.com/minio/minio.git
synced 2025-11-07 04:42:56 -05:00
Fix evaluation of NewerNoncurrentVersions (#21096)
- Move VersionPurgeStatus into replication package - ilm: Evaluate policy w/ obj retention/replication - lifecycle: Use Evaluator to enforce ILM in scanner - Unit tests covering ILM, replication and retention - Simplify NewEvaluator constructor
This commit is contained in:
committed by
GitHub
parent
07f31e574c
commit
01447d2438
153
internal/bucket/lifecycle/evaluator.go
Normal file
153
internal/bucket/lifecycle/evaluator.go
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright (c) 2015-2025 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 lifecycle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
objlock "github.com/minio/minio/internal/bucket/object/lock"
|
||||
"github.com/minio/minio/internal/bucket/replication"
|
||||
)
|
||||
|
||||
// Evaluator - evaluates lifecycle policy on objects for the given lifecycle
|
||||
// configuration, lock retention configuration and replication configuration.
|
||||
type Evaluator struct {
|
||||
policy Lifecycle
|
||||
lockRetention *objlock.Retention
|
||||
replCfg *replication.Config
|
||||
}
|
||||
|
||||
// NewEvaluator - creates a new evaluator with the given lifecycle
|
||||
func NewEvaluator(policy Lifecycle) *Evaluator {
|
||||
return &Evaluator{
|
||||
policy: policy,
|
||||
}
|
||||
}
|
||||
|
||||
// WithLockRetention - sets the lock retention configuration for the evaluator
|
||||
func (e *Evaluator) WithLockRetention(lr *objlock.Retention) *Evaluator {
|
||||
e.lockRetention = lr
|
||||
return e
|
||||
}
|
||||
|
||||
// WithReplicationConfig - sets the replication configuration for the evaluator
|
||||
func (e *Evaluator) WithReplicationConfig(rcfg *replication.Config) *Evaluator {
|
||||
e.replCfg = rcfg
|
||||
return e
|
||||
}
|
||||
|
||||
// IsPendingReplication checks if the object is pending replication.
|
||||
func (e *Evaluator) IsPendingReplication(obj ObjectOpts) bool {
|
||||
if e.replCfg == nil {
|
||||
return false
|
||||
}
|
||||
if e.replCfg.HasActiveRules(obj.Name, true) && !obj.VersionPurgeStatus.Empty() {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// IsObjectLocked checks if it is appropriate to remove an
|
||||
// object according to locking configuration when this is lifecycle/ bucket quota asking.
|
||||
// (copied over from enforceRetentionForDeletion)
|
||||
func (e *Evaluator) IsObjectLocked(obj ObjectOpts) bool {
|
||||
if e.lockRetention == nil || !e.lockRetention.LockEnabled {
|
||||
return false
|
||||
}
|
||||
|
||||
if obj.DeleteMarker {
|
||||
return false
|
||||
}
|
||||
|
||||
lhold := objlock.GetObjectLegalHoldMeta(obj.UserDefined)
|
||||
if lhold.Status.Valid() && lhold.Status == objlock.LegalHoldOn {
|
||||
return true
|
||||
}
|
||||
|
||||
ret := objlock.GetObjectRetentionMeta(obj.UserDefined)
|
||||
if ret.Mode.Valid() && (ret.Mode == objlock.RetCompliance || ret.Mode == objlock.RetGovernance) {
|
||||
t, err := objlock.UTCNowNTP()
|
||||
if err != nil {
|
||||
// it is safe to assume that the object is locked when
|
||||
// we can't get the current time
|
||||
return true
|
||||
}
|
||||
if ret.RetainUntilDate.After(t) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// eval will return a lifecycle event for each object in objs for a given time.
|
||||
func (e *Evaluator) eval(objs []ObjectOpts, now time.Time) []Event {
|
||||
events := make([]Event, len(objs))
|
||||
var newerNoncurrentVersions int
|
||||
loop:
|
||||
for i, obj := range objs {
|
||||
event := e.policy.eval(obj, now, newerNoncurrentVersions)
|
||||
switch event.Action {
|
||||
case DeleteAllVersionsAction, DelMarkerDeleteAllVersionsAction:
|
||||
// Skip if bucket has object locking enabled; To prevent the
|
||||
// possibility of violating an object retention on one of the
|
||||
// noncurrent versions of this object.
|
||||
if e.lockRetention != nil && e.lockRetention.LockEnabled {
|
||||
event = Event{}
|
||||
} else {
|
||||
// No need to evaluate remaining versions' lifecycle
|
||||
// events after DeleteAllVersionsAction*
|
||||
events[i] = event
|
||||
break loop
|
||||
}
|
||||
|
||||
case DeleteVersionAction, DeleteRestoredVersionAction:
|
||||
// Defensive code, should never happen
|
||||
if obj.VersionID == "" {
|
||||
event.Action = NoneAction
|
||||
}
|
||||
if e.IsObjectLocked(obj) {
|
||||
event = Event{}
|
||||
}
|
||||
|
||||
if e.IsPendingReplication(obj) {
|
||||
event = Event{}
|
||||
}
|
||||
}
|
||||
if !obj.IsLatest {
|
||||
switch event.Action {
|
||||
case DeleteVersionAction:
|
||||
// this noncurrent version will be expired, nothing to add
|
||||
default:
|
||||
// this noncurrent version will be spared
|
||||
newerNoncurrentVersions++
|
||||
}
|
||||
}
|
||||
events[i] = event
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
// Eval will return a lifecycle event for each object in objs
|
||||
func (e *Evaluator) Eval(objs []ObjectOpts) ([]Event, error) {
|
||||
if len(objs) != objs[0].NumVersions {
|
||||
return nil, fmt.Errorf("number of versions mismatch, expected %d, got %d", objs[0].NumVersions, len(objs))
|
||||
}
|
||||
return e.eval(objs, time.Now().UTC()), nil
|
||||
}
|
||||
177
internal/bucket/lifecycle/evaluator_test.go
Normal file
177
internal/bucket/lifecycle/evaluator_test.go
Normal file
@@ -0,0 +1,177 @@
|
||||
// Copyright (c) 2015-2025 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 lifecycle
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestNewerNoncurrentVersions(t *testing.T) {
|
||||
prepLifecycleCfg := func(tagKeys []string, retainVersions []int) Lifecycle {
|
||||
var lc Lifecycle
|
||||
for i := range retainVersions {
|
||||
ruleID := fmt.Sprintf("rule-%d", i)
|
||||
tag := Tag{
|
||||
Key: tagKeys[i],
|
||||
Value: "minio",
|
||||
}
|
||||
lc.Rules = append(lc.Rules, Rule{
|
||||
ID: ruleID,
|
||||
Status: "Enabled",
|
||||
Filter: Filter{
|
||||
Tag: tag,
|
||||
set: true,
|
||||
},
|
||||
NoncurrentVersionExpiration: NoncurrentVersionExpiration{
|
||||
NewerNoncurrentVersions: retainVersions[i],
|
||||
set: true,
|
||||
},
|
||||
})
|
||||
}
|
||||
return lc
|
||||
}
|
||||
|
||||
lc := prepLifecycleCfg([]string{"tag3", "tag4", "tag5"}, []int{3, 4, 5})
|
||||
evaluator := NewEvaluator(lc)
|
||||
tagKeys := []string{"tag3", "tag3", "tag3", "tag4", "tag4", "tag5", "tag5"}
|
||||
verIDs := []string{
|
||||
"0NdAikoUVNGEpCUuB9vl.XyoMftMXCSg", "19M6Z405yFZuYygnnU9jKzsOBamTZK_7", "0PmlJdFWi_9d6l_dAkWrrhP.bBgtFk6V", // spellchecker:disable-line
|
||||
".MmRalFNNJyOLymgCtQ3.qsdoYpy8qkB", "Bjb4OlMW9Agx.Nrggh15iU6frGu2CLde", "ngBmUd_cVl6ckONI9XsKGpJjzimohrzZ", // spellchecker:disable-line
|
||||
"T6m1heTHLUtnByW2IOWJ3zM4JP9xXt2O", // spellchecker:disable-line
|
||||
}
|
||||
wantEvents := []Event{
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: DeleteVersionAction},
|
||||
}
|
||||
var objs []ObjectOpts
|
||||
curModTime := time.Date(2025, time.February, 10, 23, 0, 0, 0, time.UTC)
|
||||
for i := range tagKeys {
|
||||
obj := ObjectOpts{
|
||||
Name: "obj",
|
||||
VersionID: verIDs[i],
|
||||
ModTime: curModTime.Add(time.Duration(-i) * time.Second),
|
||||
UserTags: fmt.Sprintf("%s=minio", tagKeys[i]),
|
||||
NumVersions: len(verIDs),
|
||||
}
|
||||
if i == 0 {
|
||||
obj.IsLatest = true
|
||||
} else {
|
||||
obj.SuccessorModTime = curModTime.Add(time.Duration(-i+1) * time.Second)
|
||||
}
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
now := time.Date(2025, time.February, 10, 23, 0, 0, 0, time.UTC)
|
||||
gotEvents := evaluator.eval(objs, now)
|
||||
for i := range wantEvents {
|
||||
if gotEvents[i].Action != wantEvents[i].Action {
|
||||
t.Fatalf("got %v, want %v", gotEvents[i], wantEvents[i])
|
||||
}
|
||||
}
|
||||
|
||||
lc = prepLifecycleCfg([]string{"tag3", "tag4", "tag5"}, []int{1, 2, 3})
|
||||
objs = objs[:len(objs)-1]
|
||||
wantEvents = []Event{
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: DeleteVersionAction},
|
||||
{Action: NoneAction},
|
||||
{Action: DeleteVersionAction},
|
||||
{Action: NoneAction},
|
||||
}
|
||||
evaluator = NewEvaluator(lc)
|
||||
gotEvents = evaluator.eval(objs, now)
|
||||
for i := range wantEvents {
|
||||
if gotEvents[i].Action != wantEvents[i].Action {
|
||||
t.Fatalf("test-%d: got %v, want %v", i+1, gotEvents[i], wantEvents[i])
|
||||
}
|
||||
}
|
||||
|
||||
lc = Lifecycle{
|
||||
Rules: []Rule{
|
||||
{
|
||||
ID: "AllVersionsExpiration",
|
||||
Status: "Enabled",
|
||||
Filter: Filter{},
|
||||
Expiration: Expiration{
|
||||
Days: 1,
|
||||
DeleteAll: Boolean{
|
||||
val: true,
|
||||
set: true,
|
||||
},
|
||||
set: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
now = time.Date(2025, time.February, 12, 23, 0, 0, 0, time.UTC)
|
||||
evaluator = NewEvaluator(lc)
|
||||
gotEvents = evaluator.eval(objs, now)
|
||||
wantEvents = []Event{
|
||||
{Action: DeleteAllVersionsAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
{Action: NoneAction},
|
||||
}
|
||||
for i := range wantEvents {
|
||||
if gotEvents[i].Action != wantEvents[i].Action {
|
||||
t.Fatalf("test-%d: got %v, want %v", i+1, gotEvents[i], wantEvents[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmptyEvaluator(t *testing.T) {
|
||||
var objs []ObjectOpts
|
||||
curModTime := time.Date(2025, time.February, 10, 23, 0, 0, 0, time.UTC)
|
||||
for i := range 5 {
|
||||
obj := ObjectOpts{
|
||||
Name: "obj",
|
||||
VersionID: uuid.New().String(),
|
||||
ModTime: curModTime.Add(time.Duration(-i) * time.Second),
|
||||
NumVersions: 5,
|
||||
}
|
||||
if i == 0 {
|
||||
obj.IsLatest = true
|
||||
} else {
|
||||
obj.SuccessorModTime = curModTime.Add(time.Duration(-i+1) * time.Second)
|
||||
}
|
||||
objs = append(objs, obj)
|
||||
}
|
||||
|
||||
evaluator := NewEvaluator(Lifecycle{})
|
||||
events, err := evaluator.Eval(objs)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, event := range events {
|
||||
if event.Action != NoneAction {
|
||||
t.Fatalf("got %v, want %v", event.Action, NoneAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/minio/minio/internal/bucket/object/lock"
|
||||
"github.com/minio/minio/internal/bucket/replication"
|
||||
xhttp "github.com/minio/minio/internal/http"
|
||||
)
|
||||
|
||||
@@ -310,6 +311,10 @@ type ObjectOpts struct {
|
||||
TransitionStatus string
|
||||
RestoreOngoing bool
|
||||
RestoreExpires time.Time
|
||||
// to determine if object is locked due to retention
|
||||
UserDefined map[string]string
|
||||
VersionPurgeStatus replication.VersionPurgeStatusType
|
||||
ReplicationStatus replication.StatusType
|
||||
}
|
||||
|
||||
// ExpiredObjectDeleteMarker returns true if an object version referred to by o
|
||||
@@ -331,12 +336,12 @@ type Event struct {
|
||||
|
||||
// Eval returns the lifecycle event applicable now.
|
||||
func (lc Lifecycle) Eval(obj ObjectOpts) Event {
|
||||
return lc.eval(obj, time.Now().UTC())
|
||||
return lc.eval(obj, time.Now().UTC(), 0)
|
||||
}
|
||||
|
||||
// eval returns the lifecycle event applicable at the given now. If now is the
|
||||
// zero value of time.Time, it returns the upcoming lifecycle event.
|
||||
func (lc Lifecycle) eval(obj ObjectOpts, now time.Time) Event {
|
||||
func (lc Lifecycle) eval(obj ObjectOpts, now time.Time, remainingVersions int) Event {
|
||||
var events []Event
|
||||
if obj.ModTime.IsZero() {
|
||||
return Event{}
|
||||
@@ -404,17 +409,22 @@ func (lc Lifecycle) eval(obj ObjectOpts, now time.Time) Event {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip rules with newer noncurrent versions specified. These rules are
|
||||
// not handled at an individual version level. eval applies only to a
|
||||
// specific version.
|
||||
if !obj.IsLatest && rule.NoncurrentVersionExpiration.NewerNoncurrentVersions > 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if !obj.IsLatest && !rule.NoncurrentVersionExpiration.IsDaysNull() {
|
||||
// Non current versions should be deleted if their age exceeds non current days configuration
|
||||
// https://docs.aws.amazon.com/AmazonS3/latest/dev/intro-lifecycle-rules.html#intro-lifecycle-rules-actions
|
||||
if expectedExpiry := ExpectedExpiryTime(obj.SuccessorModTime, int(rule.NoncurrentVersionExpiration.NoncurrentDays)); now.IsZero() || now.After(expectedExpiry) {
|
||||
// NoncurrentVersionExpiration
|
||||
if !obj.IsLatest && rule.NoncurrentVersionExpiration.set {
|
||||
var (
|
||||
retainedEnough bool
|
||||
oldEnough bool
|
||||
)
|
||||
if rule.NoncurrentVersionExpiration.NewerNoncurrentVersions == 0 || remainingVersions >= rule.NoncurrentVersionExpiration.NewerNoncurrentVersions {
|
||||
retainedEnough = true
|
||||
}
|
||||
expectedExpiry := ExpectedExpiryTime(obj.SuccessorModTime, int(rule.NoncurrentVersionExpiration.NoncurrentDays))
|
||||
if now.IsZero() || now.After(expectedExpiry) {
|
||||
oldEnough = true
|
||||
}
|
||||
// > For the deletion to occur, both the <NoncurrentDays> and the <NewerNoncurrentVersions> values must be exceeded.
|
||||
// ref: https://docs.aws.amazon.com/AmazonS3/latest/dev/intro-lifecycle-rules.html#intro-lifecycle-rules-actions
|
||||
if retainedEnough && oldEnough {
|
||||
events = append(events, Event{
|
||||
Action: DeleteVersionAction,
|
||||
RuleID: rule.ID,
|
||||
@@ -529,7 +539,7 @@ func ExpectedExpiryTime(modTime time.Time, days int) time.Time {
|
||||
// SetPredictionHeaders sets time to expiry and transition headers on w for a
|
||||
// given obj.
|
||||
func (lc Lifecycle) SetPredictionHeaders(w http.ResponseWriter, obj ObjectOpts) {
|
||||
event := lc.eval(obj, time.Time{})
|
||||
event := lc.eval(obj, time.Time{}, 0)
|
||||
switch event.Action {
|
||||
case DeleteAction, DeleteVersionAction, DeleteAllVersionsAction, DelMarkerDeleteAllVersionsAction:
|
||||
w.Header()[xhttp.AmzExpiration] = []string{
|
||||
|
||||
@@ -960,7 +960,9 @@ func TestTransitionTier(t *testing.T) {
|
||||
// Go back seven days in the past
|
||||
now = now.Add(7 * 24 * time.Hour)
|
||||
|
||||
evt := lc.eval(obj1, now)
|
||||
evaluator := NewEvaluator(lc)
|
||||
evts := evaluator.eval([]ObjectOpts{obj1, obj2}, now)
|
||||
evt := evts[0]
|
||||
if evt.Action != TransitionAction {
|
||||
t.Fatalf("Expected action: %s but got %s", TransitionAction, evt.Action)
|
||||
}
|
||||
@@ -968,7 +970,7 @@ func TestTransitionTier(t *testing.T) {
|
||||
t.Fatalf("Expected TIER-1 but got %s", evt.StorageClass)
|
||||
}
|
||||
|
||||
evt = lc.eval(obj2, now)
|
||||
evt = evts[1]
|
||||
if evt.Action != TransitionVersionAction {
|
||||
t.Fatalf("Expected action: %s but got %s", TransitionVersionAction, evt.Action)
|
||||
}
|
||||
@@ -1036,14 +1038,16 @@ func TestTransitionTierWithPrefixAndTags(t *testing.T) {
|
||||
// Go back seven days in the past
|
||||
now = now.Add(7 * 24 * time.Hour)
|
||||
|
||||
evaluator := NewEvaluator(lc)
|
||||
evts := evaluator.eval([]ObjectOpts{obj1, obj2, obj3}, now)
|
||||
// Eval object 1
|
||||
evt := lc.eval(obj1, now)
|
||||
evt := evts[0]
|
||||
if evt.Action != NoneAction {
|
||||
t.Fatalf("Expected action: %s but got %s", NoneAction, evt.Action)
|
||||
}
|
||||
|
||||
// Eval object 2
|
||||
evt = lc.eval(obj2, now)
|
||||
evt = evts[1]
|
||||
if evt.Action != TransitionAction {
|
||||
t.Fatalf("Expected action: %s but got %s", TransitionAction, evt.Action)
|
||||
}
|
||||
@@ -1052,7 +1056,7 @@ func TestTransitionTierWithPrefixAndTags(t *testing.T) {
|
||||
}
|
||||
|
||||
// Eval object 3
|
||||
evt = lc.eval(obj3, now)
|
||||
evt = evts[2]
|
||||
if evt.Action != TransitionAction {
|
||||
t.Fatalf("Expected action: %s but got %s", TransitionAction, evt.Action)
|
||||
}
|
||||
@@ -1466,7 +1470,9 @@ func TestDeleteAllVersions(t *testing.T) {
|
||||
NumVersions: 4,
|
||||
}
|
||||
|
||||
event := lc.eval(opts, time.Time{})
|
||||
evaluator := NewEvaluator(lc)
|
||||
events := evaluator.eval([]ObjectOpts{opts}, time.Time{})
|
||||
event := events[0]
|
||||
if event.Action != TransitionAction {
|
||||
t.Fatalf("Expected %v action but got %v", TransitionAction, event.Action)
|
||||
}
|
||||
@@ -1503,7 +1509,9 @@ func TestDeleteAllVersions(t *testing.T) {
|
||||
DeleteMarker: true,
|
||||
NumVersions: 4,
|
||||
}
|
||||
event = lc.eval(opts, time.Time{})
|
||||
evaluator = NewEvaluator(lc)
|
||||
events = evaluator.eval([]ObjectOpts{opts}, time.Time{})
|
||||
event = events[0]
|
||||
if event.Action != DelMarkerDeleteAllVersionsAction {
|
||||
t.Fatalf("Expected %v action but got %v", DelMarkerDeleteAllVersionsAction, event.Action)
|
||||
}
|
||||
|
||||
@@ -51,3 +51,27 @@ func (s StatusType) String() string {
|
||||
func (s StatusType) Empty() bool {
|
||||
return string(s) == ""
|
||||
}
|
||||
|
||||
// VersionPurgeStatusType represents status of a versioned delete or permanent delete w.r.t bucket replication
|
||||
type VersionPurgeStatusType string
|
||||
|
||||
const (
|
||||
// VersionPurgePending - versioned delete replication is pending.
|
||||
VersionPurgePending VersionPurgeStatusType = "PENDING"
|
||||
|
||||
// VersionPurgeComplete - versioned delete replication is now complete, erase version on disk.
|
||||
VersionPurgeComplete VersionPurgeStatusType = "COMPLETE"
|
||||
|
||||
// VersionPurgeFailed - versioned delete replication failed.
|
||||
VersionPurgeFailed VersionPurgeStatusType = "FAILED"
|
||||
)
|
||||
|
||||
// Empty returns true if purge status was not set.
|
||||
func (v VersionPurgeStatusType) Empty() bool {
|
||||
return string(v) == ""
|
||||
}
|
||||
|
||||
// Pending returns true if the version is pending purge.
|
||||
func (v VersionPurgeStatusType) Pending() bool {
|
||||
return v == VersionPurgePending || v == VersionPurgeFailed
|
||||
}
|
||||
|
||||
@@ -109,3 +109,55 @@ func (z Type) Msgsize() (s int) {
|
||||
s = msgp.IntSize
|
||||
return
|
||||
}
|
||||
|
||||
// DecodeMsg implements msgp.Decodable
|
||||
func (z *VersionPurgeStatusType) DecodeMsg(dc *msgp.Reader) (err error) {
|
||||
{
|
||||
var zb0001 string
|
||||
zb0001, err = dc.ReadString()
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = VersionPurgeStatusType(zb0001)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// EncodeMsg implements msgp.Encodable
|
||||
func (z VersionPurgeStatusType) EncodeMsg(en *msgp.Writer) (err error) {
|
||||
err = en.WriteString(string(z))
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// MarshalMsg implements msgp.Marshaler
|
||||
func (z VersionPurgeStatusType) MarshalMsg(b []byte) (o []byte, err error) {
|
||||
o = msgp.Require(b, z.Msgsize())
|
||||
o = msgp.AppendString(o, string(z))
|
||||
return
|
||||
}
|
||||
|
||||
// UnmarshalMsg implements msgp.Unmarshaler
|
||||
func (z *VersionPurgeStatusType) UnmarshalMsg(bts []byte) (o []byte, err error) {
|
||||
{
|
||||
var zb0001 string
|
||||
zb0001, bts, err = msgp.ReadStringBytes(bts)
|
||||
if err != nil {
|
||||
err = msgp.WrapError(err)
|
||||
return
|
||||
}
|
||||
(*z) = VersionPurgeStatusType(zb0001)
|
||||
}
|
||||
o = bts
|
||||
return
|
||||
}
|
||||
|
||||
// Msgsize returns an upper bound estimate of the number of bytes occupied by the serialized message
|
||||
func (z VersionPurgeStatusType) Msgsize() (s int) {
|
||||
s = msgp.StringPrefixSize + len(string(z))
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user