You are viewing a plain text version of this content. The canonical link for it is here.
Posted to hdfs-commits@hadoop.apache.org by to...@apache.org on 2011/06/10 00:56:43 UTC

svn commit: r1134125 - in /hadoop/hdfs/branches/HDFS-1073: ./ src/java/org/apache/hadoop/hdfs/server/namenode/ src/test/hdfs/org/apache/hadoop/hdfs/ src/test/hdfs/org/apache/hadoop/hdfs/server/namenode/

Author: todd
Date: Thu Jun  9 22:56:43 2011
New Revision: 1134125

URL: http://svn.apache.org/viewvc?rev=1134125&view=rev
Log:
HDFS-2048. Add upgrade tests and fix upgrade from 0.22 with corrupt image. Contributed by Todd Lipcon.

Modified:
    hadoop/hdfs/branches/HDFS-1073/CHANGES.HDFS-1073.txt
    hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java
    hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java
    hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/TestDFSUpgradeFromImage.java
    hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java

Modified: hadoop/hdfs/branches/HDFS-1073/CHANGES.HDFS-1073.txt
URL: http://svn.apache.org/viewvc/hadoop/hdfs/branches/HDFS-1073/CHANGES.HDFS-1073.txt?rev=1134125&r1=1134124&r2=1134125&view=diff
==============================================================================
--- hadoop/hdfs/branches/HDFS-1073/CHANGES.HDFS-1073.txt (original)
+++ hadoop/hdfs/branches/HDFS-1073/CHANGES.HDFS-1073.txt Thu Jun  9 22:56:43 2011
@@ -46,3 +46,5 @@ HDFS-2015. Remove checkpointTxId from VE
 HDFS-2016. Add infrastructure to remove or archive old and unneeded storage
            files within the name directories. (todd)
 HDFS-2047. Improve TestNamespace and TestEditLog in HDFS-1073 branch. (todd)
+HDFS-2048. Add upgrade tests and fix upgrade from 0.22 with corrupt image.
+           (todd)

Modified: hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java
URL: http://svn.apache.org/viewvc/hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java?rev=1134125&r1=1134124&r2=1134125&view=diff
==============================================================================
--- hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java (original)
+++ hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/FSImage.java Thu Jun  9 22:56:43 2011
@@ -609,7 +609,27 @@ public class FSImage implements Closeabl
     File imageFile = loadPlan.getImageFile();
 
     try {
-      loadFSImage(imageFile);
+      if (LayoutVersion.supports(Feature.TXID_BASED_LAYOUT,
+                                 getLayoutVersion())) {
+        // For txid-based layout, we should have a .md5 file
+        // next to the image file
+        loadFSImage(imageFile);
+      } else if (LayoutVersion.supports(Feature.FSIMAGE_CHECKSUM,
+                                        getLayoutVersion())) {
+        // In 0.22, we have the checksum stored in the VERSION file.
+        String md5 = storage.getDeprecatedProperty(
+            NNStorage.DEPRECATED_MESSAGE_DIGEST_PROPERTY);
+        if (md5 == null) {
+          throw new InconsistentFSStateException(sdForProperties.getRoot(),
+              "Message digest property " +
+              NNStorage.DEPRECATED_MESSAGE_DIGEST_PROPERTY +
+              " not set for storage directory " + sdForProperties.getRoot());
+        }
+        loadFSImage(imageFile, new MD5Hash(md5));
+      } else {
+        // We don't have any record of the md5sum
+        loadFSImage(imageFile, null);
+      }
     } catch (IOException ioe) {
       throw new IOException("Failed to load image from " + loadPlan.getImageFile(), ioe);
     }
@@ -662,6 +682,10 @@ public class FSImage implements Closeabl
    */
   void loadFSImage(File imageFile) throws IOException {
     MD5Hash expectedMD5 = MD5FileUtils.readStoredMd5ForFile(imageFile);
+    if (expectedMD5 == null) {
+      throw new IOException("No MD5 file found corresponding to image file "
+          + imageFile);
+    }
     loadFSImage(imageFile, expectedMD5);
   }
   

Modified: hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java
URL: http://svn.apache.org/viewvc/hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java?rev=1134125&r1=1134124&r2=1134125&view=diff
==============================================================================
--- hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java (original)
+++ hadoop/hdfs/branches/HDFS-1073/src/java/org/apache/hadoop/hdfs/server/namenode/NNStorage.java Thu Jun  9 22:56:43 2011
@@ -34,6 +34,7 @@ import java.security.NoSuchAlgorithmExce
 import java.security.SecureRandom;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Random;
