Browse Source

AMBARI-8657. Add Kerberos Configuration Metadata File Builder and Reader

Robert Levas 10 năm trước cách đây
mục cha
commit
3c08f6f7d7
12 tập tin đã thay đổi với 705 bổ sung293 xóa
  1. 124 0
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractKerberosDataFileBuilder.java
  2. 116 0
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractKerberosDataFileReader.java
  3. 7 4
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
  4. 3 2
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFile.java
  5. 19 72
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFileBuilder.java
  6. 2 73
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFileReader.java
  7. 31 0
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFile.java
  8. 65 0
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFileBuilder.java
  9. 44 0
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFileReader.java
  10. 3 1
      ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
  11. 145 141
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFileTest.java
  12. 146 0
      ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFileTest.java

+ 124 - 0
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractKerberosDataFileBuilder.java

@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVPrinter;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+/**
+ * AbstractKerberosDataFileBuilder provides a generic facility to write a data file using some
+ * underlying record-based file writer.
+ * <p/>
+ * This class encapsulates a {@link org.apache.commons.csv.CSVPrinter} to create a CSV-formatted file.
+ */
+public abstract class AbstractKerberosDataFileBuilder {
+
+  private File file;
+  private CSVPrinter csvPrinter;
+
+  /**
+   * Creates a new KerberosConfigDataFileBuilder
+   * <p/>
+   * The file is opened upon creation, so there is no need to manually open it unless manually
+   * closed before using.
+   *
+   * @param file a File declaring where to write the data
+   * @throws java.io.IOException
+   */
+  public AbstractKerberosDataFileBuilder(File file) throws IOException {
+    this.file = file;
+    open();
+  }
+
+
+  /**
+   * Opens the data file for writing.
+   * <p/>
+   * This may be called multiple times and the appropriate action will occur depending on if the
+   * file has been previously opened or closed.
+   *
+   * @throws java.io.IOException
+   */
+  public void open() throws IOException {
+    if (isClosed()) {
+      if (file == null) {
+        throw new IOException("Missing file path");
+      } else {
+        csvPrinter = new CSVPrinter(new FileWriter(file, true), CSVFormat.DEFAULT);
+
+        // If the file is empty, write the header; else don't write the header.
+        if (file.length() == 0) {
+          // Write the header....
+          Iterable<?> headerRecord = getHeaderRecord();
+          csvPrinter.printRecord(headerRecord);
+        }
+      }
+    }
+  }
+
+  /**
+   * Tests this KerberosConfigDataFileBuilder to see if the data file is closed.
+   *
+   * @return true if closed; otherwise false
+   */
+  public boolean isClosed() {
+    return csvPrinter == null;
+  }
+
+  /**
+   * Closes the data file
+   *
+   * @throws java.io.IOException
+   */
+  public void close() throws IOException {
+    if (csvPrinter != null) {
+      csvPrinter.close();
+      csvPrinter = null;
+    }
+  }
+
+  /**
+   * Appends a new record to the data file
+   *
+   * @param record a collection of Strings declaring values for the columns of the file
+   * @throws java.io.IOException
+   */
+  protected void appendRecord(String... record) throws IOException {
+
+    if (csvPrinter == null) {
+      throw new IOException("Data file is not open");
+    }
+
+    csvPrinter.printRecord(record);
+  }
+
+
+  /**
+   * Gets the header record for the CSV file
+   *
+   * @return an Iterable containing the (ordered) list of Strings declaring the columns names
+   */
+  protected abstract Iterable<String> getHeaderRecord();
+
+
+}

+ 116 - 0
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/AbstractKerberosDataFileReader.java

