github.com/whamcloud/lemur@v0.0.0-20190827193804-4655df8a52af/cmd/lhsm-plugin-s3/s3_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 main
     6  
     7  import (
     8  	"bytes"
     9  	"os"
    10  	"testing"
    11  	"time"
    12  
    13  	lustre "github.com/intel-hpdd/go-lustre"
    14  	"github.com/intel-hpdd/lemur/dmplugin"
    15  	"github.com/intel-hpdd/lemur/internal/testhelpers"
    16  	"github.com/intel-hpdd/lemur/pkg/checksum"
    17  	"github.com/intel-hpdd/logging/debug"
    18  )
    19  
    20  func testArchive(t *testing.T, mover *Mover, path string, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction {
    21  	action := dmplugin.NewTestAction(t, path, offset, length, fileID, data)
    22  	if err := mover.Archive(action); err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	return action
    26  }
    27  
    28  func testRemove(t *testing.T, mover *Mover, fileID string, data []byte) *dmplugin.TestAction {
    29  	action := dmplugin.NewTestAction(t, "", 0, 0, fileID, data)
    30  	if err := mover.Remove(action); err != nil {
    31  		t.Fatal(err)
    32  	}
    33  	return action
    34  }
    35  
    36  func testRestore(t *testing.T, mover *Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction {
    37  	tfile, cleanFile := testhelpers.TempFile(t, 0)
    38  	defer cleanFile()
    39  	action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data)
    40  	if err := mover.Restore(action); err != nil {
    41  		t.Fatal(err)
    42  	}
    43  	return action
    44  }
    45  
    46  func testRestoreFail(t *testing.T, mover *Mover, offset int64, length int64, fileID string, data []byte) *dmplugin.TestAction {
    47  	tfile, cleanFile := testhelpers.TempFile(t, 0)
    48  	defer cleanFile()
    49  	action := dmplugin.NewTestAction(t, tfile, offset, length, fileID, data)
    50  	if err := mover.Restore(action); err == nil {
    51  		t.Fatal("Expected restore to fail")
    52  	}
    53  	return action
    54  }
    55  
    56  /*
    57  func testDestinationFile(t *testing.T, mover *Mover, buf []byte) string {
    58  	fileID, err := posix.ParseFileID(buf)
    59  	if err != nil {
    60  		t.Fatal(err)
    61  	}
    62  
    63  	return mover.Destination(fileID.UUID)
    64  }
    65  */
    66  
    67  func TestS3Extents(t *testing.T) {
    68  	WithS3Mover(t, nil, func(t *testing.T, mover *Mover) {
    69  		type extent struct {
    70  			id     string
    71  			offset int64
    72  			length int64
    73  		}
    74  		var extents []extent
    75  		var maxExtent int64 = 1024 * 1024
    76  		var fileSize int64 = 4*1024*1024 + 42
    77  		tfile, cleanFile := testhelpers.TempFile(t, fileSize)
    78  		defer cleanFile()
    79  
    80  		st, err := os.Stat(tfile)
    81  		if err != nil {
    82  			t.Fatal(err)
    83  		}
    84  		actualSize := st.Size()
    85  		startSum, err := checksum.FileSha1Sum(tfile)
    86  		if err != nil {
    87  			t.Fatal(err)
    88  		}
    89  		debug.Printf("%s actual size: %d", tfile, actualSize)
    90  
    91  		for offset := int64(0); offset < actualSize; offset += maxExtent {
    92  			length := maxExtent
    93  			if offset+maxExtent > actualSize {
    94  				length = actualSize - offset
    95  			}
    96  			aa := dmplugin.NewTestAction(t, tfile, offset, length, "", nil)
    97  			if err := mover.Archive(aa); err != nil {
    98  				t.Fatal(err)
    99  			}
   100  			extents = append(extents, extent{aa.UUID(), offset, length})
   101  
   102  			debug.Printf("ARCHIVE %d/%d/%d: %s", offset, offset+length, actualSize, aa.UUID())
   103  		}
   104  
   105  		// Zap the test file like it was released before restoring
   106  		// the data.
   107  		if err := os.Truncate(tfile, 0); err != nil {
   108  			t.Fatal(err)
   109  		}
   110  		for _, extent := range extents {
   111  			ra := dmplugin.NewTestAction(t, tfile, extent.offset, extent.length, extent.id, nil)
   112  
   113  			if err := mover.Restore(ra); err != nil {
   114  				t.Fatal(err)
   115  			}
   116  
   117  			debug.Printf("RESTORE %d/%d/%d: %s", extent.offset, extent.offset+extent.length, actualSize, ra.UUID())
   118  		}
   119  
   120  		endSum, err := checksum.FileSha1Sum(tfile)
   121  		if err != nil {
   122  			t.Fatal(err)
   123  		}
   124  
   125  		if !bytes.Equal(endSum, startSum) {
   126  			t.Fatalf("end sum (%x) != start sum (%x)", endSum, startSum)
   127  		}
   128  	})
   129  }
   130  
   131  func TestS3Archive(t *testing.T) {
   132  	// Quick hack to let us test against radosgw; a better solution
   133  	// would be to figure out some kind of fallback.
   134  	embiggenUploadPartSize := func(cfg *archiveConfig) *archiveConfig {
   135  		cfg.UploadPartSize = 128 * 1024 * 1024
   136  		return cfg
   137  	}
   138  	if os.Getenv("LHSM_TEST_RADOSGW") == "" {
   139  		embiggenUploadPartSize = nil
   140  	}
   141  
   142  	WithS3Mover(t, embiggenUploadPartSize, func(t *testing.T, mover *Mover) {
   143  		// trigger two updates (at current interval of 10MB
   144  		var length int64 = 20 * 1024 * 1024
   145  		tfile, cleanFile := testhelpers.TempFile(t, length)
   146  		defer cleanFile()
   147  
   148  		start := time.Now()
   149  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   150  
   151  		// TODO: parameterize the update interval
   152  		expectedUpdates := int((time.Since(start) / time.Second) / 10)
   153  
   154  		if action.Updates != expectedUpdates {
   155  			t.Errorf("expected %d updates, got %d", expectedUpdates, action.Updates)
   156  		}
   157  
   158  		start = time.Now()
   159  		restore := testRestore(t, mover, 0, length, action.UUID(), nil)
   160  		// TODO: parameterize the update interval
   161  		duration := time.Since(start)
   162  		expectedUpdates = int((duration / time.Second) / 10)
   163  		if restore.Updates != expectedUpdates {
   164  			t.Errorf("expected %d updates, got %d, duration: %v", expectedUpdates, restore.Updates, duration)
   165  		}
   166  		testRemove(t, mover, action.UUID(), nil)
   167  	})
   168  }
   169  
   170  func TestS3ArchiveMaxSize(t *testing.T) {
   171  	WithS3Mover(t, nil, func(t *testing.T, mover *Mover) {
   172  		var length int64 = 1000000
   173  		tfile, cleanFile := testhelpers.TempFile(t, length)
   174  		defer cleanFile()
   175  
   176  		// we received MaxExtentLength from coordinator, so test this as well
   177  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, "", nil)
   178  		testRestore(t, mover, 0, lustre.MaxExtentLength, action.UUID(), nil)
   179  		testRemove(t, mover, action.UUID(), nil)
   180  	})
   181  }
   182  
   183  /*
   184  func TestS3ArchiveNoChecksum(t *testing.T) {
   185  	disableChecksum := func(cfg *MoverConfig) *MoverConfig {
   186  		cfg.Checksums.Disabled = true
   187  		return cfg
   188  	}
   189  
   190  	WithS3Mover(t, disableChecksum, func(t *testing.T, mover *Mover) {
   191  		var length int64 = 1000000
   192  		tfile, cleanFile := testTempFile(t, length)
   193  		defer cleanFile()
   194  
   195  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, nil, nil)
   196  		// we received MaxExtentLength from coordinator, so test this as well
   197  
   198  		fileID, err := posix.ParseFileID(action.FileID())
   199  		if err != nil {
   200  			t.Fatal(err)
   201  		}
   202  
   203  		testCorruptFile(t, mover.Destination(fileID.UUID))
   204  
   205  		// Successfully restore corrupt data
   206  		testRestore(t, mover, 0, lustre.MaxExtentLength, action.FileID(), nil)
   207  	})
   208  }
   209  
   210  func TestS3ArchiveNoChecksumRestore(t *testing.T) {
   211  	disableChecksum := func(cfg *MoverConfig) *MoverConfig {
   212  		cfg.Checksums.DisableCompareOnRestore = true
   213  		return cfg
   214  	}
   215  
   216  	WithS3Mover(t, disableChecksum, func(t *testing.T, mover *Mover) {
   217  		var length int64 = 1000000
   218  		tfile, cleanFile := testTempFile(t, length)
   219  		defer cleanFile()
   220  
   221  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, nil, nil)
   222  		// we received MaxExtentLength from coordinator, so test this as well
   223  
   224  		fileID, err := posix.ParseFileID(action.FileID())
   225  		if err != nil {
   226  			t.Fatal(err)
   227  		}
   228  
   229  		testCorruptFile(t, mover.Destination(fileID.UUID))
   230  		// Successfully restore corrupt data
   231  		testRestore(t, mover, 0, lustre.MaxExtentLength, action.FileID(), nil)
   232  	})
   233  }
   234  
   235  func TestS3ArchiveChecksumAfter(t *testing.T) {
   236  	WithS3Mover(t, nil, func(t *testing.T, mover *Mover) {
   237  		var length int64 = 1000000
   238  		tfile, cleanFile := testTempFile(t, length)
   239  		defer cleanFile()
   240  
   241  		// we received MaxExtentLength from coordinator, so test this as well
   242  		action := testArchive(t, mover, tfile, 0, lustre.MaxExtentLength, nil, nil)
   243  		// Disable checksum generation but should still check existing checksums
   244  		mover.ChecksumConfig().Disabled = true
   245  		testCorruptFile(t, testDestinationFile(t, mover, action.FileID()))
   246  		// Don't  restore corrupt data
   247  		testRestoreFail(t, mover, 0, lustre.MaxExtentLength, action.FileID(), nil)
   248  	})
   249  }
   250  */
   251  
   252  /*
   253  func TestS3CorruptArchive(t *testing.T) {
   254  	WithS3Mover(t, nil, func(t *testing.T, mover *Mover) {
   255  		var length int64 = 1000000
   256  		tfile, cleanFile := testTempFile(t, length)
   257  		defer cleanFile()
   258  
   259  		action := dmplugin.NewTestAction(t, tfile, 0, length, nil, nil)
   260  		if err := mover.Archive(action); err != nil {
   261  			t.Fatal(err)
   262  		}
   263  
   264  		path := testDestinationFile(t, mover, action.FileID())
   265  
   266  		testCorruptFile(t, path)
   267  
   268  		// TODO check for specific CheckSum error
   269  		testRestoreFail(t, mover, 0, length, action.FileID(), nil)
   270  
   271  	})
   272  }
   273  */
   274  func TestS3Remove(t *testing.T) {
   275  	WithS3Mover(t, nil, func(t *testing.T, mover *Mover) {
   276  		var length int64 = 1000000
   277  		tfile, cleanFile := testhelpers.TempFile(t, length)
   278  		defer cleanFile()
   279  
   280  		action := testArchive(t, mover, tfile, 0, length, "", nil)
   281  
   282  		testRemove(t, mover, action.UUID(), nil)
   283  		testRestoreFail(t, mover, 0, length, action.UUID(), nil)
   284  	})
   285  }
   286  
   287  func WithS3Mover(t *testing.T, updateConfig func(*archiveConfig) *archiveConfig,
   288  	tester func(t *testing.T, mover *Mover)) {
   289  	bucketVar := "LHSM_TEST_BUCKET"
   290  	// Default region to us-east-1
   291  	region := os.Getenv("AWS_REGION")
   292  	if region == "" {
   293  		region = "us-east-1"
   294  	}
   295  
   296  	bucket := os.Getenv(bucketVar)
   297  	if bucket == "" {
   298  		t.Skipf("Set %q in environment to test S3 mover.", bucketVar)
   299  	}
   300  
   301  	config := &archiveConfig{
   302  		Name:     "test-s3",
   303  		Region:   region,
   304  		Bucket:   bucket,
   305  		Prefix:   "ptest",
   306  		Endpoint: os.Getenv("AWS_S3_ENDPOINT"),
   307  	}
   308  
   309  	if updateConfig != nil {
   310  		config = updateConfig(config)
   311  	}
   312  
   313  	defer testhelpers.ChdirTemp(t)()
   314  	mover := S3Mover(config, s3Svc(config), 1)
   315  
   316  	tester(t, mover)
   317  }