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(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(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(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     assertEquals(regions.size(), regionManifests.size());
249 
250     // Verify Regions (redundant check, see MasterSnapshotVerifier)
251     for (HRegionInfo info : regions) {
252       String regionName = info.getEncodedName();
253       assertTrue(regionManifests.containsKey(regionName));
254     }
255   }
256 
257   /**
258    * Helper method for testing async snapshot operations. Just waits for the
259    * given snapshot to complete on the server by repeatedly checking the master.
260    *
261    * @param master: the master running the snapshot
262    * @param snapshot: the snapshot to check
263    * @param sleep: amount to sleep between checks to see if the snapshot is done
264    * @throws ServiceException if the snapshot fails
265    */
266   public static void waitForSnapshotToComplete(HMaster master,
267       SnapshotDescription snapshot, long sleep) throws ServiceException {
268     final IsSnapshotDoneRequest request = IsSnapshotDoneRequest.newBuilder()
269         .setSnapshot(snapshot).build();
270     IsSnapshotDoneResponse done = IsSnapshotDoneResponse.newBuilder()
271         .buildPartial();
272     while (!done.getDone()) {
273       done = master.getMasterRpcServices().isSnapshotDone(null, request);
274       try {
275         Thread.sleep(sleep);
276       } catch (InterruptedException e) {
277         throw new ServiceException(e);
278       }
279     }
280   }
281 
282   /*
283    * Take snapshot with maximum of numTries attempts, ignoring CorruptedSnapshotException
284    * except for the last CorruptedSnapshotException
285    */
286   public static void snapshot(Admin admin,
287       final String snapshotName, final String tableName,
288       SnapshotDescription.Type type, int numTries) throws IOException {
289     int tries = 0;
290     CorruptedSnapshotException lastEx = null;
291     while (tries++ < numTries) {
292       try {
293         admin.snapshot(snapshotName, TableName.valueOf(tableName), type);
294         return;
295       } catch (CorruptedSnapshotException cse) {
296         LOG.warn("Got CorruptedSnapshotException", cse);
297         lastEx = cse;
298       }
299     }
300     throw lastEx;
301   }
302 
303   public static void cleanupSnapshot(Admin admin, byte[] tableName)
304       throws IOException {
305     SnapshotTestingUtils.cleanupSnapshot(admin, Bytes.toString(tableName));
306   }
307 
308   public static void cleanupSnapshot(Admin admin, String snapshotName)
309       throws IOException {
310     // delete the taken snapshot
311     admin.deleteSnapshot(snapshotName);
312     assertNoSnapshots(admin);
313   }
314 
315   /**
316    * Expect the snapshot to throw an error when checking if the snapshot is
317    * complete
318    *
319    * @param master master to check
320    * @param snapshot the {@link SnapshotDescription} request to pass to the master
321    * @param clazz expected exception from the master
322    */
323   public static void expectSnapshotDoneException(HMaster master,
324       IsSnapshotDoneRequest snapshot,
325       Class<? extends HBaseSnapshotException> clazz) {
326     try {
327       master.getMasterRpcServices().isSnapshotDone(null, snapshot);
328       Assert.fail("didn't fail to lookup a snapshot");
329     } catch (ServiceException se) {
330       try {
331         throw ProtobufUtil.getRemoteException(se);
332       } catch (HBaseSnapshotException e) {
333         assertEquals("Threw wrong snapshot exception!", clazz, e.getClass());
334       } catch (Throwable t) {
335         Assert.fail("Threw an unexpected exception:" + t);
336       }
337     }
338   }
339 
340   /**
341    * List all the HFiles in the given table
342    *
343    * @param fs: FileSystem where the table lives
344    * @param tableDir directory of the table
345    * @return array of the current HFiles in the table (could be a zero-length array)
346    * @throws IOException on unexecpted error reading the FS
347    */
348   public static ArrayList<String> listHFileNames(final FileSystem fs, final Path tableDir)
349       throws IOException {
350     final ArrayList<String> hfiles = new ArrayList<String>();
351     FSVisitor.visitTableStoreFiles(fs, tableDir, new FSVisitor.StoreFileVisitor() {
352       @Override
353       public void storeFile(final String region, final String family, final String hfileName)
354           throws IOException {
355         hfiles.add(hfileName);
356       }
357     });
358     Collections.sort(hfiles);
359     return hfiles;
360   }
361 
362   /**
363    * Take a snapshot of the specified table and verify that the given family is
364    * not empty. Note that this will leave the table disabled
365    * in the case of an offline snapshot.
366    */
367   public static void createSnapshotAndValidate(Admin admin,
368       TableName tableName, String familyName, String snapshotNameString,
369       Path rootDir, FileSystem fs, boolean onlineSnapshot)
370       throws Exception {
371     ArrayList<byte[]> nonEmptyFamilyNames = new ArrayList<byte[]>(1);
372     nonEmptyFamilyNames.add(Bytes.toBytes(familyName));
373     createSnapshotAndValidate(admin, tableName, nonEmptyFamilyNames, /* emptyFamilyNames= */ null,
374                               snapshotNameString, rootDir, fs, onlineSnapshot);
375   }
376 
377   /**
378    * Take a snapshot of the specified table and verify the given families.
379    * Note that this will leave the table disabled in the case of an offline snapshot.
380    */
381   public static void createSnapshotAndValidate(Admin admin,
382       TableName tableName, List<byte[]> nonEmptyFamilyNames, List<byte[]> emptyFamilyNames,
383       String snapshotNameString, Path rootDir, FileSystem fs, boolean onlineSnapshot)
384         throws Exception {
385     if (!onlineSnapshot) {
386       try {
387         admin.disableTable(tableName);
388       } catch (TableNotEnabledException tne) {
389         LOG.info("In attempting to disable " + tableName + " it turns out that the this table is " +
390             "already disabled.");
391       }
392     }
393     admin.snapshot(snapshotNameString, tableName);
394 
395     List<SnapshotDescription> snapshots = SnapshotTestingUtils.assertExistsMatchingSnapshot(admin,
396       snapshotNameString, tableName);
397     if (snapshots == null || snapshots.size() != 1) {
398       Assert.fail("Incorrect number of snapshots for table " + tableName);
399     }
400 
401     SnapshotTestingUtils.confirmSnapshotValid(snapshots.get(0), tableName, nonEmptyFamilyNames,
402       emptyFamilyNames, rootDir, admin, fs);
403   }
404 
405   /**
406    * Corrupt the specified snapshot by deleting some files.
407    *
408    * @param util {@link HBaseTestingUtility}
409    * @param snapshotName name of the snapshot to corrupt
410    * @return array of the corrupted HFiles
411    * @throws IOException on unexecpted error reading the FS
412    */
413   public static ArrayList corruptSnapshot(final HBaseTestingUtility util, final String snapshotName)
414       throws IOException {
415     final MasterFileSystem mfs = util.getHBaseCluster().getMaster().getMasterFileSystem();
416     final FileSystem fs = mfs.getFileSystem();
417 
418     Path snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(snapshotName,
419                                                                         mfs.getRootDir());
420     SnapshotDescription snapshotDesc = SnapshotDescriptionUtils.readSnapshotInfo(fs, snapshotDir);
421     final TableName table = TableName.valueOf(snapshotDesc.getTable());
422 
423     final ArrayList corruptedFiles = new ArrayList();
424     final Configuration conf = util.getConfiguration();
425     SnapshotReferenceUtil.visitTableStoreFiles(conf, fs, snapshotDir, snapshotDesc,
426         new SnapshotReferenceUtil.StoreFileVisitor() {
427       @Override
428       public void storeFile(final HRegionInfo regionInfo, final String family,
429             final SnapshotRegionManifest.StoreFile storeFile) throws IOException {
430         String region = regionInfo.getEncodedName();
431         String hfile = storeFile.getName();
432         HFileLink link = HFileLink.build(conf, table, region, family, hfile);
433         if (corruptedFiles.size() % 2 == 0) {
434           fs.delete(link.getAvailablePath(fs), true);
435           corruptedFiles.add(hfile);
436         }
437       }
438     });
439 
440     assertTrue(corruptedFiles.size() > 0);
441     return corruptedFiles;
442   }
443 
444   // ==========================================================================
445   //  Snapshot Mock
446   // ==========================================================================
447   public static class SnapshotMock {
448     private final static String TEST_FAMILY = "cf";
449     public final static int TEST_NUM_REGIONS = 4;
450 
451     private final Configuration conf;
452     private final FileSystem fs;
453     private final Path rootDir;
454 
455     static class RegionData {
456       public HRegionInfo hri;
457       public Path tableDir;
458       public Path[] files;
459 
460       public RegionData(final Path tableDir, final HRegionInfo hri, final int nfiles) {
461         this.tableDir = tableDir;
462         this.hri = hri;
463         this.files = new Path[nfiles];
464       }
465     }
466 
467     public static class SnapshotBuilder {
468       private final RegionData[] tableRegions;
469       private final SnapshotDescription desc;
470       private final HTableDescriptor htd;
471       private final Configuration conf;
472       private final FileSystem fs;
473       private final Path rootDir;
474       private Path snapshotDir;
475       private int snapshotted = 0;
476 
477       public SnapshotBuilder(final Configuration conf, final FileSystem fs,
478           final Path rootDir, final HTableDescriptor htd,
479           final SnapshotDescription desc, final RegionData[] tableRegions)
480           throws IOException {
481         this.fs = fs;
482         this.conf = conf;
483         this.rootDir = rootDir;
484         this.htd = htd;
485         this.desc = desc;
486         this.tableRegions = tableRegions;
487         this.snapshotDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir);
488         new FSTableDescriptors(conf)
489           .createTableDescriptorForTableDirectory(snapshotDir, htd, false);
490       }
491 
492       public HTableDescriptor getTableDescriptor() {
493         return this.htd;
494       }
495 
496       public SnapshotDescription getSnapshotDescription() {
497         return this.desc;
498       }
499 
500       public Path getSnapshotsDir() {
501         return this.snapshotDir;
502       }
503 
504       public Path[] addRegion() throws IOException {
505         return addRegion(desc);
506       }
507 
508       public Path[] addRegionV1() throws IOException {
509         return addRegion(desc.toBuilder()
510                           .setVersion(SnapshotManifestV1.DESCRIPTOR_VERSION)
511                           .build());
512       }
513 
514       public Path[] addRegionV2() throws IOException {
515         return addRegion(desc.toBuilder()
516                           .setVersion(SnapshotManifestV2.DESCRIPTOR_VERSION)
517                           .build());
518       }
519 
520       private Path[] addRegion(final SnapshotDescription desc) throws IOException {
521         if (this.snapshotted == tableRegions.length) {
522           throw new UnsupportedOperationException("No more regions in the table");
523         }
524 
525         RegionData regionData = tableRegions[this.snapshotted++];
526         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
527         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
528         manifest.addRegion(regionData.tableDir, regionData.hri);
529         return regionData.files;
530       }
531 
532       private void corruptFile(Path p) throws IOException {
533         String manifestName = p.getName();
534 
535         // Rename the original region-manifest file
536         Path newP = new Path(p.getParent(), manifestName + "1");
537         fs.rename(p, newP);
538 
539         // Create a new region-manifest file
540         FSDataOutputStream out = fs.create(p);
541 
542         //Copy the first 25 bytes of the original region-manifest into the new one,
543         //make it a corrupted region-manifest file.
544         FSDataInputStream input = fs.open(newP);
545         byte[] buffer = new byte[25];
546         int len = input.read(0, buffer, 0, 25);
547         if (len > 1) {
548           out.write(buffer, 0, len - 1);
549         }
550         out.close();
551 
552         // Delete the original region-manifest
553         fs.delete(newP);
554       }
555 
556       /**
557        * Corrupt one region-manifest file
558        *
559        * @throws IOException on unexecpted error from the FS
560        */
561       public void corruptOneRegionManifest() throws IOException {
562         FileStatus[] manifestFiles = FSUtils.listStatus(fs, snapshotDir, new PathFilter() {
563           @Override public boolean accept(Path path) {
564             return path.getName().startsWith(SnapshotManifestV2.SNAPSHOT_MANIFEST_PREFIX);
565           }
566         });
567 
568         if (manifestFiles.length == 0) return;
569 
570         // Just choose the first one
571         Path p = manifestFiles[0].getPath();
572         corruptFile(p);
573       }
574 
575       public void missOneRegionSnapshotFile() throws IOException {
576         FileStatus[] manifestFiles = FSUtils.listStatus(fs, snapshotDir);
577         for (FileStatus fileStatus : manifestFiles) {
578           String fileName = fileStatus.getPath().getName();
579           if (fileName.endsWith(SnapshotDescriptionUtils.SNAPSHOTINFO_FILE)
580             || fileName.endsWith(".tabledesc")
581             || fileName.endsWith(SnapshotDescriptionUtils.SNAPSHOT_TMP_DIR_NAME)) {
582               fs.delete(fileStatus.getPath(), true);
583           }
584         }
585       }
586 
587       /**
588        * Corrupt data-manifest file
589        *
590        * @throws IOException on unexecpted error from the FS
591        */
592       public void corruptDataManifest() throws IOException {
593         FileStatus[] manifestFiles = FSUtils.listStatus(fs, snapshotDir, new PathFilter() {
594           @Override
595           public boolean accept(Path path) {
596             return path.getName().startsWith(SnapshotManifest.DATA_MANIFEST_NAME);
597           }
598         });
599 
600         if (manifestFiles.length == 0) return;
601 
602         // Just choose the first one
603         Path p = manifestFiles[0].getPath();
604         corruptFile(p);
605       }
606 
607       public Path commit() throws IOException {
608         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
609         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
610         manifest.addTableDescriptor(htd);
611         manifest.consolidate();
612         SnapshotDescriptionUtils.completeSnapshot(desc, rootDir, snapshotDir, fs);
613         snapshotDir = SnapshotDescriptionUtils.getCompletedSnapshotDir(desc, rootDir);
614         return snapshotDir;
615       }
616 
617       public void consolidate() throws IOException {
618         ForeignExceptionDispatcher monitor = new ForeignExceptionDispatcher(desc.getName());
619         SnapshotManifest manifest = SnapshotManifest.create(conf, fs, snapshotDir, desc, monitor);
620         manifest.addTableDescriptor(htd);
621         manifest.consolidate();
622       }
623     }
624 
625     public SnapshotMock(final Configuration conf, final FileSystem fs, final Path rootDir) {
626       this.fs = fs;
627       this.conf = conf;
628       this.rootDir = rootDir;
629     }
630 
631     public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName)
632         throws IOException {
633       return createSnapshot(snapshotName, tableName, SnapshotManifestV1.DESCRIPTOR_VERSION);
634     }
635 
636     public SnapshotBuilder createSnapshotV1(final String snapshotName, final String tableName,
637         final int numRegions) throws IOException {
638       return createSnapshot(snapshotName, tableName, numRegions, SnapshotManifestV1.DESCRIPTOR_VERSION);
639     }
640 
641     public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName)
642         throws IOException {
643       return createSnapshot(snapshotName, tableName, SnapshotManifestV2.DESCRIPTOR_VERSION);
644     }
645 
646     public SnapshotBuilder createSnapshotV2(final String snapshotName, final String tableName,
647         final int numRegions) throws IOException {
648       return createSnapshot(snapshotName, tableName, numRegions, SnapshotManifestV2.DESCRIPTOR_VERSION);
649     }
650 
651     private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName,
652         final int version) throws IOException {
653       return createSnapshot(snapshotName, tableName, TEST_NUM_REGIONS, version);
654     }
655 
656     private SnapshotBuilder createSnapshot(final String snapshotName, final String tableName,
657         final int numRegions, final int version) throws IOException {
658       HTableDescriptor htd = createHtd(tableName);
659       RegionData[] regions = createTable(htd, numRegions);
660 
661       SnapshotDescription desc = SnapshotDescription.newBuilder()
662         .setTable(htd.getNameAsString())
663         .setName(snapshotName)
664         .setVersion(version)
665         .build();
666 
667       Path workingDir = SnapshotDescriptionUtils.getWorkingSnapshotDir(desc, rootDir);
668       SnapshotDescriptionUtils.writeSnapshotInfo(desc, workingDir, fs);
669       return new SnapshotBuilder(conf, fs, rootDir, htd, desc, regions);
670     }
671 
672     public HTableDescriptor createHtd(final String tableName) {
673       HTableDescriptor htd = new HTableDescriptor(tableName);
674       htd.addFamily(new HColumnDescriptor(TEST_FAMILY));
675       return htd;
676     }
677 
678     private RegionData[] createTable(final HTableDescriptor htd, final int nregions)
679         throws IOException {
680       Path tableDir = FSUtils.getTableDir(rootDir, htd.getTableName());
681       new FSTableDescriptors(conf).createTableDescriptorForTableDirectory(tableDir, htd, false);
682 
683       assertTrue(nregions % 2 == 0);
684       RegionData[] regions = new RegionData[nregions];
685       for (int i = 0; i < regions.length; i += 2) {
686         byte[] startKey = Bytes.toBytes(0 + i * 2);
687         byte[] endKey = Bytes.toBytes(1 + i * 2);
688 
689         // First region, simple with one plain hfile.
690         HRegionInfo hri = new HRegionInfo(htd.getTableName(), startKey, endKey);
691         HRegionFileSystem rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri);
692         regions[i] = new RegionData(tableDir, hri, 3);
693         for (int j = 0; j < regions[i].files.length; ++j) {
694           Path storeFile = createStoreFile(rfs.createTempName());
695           regions[i].files[j] = rfs.commitStoreFile(TEST_FAMILY, storeFile);
696         }
697 
698         // Second region, used to test the split case.
699         // This region contains a reference to the hfile in the first region.
700         startKey = Bytes.toBytes(2 + i * 2);
701         endKey = Bytes.toBytes(3 + i * 2);
702         hri = new HRegionInfo(htd.getTableName());
703         rfs = HRegionFileSystem.createRegionOnFileSystem(conf, fs, tableDir, hri);
704         regions[i+1] = new RegionData(tableDir, hri, regions[i].files.length);
705         for (int j = 0; j < regions[i].files.length; ++j) {
706           String refName = regions[i].files[j].getName() + '.' + regions[i].hri.getEncodedName();
707           Path refFile = createStoreFile(new Path(rootDir, refName));
708           regions[i+1].files[j] = rfs.commitStoreFile(TEST_FAMILY, refFile);
709         }
710       }
711       return regions;
712     }
713 
714     private Path createStoreFile(final Path storeFile)
715         throws IOException {
716       FSDataOutputStream out = fs.create(storeFile);
717       try {
718         out.write(Bytes.toBytes(storeFile.toString()));
719       } finally {
720         out.close();
721       }
722       return storeFile;
723     }
724   }
725 
726   // ==========================================================================
727   //  Table Helpers
728   // ==========================================================================
729   public static void waitForTableToBeOnline(final HBaseTestingUtility util,
730                                             final TableName tableName)
731       throws IOException, InterruptedException {
732     HRegionServer rs = util.getRSForFirstRegionInTable(tableName);
733     List<Region> onlineRegions = rs.getOnlineRegions(tableName);
734     for (Region region : onlineRegions) {
735       region.waitForFlushesAndCompactions();
736     }
737     // Wait up to 60 seconds for a table to be available.
738     util.waitFor(60000, util.predicateTableAvailable(tableName));
739   }
740 
741   public static void createTable(final HBaseTestingUtility util, final TableName tableName,
742       int regionReplication, final byte[]... families) throws IOException, InterruptedException {
743     HTableDescriptor htd = new HTableDescriptor(tableName);
744     htd.setRegionReplication(regionReplication);
745     for (byte[] family : families) {
746       HColumnDescriptor hcd = new HColumnDescriptor(family);
747       htd.addFamily(hcd);
748     }
749     byte[][] splitKeys = getSplitKeys();
750     util.createTable(htd, splitKeys);
751     assertEquals((splitKeys.length + 1) * regionReplication,
752         util.getHBaseAdmin().getTableRegions(tableName).size());
753   }
754 
755   public static byte[][] getSplitKeys() {
756     byte[][] splitKeys = new byte[KEYS.length-2][];
757     for (int i = 0; i < splitKeys.length; ++i) {
758       splitKeys[i] = new byte[] { KEYS[i+1] };
759     }
760     return splitKeys;
761   }
762 
763   public static void createTable(final HBaseTestingUtility util, final TableName tableName,
764       final byte[]... families) throws IOException, InterruptedException {
765     createTable(util, tableName, 1, families);
766   }
767 
768   public static void loadData(final HBaseTestingUtility util, final TableName tableName, int rows,
769       byte[]... families) throws IOException, InterruptedException {
770     try (BufferedMutator mutator = util.getConnection().getBufferedMutator(tableName)) {
771       loadData(util, mutator, rows, families);
772     }
773   }
774 
775   public static void loadData(final HBaseTestingUtility util, final BufferedMutator mutator, int rows,
776       byte[]... families) throws IOException, InterruptedException {
777     // Ensure one row per region
778     assertTrue(rows >= KEYS.length);
779     for (byte k0: KEYS) {
780       byte[] k = new byte[] { k0 };
781       byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), k);
782       byte[] key = Bytes.add(k, Bytes.toBytes(MD5Hash.getMD5AsHex(value)));
783       final byte[][] families1 = families;
784       final byte[] key1 = key;
785       final byte[] value1 = value;
786       mutator.mutate(createPut(families1, key1, value1));
787       rows--;
788     }
789 
790     // Add other extra rows. more rows, more files
791     while (rows-- > 0) {
792       byte[] value = Bytes.add(Bytes.toBytes(System.currentTimeMillis()), Bytes.toBytes(rows));
793       byte[] key = Bytes.toBytes(MD5Hash.getMD5AsHex(value));
794       final byte[][] families1 = families;
795       final byte[] key1 = key;
796       final byte[] value1 = value;
797       mutator.mutate(createPut(families1, key1, value1));
798     }
799     mutator.flush();
800 
801     waitForTableToBeOnline(util, mutator.getName());
802   }
803 
804   private static Put createPut(final byte[][] families, final byte[] key, final byte[] value) {
805     byte[] q = Bytes.toBytes("q");
806     Put put = new Put(key);
807     put.setDurability(Durability.SKIP_WAL);
808     for (byte[] family: families) {
809       put.add(family, q, value);
810     }
811     return put;
812   }
813 
814   public static void deleteAllSnapshots(final Admin admin)
815       throws IOException {
816     // Delete all the snapshots
817     for (SnapshotDescription snapshot: admin.listSnapshots()) {
818       admin.deleteSnapshot(snapshot.getName());
819     }
820     SnapshotTestingUtils.assertNoSnapshots(admin);
821   }
822 
823   public static void deleteArchiveDirectory(final HBaseTestingUtility util)
824       throws IOException {
825     // Ensure the archiver to be empty
826     MasterFileSystem mfs = util.getMiniHBaseCluster().getMaster().getMasterFileSystem();
827     Path archiveDir = new Path(mfs.getRootDir(), HConstants.HFILE_ARCHIVE_DIRECTORY);
828     mfs.getFileSystem().delete(archiveDir, true);
829   }
830 
831   public static void verifyRowCount(final HBaseTestingUtility util, final TableName tableName,
832       long expectedRows) throws IOException {
833     Table table = new HTable(util.getConfiguration(), tableName);
834     try {
835       assertEquals(expectedRows, util.countRows(table));
836     } finally {
837       table.close();
838     }
839   }
840 
841   public static void verifyReplicasCameOnline(TableName tableName, Admin admin,
842       int regionReplication) throws IOException {
843     List<HRegionInfo> regions = admin.getTableRegions(tableName);
844     HashSet<HRegionInfo> set = new HashSet<HRegionInfo>();
845     for (HRegionInfo hri : regions) {
846       set.add(RegionReplicaUtil.getRegionInfoForDefaultReplica(hri));
847       for (int i = 0; i < regionReplication; i++) {
848         HRegionInfo replica = RegionReplicaUtil.getRegionInfoForReplica(hri, i);
849         if (!regions.contains(replica)) {
850           Assert.fail(replica + " is not contained in the list of online regions");
851         }
852       }
853     }
854     assert(set.size() == getSplitKeys().length + 1);
855   }
856 }