@@ -77,7 +78,7 @@ import com.google.common.annotations.Vis
 public class NNStorage extends Storage implements Closeable {
   private static final Log LOG = LogFactory.getLog(NNStorage.class.getName());
 
-  static final String MESSAGE_DIGEST_PROPERTY = "imageMD5Digest";
+  static final String DEPRECATED_MESSAGE_DIGEST_PROPERTY = "imageMD5Digest";
   
   //
   // The filenames used for storing the images
@@ -146,6 +147,12 @@ public class NNStorage extends Storage i
     = new CopyOnWriteArrayList<StorageDirectory>();
 
   /**
+   * Properties from old layout versions that may be needed
+   * during upgrade only.
+   */
+  private HashMap<String, String> deprecatedProperties;
+
+  /**
    * Construct the NNStorage.
    * @param conf Namenode configuration.
    * @param imageDirs Directories the image can be stored in.
@@ -589,7 +596,32 @@ public class NNStorage extends Storage i
     setDistributedUpgradeState(
         sDUS == null? false : Boolean.parseBoolean(sDUS),
         sDUV == null? getLayoutVersion() : Integer.parseInt(sDUV));
+    setDeprecatedPropertiesForUpgrade(props);
+  }
+
+  /**
+   * Pull any properties out of the VERSION file that are from older
+   * versions of HDFS and only necessary during upgrade.
+   */
+  private void setDeprecatedPropertiesForUpgrade(Properties props) {
+    deprecatedProperties = new HashMap<String, String>();
+    String md5 = props.getProperty(DEPRECATED_MESSAGE_DIGEST_PROPERTY);
+    if (md5 != null) {
+      deprecatedProperties.put(DEPRECATED_MESSAGE_DIGEST_PROPERTY, md5);
     }
+  }
+  
+  /**
+   * Return a property that was stored in an earlier version of HDFS.
+   * 
+   * This should only be used during upgrades.
+   */
+  String getDeprecatedProperty(String prop) {
+    assert getLayoutVersion() > FSConstants.LAYOUT_VERSION :
+      "getDeprecatedProperty should only be done when loading " +
+      "storage from past versions during upgrade.";
+    return deprecatedProperties.get(prop);
+  }
 
   /**
    * Write version file into the storage directory.
@@ -617,18 +649,6 @@ public class NNStorage extends Storage i
       props.setProperty("distributedUpgradeVersion",
                         Integer.toString(uVersion));
     }
-    /* TODO: resolve merge here.
-    if (LayoutVersion.supports(Feature.FSIMAGE_CHECKSUM, layoutVersion)) {
-    // TODO && ! transactional storage
-      // Though the current NN supports this feature, this function
-      // is called with old layoutVersions from the upgrade tests.
-      
-        // May be null on the first save after an upgrade.
-        imageDigest = MD5Hash.digest(
-            new FileInputStream(getStorageFile(sd, NameNodeFile.IMAGE)));
-      }
-      props.setProperty(MESSAGE_DIGEST_PROPERTY, imageDigest.toString());
-    } */
   }
   
   static File getStorageFile(StorageDirectory sd, NameNodeFile type, long imageTxId) {

Modified: hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/TestDFSUpgradeFromImage.java
URL: http://svn.apache.org/viewvc/hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/TestDFSUpgradeFromImage.java?rev=1134125&r1=1134124&r2=1134125&view=diff
==============================================================================
--- hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/TestDFSUpgradeFromImage.java (original)
+++ hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/TestDFSUpgradeFromImage.java Thu Jun  9 22:56:43 2011
@@ -32,6 +32,7 @@ import org.apache.hadoop.fs.FileUtil;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hdfs.protocol.FSConstants;
 import org.apache.hadoop.hdfs.server.common.HdfsConstants.StartupOption;
+import org.apache.hadoop.hdfs.server.namenode.FSImageTestUtil;
 import org.apache.hadoop.util.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -182,38 +183,6 @@ public class TestDFSUpgradeFromImage ext
     }
   }
   
