github.com/yankunsam/loki/v2@v2.6.3-0.20220817130409-389df5235c27/pkg/storage/chunk/client/local/fs_object_client_test.go (about)

     1  package local
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"io/ioutil"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  	"strings"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/grafana/loki/pkg/storage/chunk/client/util"
    17  )
    18  
    19  func TestFSObjectClient_DeleteChunksBefore(t *testing.T) {
    20  	deleteFilesOlderThan := 10 * time.Minute
    21  
    22  	fsChunksDir := t.TempDir()
    23  
    24  	bucketClient, err := NewFSObjectClient(FSConfig{
    25  		Directory: fsChunksDir,
    26  	})
    27  	require.NoError(t, err)
    28  
    29  	file1 := "file1"
    30  	file2 := "file2"
    31  
    32  	// Creating dummy files
    33  	require.NoError(t, os.Chdir(fsChunksDir))
    34  
    35  	f, err := os.Create(file1)
    36  	require.NoError(t, err)
    37  	require.NoError(t, f.Close())
    38  
    39  	f, err = os.Create(file2)
    40  	require.NoError(t, err)
    41  	require.NoError(t, f.Close())
    42  
    43  	// Verify whether all files are created
    44  	files, _ := ioutil.ReadDir(".")
    45  	require.Equal(t, 2, len(files), "Number of files should be 2")
    46  
    47  	// No files should be deleted, since all of them are not much older
    48  	require.NoError(t, bucketClient.DeleteChunksBefore(context.Background(), time.Now().Add(-deleteFilesOlderThan)))
    49  	files, _ = ioutil.ReadDir(".")
    50  	require.Equal(t, 2, len(files), "Number of files should be 2")
    51  
    52  	// Changing mtime of file1 to make it look older
    53  	require.NoError(t, os.Chtimes(file1, time.Now().Add(-deleteFilesOlderThan), time.Now().Add(-deleteFilesOlderThan)))
    54  	require.NoError(t, bucketClient.DeleteChunksBefore(context.Background(), time.Now().Add(-deleteFilesOlderThan)))
    55  
    56  	// Verifying whether older file got deleted
    57  	files, _ = ioutil.ReadDir(".")
    58  	require.Equal(t, 1, len(files), "Number of files should be 1 after enforcing retention")
    59  }
    60  
    61  func TestFSObjectClient_List(t *testing.T) {
    62  	fsObjectsDir := t.TempDir()
    63  
    64  	bucketClient, err := NewFSObjectClient(FSConfig{
    65  		Directory: fsObjectsDir,
    66  	})
    67  	require.NoError(t, err)
    68  
    69  	allFiles := []string{
    70  		"outer-file1",
    71  		"outer-file2",
    72  		"folder1/file1",
    73  		"folder1/file2",
    74  		"folder2/file3",
    75  		"folder2/file4",
    76  		"folder2/file5",
    77  		"deeply/nested/folder/a",
    78  		"deeply/nested/folder/b",
    79  		"deeply/nested/folder/c",
    80  	}
    81  
    82  	topLevelFolders := map[string]bool{}
    83  	topLevelFiles := map[string]bool{}
    84  	filesInTopLevelFolders := map[string]map[string]bool{}
    85  
    86  	for _, f := range allFiles {
    87  		require.NoError(t, bucketClient.PutObject(context.Background(), f, bytes.NewReader([]byte(f))))
    88  
    89  		s := strings.Split(f, "/")
    90  		if len(s) > 1 {
    91  			topLevelFolders[s[0]] = true
    92  		} else {
    93  			topLevelFiles[s[0]] = true
    94  		}
    95  
    96  		if len(s) == 2 {
    97  			if filesInTopLevelFolders[s[0]] == nil {
    98  				filesInTopLevelFolders[s[0]] = map[string]bool{}
    99  			}
   100  			filesInTopLevelFolders[s[0]][s[1]] = true
   101  		}
   102  	}
   103  
   104  	// create an empty directory which should get excluded from the list
   105  	require.NoError(t, util.EnsureDirectory(filepath.Join(fsObjectsDir, "empty-folder")))
   106  
   107  	storageObjects, commonPrefixes, err := bucketClient.List(context.Background(), "", "/")
   108  	require.NoError(t, err)
   109  
   110  	require.Len(t, storageObjects, len(topLevelFiles))
   111  	for _, so := range storageObjects {
   112  		require.True(t, topLevelFiles[so.Key])
   113  	}
   114  
   115  	require.Len(t, commonPrefixes, len(topLevelFolders))
   116  	for _, commonPrefix := range commonPrefixes {
   117  		require.True(t, topLevelFolders[string(commonPrefix)[:len(commonPrefix)-1]]) // 1 to remove "/" separator.
   118  	}
   119  
   120  	for folder, files := range filesInTopLevelFolders {
   121  		storageObjects, commonPrefixes, err := bucketClient.List(context.Background(), folder, "/")
   122  		require.NoError(t, err)
   123  
   124  		require.Len(t, storageObjects, len(files))
   125  		for _, so := range storageObjects {
   126  			require.True(t, strings.HasPrefix(so.Key, folder+"/"))
   127  			require.True(t, files[path.Base(so.Key)])
   128  		}
   129  
   130  		require.Len(t, commonPrefixes, 0)
   131  	}
   132  
   133  	// List everything from the top, recursively.
   134  	storageObjects, commonPrefixes, err = bucketClient.List(context.Background(), "", "")
   135  	require.NoError(t, err)
   136  
   137  	// Since delimiter is empty, there are no commonPrefixes.
   138  	require.Empty(t, commonPrefixes)
   139  
   140  	var storageObjectPaths []string
   141  	for _, so := range storageObjects {
   142  		storageObjectPaths = append(storageObjectPaths, so.Key)
   143  	}
   144  	require.ElementsMatch(t, allFiles, storageObjectPaths)
   145  
   146  	storageObjects, commonPrefixes, err = bucketClient.List(context.Background(), "doesnt_exist", "")
   147  	require.NoError(t, err)
   148  	require.Empty(t, storageObjects)
   149  	require.Empty(t, commonPrefixes)
   150  
   151  	storageObjects, commonPrefixes, err = bucketClient.List(context.Background(), "outer-file1", "")
   152  	require.NoError(t, err)
   153  	require.Len(t, storageObjects, 1)
   154  	require.Equal(t, "outer-file1", storageObjects[0].Key)
   155  	require.Empty(t, commonPrefixes)
   156  }
   157  
   158  func TestFSObjectClient_DeleteObject(t *testing.T) {
   159  	fsObjectsDir := t.TempDir()
   160  
   161  	bucketClient, err := NewFSObjectClient(FSConfig{
   162  		Directory: fsObjectsDir,
   163  	})
   164  	require.NoError(t, err)
   165  
   166  	foldersWithFiles := make(map[string][]string)
   167  	foldersWithFiles["folder1"] = []string{"file1", "file2"}
   168  
   169  	for folder, files := range foldersWithFiles {
   170  		for _, filename := range files {
   171  			err := bucketClient.PutObject(context.Background(), path.Join(folder, filename), bytes.NewReader([]byte(filename)))
   172  			require.NoError(t, err)
   173  		}
   174  	}
   175  
   176  	// let us check if we have right folders created
   177  	_, commonPrefixes, err := bucketClient.List(context.Background(), "", "/")
   178  	require.NoError(t, err)
   179  	require.Len(t, commonPrefixes, len(foldersWithFiles))
   180  
   181  	// let us delete file1 from folder1 and check that file1 is gone but folder1 with file2 is still there
   182  	require.NoError(t, bucketClient.DeleteObject(context.Background(), path.Join("folder1", "file1")))
   183  	_, err = os.Stat(filepath.Join(fsObjectsDir, filepath.Join("folder1", "file1")))
   184  	require.True(t, os.IsNotExist(err))
   185  
   186  	_, err = os.Stat(filepath.Join(fsObjectsDir, filepath.Join("folder1", "file2")))
   187  	require.NoError(t, err)
   188  
   189  	// let us delete second file as well and check that folder1 also got removed
   190  	require.NoError(t, bucketClient.DeleteObject(context.Background(), path.Join("folder1", "file2")))
   191  	_, err = os.Stat(filepath.Join(fsObjectsDir, "folder1"))
   192  	require.True(t, os.IsNotExist(err))
   193  
   194  	_, err = os.Stat(fsObjectsDir)
   195  	require.NoError(t, err)
   196  
   197  	// let us see ensure folder2 is still there will all the files:
   198  	/*files, commonPrefixes, err := bucketClient.List(context.Background(), "folder2/")
   199  	require.NoError(t, err)
   200  	require.Len(t, commonPrefixes, 0)
   201  	require.Len(t, files, len(foldersWithFiles["folder2/"]))*/
   202  }