|
4 | 4 | // you may not use this file except in compliance with the License. |
5 | 5 | // You may obtain a copy of the License at |
6 | 6 | // |
7 | | -// http://www.apache.org/licenses/LICENSE-2.0 |
| 7 | +// http://www.apache.org/licenses/LICENSE-2.0 |
8 | 8 | // |
9 | 9 | // Unless required by applicable law or agreed to in writing, software |
10 | 10 | // distributed under the License is distributed on an "AS IS" BASIS, |
|
15 | 15 | package stale_handle |
16 | 16 |
|
17 | 17 | import ( |
| 18 | + "log" |
18 | 19 | "path" |
19 | 20 | "slices" |
20 | 21 | "testing" |
21 | 22 |
|
| 23 | + "cloud.google.com/go/storage" |
22 | 24 | "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/client" |
| 25 | + . "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/client" |
23 | 26 | "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/operations" |
24 | 27 | "github.com/googlecloudplatform/gcsfuse/v3/tools/integration_tests/util/setup" |
| 28 | + "github.com/stretchr/testify/assert" |
25 | 29 | "github.com/stretchr/testify/suite" |
26 | 30 | ) |
27 | 31 |
|
28 | 32 | // ////////////////////////////////////////////////////////////////////// |
29 | 33 | // Boilerplate |
30 | 34 | // ////////////////////////////////////////////////////////////////////// |
31 | 35 |
|
32 | | -type staleFileHandleSyncedFileTest struct { |
| 36 | +type staleFileHandleEmptyGcsFile struct { |
33 | 37 | staleFileHandleCommon |
34 | 38 | } |
35 | 39 |
|
36 | 40 | // ////////////////////////////////////////////////////////////////////// |
37 | 41 | // Helpers |
38 | 42 | // ////////////////////////////////////////////////////////////////////// |
39 | 43 |
|
40 | | -func (s *staleFileHandleSyncedFileTest) SetupTest() { |
| 44 | +func (s *staleFileHandleEmptyGcsFile) SetupTest() { |
41 | 45 | s.fileName = path.Base(s.T().Name()) + setup.GenerateRandomString(5) |
42 | 46 | // Create a file and sync it to GCS. |
43 | 47 | client.SetupFileInTestDirectory(testEnv.ctx, testEnv.storageClient, testDirName, s.fileName, 1, s.T()) |
44 | 48 | s.f1 = operations.OpenFileWithODirect(s.T(), path.Join(testEnv.testDirPath, s.fileName)) |
45 | | - s.isLocal = false |
| 49 | + // s.isLocal = false |
| 50 | +} |
| 51 | +func (s *staleFileHandleEmptyGcsFile) TearDownTest() { |
| 52 | + setup.SaveGCSFuseLogFileInCaseOfFailure(s.T()) |
| 53 | +} |
| 54 | + |
| 55 | +//////////////////////////////////////////////////////////////////////// |
| 56 | +// Tests |
| 57 | +//////////////////////////////////////////////////////////////////////// |
| 58 | + |
| 59 | +func (s *staleFileHandleEmptyGcsFile) TestClobberedFileReadThrowsStaleFileHandleError() { |
| 60 | + // TODO(b/410698332): Remove skip condition once takeover support is available. |
| 61 | + if s.isStreamingWritesEnabled && setup.IsZonalBucketRun() { |
| 62 | + s.T().Skip("Skip test due to takeover support not available.") |
| 63 | + } |
| 64 | + // Dirty the file by giving it some contents. |
| 65 | + _, err := s.f1.WriteAt([]byte(s.data), 0) |
| 66 | + assert.NoError(s.T(), err) |
| 67 | + operations.SyncFile(s.f1, s.T()) |
| 68 | + |
| 69 | + // Replace the underlying object with a new generation. |
| 70 | + err = WriteToObject(testEnv.ctx, testEnv.storageClient, path.Join(testDirName, s.fileName), FileContents, storage.Conditions{}) |
| 71 | + |
| 72 | + assert.NoError(s.T(), err) |
| 73 | + buffer := make([]byte, len(s.data)) |
| 74 | + _, err = s.f1.Read(buffer) |
| 75 | + operations.ValidateESTALEError(s.T(), err) |
| 76 | +} |
| 77 | + |
| 78 | +func (s *staleFileHandleEmptyGcsFile) TestClobberedFileFirstWriteThrowsStaleFileHandleError() { |
| 79 | + // TODO(b/410698332): Remove skip condition once takeover support is available. |
| 80 | + if s.isStreamingWritesEnabled && setup.IsZonalBucketRun() { |
| 81 | + s.T().Skip("Skip test due to takeover support not available.") |
| 82 | + } |
| 83 | + // Clobber file by replacing the underlying object with a new generation. |
| 84 | + err := WriteToObject(testEnv.ctx, testEnv.storageClient, path.Join(testDirName, s.fileName), FileContents, storage.Conditions{}) |
| 85 | + assert.NoError(s.T(), err) |
| 86 | + |
| 87 | + // Attempt first write to the file should give stale NFS file handle error. |
| 88 | + _, err = s.f1.Write([]byte(s.data)) |
| 89 | + |
| 90 | + assert.NoError(s.T(), err) |
| 91 | + operations.ValidateSyncGivenThatFileIsClobbered(s.T(), s.f1, s.isStreamingWritesEnabled) |
| 92 | + // if s.isStreamingWritesEnabled { |
| 93 | + // err = s.f1.Sync() |
| 94 | + // operations.ValidateESTALEError(s.T(), err) |
| 95 | + // } else { |
| 96 | + // operations.ValidateSyncGivenThatFileIsClobbered(s.T(), s.f1, s.isStreamingWritesEnabled) |
| 97 | + // } |
| 98 | + err = s.f1.Close() |
| 99 | + operations.ValidateESTALEError(s.T(), err) |
| 100 | + ValidateObjectContentsFromGCS(testEnv.ctx, testEnv.storageClient, testDirName, s.fileName, FileContents, s.T()) |
| 101 | +} |
| 102 | + |
| 103 | +func (s *staleFileHandleEmptyGcsFile) TestFileDeletedRemotelySyncAndCloseThrowsStaleFileHandleError() { |
| 104 | + // TODO(mohitkyadav): Enable test once fix in b/415713332 is released |
| 105 | + if s.isStreamingWritesEnabled && setup.IsZonalBucketRun() { |
| 106 | + s.T().Skip("Skip test due to bug (b/415713332) in client.") |
| 107 | + } |
| 108 | + // Dirty the file by giving it some contents. |
| 109 | + operations.WriteWithoutClose(s.f1, s.data, s.T()) |
| 110 | + // Delete the file remotely. |
| 111 | + err := DeleteObjectOnGCS(testEnv.ctx, testEnv.storageClient, path.Join(testDirName, s.fileName)) |
| 112 | + assert.NoError(s.T(), err) |
| 113 | + // Verify unlink operation succeeds. |
| 114 | + ValidateObjectNotFoundErrOnGCS(testEnv.ctx, testEnv.storageClient, testDirName, s.fileName, s.T()) |
| 115 | + // Attempt to write to file should not give any error. |
| 116 | + operations.WriteWithoutClose(s.f1, s.data, s.T()) |
| 117 | + |
| 118 | + operations.ValidateSyncGivenThatFileIsClobbered(s.T(), s.f1, s.isStreamingWritesEnabled) |
| 119 | + // if s.isStreamingWritesEnabled { |
| 120 | + // err = s.f1.Sync() |
| 121 | + // operations.ValidateESTALEError(s.T(), err) |
| 122 | + // } else { |
| 123 | + // operations.ValidateSyncGivenThatFileIsClobbered(s.T(), s.f1, s.isStreamingWritesEnabled) |
| 124 | + // } |
| 125 | + |
| 126 | + err = s.f1.Close() |
| 127 | + operations.ValidateESTALEError(s.T(), err) |
| 128 | + ValidateObjectNotFoundErrOnGCS(testEnv.ctx, testEnv.storageClient, testDirName, s.fileName, s.T()) |
46 | 129 | } |
47 | 130 |
|
48 | 131 | //////////////////////////////////////////////////////////////////////// |
49 | 132 | // Test Function (Runs once before all tests) |
50 | 133 | //////////////////////////////////////////////////////////////////////// |
51 | 134 |
|
52 | | -func TestStaleFileHandleSyncedFileTest(t *testing.T) { |
| 135 | +func TestStaleFileHandleEmptyGcsFileTest(t *testing.T) { |
53 | 136 | // Run tests for mounted directory if the flag is set and return. |
54 | 137 | if setup.AreBothMountedDirectoryAndTestBucketFlagsSet() { |
55 | | - suite.Run(t, new(staleFileHandleSyncedFileTest)) |
| 138 | + suite.Run(t, new(staleFileHandleEmptyGcsFile)) |
56 | 139 | return |
57 | 140 | } |
58 | 141 |
|
59 | 142 | flagsSet := setup.BuildFlagSets(*testEnv.cfg, testEnv.bucketType, t.Name()) |
60 | 143 | for _, flags := range flagsSet { |
61 | | - s := new(staleFileHandleSyncedFileTest) |
| 144 | + s := new(staleFileHandleEmptyGcsFile) |
62 | 145 | s.flags = flags |
| 146 | + log.Printf("Running tests with flags: %s", s.flags) |
63 | 147 | s.isStreamingWritesEnabled = !slices.Contains(s.flags, "--enable-streaming-writes=false") |
64 | 148 | suite.Run(t, s) |
65 | 149 | } |
|
0 commit comments