github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/cmd/lhsm-plugin-posix/posix/posix_test.go (about)

     1  // Copyright (c) 2018 DDN. All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package posix_test
     6  
     7  import (
     8  	"bytes"
     9  	"os"
    10  	"path/filepath"
    11  	"testing"
    12  
    13  	"github.com/pborman/uuid"
    14  	"github.com/pkg/errors"
    15  
    16  	lustre "github.com/intel-hpdd/go-lustre"
    17  	"github.com/intel-hpdd/lemur/cmd/lhsm-plugin-posix/posix"
    18  	"github.com/intel-hpdd/lemur/dmplugin"
    19  	"github.com/intel-hpdd/lemur/internal/testhelpers"
    20  	"github.com/intel-hpdd/lemur/pkg/checksum"
    21  	"github.com/intel-hpdd/logging/debug"
    22  )
    23  
    24  func testArchive(t *testing.T, mover *posix.Mover, path string, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction {
    25  	action := dmplugin.NewTestAction(t, path, offset, length, fileID, data)
    26  	if err := mover.Archive(action); err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	return action
    30  }
    31  
    32  func testRemove(t *testing.T, mover *posix.Mover, fileID string, data []byte) *dmplugin.TestAction {
    33  	action := dmplugin.NewTestAction(t, "", 0, 0, fileID, data)
    34  	if err := mover.Remove(action); err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	return action
    38  }
    39  
    40  func testRestore(t *testing.T, mover *posix.Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction {
    41  	tfile, cleanFile := testhelpers.TempFile(t, 0)
    42  	defer cleanFile()
    43  	action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data)
    44  	if err := mover.Restore(action); err != nil {
    45  		t.Fatal(err)
    46  	}
    47  	return action
    48  }
    49  
    50  func testRestoreFail(t *testing.T, mover *posix.Mover, offset int64, length int64, fileID string, data []byte, outer error) *dmplugin.TestAction {
    51  	debug.Printf("restore %s", fileID)
    52  	tfile, cleanFile := testhelpers.TempFile(t, 0)
    53  	defer cleanFile()
    54  	action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data)
    55  	action.SetHash(data)
    56  	if err := mover.Restore(action); err == nil {
    57  		t.Fatalf("expected restore failure at: %s", outer)
    58  	} else {
    59  		t.Logf("got expected error: %v", err)
    60  	}
    61  	return action
    62  }
    63  
    64  func testDestinationFile(t *testing.T, mover *posix.Mover, fileID string) string {
    65  	return mover.Destination(fileID)
    66  }
    67  
    68  func defaultChecksum(cfg *posix.ArchiveConfig) *posix.ArchiveConfig {
    69  	cfg.Checksums = &posix.DefaultChecksums
    70  	return cfg
    71  }
    72  
    73  func TestPosixExtents(t *testing.T) {
    74  	WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) {
    75  		type extent struct {
    76  			id     string
    77  			offset int64
    78  			length int64
    79  		}
    80  		var extents []extent
    81  		var maxExtent int64 = 1024 * 1024
    82  		var fileSize int64 = 4*1024*1024 + 42
    83  		tfile, cleanFile := testhelpers.TempFile(t, fileSize)
    84  		defer cleanFile()
    85  
    86  		st, err := os.Stat(tfile)
    87  		if err != nil {
    88  			t.Fatal(err)
    89  		}
    90  		actualSize := st.Size()
    91  		startSum, err := checksum.FileSha1Sum(tfile)
    92  		if err != nil {
    93  			t.Fatal(err)
    94  		}
    95  
    96  		for offset := int64(0); offset < actualSize; offset += maxExtent {
    97  			length := maxExtent
    98  			if offset+maxExtent > actualSize {
    99  				length = actualSize - offset
   100  			}
   101  			aa := dmplugin.NewTestAction(t, tfile, offset, length, "", nil)
   102  			if err := mover.Archive(aa); err != nil {
   103  				t.Fatal(err)
   104  			}
   105  			extents = append(extents, extent{aa.UUID(), offset, length})
   106  
   107  			debug.Printf("%d/%d/%d: %s", offset, offset+length, actualSize, aa.UUID())
   108  		}
   109  
   110  		// Zap the test file like it was released before restoring
   111  		// the data.
   112  		if err := os.Truncate(tfile, 0); err != nil {
   113  			t.Fatal(err)
   114  		}
   115  
   116  		for _, extent := range extents {
   117  			ra := dmplugin.NewTestAction(t, tfile, extent.offset, extent.length, extent.id, nil)
   118  
   119  			if err := mover.Restore(ra); err != nil {
   120  				t.Fatal(err)
   121  			}
   122  		}
   123  
   124  		endSum, err := checksum.FileSha1Sum(tfile)
   125  		if err != nil {
   126  			t.Fatal(err)
   127  		}
   128  
   129  		if !bytes.Equal(endSum, startSum) {
   130  			t.Fatalf("end sum (%x) != start sum (%x)", endSum, startSum)
   131  		}
   132  	})
   133  }
   134  
   135  func TestPosixArchive(t *testing.T) {
   136  	WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) {
   137  		// trigger two updates (at current interval of 10MB
   138  		var length int64 = 20 * 1024 * 1024
   139  		tfile, cleanFile := testhelpers.TempFile(t, length)
   140  		defer cleanFile()
   141  
   142  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   143  
   144  		// Need to introduce a delay to  test new time based updates.
   145  		if action.Updates != 0 {
   146  			t.Fatalf("expected 0 updates, got %d", action.Updates)
   147  		}
   148  
   149  		testRestore(t, mover, 0, length, action.UUID(), nil)
   150  	})
   151  }
   152  
   153  func TestPosixArchiveMaxSize(t *testing.T) {
   154  	WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) {
   155  		var length int64 = 1000000
   156  		tfile, cleanFile := testhelpers.TempFile(t, length)
   157  		defer cleanFile()
   158  
   159  		// we received MaxExtentLength from coordinator, so test this as well
   160  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil)
   161  		testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil)
   162  	})
   163  }
   164  
   165  func TestPosixArchiveDefaultChecksum(t *testing.T) {
   166  	WithPosixMover(t, defaultChecksum, func(t *testing.T, mover *posix.Mover) {
   167  		var length int64 = 100
   168  		tfile, cleanFile := testhelpers.TempFile(t, length)
   169  		defer cleanFile()
   170  
   171  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   172  		testRestore(t, mover, 0, length, action.UUID(), nil)
   173  	})
   174  }
   175  
   176  func TestPosixArchiveDefaultChecksumCompress(t *testing.T) {
   177  	enableCompress := func(cfg *posix.ArchiveConfig) *posix.ArchiveConfig {
   178  		return cfg.Merge(&posix.ArchiveConfig{
   179  			Compression: "on"})
   180  
   181  	}
   182  	WithPosixMover(t, enableCompress, func(t *testing.T, mover *posix.Mover) {
   183  		var length int64 = 100
   184  		tfile, cleanFile := testhelpers.TempFile(t, length)
   185  		defer cleanFile()
   186  
   187  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   188  		if filepath.Ext(action.UUID()) != ".gz" {
   189  			t.Fatal(errors.New("file not compressed"))
   190  		}
   191  		testRestore(t, mover, 0, length, action.UUID(), nil)
   192  	})
   193  }
   194  
   195  func TestPosixArchiveRestoreBrokenFileID(t *testing.T) {
   196  	WithPosixMover(t, defaultChecksum, func(t *testing.T, mover *posix.Mover) {
   197  		var length int64 = 100
   198  		tfile, cleanFile := testhelpers.TempFile(t, length)
   199  		defer cleanFile()
   200  
   201  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   202  		newID := uuid.New()
   203  		// Wrong UUID
   204  		action.SetUUID(newID)
   205  		testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New(""))
   206  
   207  		// Missing FileID
   208  		action.SetUUID("")
   209  		testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New(""))
   210  
   211  		// Garbage FildID
   212  		action.SetUUID("Not a FileID")
   213  		testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New(""))
   214  	})
   215  }
   216  
   217  func TestPosixArchiveRestoreError(t *testing.T) {
   218  	WithPosixMover(t, defaultChecksum, func(t *testing.T, mover *posix.Mover) {
   219  		var length int64 = 100
   220  		tfile, cleanFile := testhelpers.TempFile(t, length)
   221  		defer cleanFile()
   222  
   223  		// we received MaxExtentLength from coordinator, so test this as well
   224  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   225  
   226  		failRestore := func(t *testing.T, mover *posix.Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction {
   227  			tfile, cleanFile := testhelpers.TempFile(t, 0)
   228  			defer cleanFile()
   229  			os.Chmod(tfile, 0444)
   230  			action := dmplugin.NewTestAction(t, tfile+".oops", offset, length, fileID, data)
   231  			if err := mover.Restore(action); err != nil {
   232  				if !os.IsNotExist(errors.Cause(err)) {
   233  					t.Fatalf("Unexpected failure: %v", err)
   234  				}
   235  			} else {
   236  				fi, _ := os.Stat(tfile)
   237  				t.Fatalf("Expected ENOENT failure: %s mode:0%o", fi.Name(), fi.Mode())
   238  			}
   239  
   240  			return action
   241  		}
   242  
   243  		failRestore(t, mover, 0, length, action.UUID(), nil)
   244  	})
   245  }
   246  
   247  func TestPosixArchiveNoChecksum(t *testing.T) {
   248  	disableChecksum := func(cfg *posix.ArchiveConfig) *posix.ArchiveConfig {
   249  		return cfg.Merge(&posix.ArchiveConfig{
   250  			Compression: "off",
   251  			Checksums:   &posix.ChecksumConfig{Disabled: true}})
   252  
   253  	}
   254  	WithPosixMover(t, disableChecksum, func(t *testing.T, mover *posix.Mover) {
   255  		var length int64 = 1000000
   256  		tfile, cleanFile := testhelpers.TempFile(t, length)
   257  		defer cleanFile()
   258  
   259  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil)
   260  		// we received MaxExtentLength from coordinator, so test this as well
   261  
   262  		testhelpers.CorruptFile(t, mover.Destination(action.UUID()))
   263  
   264  		// Successfully restore corrupt data
   265  		testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil)
   266  	})
   267  }
   268  
   269  func combine(fnlist ...func(*posix.ArchiveConfig) *posix.ArchiveConfig) func(*posix.ArchiveConfig) *posix.ArchiveConfig {
   270  	return func(v *posix.ArchiveConfig) *posix.ArchiveConfig {
   271  		for _, fn := range fnlist {
   272  			v = fn(v)
   273  		}
   274  		return v
   275  	}
   276  }
   277  
   278  func TestPosixArchiveNoChecksumRestore(t *testing.T) {
   279  	updateConf := func(cfg *posix.ArchiveConfig) *posix.ArchiveConfig {
   280  		return cfg.Merge(&posix.ArchiveConfig{
   281  			Compression: "off",
   282  			Checksums:   &posix.ChecksumConfig{DisableCompareOnRestore: true}})
   283  	}
   284  
   285  	WithPosixMover(t, updateConf, func(t *testing.T, mover *posix.Mover) {
   286  		var length int64 = 1000000
   287  		tfile, cleanFile := testhelpers.TempFile(t, length)
   288  		defer cleanFile()
   289  
   290  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil)
   291  		// we received MaxExtentLength from coordinator, so test this as well
   292  
   293  		testhelpers.CorruptFile(t, mover.Destination(action.UUID()))
   294  		// Successfully restore corrupt data
   295  		testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil)
   296  	})
   297  }
   298  
   299  func TestPosixArchiveChecksumAfter(t *testing.T) {
   300  	WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) {
   301  		var length int64 = 1000000
   302  		tfile, cleanFile := testhelpers.TempFile(t, length)
   303  		defer cleanFile()
   304  
   305  		// we received MaxExtentLength from coordinator, so test this as well
   306  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil)
   307  		// Disable checksum generation but should still check existing checksums
   308  		mover.ChecksumConfig().Disabled = true
   309  		testhelpers.CorruptFile(t, testDestinationFile(t, mover, action.UUID()))
   310  		// Don't  restore corrupt data
   311  		testRestoreFail(t, mover, 0, lustre.MaxExtentLength, action.UUID(), action.Hash(), errors.New(""))
   312  	})
   313  }
   314  
   315  func TestPosixCorruptArchive(t *testing.T) {
   316  	WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) {
   317  		var length int64 = 1000000
   318  		tfile, cleanFile := testhelpers.TempFile(t, length)
   319  		defer cleanFile()
   320  
   321  		action := dmplugin.NewTestAction(t, tfile, 0, length, "", nil)
   322  		if err := mover.Archive(action); err != nil {
   323  			t.Fatal(err)
   324  		}
   325  
   326  		path := testDestinationFile(t, mover, action.UUID())
   327  
   328  		testhelpers.CorruptFile(t, path)
   329  
   330  		// TODO check for specific CheckSum error
   331  		testRestoreFail(t, mover, 0, length, action.UUID(), action.Hash(), errors.New(""))
   332  
   333  	})
   334  }
   335  
   336  func TestPosixRemove(t *testing.T) {
   337  	WithPosixMover(t, nil, func(t *testing.T, mover *posix.Mover) {
   338  		var length int64 = 1000000
   339  		tfile, cleanFile := testhelpers.TempFile(t, length)
   340  		defer cleanFile()
   341  
   342  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   343  		path := testDestinationFile(t, mover, action.UUID())
   344  
   345  		if _, err := os.Stat(path); err != nil {
   346  			t.Fatalf("Destination file is missing: %v", err)
   347  		}
   348  
   349  		testRemove(t, mover, action.UUID(), nil)
   350  
   351  		_, err := os.Stat(path)
   352  		if !os.IsNotExist(err) {
   353  			t.Fatalf("Unexpected or missing error: %v", err)
   354  		}
   355  
   356  		testRestoreFail(t, mover, 0, length, action.UUID(), nil, errors.New(""))
   357  	})
   358  }
   359  
   360  func WithPosixMover(t *testing.T, updateConfig func(*posix.ArchiveConfig) *posix.ArchiveConfig,
   361  	tester func(t *testing.T, mover *posix.Mover)) {
   362  
   363  	config := new(posix.ArchiveConfig)
   364  	config.Name = "posix-test"
   365  
   366  	defer testhelpers.ChdirTemp(t)()
   367  	archiveDir, cleanArchive := testhelpers.TempDir(t)
   368  	defer cleanArchive()
   369  
   370  	config.Root = archiveDir
   371  	if updateConfig != nil {
   372  		config = updateConfig(config)
   373  	}
   374  
   375  	mover, err := posix.NewMover(config)
   376  	if err != nil {
   377  		t.Fatal(err)
   378  	}
   379  
   380  	tester(t, mover)
   381  }