-  public void testUpgradeFromRel14Image() throws IOException {
-    unpackStorage();
-    MiniDFSCluster cluster = null;
-    try {
-      Configuration conf = new HdfsConfiguration();
-      if (System.getProperty("test.build.data") == null) { // to allow test to be run outside of Ant
-        System.setProperty("test.build.data", "build/test/data");
-      }
-      conf.setInt(DFSConfigKeys.DFS_DATANODE_SCAN_PERIOD_HOURS_KEY, -1); // block scanning off
-      cluster = new MiniDFSCluster.Builder(conf)
-                                  .numDataNodes(numDataNodes)
-                                  .format(false)
-                                  .startupOption(StartupOption.UPGRADE)
-                                  .clusterId("testClusterId")
-                                  .build();
-      cluster.waitActive();
-      DistributedFileSystem dfs = (DistributedFileSystem)cluster.getFileSystem();
-      DFSClient dfsClient = dfs.dfs;
-      //Safemode will be off only after upgrade is complete. Wait for it.
-      while ( dfsClient.setSafeMode(FSConstants.SafeModeAction.SAFEMODE_GET) ) {
-        LOG.info("Waiting for SafeMode to be OFF.");
-        try {
-          Thread.sleep(1000);
-        } catch (InterruptedException ignored) {}
-      }
-
-      verifyFileSystem(dfs);
-    } finally {
-      if (cluster != null) { cluster.shutdown(); }
-    }
-  }
-
   /**
    * Test that sets up a fake image from Hadoop 0.3.0 and tries to start a
    * NN, verifying that the correct error message is thrown.
@@ -257,10 +226,50 @@ public class TestDFSUpgradeFromImage ext
   }
   
   /**
+   * Test upgrade from an 0.14 image
+   */
+  public void testUpgradeFromRel14Image() throws IOException {
+    unpackStorage();
+    upgradeAndVerify();
+  }
+  
+  /**
    * Test upgrade from 0.22 image
    */
   public void testUpgradeFromRel22Image() throws IOException {
     unpackStorage(HADOOP22_IMAGE);
+    upgradeAndVerify();
+  }
+  
+  /**
+   * Test upgrade from 0.22 image with corrupt md5, make sure it
+   * fails to upgrade
+   */
+  public void testUpgradeFromCorruptRel22Image() throws IOException {
+    unpackStorage(HADOOP22_IMAGE);
+    
+    // Overwrite the md5 stored in the VERSION files
+    File baseDir = new File(MiniDFSCluster.getBaseDirectory());
+    FSImageTestUtil.corruptVersionFile(
+        new File(baseDir, "name1/current/VERSION"),
+        "imageMD5Digest", "22222222222222222222222222222222");
+    FSImageTestUtil.corruptVersionFile(
+        new File(baseDir, "name2/current/VERSION"),
+        "imageMD5Digest", "22222222222222222222222222222222");
+    
+    // Upgrade should now fail
+    try {
+      upgradeAndVerify();
+      fail("Upgrade did not fail with bad MD5");
+    } catch (IOException ioe) {
+      String msg = StringUtils.stringifyException(ioe);
+      if (!msg.contains("is corrupt with MD5 checksum")) {
+        throw ioe;
+      }
+    }
+  }
+
+  private void upgradeAndVerify() throws IOException {
     MiniDFSCluster cluster = null;
     try {
       Configuration conf = new HdfsConfiguration();
@@ -284,8 +293,12 @@ public class TestDFSUpgradeFromImage ext
           Thread.sleep(1000);
         } catch (InterruptedException ignored) {}
       }
+
+      verifyFileSystem(dfs);
     } finally {
       if (cluster != null) { cluster.shutdown(); }
-    }
+    } 
   }
+
+
 }

Modified: hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java
URL: http://svn.apache.org/viewvc/hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java?rev=1134125&r1=1134124&r2=1134125&view=diff
==============================================================================
--- hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java (original)
+++ hadoop/hdfs/branches/HDFS-1073/src/test/hdfs/org/apache/hadoop/hdfs/server/namenode/FSImageTestUtil.java Thu Jun  9 22:56:43 2011
@@ -22,6 +22,7 @@ import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
 import java.io.IOException;
 import java.math.BigInteger;
 import java.security.MessageDigest;
@@ -30,12 +31,14 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Properties;
 import java.util.Set;
 
 import org.apache.hadoop.hdfs.server.common.Storage.StorageDirectory;
 import org.apache.hadoop.hdfs.server.namenode.FSImageTransactionalStorageInspector.FoundFSImage;
 import org.apache.hadoop.hdfs.server.namenode.NNStorage.NameNodeDirType;
 import org.apache.hadoop.hdfs.util.MD5FileUtils;
+import org.apache.hadoop.io.IOUtils;
 import org.apache.hadoop.io.MD5Hash;
 import org.mockito.Mockito;
 
@@ -232,5 +235,32 @@ public abstract class FSImageTestUtil {
     return (latestImage == null) ? null : latestImage.getFile();
   }
 
+  /**
+   * Corrupt the given VERSION file by replacing a given
+   * key with a new value and re-writing the file.
+   * 
+   * @param versionFile the VERSION file to corrupt
+   * @param key the key to replace
+   * @param value the new value for this key
+   */
+  public static void corruptVersionFile(File versionFile, String key, String value)
+      throws IOException {
+    Properties props = new Properties();
+    FileInputStream fis = new FileInputStream(versionFile);
+    FileOutputStream out = null;
+    try {
+      props.load(fis);
+      IOUtils.closeStream(fis);
+  
+      props.setProperty(key, value);
+      
+      out = new FileOutputStream(versionFile);
+      props.store(out, null);
+      
+    } finally {
+      IOUtils.cleanup(null, fis, out);
+    }    
+  }
+
 
 }