@@ -0,0 +1,116 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import org.apache.commons.csv.CSVFormat;
+import org.apache.commons.csv.CSVParser;
+import org.apache.commons.csv.CSVRecord;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * AbstractKerberosDataFileReader implements common code to read existing Kerberos data files.
+ * <p/>
+ * This class encapsulates a {@link org.apache.commons.csv.CSVParser} to read a CSV-formatted file.
+ */
+public abstract class AbstractKerberosDataFileReader implements Iterable<Map<String, String>> {
+
+  private File file;
+  private CSVParser csvParser = null;
+
+  /**
+   * Creates a new AbstractKerberosDataFileReader
+   * <p/>
+   * The file is opened upon creation, so there is no need to manually open it unless manually
+   * closed before using.
+   *
+   * @param file a File declaring where to write the data
+   * @throws java.io.IOException
+   */
+  protected AbstractKerberosDataFileReader(File file) throws IOException {
+    this.file = file;
+    open();
+  }
+
+  /**
+   * Opens the data file for reading.
+   * <p/>
+   * This may be called multiple times and the appropriate action will occur depending on if the
+   * file has been previously opened or closed.
+   *
+   * @throws java.io.IOException
+   */
+  public void open() throws IOException {
+    if (isClosed()) {
+      csvParser = CSVParser.parse(file, Charset.defaultCharset(), CSVFormat.DEFAULT.withHeader());
+    }
+  }
+
+  /**
+   * Tests this AbstractKerberosDataFileReader to see if the data file is closed.
+   *
+   * @return true if closed; otherwise false
+   */
+  public boolean isClosed() {
+    return csvParser == null;
+  }
+
+  public void close() throws IOException {
+    if (csvParser != null) {
+      csvParser.close();
+      csvParser = null;
+    }
+  }
+
+  /**
+   * Gets an iterator to use to access the records in the data file.
+   * <p/>
+   * Each item is a Map of column names to values.
+   *
+   * @return an Iterator of records from the data file, each record is represented as a Map of
+   * column name (String) to column value (String)
+   */
+  @Override
+  public Iterator<Map<String, String>> iterator() {
+    return new Iterator<Map<String, String>>() {
+      Iterator<CSVRecord> iterator = (csvParser == null) ? null : csvParser.iterator();
+
+      @Override
+      public boolean hasNext() {
+        return (iterator != null) && iterator.hasNext();
+      }
+
+      @Override
+      public Map<String, String> next() {
+        return (iterator == null) ? null : iterator.next().toMap();
+      }
+
+      @Override
+      public void remove() {
+        if (iterator != null) {
+          iterator.remove();
+        }
+      }
+    };
+  }
+}

+ 7 - 4
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java

@@ -29,6 +29,9 @@ import java.io.File;
 import java.util.Map;
 import java.util.concurrent.ConcurrentMap;
 
+import static org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile.HOSTNAME;
+import static org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile.KEYTAB_FILE_PATH;
+
 /**
  * CreateKeytabFilesServerAction is a ServerAction implementation that creates keytab files as
  * instructed.
@@ -73,9 +76,9 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
    * {@link org.apache.ambari.server.serveraction.kerberos.KerberosOperationHandler} to generate
    * the keytab file. To help avoid filename collisions and to build a structure that is easy to
    * discover, each keytab file is stored in host-specific
-   * ({@link org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFileReader#HOSTNAME})
+   * ({@link org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile#HOSTNAME})
    * directory using the SHA1 hash of its destination file path
-   * ({@link org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFileReader#KEYTAB_FILE_PATH})
+   * ({@link org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile#KEYTAB_FILE_PATH})
    * <p/>
    * <pre>
    *   data_directory
@@ -114,8 +117,8 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
         Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
 
         if (principalPasswordMap != null) {
-          String host = identityRecord.get(KerberosActionDataFileBuilder.HOSTNAME);
-          String keytabFilePath = identityRecord.get(KerberosActionDataFileBuilder.KEYTAB_FILE_PATH);
+          String host = identityRecord.get(HOSTNAME);
+          String keytabFilePath = identityRecord.get(KEYTAB_FILE_PATH);
 
           if ((host != null) && !host.isEmpty() && (keytabFilePath != null) && !keytabFilePath.isEmpty()) {
             // Look up the current evaluatedPrincipal's password.

+ 3 - 2
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFile.java

@@ -19,9 +19,10 @@
 package org.apache.ambari.server.serveraction.kerberos;
 
 /**
- * An interface to contain common record column names for KerberosActionDataFile classes.
+ * KerberosActionDataFile declares the default data file name and the common record column names
+ * for the Kerberos action (metadata) data files.
  */
