1818
1919package org .apache .hadoop .ozone .repair .om ;
2020
21+ import org .apache .commons .lang3 .RandomStringUtils ;
2122import org .apache .hadoop .hdds .utils .IOUtils ;
2223import org .apache .hadoop .hdds .utils .db .StringCodec ;
2324import org .apache .hadoop .hdds .utils .db .managed .ManagedRocksDB ;
2425import org .apache .hadoop .ozone .debug .RocksDBUtils ;
2526import org .apache .hadoop .ozone .om .helpers .SnapshotInfo ;
2627import org .apache .hadoop .ozone .repair .OzoneRepair ;
2728import org .apache .ozone .test .GenericTestUtils ;
28- import org .assertj .core .api .Assertions ;
2929import org .junit .jupiter .api .AfterEach ;
3030import org .junit .jupiter .api .BeforeEach ;
3131import org .junit .jupiter .api .Test ;
3434import org .mockito .MockedStatic ;
3535import org .rocksdb .ColumnFamilyHandle ;
3636import org .rocksdb .RocksDB ;
37+ import org .rocksdb .RocksDBException ;
3738import org .rocksdb .RocksIterator ;
3839import org .rocksdb .ColumnFamilyDescriptor ;
3940
4748
4849import static org .apache .ozone .test .IntLambda .withTextFromSystemIn ;
4950import static org .apache .hadoop .ozone .OzoneConsts .SNAPSHOT_INFO_TABLE ;
50- import static org .junit . jupiter .api .Assertions .assertTrue ;
51+ import static org .assertj . core .api .Assertions .assertThat ;
5152import static org .mockito .ArgumentMatchers .anyList ;
5253import static org .mockito .ArgumentMatchers .anyString ;
5354import static org .mockito .ArgumentMatchers .eq ;
5455import static org .mockito .Mockito .mock ;
5556import static org .mockito .Mockito .mockStatic ;
56- import static org .mockito .Mockito .never ;
57+ import static org .mockito .Mockito .times ;
5758import static org .mockito .Mockito .verify ;
5859import static org .mockito .Mockito .when ;
5960
6263 */
6364public class TestSnapshotChainRepair {
6465
66+ private static final String VOLUME_NAME = "vol1" ;
67+ private static final String BUCKET_NAME = "bucket1" ;
68+ private static final String DB_PATH = "testDBPath" ;
69+
6570 private ManagedRocksDB managedRocksDB ;
6671 private RocksDB rocksDB ;
6772 private ColumnFamilyHandle columnFamilyHandle ;
6873
69- private static final String DB_PATH = "testDBPath" ;
70-
7174 private MockedStatic <ManagedRocksDB > mockedDB ;
7275 private MockedStatic <RocksDBUtils > mockedUtils ;
7376
@@ -86,18 +89,11 @@ public void setup() throws Exception {
8689
8790 @ AfterEach
8891 public void tearDown () {
89- IOUtils .closeQuietly (out , err );
90-
91- if (mockedDB != null ) {
92- mockedDB .close ();
93- }
94- if (mockedUtils != null ) {
95- mockedUtils .close ();
96- }
92+ IOUtils .closeQuietly (out , err , mockedDB , mockedUtils );
9793 }
9894
9995 private void setupMockDB (SnapshotInfo snapshotInfo ,
100- List < SnapshotInfo > iteratorSnapshots ) throws Exception {
96+ SnapshotInfo ... snapshots ) throws Exception {
10197
10298 managedRocksDB = mock (ManagedRocksDB .class );
10399 rocksDB = mock (RocksDB .class );
@@ -134,28 +130,22 @@ private void setupMockDB(SnapshotInfo snapshotInfo,
134130 when (rocksDB .newIterator (columnFamilyHandle )).thenReturn (rocksIterator );
135131
136132 // Setup iterator behavior based on provided snapshots
137- if (iteratorSnapshots . isEmpty () ) {
133+ if (snapshots . length == 0 ) {
138134 when (rocksIterator .isValid ()).thenReturn (false );
139135 } else {
140- Boolean [] remainingValidResponses = new Boolean [iteratorSnapshots .size ()];
141- for (int i = 0 ; i < iteratorSnapshots .size () - 1 ; i ++) {
142- remainingValidResponses [i ] = true ;
143- }
144- remainingValidResponses [iteratorSnapshots .size () - 1 ] = false ;
136+ Boolean [] remainingValidResponses = new Boolean [snapshots .length + 1 ];
137+ Arrays .fill (remainingValidResponses , true );
138+ remainingValidResponses [remainingValidResponses .length - 1 ] = false ;
145139
146140 when (rocksIterator .isValid ())
147141 .thenReturn (true , remainingValidResponses );
148142
149143 ArrayList <byte []> valueResponses = new ArrayList <>();
150- for (SnapshotInfo snap : iteratorSnapshots ) {
151- try {
152- valueResponses .add (SnapshotInfo .getCodec ().toPersistedFormat (snap ));
153- } catch (IOException e ) {
154- Assertions .fail ("Failed to serialize snapshot info" );
155- }
144+ for (SnapshotInfo snap : snapshots ) {
145+ valueResponses .add (SnapshotInfo .getCodec ().toPersistedFormat (snap ));
156146 }
157- byte [] firstValue = valueResponses . get ( 0 );
158- byte [][] remainingValueResponses = valueResponses .subList ( 1 , valueResponses . size ()). toArray (new byte [0 ][]);
147+ byte [] firstValue = SnapshotInfo . getCodec (). toPersistedFormat ( snapshotInfo );
148+ byte [][] remainingValueResponses = valueResponses .toArray (new byte [0 ][]);
159149 when (rocksIterator .value ())
160150 .thenReturn (firstValue , remainingValueResponses );
161151 }
@@ -164,213 +154,151 @@ private void setupMockDB(SnapshotInfo snapshotInfo,
164154 @ ParameterizedTest
165155 @ ValueSource (booleans = {true , false })
166156 public void testSuccessfulRepair (boolean dryRun ) throws Exception {
167- String volumeName = "vol1" ;
168- String bucketName = "bucket1" ;
169- String snapshotName = "snap1" ;
170- String globalPrevSnapshotName = "global-prev-snap1" ;
171- String pathPrevSnapshotName = "path-prev-snap1" ;
172-
173- UUID snapshotId = UUID .randomUUID ();
174- UUID globalPrevSnapshotId = UUID .randomUUID ();
175- UUID pathPrevSnapshotId = UUID .randomUUID ();
176-
177- SnapshotInfo snapshotInfo = SnapshotInfo .newInstance (volumeName , bucketName , snapshotName , snapshotId , 0 );
178- SnapshotInfo globalPrevSnapshot = SnapshotInfo .newInstance (volumeName , bucketName , globalPrevSnapshotName ,
179- globalPrevSnapshotId , 0 );
180- SnapshotInfo pathPrevSnapshot = SnapshotInfo .newInstance (volumeName , bucketName , pathPrevSnapshotName ,
181- pathPrevSnapshotId , 0 );
182-
183- List <SnapshotInfo > iteratorSnapshots = Arrays .asList (
184- snapshotInfo , globalPrevSnapshot , pathPrevSnapshot );
157+ SnapshotInfo snapshotInfo = newSnapshot ();
158+ SnapshotInfo globalPrevSnapshot = newSnapshot ();
159+ SnapshotInfo pathPrevSnapshot = newSnapshot ();
185160
186161 List <String > argsList = new ArrayList <>(Arrays .asList (
187162 "om" , "snapshot" , "chain" ,
188- volumeName + "/" + bucketName ,
189- snapshotName ,
163+ VOLUME_NAME + "/" + BUCKET_NAME ,
164+ snapshotInfo . getName () ,
190165 "--db" , DB_PATH ,
191- "--global-previous" , globalPrevSnapshotId .toString (),
192- "--path-previous" , pathPrevSnapshotId .toString ()));
166+ "--global-previous" , globalPrevSnapshot . getSnapshotId () .toString (),
167+ "--path-previous" , pathPrevSnapshot . getSnapshotId () .toString ()));
193168
194169 if (dryRun ) {
195170 argsList .add ("--dry-run" );
196171 }
197172
198- setupMockDB (snapshotInfo , iteratorSnapshots );
173+ setupMockDB (snapshotInfo , globalPrevSnapshot , pathPrevSnapshot );
199174
200175 CommandLine cli = new OzoneRepair ().getCmd ();
201176 withTextFromSystemIn ("y" )
202177 .execute (() -> cli .execute (argsList .toArray (new String [0 ])));
203178
204- String output = out .getOutput ();
205- assertTrue (output .contains ("Updating SnapshotInfo to" ));
179+ assertThat (out .getOutput ()).contains ("Updating SnapshotInfo to" );
206180
207- if (dryRun ) {
208- // Verify DB update was NOT called in dry run mode
209- verify (rocksDB , never ()).put (
210- eq (columnFamilyHandle ),
211- eq (StringCodec .get ().toPersistedFormat (snapshotInfo .getTableKey ())),
212- eq (SnapshotInfo .getCodec ().toPersistedFormat (snapshotInfo )));
213- } else {
214- // Verify DB update was called with correct parameters
215- verify (rocksDB ).put (
216- eq (columnFamilyHandle ),
217- eq (StringCodec .get ().toPersistedFormat (snapshotInfo .getTableKey ())),
218- eq (SnapshotInfo .getCodec ().toPersistedFormat (snapshotInfo )));
219- assertTrue (output .contains ("Snapshot Info is updated" ));
220- }
181+ verifyDbWrite (snapshotInfo , !dryRun );
221182 }
222183
223184 @ Test
224185 public void testGlobalPreviousMatchesSnapshotId () throws Exception {
225- String volumeName = "vol1" ;
226- String bucketName = "bucket1" ;
227- String snapshotName = "snap1" ;
228-
229- UUID snapshotId = UUID .randomUUID ();
230- // Use same ID for global previous to trigger error
231- UUID globalPrevSnapshotId = snapshotId ;
232- UUID pathPrevSnapshotId = UUID .randomUUID ();
233-
234- SnapshotInfo snapshotInfo = SnapshotInfo .newInstance (volumeName , bucketName ,
235- snapshotName , snapshotId , 0 );
236- SnapshotInfo pathPrevSnapshot = SnapshotInfo .newInstance (volumeName , bucketName ,
237- "path-prev" , pathPrevSnapshotId , 0 );
238-
239- List <SnapshotInfo > iteratorSnapshots = Arrays .asList (
240- snapshotInfo , pathPrevSnapshot );
186+ SnapshotInfo snapshotInfo = newSnapshot ();
187+ SnapshotInfo pathPrevSnapshot = newSnapshot ();
241188
242189 String [] args = new String [] {
243190 "om" , "snapshot" , "chain" ,
244- volumeName + "/" + bucketName ,
245- snapshotName ,
191+ VOLUME_NAME + "/" + BUCKET_NAME ,
192+ snapshotInfo . getName () ,
246193 "--db" , DB_PATH ,
247- "--global-previous" , globalPrevSnapshotId .toString (),
248- "--path-previous" , pathPrevSnapshotId .toString (),
194+ // Use same ID for global previous to trigger error
195+ "--global-previous" , snapshotInfo .getSnapshotId ().toString (),
196+ "--path-previous" , pathPrevSnapshot .getSnapshotId ().toString (),
249197 };
250198
251- setupMockDB (snapshotInfo , iteratorSnapshots );
199+ setupMockDB (snapshotInfo , pathPrevSnapshot );
252200
253201 CommandLine cli = new OzoneRepair ().getCmd ();
254202 withTextFromSystemIn ("y" )
255203 .execute (() -> cli .execute (args ));
256204
257- String errorOutput = err . getOutput ();
258- assertTrue ( errorOutput . contains ( "globalPreviousSnapshotId: '" + globalPrevSnapshotId +
259- "' is equal to given snapshot's ID" ) );
205+ assertThat ( err . getOutput ()). contains ( "globalPreviousSnapshotId: '" + snapshotInfo . getSnapshotId () +
206+ "' is equal to given snapshot's ID" );
207+ verifyDbWrite ( snapshotInfo , false );
260208 }
261209
262210 @ Test
263211 public void testPathPreviousMatchesSnapshotId () throws Exception {
264- String volumeName = "vol1" ;
265- String bucketName = "bucket1" ;
266- String snapshotName = "snap1" ;
267-
268- UUID snapshotId = UUID .randomUUID ();
269- UUID globalPrevSnapshotId = UUID .randomUUID ();
270- // Use same ID for path previous to trigger error
271- UUID pathPrevSnapshotId = snapshotId ;
272-
273- SnapshotInfo snapshotInfo = SnapshotInfo .newInstance (volumeName , bucketName ,
274- snapshotName , snapshotId , 0 );
275- SnapshotInfo globalPrevSnapshot = SnapshotInfo .newInstance (volumeName , bucketName ,
276- "global-prev" , globalPrevSnapshotId , 0 );
277-
278- List <SnapshotInfo > iteratorSnapshots = Arrays .asList (
279- snapshotInfo , globalPrevSnapshot );
212+ SnapshotInfo snapshotInfo = newSnapshot ();
213+ SnapshotInfo globalPrevSnapshot = newSnapshot ();
280214
281215 String [] args = new String [] {
282216 "om" , "snapshot" , "chain" ,
283- volumeName + "/" + bucketName ,
284- snapshotName ,
217+ VOLUME_NAME + "/" + BUCKET_NAME ,
218+ snapshotInfo . getName () ,
285219 "--db" , DB_PATH ,
286- "--global-previous" , globalPrevSnapshotId .toString (),
287- "--path-previous" , pathPrevSnapshotId .toString (),
220+ "--global-previous" , globalPrevSnapshot .getSnapshotId ().toString (),
221+ // Use same ID for path previous to trigger error
222+ "--path-previous" , snapshotInfo .getSnapshotId ().toString (),
288223 };
289224
290- setupMockDB (snapshotInfo , iteratorSnapshots );
225+ setupMockDB (snapshotInfo , globalPrevSnapshot );
291226
292227 CommandLine cli = new OzoneRepair ().getCmd ();
293228 withTextFromSystemIn ("y" )
294229 .execute (() -> cli .execute (args ));
295230
296- String errorOutput = err . getOutput ();
297- assertTrue ( errorOutput . contains ( "pathPreviousSnapshotId: '" + pathPrevSnapshotId +
298- "' is equal to given snapshot's ID" ) );
231+ assertThat ( err . getOutput ()). contains ( "pathPreviousSnapshotId: '" + snapshotInfo . getSnapshotId () +
232+ "' is equal to given snapshot's ID" );
233+ verifyDbWrite ( snapshotInfo , false );
299234 }
300235
301236 @ Test
302237 public void testGlobalPreviousDoesNotExist () throws Exception {
303- String volumeName = "vol1" ;
304- String bucketName = "bucket1" ;
305- String snapshotName = "snap1" ;
306-
307- UUID snapshotId = UUID .randomUUID ();
308- UUID globalPrevSnapshotId = UUID .randomUUID ();
309- UUID pathPrevSnapshotId = UUID .randomUUID ();
238+ String nonexistent = UUID .randomUUID ().toString ();
310239
311- SnapshotInfo snapshotInfo = SnapshotInfo .newInstance (volumeName , bucketName ,
312- snapshotName , snapshotId , 0 );
313- SnapshotInfo pathPrevSnapshot = SnapshotInfo .newInstance (volumeName , bucketName ,
314- "path-prev" , pathPrevSnapshotId , 0 );
315-
316- List <SnapshotInfo > iteratorSnapshots = Arrays .asList (
317- snapshotInfo , pathPrevSnapshot );
240+ SnapshotInfo snapshotInfo = newSnapshot ();
241+ SnapshotInfo pathPrevSnapshot = newSnapshot ();
318242
319243 String [] args = new String [] {
320244 "om" , "snapshot" , "chain" ,
321- volumeName + "/" + bucketName ,
322- snapshotName ,
245+ VOLUME_NAME + "/" + BUCKET_NAME ,
246+ snapshotInfo . getName () ,
323247 "--db" , DB_PATH ,
324- "--global-previous" , globalPrevSnapshotId . toString () ,
325- "--path-previous" , pathPrevSnapshotId .toString (),
248+ "--global-previous" , nonexistent ,
249+ "--path-previous" , pathPrevSnapshot . getSnapshotId () .toString (),
326250 };
327251
328- setupMockDB (snapshotInfo , iteratorSnapshots );
252+ setupMockDB (snapshotInfo , pathPrevSnapshot );
329253
330254 CommandLine cli = new OzoneRepair ().getCmd ();
331255 withTextFromSystemIn ("y" )
332256 .execute (() -> cli .execute (args ));
333257
334- String errorOutput = err .getOutput ();
335- assertTrue ( errorOutput . contains ( "globalPreviousSnapshotId: '" + globalPrevSnapshotId +
336- "' does not exist in snapshotInfoTable" ) );
258+ assertThat ( err .getOutput ()). contains ( "globalPreviousSnapshotId: '" + nonexistent +
259+ "' does not exist in snapshotInfoTable" );
260+ verifyDbWrite ( snapshotInfo , false );
337261 }
338262
339263 @ Test
340264 public void testPathPreviousDoesNotExist () throws Exception {
341- String volumeName = "vol1" ;
342- String bucketName = "bucket1" ;
343- String snapshotName = "snap1" ;
344-
345- UUID snapshotId = UUID .randomUUID ();
346- UUID globalPrevSnapshotId = UUID .randomUUID ();
347- UUID pathPrevSnapshotId = UUID .randomUUID ();
265+ String nonexistent = UUID .randomUUID ().toString ();
348266
349- SnapshotInfo snapshotInfo = SnapshotInfo .newInstance (volumeName , bucketName ,
350- snapshotName , snapshotId , 0 );
351- SnapshotInfo globalPrevSnapshot = SnapshotInfo .newInstance (volumeName , bucketName ,
352- "global-prev" , globalPrevSnapshotId , 0 );
353-
354- List <SnapshotInfo > iteratorSnapshots = Arrays .asList (
355- snapshotInfo , globalPrevSnapshot );
267+ SnapshotInfo snapshotInfo = newSnapshot ();
268+ SnapshotInfo globalPrevSnapshot = newSnapshot ();
356269
357270 String [] args = new String [] {
358271 "om" , "snapshot" , "chain" ,
359- volumeName + "/" + bucketName ,
360- snapshotName ,
272+ VOLUME_NAME + "/" + BUCKET_NAME ,
273+ snapshotInfo . getName () ,
361274 "--db" , DB_PATH ,
362- "--global-previous" , globalPrevSnapshotId .toString (),
363- "--path-previous" , pathPrevSnapshotId . toString () ,
275+ "--global-previous" , globalPrevSnapshot . getSnapshotId () .toString (),
276+ "--path-previous" , nonexistent ,
364277 };
365278
366- setupMockDB (snapshotInfo , iteratorSnapshots );
279+ setupMockDB (snapshotInfo , globalPrevSnapshot );
367280
368281 CommandLine cli = new OzoneRepair ().getCmd ();
369282 withTextFromSystemIn ("y" )
370283 .execute (() -> cli .execute (args ));
371284
372- String errorOutput = err .getOutput ();
373- assertTrue (errorOutput .contains ("pathPreviousSnapshotId: '" + pathPrevSnapshotId +
374- "' does not exist in snapshotInfoTable" ));
285+ assertThat (err .getOutput ()).contains ("pathPreviousSnapshotId: '" + nonexistent +
286+ "' does not exist in snapshotInfoTable" );
287+ verifyDbWrite (snapshotInfo , false );
288+ }
289+
290+ private static SnapshotInfo newSnapshot () {
291+ String name = RandomStringUtils .insecure ().nextAlphanumeric (10 );
292+ return SnapshotInfo .newInstance (VOLUME_NAME , BUCKET_NAME , name , UUID .randomUUID (), 0 );
293+ }
294+
295+ private void verifyDbWrite (SnapshotInfo snapshotInfo , boolean updateExpected ) throws RocksDBException , IOException {
296+ verify (rocksDB , times (updateExpected ? 1 : 0 )).put (
297+ eq (columnFamilyHandle ),
298+ eq (StringCodec .get ().toPersistedFormat (snapshotInfo .getTableKey ())),
299+ eq (SnapshotInfo .getCodec ().toPersistedFormat (snapshotInfo )));
300+ if (updateExpected ) {
301+ assertThat (out .get ()).contains ("Snapshot Info is updated" );
302+ }
375303 }
376304}
0 commit comments