github.com/rohankumardubey/proxyfs@v0.0.0-20210108201508-653efa9ab00e/inode/validate_test.go (about)

     1  package inode
     2  
     3  import (
     4  	"strings"
     5  	"testing"
     6  
     7  	"github.com/swiftstack/ProxyFS/blunder"
     8  	"github.com/swiftstack/ProxyFS/swiftclient"
     9  )
    10  
    11  // Helper to fetch a volume handle and create a file for a test. Return the
    12  // volume handle and the file inode number.
    13  func volumeAndFileInoForTest(t *testing.T) (VolumeHandle, InodeNumber) {
    14  	testVolumeHandle, err := FetchVolumeHandle("TestVolume")
    15  	if err != nil {
    16  		t.Fatalf("FetchVolumeHandle(\"TestVolume\") should have worked - got error: %v", err)
    17  	}
    18  
    19  	fileInodeNumber, err := testVolumeHandle.CreateFile(PosixModePerm, 0, 0)
    20  	if err != nil {
    21  		t.Fatalf("CreateFile() failed: %v", err)
    22  	}
    23  
    24  	err = testVolumeHandle.Write(fileInodeNumber, 0, []byte{0x00, 0x01, 0x02, 0x03, 0x04}, nil)
    25  	if err != nil {
    26  		t.Fatalf("Write(fileInodeNumber, 0, []byte{0x00, 0x01, 0x02, 0x03, 0x04}) failed: %v", err)
    27  	}
    28  
    29  	err = testVolumeHandle.Flush(fileInodeNumber, true)
    30  	if err != nil {
    31  		t.Fatalf("Flush of inode %v failed", fileInodeNumber)
    32  	}
    33  
    34  	return testVolumeHandle, fileInodeNumber
    35  }
    36  
    37  func TestValidate(t *testing.T) {
    38  	testSetup(t, false)
    39  
    40  	testVolumeHandle, fileInodeNumber := volumeAndFileInoForTest(t)
    41  
    42  	// Validation succeeds if nothing has gone wrong
    43  	err := testVolumeHandle.Validate(fileInodeNumber, false)
    44  	if err != nil {
    45  		t.Fatalf("Validate() failed on presumably-good inode")
    46  	}
    47  
    48  	// Now let's artificially make something go wrong and confirm that
    49  	// Validate() catches it!
    50  
    51  	testVolume := testVolumeHandle.(*volumeStruct)
    52  
    53  	// Grab the InodeRec
    54  	inodeRec, ok, err := testVolume.headhunterVolumeHandle.GetInodeRec(uint64(fileInodeNumber))
    55  	if err != nil || !ok {
    56  		t.Fatalf("failed to fetch InodeRec for inode 0x%016X", fileInodeNumber)
    57  	}
    58  
    59  	// Corrupt the non-preamble part
    60  	for i := len(globals.inodeRecDefaultPreambleBuf); i < len(inodeRec); i++ {
    61  		inodeRec[i] = 0xff
    62  	}
    63  
    64  	// And put it back
    65  	err = testVolume.headhunterVolumeHandle.PutInodeRec(uint64(fileInodeNumber), inodeRec)
    66  	if err != nil {
    67  		t.Fatalf("failed to save corrupted InodeRec for inode 0x%016X", fileInodeNumber)
    68  	}
    69  
    70  	// Now remove fileInodeNumber from inodeCache
    71  	fileInode, ok, err := testVolume.inodeCacheFetch(fileInodeNumber)
    72  	if err != nil {
    73  		t.Fatalf("inodeCacheFetch(fileInodeNumber) failed: %v", err)
    74  	}
    75  	if ok {
    76  		ok, err = testVolume.inodeCacheDrop(fileInode)
    77  		if err != nil {
    78  			t.Fatalf("inodeCacheDrop(fileInode) failed: %v", err)
    79  		}
    80  		if !ok {
    81  			t.Fatalf("inodeCacheDrop(fileInode) returned !ok")
    82  		}
    83  	}
    84  
    85  	// Try to Validate, observe that it fails
    86  	validationErr := testVolumeHandle.Validate(fileInodeNumber, false)
    87  	if validationErr == nil {
    88  		t.Fatalf("expected validation to fail")
    89  	}
    90  	if blunder.IsNot(validationErr, blunder.CorruptInodeError) {
    91  		t.Fatalf("expected validation error %q to have error value %v, actual value was %v", validationErr, blunder.CorruptInodeError, blunder.FsError(blunder.Errno(validationErr)).String())
    92  	}
    93  
    94  	// Try to fetch from disk, observe that corruption was marked in the headhunter database
    95  	_, ok, corruptionErr := testVolume.fetchOnDiskInode(fileInodeNumber)
    96  	if corruptionErr == nil && ok {
    97  		t.Fatalf("expected not to get inode pointer when fetching presumptively corrupt inode %v", fileInodeNumber)
    98  	}
    99  
   100  	testTeardown(t)
   101  }
   102  
   103  func TestValidateFileExtents(t *testing.T) {
   104  	testSetup(t, false)
   105  
   106  	testVolumeHandle, fileInodeNumber := volumeAndFileInoForTest(t)
   107  
   108  	// then let's write some more data into the file so that there will be more segments to verify
   109  	testVolumeHandle.Write(fileInodeNumber, 2, []byte{0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06}, nil)
   110  	testVolumeHandle.Flush(fileInodeNumber, true)
   111  	testVolumeHandle.Write(fileInodeNumber, 12, []byte{0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07}, nil)
   112  	testVolumeHandle.Write(fileInodeNumber, 22, []byte{0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08}, nil)
   113  	testVolumeHandle.Flush(fileInodeNumber, true)
   114  	testVolumeHandle.Write(fileInodeNumber, 32, []byte{0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09}, nil)
   115  	testVolumeHandle.Write(fileInodeNumber, 39, []byte{0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A}, nil)
   116  	testVolumeHandle.Write(fileInodeNumber, 48, []byte{0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B}, nil)
   117  	testVolumeHandle.Flush(fileInodeNumber, false)
   118  
   119  	err := testVolumeHandle.Validate(fileInodeNumber, true)
   120  	if err != nil {
   121  		t.Fatalf("Validate() failed on presumably-good inode")
   122  	}
   123  
   124  	// let's get a read plan and then sneakily sabotage the log segments
   125  	var zero uint64
   126  	readPlan, err := testVolumeHandle.GetReadPlan(fileInodeNumber, &zero, nil)
   127  	if err != nil {
   128  		t.Fatalf("failed to get read plan for inode %v", fileInodeNumber)
   129  	}
   130  
   131  	// go delete the LogSegment referenced in the 1st readPlan step (that must exist)
   132  
   133  	if 0 == len(readPlan) {
   134  		t.Fatalf("readPlan should have contained at least one entry")
   135  	}
   136  
   137  	readPlanStep := readPlan[0]
   138  
   139  	deleteErr := swiftclient.ObjectDelete(readPlanStep.AccountName, readPlanStep.ContainerName, readPlanStep.ObjectName, 0)
   140  	if nil != deleteErr {
   141  		t.Fatalf("HTTP DELETE %v should have worked... failed: %v", readPlanStep.ObjectPath, deleteErr)
   142  	}
   143  
   144  	err = testVolumeHandle.Validate(fileInodeNumber, true)
   145  	if err == nil {
   146  		t.Fatalf("expected validation to fail!")
   147  	}
   148  	// TODO: validate error type more rigorously with blunder &c.
   149  	if !strings.Contains(err.Error(), "returned HTTP StatusCode 404") {
   150  		t.Fatalf("expected error to contain 'returned HTTP StatusCode 404'")
   151  	}
   152  
   153  	testTeardown(t)
   154  }