-public interface KerberosActionDataFile {
+public class KerberosActionDataFile {
   public static final String DATA_FILE_NAME = "index.dat";
 
   public static final String HOSTNAME = "hostname";

+ 19 - 72
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFileBuilder.java

@@ -18,12 +18,11 @@
 
 package org.apache.ambari.server.serveraction.kerberos;
 
-import org.apache.commons.csv.CSVFormat;
-import org.apache.commons.csv.CSVPrinter;
+import static org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile.*;
 
 import java.io.File;
-import java.io.FileWriter;
 import java.io.IOException;
+import java.util.Arrays;
 
 /**
  * KerberosActionDataFileBuilder is an implementation of a KerberosActionDataFile that is used to
@@ -31,10 +30,7 @@ import java.io.IOException;
  * <p/>
  * This class encapsulates a {@link org.apache.commons.csv.CSVPrinter} to create a CSV-formatted file.
  */
-public class KerberosActionDataFileBuilder implements KerberosActionDataFile {
-
-  private File file;
-  private CSVPrinter csvPrinter;
+public class KerberosActionDataFileBuilder extends AbstractKerberosDataFileBuilder {
 
   /**
    * Creates a new KerberosActionDataFileBuilder
@@ -46,67 +42,9 @@ public class KerberosActionDataFileBuilder implements KerberosActionDataFile {
    * @throws IOException
    */
   public KerberosActionDataFileBuilder(File file) throws IOException {
-    this.file = file;
-    open();
-  }
-
-
-  /**
-   * Opens the data file for writing.
-   * <p/>
-   * This may be called multiple times and the appropriate action will occur depending on if the
-   * file has been previously opened or closed.
-   *
-   * @throws IOException
-   */
-  public void open() throws IOException {
-    if (isClosed()) {
-      if (file == null) {
-        throw new IOException("Missing file path");
-      } else {
-        // If the file is empty, write the header; else don't write the header.
-        boolean writeHeader = file.length() == 0;
-
-        csvPrinter = new CSVPrinter(new FileWriter(file, true), CSVFormat.DEFAULT);
-
-        if (writeHeader) {
-          // Write the header....
-          csvPrinter.printRecord(HOSTNAME,
-              SERVICE,
-              COMPONENT,
-              PRINCIPAL,
-              PRINCIPAL_CONFIGURATION,
-              KEYTAB_FILE_PATH,
-              KEYTAB_FILE_OWNER_NAME,
-              KEYTAB_FILE_OWNER_ACCESS,
-              KEYTAB_FILE_GROUP_NAME,
-              KEYTAB_FILE_GROUP_ACCESS,
-              KEYTAB_FILE_CONFIGURATION);
-        }
-      }
-    }
-  }
-
-  /**
-   * Tests this KerberosActionDataFileBuilder to see if the data file is closed.
-   *
-   * @return true if closed; otherwise false
-   */
-  public boolean isClosed() {
-    return csvPrinter == null;
+    super(file);
   }
 
-  /**
-   * Closes the data file
-   *
-   * @throws IOException
-   */
-  public void close() throws IOException {
-    if (csvPrinter != null) {
-      csvPrinter.close();
-      csvPrinter = null;
-    }
-  }
 
   /**
    * Appends a new record to the data file
@@ -138,12 +76,7 @@ public class KerberosActionDataFileBuilder implements KerberosActionDataFile {
                         String keytabFileOwnerName, String keytabFileOwnerAccess,
                         String keytabFileGroupName, String keytabFileGroupAccess,
                         String keytabFileConfiguration) throws IOException {
-
-    if (csvPrinter == null) {
-      throw new IOException("Data file is not open");
-    }
-
-    csvPrinter.printRecord(hostName,
+    super.appendRecord(hostName,
         serviceName,
         serviceComponentName,
         principal,
@@ -156,4 +89,18 @@ public class KerberosActionDataFileBuilder implements KerberosActionDataFile {
         keytabFileConfiguration);
   }
 
+  @Override
+  protected Iterable<String> getHeaderRecord() {
+    return Arrays.asList(HOSTNAME,
+        SERVICE,
+        COMPONENT,
+        PRINCIPAL,
+        PRINCIPAL_CONFIGURATION,
+        KEYTAB_FILE_PATH,
+        KEYTAB_FILE_OWNER_NAME,
+        KEYTAB_FILE_OWNER_ACCESS,
+        KEYTAB_FILE_GROUP_NAME,
+        KEYTAB_FILE_GROUP_ACCESS,
+        KEYTAB_FILE_CONFIGURATION);
+  }
 }

+ 2 - 73
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFileReader.java

@@ -18,15 +18,8 @@
 
 package org.apache.ambari.server.serveraction.kerberos;
 
-import org.apache.commons.csv.CSVFormat;
-import org.apache.commons.csv.CSVParser;
-import org.apache.commons.csv.CSVRecord;
-
 import java.io.File;
 import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.Iterator;
-import java.util.Map;
 
 /**
  * KerberosActionDataFileReader is an implementation of a KerberosActionDataFile that is used to
@@ -34,11 +27,7 @@ import java.util.Map;
  * <p/>
  * This class encapsulates a {@link org.apache.commons.csv.CSVParser} to read a CSV-formatted file.
  */
-public class KerberosActionDataFileReader implements KerberosActionDataFile, Iterable<Map<String, String>> {
-
-  private File file;
-  private CSVParser csvParser = null;
-
+public class KerberosActionDataFileReader extends AbstractKerberosDataFileReader {
 
   /**
    * Creates a new KerberosActionDataFileReader
@@ -50,66 +39,6 @@ public class KerberosActionDataFileReader implements KerberosActionDataFile, Ite
    * @throws IOException
    */
   public KerberosActionDataFileReader(File file) throws IOException {
-    this.file = file;
-    open();
-  }
-
-
-  /**
-   * Opens the data file for reading.
-   * <p/>
-   * This may be called multiple times and the appropriate action will occur depending on if the
-   * file has been previously opened or closed.
-   *
-   * @throws IOException
-   */
-  public void open() throws IOException {
-    if (isClosed()) {
-      csvParser = CSVParser.parse(file, Charset.defaultCharset(), CSVFormat.DEFAULT.withHeader());
-    }
-  }
-
-
-  /**
-   * Tests this KerberosActionDataFileReader  to see if the data file is closed.
-   *
-   * @return true if closed; otherwise false
-   */
-  public boolean isClosed() {
-    return csvParser == null;
-  }
-
-  public void close() throws IOException {
-    csvParser.close();
-    csvParser = null;
-  }
-
-  /**
-   * Gets an iterator to use to access the records in the data file.
-   * <p/>
-   * Each item is a Map of column names to values.
-   *
-   * @return an Iterator
-   */
-  @Override
-  public Iterator<Map<String, String>> iterator() {
-    return new Iterator<Map<String, String>>() {
-      Iterator<CSVRecord> iterator = csvParser.iterator();
-
-      @Override
-      public boolean hasNext() {
-        return iterator.hasNext();
-      }
-
-      @Override
-      public Map<String, String> next() {
-        return iterator.next().toMap();
-      }
-
-      @Override
-      public void remove() {
-        iterator.remove();
-      }
-    };
+    super(file);
   }
 }

+ 31 - 0
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFile.java

@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+/**
+ * KerberosConfigDataFile declares the default data file name and the common record column names
+ * for the Kerberos configuration data files.
+ */
+public class KerberosConfigDataFile {
+  public static final String DATA_FILE_NAME = "configs.dat";
+
+  public static final String CONFIGURATION_TYPE = "config";
+  public static final String KEY = "key";
+  public static final String VALUE = "value";
+}

+ 65 - 0
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFileBuilder.java

@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import static org.apache.ambari.server.serveraction.kerberos.KerberosConfigDataFile.*;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * KerberosConfigDataFileBuilder is an implementation of a KerberosConfigDataFile that is used to
+ * create a new KerberosConfigDataFile.
+ * <p/>
+ * This class encapsulates a {@link org.apache.commons.csv.CSVPrinter} to create a CSV-formatted file.
+ */
+public class KerberosConfigDataFileBuilder extends AbstractKerberosDataFileBuilder {
+
+  /**
+   * Creates a new KerberosConfigDataFileBuilder
+   * <p/>
+   * The file is opened upon creation, so there is no need to manually open it unless manually
+   * closed before using.
+   *
+   * @param file a File declaring where to write the data
+   * @throws java.io.IOException
+   */
+  public KerberosConfigDataFileBuilder(File file) throws IOException {
+    super(file);
+  }
+
+
+  /**
+   * Appends a new record to the data file
+   *
+   * @param config a String declaring the relevant configuration type for the key and value
+   * @param key    a String declaring the key (or property name) with in the relevant configuration type
+   * @param value  a String containing the value of the configuration property
+   * @throws java.io.IOException
+   */
+  public void addRecord(String config, String key, String value) throws IOException {
+    super.appendRecord(config, key, value);
+  }
+
+  @Override
+  protected Iterable<String> getHeaderRecord() {
+    return Arrays.asList(CONFIGURATION_TYPE, KEY, VALUE);
+  }
+}

+ 44 - 0
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFileReader.java

@@ -0,0 +1,44 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * KerberosConfigDataFileReader is an implementation of a KerberosConfigDataFile that is used to
+ * read existing KerberosConfigDataFiles.
+ * <p/>
+ * This class encapsulates a {@link org.apache.commons.csv.CSVParser} to read a CSV-formatted file.
+ */
+public class KerberosConfigDataFileReader extends AbstractKerberosDataFileReader {
+
+  /**
+   * Creates a new KerberosConfigDataFileReader
+   * <p/>
+   * The file is opened upon creation, so there is no need to manually open it unless manually
+   * closed before using.
+   *
+   * @param file a File declaring where to write the data
+   * @throws java.io.IOException
+   */
+  public KerberosConfigDataFileReader(File file) throws IOException {
+    super(file);
+  }
+}

+ 3 - 1
ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java

@@ -25,6 +25,8 @@ import org.apache.ambari.server.serveraction.AbstractServerAction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import static org.apache.ambari.server.serveraction.kerberos.KerberosActionDataFile.DATA_FILE_NAME;
+
 import java.io.File;
 import java.io.IOException;
 import java.util.HashMap;
@@ -250,7 +252,7 @@ public abstract class KerberosServerAction extends AbstractServerAction {
           throw new AmbariException(message);
         }
         // The "index" file is expected to be in the specified data directory and named "index.dat"
-        File indexFile = new File(dataDirectory, KerberosActionDataFileBuilder.DATA_FILE_NAME);
+        File indexFile = new File(dataDirectory, DATA_FILE_NAME);
 
         if (!indexFile.canRead()) {
           String message = String.format("Failed to process the identities, cannot read the index file: %s",

+ 145 - 141
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosActionDataFileTest.java

@@ -19,7 +19,9 @@
 package org.apache.ambari.server.serveraction.kerberos;
 
 import junit.framework.Assert;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
 
 import java.io.File;
 import java.util.Iterator;
@@ -31,172 +33,174 @@ import java.util.Map;
  */
 public class KerberosActionDataFileTest {
 
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
   @Test
   public void testKerberosActionDataFile() throws Exception {
-    File file = File.createTempFile("ambari_ut_", "dat");
+    File file = folder.newFile();
     Assert.assertNotNull(file);
 
-    try {
-      // Write the data
-      KerberosActionDataFileBuilder builder = new KerberosActionDataFileBuilder(file);
-      Assert.assertFalse(builder.isClosed());
-
-      for (int i = 0; i < 10; i++) {
-        builder.addRecord("hostName" + i, "serviceName" + i, "serviceComponentName" + i,
-            "principal" + i, "principalConfiguration" + i, "keytabFilePath" + i,
-            "keytabFileOwnerName" + i, "keytabFileOwnerAccess" + i,
-            "keytabFileGroupName" + i, "keytabFileGroupAccess" + i,
-            "keytabFileConfiguration" + i);
-      }
+    // Write the data
+    KerberosActionDataFileBuilder builder = new KerberosActionDataFileBuilder(file);
+    Assert.assertFalse(builder.isClosed());
+
+    for (int i = 0; i < 10; i++) {
+      builder.addRecord("hostName" + i, "serviceName" + i, "serviceComponentName" + i,
+          "principal" + i, "principalConfiguration" + i, "keytabFilePath" + i,
+          "keytabFileOwnerName" + i, "keytabFileOwnerAccess" + i,
+          "keytabFileGroupName" + i, "keytabFileGroupAccess" + i,
+          "keytabFileConfiguration" + i);
+    }
 
-      // Add some odd characters
-      builder.addRecord("hostName's", "serviceName#", "serviceComponentName\"",
-          "principal", "principalConfiguration", "keytabFilePath",
-          "'keytabFileOwnerName'", "<keytabFileOwnerAccess>",
-          "\"keytabFileGroupName\"", "keytab,File,Group,Access",
-          "\"keytab,'File',Configuration\"");
-
-      builder.close();
-      Assert.assertTrue(builder.isClosed());
-
-      // Read the data...
-      KerberosActionDataFileReader reader = new KerberosActionDataFileReader(file);
-      Assert.assertFalse(reader.isClosed());
-
-      Iterator<Map<String, String>> iterator = reader.iterator();
-      Assert.assertNotNull(iterator);
-
-      // Test iterator
-      int i = 0;
-      while (iterator.hasNext()) {
-        Map<String, String> record = iterator.next();
-
-        if (i < 10) {
-          Assert.assertEquals("hostName" + i, record.get(KerberosActionDataFile.HOSTNAME));
-          Assert.assertEquals("serviceName" + i, record.get(KerberosActionDataFile.SERVICE));
-          Assert.assertEquals("serviceComponentName" + i, record.get(KerberosActionDataFile.COMPONENT));
-          Assert.assertEquals("principal" + i, record.get(KerberosActionDataFile.PRINCIPAL));
-          Assert.assertEquals("principalConfiguration" + i, record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
-          Assert.assertEquals("keytabFilePath" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
-          Assert.assertEquals("keytabFileOwnerName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
-          Assert.assertEquals("keytabFileOwnerAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
-          Assert.assertEquals("keytabFileGroupName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
-          Assert.assertEquals("keytabFileGroupAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
-          Assert.assertEquals("keytabFileConfiguration" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
-        } else {
-          Assert.assertEquals("hostName's", record.get(KerberosActionDataFile.HOSTNAME));
-          Assert.assertEquals("serviceName#", record.get(KerberosActionDataFile.SERVICE));
-          Assert.assertEquals("serviceComponentName\"", record.get(KerberosActionDataFile.COMPONENT));
-          Assert.assertEquals("principal", record.get(KerberosActionDataFile.PRINCIPAL));
-          Assert.assertEquals("principalConfiguration", record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
-          Assert.assertEquals("keytabFilePath", record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
-          Assert.assertEquals("'keytabFileOwnerName'", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
-          Assert.assertEquals("<keytabFileOwnerAccess>", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
-          Assert.assertEquals("\"keytabFileGroupName\"", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
-          Assert.assertEquals("keytab,File,Group,Access", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
-          Assert.assertEquals("\"keytab,'File',Configuration\"", record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
-        }
-
-        i++;
+    // Add some odd characters
+    builder.addRecord("hostName's", "serviceName#", "serviceComponentName\"",
+        "principal", "principalConfiguration", "keytabFilePath",
+        "'keytabFileOwnerName'", "<keytabFileOwnerAccess>",
+        "\"keytabFileGroupName\"", "keytab,File,Group,Access",
+        "\"keytab,'File',Configuration\"");
+
+    builder.close();
+    Assert.assertTrue(builder.isClosed());
+
+    // Read the data...
+    KerberosActionDataFileReader reader = new KerberosActionDataFileReader(file);
+    Assert.assertFalse(reader.isClosed());
+
+    Iterator<Map<String, String>> iterator = reader.iterator();
+    Assert.assertNotNull(iterator);
+
+    // Test iterator
+    int i = 0;
+    while (iterator.hasNext()) {
+      Map<String, String> record = iterator.next();
+
+      if (i < 10) {
+        Assert.assertEquals("hostName" + i, record.get(KerberosActionDataFile.HOSTNAME));
+        Assert.assertEquals("serviceName" + i, record.get(KerberosActionDataFile.SERVICE));
+        Assert.assertEquals("serviceComponentName" + i, record.get(KerberosActionDataFile.COMPONENT));
+        Assert.assertEquals("principal" + i, record.get(KerberosActionDataFile.PRINCIPAL));
+        Assert.assertEquals("principalConfiguration" + i, record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
+        Assert.assertEquals("keytabFilePath" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
+        Assert.assertEquals("keytabFileOwnerName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
+        Assert.assertEquals("keytabFileOwnerAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
+        Assert.assertEquals("keytabFileGroupName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
+        Assert.assertEquals("keytabFileGroupAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
+        Assert.assertEquals("keytabFileConfiguration" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
+      } else {
+        Assert.assertEquals("hostName's", record.get(KerberosActionDataFile.HOSTNAME));
+        Assert.assertEquals("serviceName#", record.get(KerberosActionDataFile.SERVICE));
+        Assert.assertEquals("serviceComponentName\"", record.get(KerberosActionDataFile.COMPONENT));
+        Assert.assertEquals("principal", record.get(KerberosActionDataFile.PRINCIPAL));
+        Assert.assertEquals("principalConfiguration", record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
+        Assert.assertEquals("keytabFilePath", record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
+        Assert.assertEquals("'keytabFileOwnerName'", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
+        Assert.assertEquals("<keytabFileOwnerAccess>", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
+        Assert.assertEquals("\"keytabFileGroupName\"", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
+        Assert.assertEquals("keytab,File,Group,Access", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
+        Assert.assertEquals("\"keytab,'File',Configuration\"", record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
       }
 
-      reader.close();
-      Assert.assertTrue(reader.isClosed());
-      reader.open();
-      Assert.assertFalse(reader.isClosed());
-
-      i = 0;
-      for (Map<String, String> record : reader) {
-        if (i < 10) {
-          Assert.assertEquals("hostName" + i, record.get(KerberosActionDataFile.HOSTNAME));
-          Assert.assertEquals("serviceName" + i, record.get(KerberosActionDataFile.SERVICE));
-          Assert.assertEquals("serviceComponentName" + i, record.get(KerberosActionDataFile.COMPONENT));
-          Assert.assertEquals("principal" + i, record.get(KerberosActionDataFile.PRINCIPAL));
-          Assert.assertEquals("principalConfiguration" + i, record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
-          Assert.assertEquals("keytabFilePath" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
-          Assert.assertEquals("keytabFileOwnerName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
-          Assert.assertEquals("keytabFileOwnerAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
-          Assert.assertEquals("keytabFileGroupName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
-          Assert.assertEquals("keytabFileGroupAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
-          Assert.assertEquals("keytabFileConfiguration" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
-        } else {
-          Assert.assertEquals("hostName's", record.get(KerberosActionDataFile.HOSTNAME));
-          Assert.assertEquals("serviceName#", record.get(KerberosActionDataFile.SERVICE));
-          Assert.assertEquals("serviceComponentName\"", record.get(KerberosActionDataFile.COMPONENT));
-          Assert.assertEquals("principal", record.get(KerberosActionDataFile.PRINCIPAL));
-          Assert.assertEquals("principalConfiguration", record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
-          Assert.assertEquals("keytabFilePath", record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
-          Assert.assertEquals("'keytabFileOwnerName'", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
-          Assert.assertEquals("<keytabFileOwnerAccess>", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
-          Assert.assertEquals("\"keytabFileGroupName\"", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
-          Assert.assertEquals("keytab,File,Group,Access", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
-          Assert.assertEquals("\"keytab,'File',Configuration\"", record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
-        }
-
-        i++;
+      i++;
+    }
+
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
+    reader.open();
+    Assert.assertFalse(reader.isClosed());
+
+    i = 0;
+    for (Map<String, String> record : reader) {
+      if (i < 10) {
+        Assert.assertEquals("hostName" + i, record.get(KerberosActionDataFile.HOSTNAME));
+        Assert.assertEquals("serviceName" + i, record.get(KerberosActionDataFile.SERVICE));
+        Assert.assertEquals("serviceComponentName" + i, record.get(KerberosActionDataFile.COMPONENT));
+        Assert.assertEquals("principal" + i, record.get(KerberosActionDataFile.PRINCIPAL));
+        Assert.assertEquals("principalConfiguration" + i, record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
+        Assert.assertEquals("keytabFilePath" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
+        Assert.assertEquals("keytabFileOwnerName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
+        Assert.assertEquals("keytabFileOwnerAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
+        Assert.assertEquals("keytabFileGroupName" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
+        Assert.assertEquals("keytabFileGroupAccess" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
+        Assert.assertEquals("keytabFileConfiguration" + i, record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
+      } else {
+        Assert.assertEquals("hostName's", record.get(KerberosActionDataFile.HOSTNAME));
+        Assert.assertEquals("serviceName#", record.get(KerberosActionDataFile.SERVICE));
+        Assert.assertEquals("serviceComponentName\"", record.get(KerberosActionDataFile.COMPONENT));
+        Assert.assertEquals("principal", record.get(KerberosActionDataFile.PRINCIPAL));
+        Assert.assertEquals("principalConfiguration", record.get(KerberosActionDataFile.PRINCIPAL_CONFIGURATION));
+        Assert.assertEquals("keytabFilePath", record.get(KerberosActionDataFile.KEYTAB_FILE_PATH));
+        Assert.assertEquals("'keytabFileOwnerName'", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_NAME));
+        Assert.assertEquals("<keytabFileOwnerAccess>", record.get(KerberosActionDataFile.KEYTAB_FILE_OWNER_ACCESS));
+        Assert.assertEquals("\"keytabFileGroupName\"", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_NAME));
+        Assert.assertEquals("keytab,File,Group,Access", record.get(KerberosActionDataFile.KEYTAB_FILE_GROUP_ACCESS));
+        Assert.assertEquals("\"keytab,'File',Configuration\"", record.get(KerberosActionDataFile.KEYTAB_FILE_CONFIGURATION));
       }
 
-      reader.close();
-      Assert.assertTrue(reader.isClosed());
+      i++;
+    }
 
-      // Add an additional record
-      builder.open();
-      Assert.assertFalse(builder.isClosed());
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
 
-      builder.addRecord("hostName", "serviceName", "serviceComponentName",
-          "principal", "principalConfiguration", "keytabFilePath",
-          "keytabFileOwnerName", "keytabFileOwnerAccess",
-          "keytabFileGroupName", "keytabFileGroupAccess",
-          "keytabFileConfiguration");
+    // Add an additional record
+    builder.open();
+    Assert.assertFalse(builder.isClosed());
 
-      builder.close();
-      Assert.assertTrue(builder.isClosed());
+    builder.addRecord("hostName", "serviceName", "serviceComponentName",
+        "principal", "principalConfiguration", "keytabFilePath",
+        "keytabFileOwnerName", "keytabFileOwnerAccess",
+        "keytabFileGroupName", "keytabFileGroupAccess",
+        "keytabFileConfiguration");
 
-      reader = new KerberosActionDataFileReader(file);
-      Assert.assertFalse(reader.isClosed());
+    builder.close();
+    Assert.assertTrue(builder.isClosed());
 
-      i = 0;
-      for (Map<String, String> record : reader) {
-        i++;
-      }
+    reader = new KerberosActionDataFileReader(file);
+    Assert.assertFalse(reader.isClosed());
 
-      Assert.assertEquals(12, i);
+    i = 0;
+    for (Map<String, String> record : reader) {
+      i++;
+    }
 
-      reader.close();
-      Assert.assertTrue(reader.isClosed());
+    Assert.assertEquals(12, i);
 
-      // Add an additional record
-      builder = new KerberosActionDataFileBuilder(file);
-      Assert.assertFalse(builder.isClosed());
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
 
-      builder.addRecord("hostName", "serviceName", "serviceComponentName",
-          "principal", "principalConfiguration", "keytabFilePath",
-          "keytabFileOwnerName", "keytabFileOwnerAccess",
-          "keytabFileGroupName", "keytabFileGroupAccess",
-          "keytabFileConfiguration");
+    // Add an additional record
+    builder = new KerberosActionDataFileBuilder(file);
+    Assert.assertFalse(builder.isClosed());
 
-      builder.close();
-      Assert.assertTrue(builder.isClosed());
+    builder.addRecord("hostName", "serviceName", "serviceComponentName",
+        "principal", "principalConfiguration", "keytabFilePath",
+        "keytabFileOwnerName", "keytabFileOwnerAccess",
+        "keytabFileGroupName", "keytabFileGroupAccess",
+        "keytabFileConfiguration");
 
-      reader.open();
-      Assert.assertFalse(reader.isClosed());
+    builder.close();
+    Assert.assertTrue(builder.isClosed());
 
-      i = 0;
-      for (Map<String, String> record : reader) {
-        i++;
-      }
+    reader.open();
+    Assert.assertFalse(reader.isClosed());
 
-      Assert.assertEquals(13, i);
+    i = 0;
+    for (Map<String, String> record : reader) {
+      i++;
+    }
 
-      reader.close();
-      Assert.assertTrue(reader.isClosed());
+    Assert.assertEquals(13, i);
 
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
 
-    } finally {
-      if (!file.delete()) {
-        file.deleteOnExit();
-      }
+    // trying to iterate over a closed reader...
+    i = 0;
+    for (Map<String, String> record : reader) {
+      i++;
     }
-  }
+    Assert.assertEquals(0, i);
 
+  }
 }

+ 146 - 0
ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/KerberosConfigDataFileTest.java

@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ambari.server.serveraction.kerberos;
+
+import junit.framework.Assert;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This is a test to see how well the KerberosConfigDataFileBuilder and KerberosConfigDataFileReader
+ * work when the data temporaryDirectory is opened, close, reopened, and appended to.
+ */
+public class KerberosConfigDataFileTest {
+
+  @Rule
+  public TemporaryFolder folder = new TemporaryFolder();
+
+  @Test
+  public void testKerberosConfigDataFile() throws Exception {
+    File file = folder.newFile();
+    Assert.assertNotNull(file);
+
+    // Write the data
+    KerberosConfigDataFileBuilder builder = new KerberosConfigDataFileBuilder(file);
+    Assert.assertFalse(builder.isClosed());
+
+    for (int i = 0; i < 10; i++) {
+      builder.addRecord("config-type" + i, "key" + i, "value" + i);
+    }
+
+    builder.close();
+    Assert.assertTrue(builder.isClosed());
+
+    // Read the data...
+    KerberosConfigDataFileReader reader = new KerberosConfigDataFileReader(file);
+    Assert.assertFalse(reader.isClosed());
+
+    Iterator<Map<String, String>> iterator = reader.iterator();
+    Assert.assertNotNull(iterator);
+
+    // Test iterator
+    int i = 0;
+    while (iterator.hasNext()) {
+      Map<String, String> record = iterator.next();
+
+      if (i < 10) {
+        Assert.assertEquals("config-type" + i, record.get(KerberosConfigDataFile.CONFIGURATION_TYPE));
+        Assert.assertEquals("key" + i, record.get(KerberosConfigDataFile.KEY));
+        Assert.assertEquals("value" + i, record.get(KerberosConfigDataFile.VALUE));
+      }
+
+      i++;
+    }
+
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
+    reader.open();
+    Assert.assertFalse(reader.isClosed());
+
+    i = 0;
+    for (Map<String, String> record : reader) {
+      if (i < 10) {
+        Assert.assertEquals("config-type" + i, record.get(KerberosConfigDataFile.CONFIGURATION_TYPE));
+        Assert.assertEquals("key" + i, record.get(KerberosConfigDataFile.KEY));
+        Assert.assertEquals("value" + i, record.get(KerberosConfigDataFile.VALUE));
+      }
+
+      i++;
+    }
+
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
+
+    // Add an additional record
+    builder.open();
+    Assert.assertFalse(builder.isClosed());
+
+    builder.addRecord("config-type", "key", "value");
+
+    builder.close();
+    Assert.assertTrue(builder.isClosed());
+
+    reader = new KerberosConfigDataFileReader(file);
+    Assert.assertFalse(reader.isClosed());
+
+    i = 0;
+    for (Map<String, String> record : reader) {
+      i++;
+    }
+
+    Assert.assertEquals(11, i);
+
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
+
+    // Add an additional record
+    builder = new KerberosConfigDataFileBuilder(file);
+    Assert.assertFalse(builder.isClosed());
+
+    builder.addRecord("config-type", "key", "value");
+
+    builder.close();
+    Assert.assertTrue(builder.isClosed());
+
+    reader.open();
+    Assert.assertFalse(reader.isClosed());
+
+    i = 0;
+    for (Map<String, String> record : reader) {
+      i++;
+    }
+
+    Assert.assertEquals(12, i);
+
+    reader.close();
+    Assert.assertTrue(reader.isClosed());
+
+    // trying to iterate over a closed reader...
+    i = 0;
+    for (Map<String, String> record : reader) {
+      i++;
+    }
+    Assert.assertEquals(0, i);
+  }
+}