View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  package org.apache.hadoop.hbase.snapshot;
19  
20  import static org.junit.Assert.assertEquals;
21  import static org.junit.Assert.assertFalse;
22  import static org.junit.Assert.assertTrue;
23  
24  import java.io.IOException;
25  import java.util.Collections;
26  import java.util.ArrayList;
27  import java.util.HashSet;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  import java.util.TreeSet;
32  
33  import org.apache.commons.logging.Log;
34  import org.apache.commons.logging.LogFactory;
35  import org.apache.hadoop.conf.Configuration;
36  import org.apache.hadoop.fs.FSDataOutputStream;
37  import org.apache.hadoop.fs.FileSystem;
38  import org.apache.hadoop.fs.FileStatus;
39  import org.apache.hadoop.fs.FSDataInputStream;
40  import org.apache.hadoop.fs.Path;
41  import org.apache.hadoop.fs.PathFilter;
42  import org.apache.hadoop.hbase.HBaseTestingUtility;
43  import org.apache.hadoop.hbase.HColumnDescriptor;
44  import org.apache.hadoop.hbase.HConstants;
45  import org.apache.hadoop.hbase.HRegionInfo;
46  import org.apache.hadoop.hbase.HTableDescriptor;
47  import org.apache.hadoop.hbase.TableName;
48  import org.apache.hadoop.hbase.TableNotEnabledException;
49  import org.apache.hadoop.hbase.classification.InterfaceAudience;
50  import org.apache.hadoop.hbase.client.Admin;
51  import org.apache.hadoop.hbase.client.BufferedMutator;
52  import org.apache.hadoop.hbase.client.Durability;
53  import org.apache.hadoop.hbase.client.HTable;
54  import org.apache.hadoop.hbase.client.Put;
55  import org.apache.hadoop.hbase.client.Table;
56  import org.apache.hadoop.hbase.errorhandling.ForeignExceptionDispatcher;
57  import org.apache.hadoop.hbase.client.RegionReplicaUtil;
58  import org.apache.hadoop.hbase.io.HFileLink;
59  import org.apache.hadoop.hbase.master.HMaster;
60  import org.apache.hadoop.hbase.master.MasterFileSystem;
61  import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
62  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.SnapshotDescription;
63  import org.apache.hadoop.hbase.protobuf.generated.SnapshotProtos.SnapshotRegionManifest;
64  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneRequest;
65  import org.apache.hadoop.hbase.protobuf.generated.MasterProtos.IsSnapshotDoneResponse;
66  import org.apache.hadoop.hbase.regionserver.HRegionFileSystem;
67  import org.apache.hadoop.hbase.regionserver.HRegionServer;
68  import org.apache.hadoop.hbase.regionserver.Region;
69  import org.apache.hadoop.hbase.util.Bytes;
70  import org.apache.hadoop.hbase.util.FSTableDescriptors;
71  import org.apache.hadoop.hbase.util.FSVisitor;
72  import org.apache.hadoop.hbase.util.FSUtils;
73  import org.apache.hadoop.hbase.util.MD5Hash;
74  import org.junit.Assert;
75  
76  import com.google.protobuf.ServiceException;
77  
78  /**
79   * Utilities class for snapshots
80   */
81  @InterfaceAudience.Private
82  public final class SnapshotTestingUtils {
83  
84    private static final Log LOG = LogFactory.getLog(SnapshotTestingUtils.class);
85    private static byte[] KEYS = Bytes.toBytes("0123456789");
86  
87    private SnapshotTestingUtils() {
88      // private constructor for utility class
89    }
90  
91    /**
92     * Assert that we don't have any snapshots lists
93     *
94     * @throws IOException
95     *           if the admin operation fails
96     */
97    public static void assertNoSnapshots(Admin admin) throws IOException {
98      assertEquals("Have some previous snapshots", 0, admin.listSnapshots()
99          .size());
100   }
101 
102   /**
103    * Make sure that there is only one snapshot returned from the master and its
104    * name and table match the passed in parameters.
105    */
106   public static List<SnapshotDescription> assertExistsMatchingSnapshot(
107       Admin admin, String snapshotName, TableName tableName)
108       throws IOException {
109     // list the snapshot
110     List<SnapshotDescription> snapshots = admin.listSnapshots();
111 
112     List<SnapshotDescription> returnedSnapshots = new ArrayList<SnapshotDescription>();
113     for (SnapshotDescription sd : snapshots) {
114       if (snapshotName.equals(sd.getName()) &&
115           tableName.equals(TableName.valueOf(sd.getTable()))) {
116         returnedSnapshots.add(sd);
117       }
118     }
119 
120     Assert.assertTrue("No matching snapshots found.", returnedSnapshots.size()>0);
121     return returnedSnapshots;
122   }
123 
124   /**
125    * Make sure that there is only one snapshot returned from the master
126    */
127   public static void assertOneSnapshotThatMatches(Admin admin,
128       SnapshotDescription snapshot) throws IOException {
129     assertOneSnapshotThatMatches(admin, snapshot.getName(),
130         TableName.valueOf(snapshot.getTable()));
131   }
132 
133   /**
134    * Make sure that there is only one snapshot returned from the master and its
135    * name and table match the passed in parameters.
136    */
137   public static List<SnapshotDescription> assertOneSnapshotThatMatches(
138       Admin admin, String snapshotName, TableName tableName)
139       throws IOException {
140     // list the snapshot
141     List<SnapshotDescription> snapshots = admin.listSnapshots();
142 
143     assertEquals("Should only have 1 snapshot", 1, snapshots.size());
144     assertEquals(snapshotName, snapshots.get(0).getName());
145     assertEquals(tableName, TableName.valueOf(snapshots.get(0).getTable()));
146 
147     return snapshots;
148   }
149 
150   /**
151    * Make sure that there is only one snapshot returned from the master and its
152    * name and table match the passed in parameters.
153    */
154   public static List<SnapshotDescription> assertOneSnapshotThatMatches(
155       Admin admin, byte[] snapshot, TableName tableName) throws IOException {
156     return assertOneSnapshotThatMatches(admin, Bytes.toString(snapshot),
157         tableName);
158   }
159 
160   public static void confirmSnapshotValid(HBaseTestingUtility testUtil,
161       SnapshotDescription snapshotDescriptor, TableName tableName, byte[] family)
162       throws IOException {
163     MasterFileSystem mfs = testUtil.getHBaseCluster().getMaster().getMasterFileSystem();
164     confirmSnapshotValid(snapshotDescriptor, tableName, family,
165         mfs.getRootDir(), testUtil.getHBaseAdmin(), mfs.getFileSystem());
166   }
167 
168   /**
169    * Confirm that the snapshot contains references to all the files that should
170    * be in the snapshot.
171    */
172   public static void confirmSnapshotValid(
173       SnapshotDescription snapshotDescriptor, TableName tableName,
174       byte[] testFamily, Path rootDir, Admin admin, FileSystem fs)
175       throws IOException {
176     ArrayList nonEmptyTestFamilies = new ArrayList(1);
177     nonEmptyTestFamilies.add(testFamily);
178     confirmSnapshotValid(snapshotDescriptor, tableName,
179       nonEmptyTestFamilies, null, rootDir, admin, fs);
180   }
181 
182   /**
183    * Confirm that the snapshot has no references files but only metadata.
184    */
185   public static void confirmEmptySnapshotValid(
186       SnapshotDescription snapshotDescriptor, TableName tableName,
187       byte[] testFamily, Path rootDir, Admin admin, FileSystem fs)
188       throws IOException {
189     ArrayList emptyTestFamilies = new ArrayList(1);
190     emptyTestFamilies.add(testFamily);
191     confirmSnapshotValid(snapshotDescriptor, tableName,
192       null, emptyTestFamilies, rootDir, admin, fs);
193   }
194 
195   /**
196    * Confirm that the snapshot contains references to all the files that should
197    * be in the snapshot. This method also perform some redundant check like
198    * the existence of the snapshotinfo or the regioninfo which are done always
199    * by the MasterSnapshotVerifier, at the end of the snapshot operation.
200    */
201   public static void confirmSnapshotValid(
202       SnapshotDescription snapshotDescriptor, TableName tableName,
203       List<byte[]> nonEmptyTestFamilies, List<byte[]> emptyTestFamilies,
204       Path rootDir, Admin admin, FileSystem fs) throws IOException {
205     final Configuration conf = admin.getConfiguration();
206 
207     // check snapshot dir
208     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(
209         snapshotDescriptor, rootDir);
210     assertTrue("target snapshot directory, '"+ snapshotDir +"', doesn't exist.", fs.exists(snapshotDir));
211 
212     SnapshotDescription desc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
213 
214     // Extract regions and families with store files
215     final Set<byte[]> snapshotFamilies = new TreeSet<byte[]>(Bytes.BYTES_COMPARATOR);
216 
217     SnapshotManifest manifest = SnapshotManifest.open(conf, fs, snapshotDir, desc);
218     Map<String, SnapshotRegionManifest> regionManifests = manifest.getRegionManifestsMap();
219     for (SnapshotRegionManifest regionManifest: regionManifests.values()) {
220       SnapshotReferenceUtil.visitRegionStoreFiles(regionManifest,
221           new SnapshotReferenceUtil.StoreFileVisitor() {
222         @Override
223         public void storeFile(final HRegionInfo regionInfo, final String family,
224               final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
225           snapshotFamilies.add(Bytes.toBytes(family));
226         }
227       });
228     }
229 
230     // Verify that there are store files in the specified families
231     if (nonEmptyTestFamilies != null) {
232       for (final byte[] familyName: nonEmptyTestFamilies) {
233         assertTrue("Expected snapshot to contain family '" + Bytes.toString(familyName) + "', but it does not.", snapshotFamilies.contains(familyName));
234       }
235     }
236 
237     // Verify that there are no store files in the specified families
238     if (emptyTestFamilies != null) {
239       for (final byte[] familyName: emptyTestFamilies) {
240         assertFalse("Expected snapshot to skip empty family '" + Bytes.toString(familyName) + "', but it is present.", snapshotFamilies.contains(familyName));
241       }
242     }
243 
244     // check the region snapshot for all the regions
245     List<HRegionInfo> regions = admin.getTableRegions(tableName);
246     // remove the non-default regions
247     RegionReplicaUtil.removeNonDefaultRegions(regions);
248 
249     // if create snapshot when table splitting, parent region will be included to the snapshot
250     // region manifest. we should exclude the parent regions.
251     int regionCountExclusiveSplitParent = 0;
252     for (SnapshotRegionManifest snapshotRegionManifest : regionManifests.values()) {
253       HRegionInfo hri = HRegionInfo.convert(snapshotRegionManifest.getRegionInfo());
254       if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent())) {
255         continue;
256       }
257       regionCountExclusiveSplitParent++;
258     }
259     assertEquals("Wrong number of regions.", regions.size(), regionCountExclusiveSplitParent);
260 
261     // Verify Regions (redundant check, see MasterSnapshotVerifier)
262     for (HRegionInfo info : regions) {
263       String regionName = info.getEncodedName();
264       assertTrue("Missing region name: '" + regionName + "'", regionManifests.containsKey(regionName));
265     }
266   }
267 
268   /**
269    * Helper method for testing async snapshot operations. Just waits for the
270    * given snapshot to complete on the server by repeatedly checking the master.
271    *
272    * @param master: the master running the snapshot
273    * @param snapshot: the snapshot to check
274    * @param sleep: amount to sleep between checks to see if the snapshot is done
275    * @throws ServiceException if the snapshot fails
276    */
277   public static void waitForSnapshotToComplete(HMaster master, SnapshotDescription snapshot,
278       long sleep) throws ServiceException {
279     final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder()
280         .setSnapshot(snapshot).build();
281     IsSnapshotDoneResponse done = IsSnapshotDoneResponse.newBuilder()
282         .buildPartial();
283     while (!done.getDone()) {
284       done = master.getMasterRpcServices().isSnapshotDone(null, request);
285       try {
286         Thread.sleep(sleep);
287       } catch (InterruptedException e) {
288         throw new ServiceException(e);
289       }
290     }
291   }
292 
293   /*
294    * Take snapshot with maximum of numTries attempts, ignoring CorruptedSnapshotException
295    * except for the last CorruptedSnapshotException
296    */
297   public static void snapshot(Admin admin,
298       final String snapshotName, final String tableName,
299       SnapshotDescription.Type type, int numTries) throws IOException {
300     int tries = 0;
301     CorruptedSnapshotException lastEx = null;
302     while (tries++ < numTries) {
303       try {
304         admin.snapshot(snapshotName, TableName.valueOf(tableName), type);
305         return;
306       } catch (CorruptedSnapshotException cse) {
307         LOG.warn("Got CorruptedSnapshotException", cse);
308         lastEx = cse;
309       }
310     }
311     throw lastEx;
312   }
313 
314   public static void cleanupSnapshot(Admin admin, byte[] tableName)
315       throws IOException {
316     SnapshotTestingUtils.cleanupSnapshot(admin, Bytes.toString(tableName));
317   }
318 
319   public static void cleanupSnapshot(Admin admin, String snapshotName)
320       throws IOException {
321     // delete the taken snapshot
322     admin.deleteSnapshot(snapshotName);
323     assertNoSnapshots(admin);
324   }
325 
326   /**
327    * Expect the snapshot to throw an error when checking if the snapshot is
328    * complete
329    *
330    * @param master master to check
331    * @param snapshot the {@link SnapshotDescription} request to pass to the master
332    * @param clazz expected exception from the master
333    */
334   public static void expectSnapshotDoneException(HMaster master,
335       IsSnapshotDoneRequest snapshot,
336       Class<? extends HBaseSnapshotException> clazz) {
337     try {
338       master.getMasterRpcServices().isSnapshotDone(null, snapshot);
339       Assert.fail("didn't fail to lookup a snapshot");
340     } catch (ServiceException se) {
341       try {
342         throw ProtobufUtil.getRemoteException(se);
343       } catch (HBaseSnapshotException e) {
344         assertEquals("Threw wrong snapshot exception!", clazz, e.getClass());
345       } catch (Throwable t) {
346         Assert.fail("Threw an unexpected exception:" + t);
347       }
348     }
349   }
350 
351   /**
352    * List all the HFiles in the given table
353    *
354    * @param fs: FileSystem where the table lives
355    * @param tableDir directory of the table
356    * @return array of the current HFiles in the table (could be a zero-length array)
357    * @throws IOException on unexecpted error reading the FS
358    */
359   public static ArrayList<String> listHFileNames(final FileSystem fs, final Path tableDir)
360       throws IOException {
361     final ArrayList<String> hfiles = new ArrayList<String>();
362     FSVisitor.visitTableStoreFiles(fs, tableDir, new FSVisitor.StoreFileVisitor() {
363       @Override
364       public void storeFile(final String region, final String family, final String hfileName)
365           throws IOException {
366         hfiles.add(hfileName);
367       }
368     });
369     Collections.sort(hfiles);
370     return hfiles;
371   }
372 
373   /**
374    * Take a snapshot of the specified table and verify that the given family is
375    * not empty. Note that this will leave the table disabled
376    * in the case of an offline snapshot.
377    */
378   public static void createSnapshotAndValidate(Admin admin,
379       TableName tableName, String familyName, String snapshotNameString,
380       Path rootDir, FileSystem fs, boolean onlineSnapshot)
381       throws Exception {
382     ArrayList<byte[]> nonEmptyFamilyNames = new ArrayList<byte[]>(1);
383     nonEmptyFamilyNames.add(Bytes.toBytes(familyName));
384     createSnapshotAndValidate(admin, tableName, nonEmptyFamilyNames, /* emptyFamilyNames= */ null,
385                               snapshotNameString, rootDir, fs, onlineSnapshot);
386   }
387 
388   /**
389    * Take a snapshot of the specified table and verify the given families.
390    * Note that this will leave the table disabled in the case of an offline snapshot.
391    */
392   public static void createSnapshotAndValidate(Admin admin,
393       TableName tableName, List<byte[]> nonEmptyFamilyNames, List<byte[]> emptyFamilyNames,
394       String snapshotNameString, Path rootDir, FileSystem fs, boolean onlineSnapshot)
395         throws Exception {
396     if (!onlineSnapshot) {
397       try {
398         LOG.info("prepping for offline snapshot.");
399         admin.disableTable(tableName);
400       } catch (TableNotEnabledException tne) {
401         LOG.info("In attempting to disable " + tableName + " it turns out that the this table is " +
402             "already disabled.");
403       }
404     }
405     LOG.info("taking snapshot.");
406     admin.snapshot(snapshotNameString, tableName);
407 
408     LOG.info("Confirming snapshot exists.");
409     List<SnapshotDescription> snapshots = SnapshotTestingUtils.assertExistsMatchingSnapshot(admin,
410       snapshotNameString, tableName);
411     if (snapshots == null || snapshots.size() != 1) {
412       Assert.fail("Incorrect number of snapshots for table " + tableName);
413     }
414 
415     LOG.info("validating snapshot.");
416     SnapshotTestingUtils.confirmSnapshotValid(snapshots.get(0), tableName, nonEmptyFamilyNames,
417       emptyFamilyNames, rootDir, admin, fs);
418   }
419 
420   /**
421    * Corrupt the specified snapshot by deleting some files.
422    *
423    * @param util {@link HBaseTestingUtility}
424    * @param snapshotName name of the snapshot to corrupt
425    * @return array of the corrupted HFiles
426    * @throws IOException on unexecpted error reading the FS
427    */
428   public static ArrayList corruptSnapshot(final HBaseTestingUtility util, final String snapshotName)
429       throws IOException {
430     final MasterFileSystem mfs = util.getHBaseCluster().getMaster().getMasterFileSystem();
431     final FileSystem fs = mfs.getFileSystem();
432 
433     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName,
434                                                                         mfs.getRootDir());
435     SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
436     final TableName table = TableName.valueOf(snapshotDesc.getTable());
437 
438     final ArrayList corruptedFiles = new ArrayList();
439     final Configuration conf = util.getConfiguration();
440     SnapshotReferenceUtil.visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc,
441         new SnapshotReferenceUtil.StoreFileVisitor() {
442       @Override
443       public void storeFile(final HRegionInfo regionInfo, final String family,
444             final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
445         String region = regionInfo.getEncodedName();
446         String hfile = storeFile.getName();
447         HFileLink link = HFileLink.build(conf, table, region, family, hfile);
448         if (corruptedFiles.size() % 2 == 0) {
449           fs.delete(link.getAvailablePath(fs), true);
450           corruptedFiles.add(hfile);
451         }
452       }
453     });
454 
455     assertTrue(corruptedFiles.size() > 0);
456     return corruptedFiles;
457   }
458 
459   // ==========================================================================
460   //  Snapshot Mock
461   // ==========================================================================
462   public static class SnapshotMock {
463     private final static String TEST_FAMILY = "cf";
464     public final static int TEST_NUM_REGIONS = 4;
465 
466     private final Configuration conf;
467     private final FileSystem fs;
468     private final Path rootDir;
469 
470     static class RegionData {
471       public HRegionInfo hri;
472       public Path tableDir;
473       public Path[] files;
474 
475       public RegionData(final Path tableDir, final HRegionInfo hri, final int nfiles) {
476         this.tableDir = tableDir;
477         this.hri = hri;
478         this.files = new Path[nfiles];
479       }
480     }
481 
482     public static class SnapshotBuilder {
483       private final RegionData[] tableRegions;
484       private final SnapshotDescription desc;
485       private final HTableDescriptor htd;
486       private final Configuration conf;
487       private final FileSystem fs;
488       private final Path rootDir;
489       private Path snapshotDir;
490       private int snapshotted = 0;
491 
492       public SnapshotBuilder(final Configuration conf, final FileSystem fs,
493           final Path rootDir, final HTableDescriptor htd,
494           final SnapshotDescription desc, final RegionData[] tableRegions)
495           throws IOException {
496         this.fs = fs;
497         this.conf = conf;
498         this.rootDir = rootDir;
499         this.htd = htd;
500         this.desc = desc;
501         this.tableRegions = tableRegions;
502         this.snapshotDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir);
503         new FSTableDescriptors(conf)
504           .createTableDescriptorForTableDirectory(snapshotDir, htd, false);
505       }
506 
507       public HTableDescriptor getTableDescriptor() {
508         return this.htd;
509       }
510 
511       public SnapshotDescription getSnapshotDescription() {
512         return this.desc;
513       }
514 
515       public Path getSnapshotsDir() {
516         return this.snapshotDir;
517       }
518 
519       public Path[] addRegion() throws IOException {
520         return addRegion(desc);
521       }
522 
523       public Path[] addRegionV1() throws IOException {
524         return addRegion(desc.toBuilder()
525                           .setVersion(SnapshotManifestV1.DESCRIPTOR_VERSION)
526                           .build());
527       }
528 
529       public Path[] addRegionV2() throws IOException {
530         return addRegion(desc.toBuilder()
531                           .setVersion(SnapshotManifestV2.DESCRIPTOR_VERSION)
532                           .build());
533       }
534 
535       private Path[] addRegion(final SnapshotDescription desc) throws IOException {
536         if (this.snapshotted == tableRegions.length) {
537           throw new UnsupportedOperationException("No more regions in the table");
538         }
539 
540         RegionData regionData = tableRegions[this.snapshotted++];
541         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
542         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
543         manifest.addRegion(regionData.tableDir, regionData.hri);
544         return regionData.files;
545       }
546 
547       private void corruptFile(Path p) throws IOException {
548         String manifestName = p.getName();
549 
550         // Rename the original region-manifest file
551         Path newP = new Path(p.getParent(), manifestName + "1");
552         fs.rename(p, newP);
553 
554         // Create a new region-manifest file
555         FSDataOutputStream out = fs.create(p);
556 
557         //Copy the first 25 bytes of the original region-manifest into the new one,
558         //make it a corrupted region-manifest file.
559         FSDataInputStream input = fs.open(newP);
560         byte[] buffer = new byte[25];
561         int len = input.read(0, buffer, 0, 25);
562         if (len > 1) {
563           out.write(buffer, 0, len - 1);
564         }
565         out.close();
566 
567         // Delete the original region-manifest
568         fs.delete(newP);
569       }
570 
571       /**
572        * Corrupt one region-manifest file
573        *
574        * @throws IOException on unexecpted error from the FS
575        */
576       public void corruptOneRegionManifest() throws IOException {
577         FileStatus[] manifestFiles = FSUtils.listStatus(fs, snapshotDir, new PathFilter() {
578           @Override public boolean accept(Path path) {
579             return path.getName().startsWith(SnapshotManifestV2.SNAPSHOT_MANIFEST_PREFIX);
580           }
581         });
582 
583         if (manifestFiles.length == 0) return;
584 
585         // Just choose the first one
586         Path p = manifestFiles[0].getPath();
587         corruptFile(p);
588       }
589 
590       public void missOneRegionSnapshotFile() throws IOException {
591         FileStatus[] manifestFiles = FSUtils.listStatus(fs, snapshotDir);
592         for (FileStatus fileStatus : manifestFiles) {
593           String fileName = fileStatus.getPath().getName();
594           if (fileName.endsWith(SnapshotDescriptionUtils.SNAPSHOTINFO_FILE)
595             || fileName.endsWith(".tabledesc")
596             || fileName.endsWith(SnapshotDescriptionUtils.SNAPSHOT_TMP_DIR_NAME)) {
597               fs.delete(fileStatus.getPath(), true);
598           }
599         }
600       }
601 
602       /**
603        * Corrupt data-manifest file
604        *
605        * @throws IOException on unexecpted error from the FS
606        */
607       public void corruptDataManifest() throws IOException {
608         FileStatus[] manifestFiles = FSUtils.listStatus(fs, snapshotDir, new PathFilter() {
609           @Override
610           public boolean accept(Path path) {
611             return path.getName().startsWith(SnapshotManifest.DATA_MANIFEST_NAME);
612           }
613         });
614 
615         if (manifestFiles.length == 0) return;
616 
617         // Just choose the first one
618         Path p = manifestFiles[0].getPath();
619         corruptFile(p);
620       }
621 
622       public Path commit() throws IOException {
623         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
624         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
625         manifest.addTableDescriptor(htd);
626         manifest.consolidate();
627         SnapshotDescriptionUtils.completeSnapshot(desc, rootDir, snapshotDir, fs);
628         snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(desc, rootDir);
629         return snapshotDir;
630       }
631 
632       public void consolidate() throws IOException {
633         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
634         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
635         manifest.addTableDescriptor(htd);
636         manifest.consolidate();
637       }
638     }
639 
640     public SnapshotMock(final Configuration conf, final FileSystem fs, final Path rootDir) {
641       this.fs = fs;
642       this.conf = conf;
643       this.rootDir = rootDir;
644     }
645 
646     public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName)
647         throws IOException {
648       return createSnapshot(snapshotName, tableName, SnapshotManifestV1.DESCRIPTOR_VERSION);
649     }
650 
651     public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName,
652         final int numRegions) throws IOException {
653       return createSnapshot(snapshotName, tableName, numRegions, SnapshotManifestV1.DESCRIPTOR_VERSION);
654     }
655 
656     public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName)
657         throws IOException {
658       return createSnapshot(snapshotName, tableName, SnapshotManifestV2.DESCRIPTOR_VERSION);
659     }
660 
661     public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName,
662         final int numRegions) throws IOException {
663       return createSnapshot(snapshotName, tableName, numRegions, SnapshotManifestV2.DESCRIPTOR_VERSION);
664     }
665 
666     private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName,
667         final int version) throws IOException {
668       return createSnapshot(snapshotName, tableName, TEST_NUM_REGIONS, version);
669     }
670 
671     private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName,
672         final int numRegions, final int version) throws IOException {
673       HTableDescriptor htd = createHtd(tableName);
674       RegionData[] regions = createTable(htd, numRegions);
675 
676       SnapshotDescription desc = SnapshotDescription.newBuilder()
677         .setTable(htd.getNameAsString())
678         .setName(snapshotName)
679         .setVersion(version)
680         .build();
681 
682       Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir);
683       SnapshotDescriptionUtils.writeSnapshotInfo(desc, workingDir, fs);
684       return new SnapshotBuilder(conf, fs, rootDir, htd, desc, regions);
685     }
686 
687     public HTableDescriptor createHtd(final String tableName) {
688       HTableDescriptor htd = new HTableDescriptor(tableName);
689       htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
690       return htd;
691     }
692 
693     private RegionData[] createTable(final HTableDescriptor htd, final int nregions)
694         throws IOException {
695       Path tableDir = FSUtils.getTableDir(rootDir, htd.getTableName());
696       new FSTableDescriptors(conf).createTableDescriptorForTableDirectory(tableDir, htd, false);
697 
698       assertTrue(nregions % 2 == 0);
699       RegionData[] regions = new RegionData[nregions];
700       for (int i = 0; i < regions.length; i += 2) {
701         byte[] startKey = Bytes.toBytes(0 + i * 2);
702         byte[] endKey = Bytes.toBytes(1 + i * 2);
703 
704         // First region, simple with one plain hfile.
705         HRegionInfo hri = new HRegionInfo(htd.getTableName(), startKey, endKey);
706         HRegionFileSystem rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri);
707         regions[i] = new RegionData(tableDir, hri, 3);
708         for (int j = 0; j < regions[i].files.length; ++j) {
709           Path storeFile = createStoreFile(rfs.createTempName());
710           regions[i].files[j] = rfs.commitStoreFile(TEST_FAMILY, storeFile);
711         }
712 
713         // Second region, used to test the split case.
714         // This region contains a reference to the hfile in the first region.
715         startKey = Bytes.toBytes(2 + i * 2);
716         endKey = Bytes.toBytes(3 + i * 2);
717         hri = new HRegionInfo(htd.getTableName());
718         rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri);
719         regions[i+1] = new RegionData(tableDir, hri, regions[i].files.length);
720         for (int j = 0; j < regions[i].files.length; ++j) {
721           String refName = regions[i].files[j].getName() + '.' + regions[i].hri.getEncodedName();
722           Path refFile = createStoreFile(new Path(rootDir, refName));
723           regions[i+1].files[j] = rfs.commitStoreFile(TEST_FAMILY, refFile);
724         }
725       }
726       return regions;
727     }
728 
729     private Path createStoreFile(final Path storeFile)
730         throws IOException {
731       FSDataOutputStream out = fs.create(storeFile);
732       try {
733         out.write(Bytes.toBytes(storeFile.toString()));
734       } finally {
735         out.close();
736       }
737       return storeFile;
738     }
739   }
740 
741   // ==========================================================================
742   //  Table Helpers
743   // ==========================================================================
744   public static void waitForTableToBeOnline(final HBaseTestingUtility util,
745                                             final TableName tableName)
746       throws IOException, InterruptedException {
747     HRegionServer rs = util.getRSForFirstRegionInTable(tableName);
748     List<Region> onlineRegions = rs.getOnlineRegions(tableName);
749     for (Region region : onlineRegions) {
750       region.waitForFlushesAndCompactions();
751     }
752     // Wait up to 60 seconds for a table to be available.
753     util.waitFor(60000, util.predicateTableAvailable(tableName));
754   }
755 
756   public static void createTable(final HBaseTestingUtility util, final TableName tableName,
757       int regionReplication, final byte[]... families) throws IOException, InterruptedException {
758     HTableDescriptor htd = new HTableDescriptor(tableName);
759     htd.setRegionReplication(regionReplication);
760     for (byte[] family : families) {
761       HColumnDescriptor hcd = new HColumnDescriptor(family);
762       htd.addFamily(hcd);
763     }
764     byte[][] splitKeys = getSplitKeys();
765     util.createTable(htd, splitKeys);
766     assertEquals((splitKeys.length + 1) * regionReplication,
767         util.getHBaseAdmin().getTableRegions(tableName).size());
768   }
769 
770   public static byte[][] getSplitKeys() {
771     byte[][] splitKeys = new byte[KEYS.length-2][];
772     for (int i = 0; i < splitKeys.length; ++i) {
773       splitKeys[i] = new byte[] { KEYS[i+1] };
774     }
775     return splitKeys;
776   }
777 
778   public static void createTable(final HBaseTestingUtility util, final TableName tableName,
779       final byte[]... families) throws IOException, InterruptedException {
780     createTable(util, tableName, 1, families);
781   }
782 
783   public static void loadData(final HBaseTestingUtility util, final TableName tableName, int rows,
784       byte[]... families) throws IOException, InterruptedException {
785     try (BufferedMutator mutator = util.getConnection().getBufferedMutator(tableName)) {
786       loadData(util, mutator, rows, families);
787     }
788   }
789 
790   public static void loadData(final HBaseTestingUtility util, final BufferedMutator mutator, int rows,
791       byte[]... families) throws IOException, InterruptedException {
792     // Ensure one row per region
793     assertTrue(rows >= KEYS.length);
794     for (byte k0: KEYS) {
795       byte[] k = new byte[] { k0 };
796       byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), k);
797       byte[] key = Bytes.add(k, Bytes.toBytes(MD5Hash.getMD5AsHex(value)));
798       final byte[][] families1 = families;
799       final byte[] key1 = key;
800       final byte[] value1 = value;
801       mutator.mutate(createPut(families1, key1, value1));
802       rows--;
803     }
804 
805     // Add other extra rows. more rows, more files
806     while (rows-- > 0) {
807       byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), Bytes.toBytes(rows));
808       byte[] key = Bytes.toBytes(MD5Hash.getMD5AsHex(value));
809       final byte[][] families1 = families;
810       final byte[] key1 = key;
811       final byte[] value1 = value;
812       mutator.mutate(createPut(families1, key1, value1));
813     }
814     mutator.flush();
815 
816     waitForTableToBeOnline(util, mutator.getName());
817   }
818 
819   private static Put createPut(final byte[][] families, final byte[] key, final byte[] value) {
820     byte[] q = Bytes.toBytes("q");
821     Put put = new Put(key);
822     put.setDurability(Durability.SKIP_WAL);
823     for (byte[] family: families) {
824       put.add(family, q, value);
825     }
826     return put;
827   }
828 
829   public static void deleteAllSnapshots(final Admin admin)
830       throws IOException {
831     // Delete all the snapshots
832     for (SnapshotDescription snapshot: admin.listSnapshots()) {
833       admin.deleteSnapshot(snapshot.getName());
834     }
835     SnapshotTestingUtils.assertNoSnapshots(admin);
836   }
837 
838   public static void deleteArchiveDirectory(final HBaseTestingUtility util)
839       throws IOException {
840     // Ensure the archiver to be empty
841     MasterFileSystem mfs = util.getMiniHBaseCluster().getMaster().getMasterFileSystem();
842     Path archiveDir = new Path(mfs.getRootDir(), HConstants.HFILE_ARCHIVE_DIRECTORY);
843     mfs.getFileSystem().delete(archiveDir, true);
844   }
845 
846   public static void verifyRowCount(final HBaseTestingUtility util, final TableName tableName,
847       long expectedRows) throws IOException {
848     Table table = new HTable(util.getConfiguration(), tableName);
849     try {
850       assertEquals(expectedRows, util.countRows(table));
851     } finally {
852       table.close();
853     }
854   }
855 
856   public static void verifyReplicasCameOnline(TableName tableName, Admin admin,
857       int regionReplication) throws IOException {
858     List<HRegionInfo> regions = admin.getTableRegions(tableName);
859     HashSet<HRegionInfo> set = new HashSet<HRegionInfo>();
860     for (HRegionInfo hri : regions) {
861       set.add(RegionReplicaUtil.getRegionInfoForDefaultReplica(hri));
862       for (int i = 0; i < regionReplication; i++) {
863         HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hri, i);
864         if (!regions.contains(replica)) {
865           Assert.fail(replica + " is not contained in the list of online regions");
866         }
867       }
868     }
869     assert(set.size() == getSplitKeys().length + 1);
870   }
871 }