github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/data-scanner_test.go (about)

     1  // Copyright (c) 2015-2023 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package cmd
    19  
    20  import (
    21  	"context"
    22  	"encoding/xml"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/google/uuid"
    28  	"github.com/minio/minio/internal/bucket/lifecycle"
    29  	"github.com/minio/minio/internal/bucket/versioning"
    30  )
    31  
    32  func TestApplyNewerNoncurrentVersionsLimit(t *testing.T) {
    33  	objAPI, disks, err := prepareErasure(context.Background(), 8)
    34  	if err != nil {
    35  		t.Fatalf("Failed to initialize object layer: %v", err)
    36  	}
    37  	defer removeRoots(disks)
    38  	setObjectLayer(objAPI)
    39  	globalBucketMetadataSys = NewBucketMetadataSys()
    40  	globalBucketObjectLockSys = &BucketObjectLockSys{}
    41  	globalBucketVersioningSys = &BucketVersioningSys{}
    42  	es := newExpiryState(context.Background(), objAPI, 0)
    43  	workers := []chan expiryOp{make(chan expiryOp)}
    44  	es.workers.Store(&workers)
    45  	globalExpiryState = es
    46  	var wg sync.WaitGroup
    47  	wg.Add(1)
    48  	expired := make([]ObjectToDelete, 0, 5)
    49  	go func() {
    50  		defer wg.Done()
    51  		workers := globalExpiryState.workers.Load()
    52  		for t := range (*workers)[0] {
    53  			if t, ok := t.(newerNoncurrentTask); ok {
    54  				expired = append(expired, t.versions...)
    55  			}
    56  		}
    57  	}()
    58  	lc := lifecycle.Lifecycle{
    59  		Rules: []lifecycle.Rule{
    60  			{
    61  				ID:     "max-versions",
    62  				Status: "Enabled",
    63  				NoncurrentVersionExpiration: lifecycle.NoncurrentVersionExpiration{
    64  					NewerNoncurrentVersions: 1,
    65  				},
    66  			},
    67  		},
    68  	}
    69  	lcXML, err := xml.Marshal(lc)
    70  	if err != nil {
    71  		t.Fatalf("Failed to marshal lifecycle config: %v", err)
    72  	}
    73  	vcfg := versioning.Versioning{
    74  		Status: "Enabled",
    75  	}
    76  	vcfgXML, err := xml.Marshal(vcfg)
    77  	if err != nil {
    78  		t.Fatalf("Failed to marshal versioning config: %v", err)
    79  	}
    80  
    81  	bucket := "bucket"
    82  	obj := "obj-1"
    83  	now := time.Now()
    84  	meta := BucketMetadata{
    85  		Name:                      bucket,
    86  		Created:                   now,
    87  		LifecycleConfigXML:        lcXML,
    88  		VersioningConfigXML:       vcfgXML,
    89  		VersioningConfigUpdatedAt: now,
    90  		LifecycleConfigUpdatedAt:  now,
    91  		lifecycleConfig:           &lc,
    92  		versioningConfig:          &vcfg,
    93  	}
    94  	globalBucketMetadataSys.Set(bucket, meta)
    95  	item := scannerItem{
    96  		Path:       obj,
    97  		bucket:     bucket,
    98  		prefix:     "",
    99  		objectName: obj,
   100  		lifeCycle:  &lc,
   101  	}
   102  
   103  	modTime := time.Now()
   104  	uuids := make([]uuid.UUID, 5)
   105  	for i := range uuids {
   106  		uuids[i] = uuid.UUID([16]byte{15: uint8(i + 1)})
   107  	}
   108  	fivs := make([]FileInfo, 5)
   109  	for i := 0; i < 5; i++ {
   110  		fivs[i] = FileInfo{
   111  			Volume:      bucket,
   112  			Name:        obj,
   113  			VersionID:   uuids[i].String(),
   114  			IsLatest:    i == 0,
   115  			ModTime:     modTime.Add(-1 * time.Duration(i) * time.Minute),
   116  			Size:        1 << 10,
   117  			NumVersions: 5,
   118  		}
   119  	}
   120  	versioned := vcfg.Status == "Enabled"
   121  	wants := make([]ObjectInfo, 2)
   122  	for i, fi := range fivs[:2] {
   123  		wants[i] = fi.ToObjectInfo(bucket, obj, versioned)
   124  	}
   125  	gots, err := item.applyNewerNoncurrentVersionLimit(context.TODO(), objAPI, fivs, es)
   126  	if err != nil {
   127  		t.Fatalf("Failed with err: %v", err)
   128  	}
   129  	if len(gots) != len(wants) {
   130  		t.Fatalf("Expected %d objects but got %d", len(wants), len(gots))
   131  	}
   132  
   133  	// Close expiry state's channel to inspect object versions enqueued for expiration
   134  	close(workers[0])
   135  	wg.Wait()
   136  	for _, obj := range expired {
   137  		switch obj.ObjectV.VersionID {
   138  		case uuids[2].String(), uuids[3].String(), uuids[4].String():
   139  		default:
   140  			t.Errorf("Unexpected versionID being expired: %#v\n", obj)
   141  		}
   142  	}
   143  }