Author: rkanter
Date: Tue Dec 3 01:41:59 2013
New Revision: 1547259
URL: http://svn.apache.org/r1547259
Log:
OOZIE-1491 Make sure HA works with a secure ZooKeeper (rkanter)
Added:
oozie/trunk/core/src/main/java/org/apache/oozie/util/JaasConfiguration.java
oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestJaasConfiguration.java
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java
Modified:
oozie/trunk/core/pom.xml
oozie/trunk/core/src/main/java/org/apache/oozie/util/ZKUtils.java
oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtils.java
oozie/trunk/docs/src/site/twiki/AG_Install.twiki
oozie/trunk/pom.xml
oozie/trunk/release-log.txt
Modified: oozie/trunk/core/pom.xml
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/pom.xml?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/core/pom.xml (original)
+++ oozie/trunk/core/pom.xml Tue Dec 3 01:41:59 2013
@@ -43,6 +43,12 @@
</dependency>
<dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-minikdc</artifactId>
+ <scope>test</scope>
+ </dependency>
+
+ <dependency>
<groupId>org.apache.oozie</groupId>
<artifactId>oozie-hadoop</artifactId>
<scope>provided</scope>
@@ -427,6 +433,12 @@
</dependency>
</dependencies>
</plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <inherited>true</inherited>
+ <extensions>true</extensions>
+ </plugin>
</plugins>
</build>
Added:
oozie/trunk/core/src/main/java/org/apache/oozie/util/JaasConfiguration.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/util/JaasConfiguration.java?rev=1547259&view=auto
==============================================================================
--- oozie/trunk/core/src/main/java/org/apache/oozie/util/JaasConfiguration.java
(added)
+++ oozie/trunk/core/src/main/java/org/apache/oozie/util/JaasConfiguration.java
Tue Dec 3 01:41:59 2013
@@ -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.oozie.util;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.security.auth.login.AppConfigurationEntry;
+import javax.security.auth.login.Configuration;
+
+
+/**
+ * Creates a programmatic version of a jaas.conf file. This can be used
instead of writing a jaas.conf file and setting
+ * the system property, "java.security.auth.login.config", to point to that
file. It is meant to be used for connecting to
+ * ZooKeeper.
+ * <p>
+ * example usage:
+ * JaasConfiguration.addEntry("Client", principal, keytabFile);
+ *
javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
+ */
+public class JaasConfiguration extends Configuration {
+ private static Map<String, AppConfigurationEntry> entries = new
HashMap<String, AppConfigurationEntry>();
+ private static JaasConfiguration me = null;
+ private static final String krb5LoginModuleName;
+
+ static {
+ if (System.getProperty("java.vendor").contains("IBM")) {
+ krb5LoginModuleName =
"com.ibm.security.auth.module.Krb5LoginModule";
+ }
+ else {
+ krb5LoginModuleName =
"com.sun.security.auth.module.Krb5LoginModule";
+ }
+ }
+
+ private JaasConfiguration() {
+ // don't need to do anything here but we want to make it private
+ }
+
+ /**
+ * Return the singleton. You'd typically use it only to do this:
+ * <p>
+ *
javax.security.auth.login.Configuration.setConfiguration(JaasConfiguration.getInstance());
+ *
+ * @return
+ */
+ public static Configuration getInstance() {
+ if (me == null) {
+ me = new JaasConfiguration();
+ }
+ return me;
+ }
+
+ /**
+ * Add an entry to the jaas configuration with the passed in name,
principal, and keytab. The other necessary options will be
+ * set for you.
+ *
+ * @param name The name of the entry (e.g. "Client")
+ * @param principal The principal of the user
+ * @param keytab The location of the keytab
+ */
+ public static void addEntry(String name, String principal, String keytab) {
+ Map<String, String> options = new HashMap<String, String>();
+ options.put("keyTab", keytab);
+ options.put("principal", principal);
+ options.put("useKeyTab", "true");
+ options.put("storeKey", "true");
+ options.put("useTicketCache", "false");
+ AppConfigurationEntry entry = new
AppConfigurationEntry(krb5LoginModuleName,
+ AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
options);
+ entries.put(name, entry);
+ }
+
+ /**
+ * Removes the specified entry.
+ *
+ * @param name The name of the entry to remove
+ */
+ public static void removeEntry(String name) {
+ entries.remove(name);
+ }
+
+ /**
+ * Clears all entries.
+ */
+ public static void clearEntries() {
+ entries.clear();
+ }
+
+ /**
+ * Returns the entries map.
+ *
+ * @return the entries map
+ */
+ public static Map<String, AppConfigurationEntry> getEntries() {
+ return entries;
+ }
+
+ @Override
+ public AppConfigurationEntry[] getAppConfigurationEntry(String name) {
+ return new AppConfigurationEntry[]{entries.get(name)};
+ }
+}
Modified: oozie/trunk/core/src/main/java/org/apache/oozie/util/ZKUtils.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/main/java/org/apache/oozie/util/ZKUtils.java?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/core/src/main/java/org/apache/oozie/util/ZKUtils.java (original)
+++ oozie/trunk/core/src/main/java/org/apache/oozie/util/ZKUtils.java Tue Dec
3 01:41:59 2013
@@ -18,14 +18,19 @@
package org.apache.oozie.util;
import com.google.common.annotations.VisibleForTesting;
+import java.io.IOException;
+import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import javax.security.auth.login.Configuration;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
+import org.apache.curator.framework.api.ACLProvider;
+import org.apache.curator.framework.imps.DefaultACLProvider;
import org.apache.curator.retry.ExponentialBackoffRetry;
import org.apache.curator.utils.EnsurePath;
import org.apache.curator.x.discovery.ServiceCache;
@@ -33,7 +38,16 @@ import org.apache.curator.x.discovery.Se
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.details.InstanceSerializer;
+import org.apache.oozie.ErrorCode;
+import static org.apache.oozie.service.HadoopAccessorService.KERBEROS_KEYTAB;
+import static
org.apache.oozie.service.HadoopAccessorService.KERBEROS_PRINCIPAL;
+import org.apache.oozie.service.ServiceException;
import org.apache.oozie.service.Services;
+import org.apache.zookeeper.ZooDefs.Perms;
+import org.apache.zookeeper.client.ZooKeeperSaslClient;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Id;
+import org.apache.zookeeper.data.Stat;
/**
@@ -48,10 +62,15 @@ import org.apache.oozie.service.Services
* Each Oozie Server provides metadata that can be shared with the other Oozie
Servers. To keep things simple and to make it easy
* to add additional metadata in the future, we share a Map. They keys are
defined in {@link ZKMetadataKeys}.
* <p>
- * For the service discovery, the structure in ZooKeeper is
/oozie.zookeeper.namespace/ZK_BASE_PATH/ (default is /oozie/services/).
- * There is currently only one service, named "servers" under which each Oozie
server creates a ZNode named
+ * For the service discovery, the structure in ZooKeeper is
/oozie.zookeeper.namespace/ZK_BASE_SERVICES_PATH/ (default is
+ * /oozie/services/). There is currently only one service, named "servers"
under which each Oozie server creates a ZNode named
* ${OOZIE_SERVICE_INSTANCE} (default is the hostname) that contains the
metadata payload. For example, with the default settings,
* an Oozie server named "foo" would create a ZNode at
/oozie/services/servers/foo where the foo ZNode contains the metadata.
+ * <p>
+ * If oozie.zookeeper.secure is set to true, then Oozie will (a) use jaas to
connect to ZooKeeper using SASL/Kerberos based on
+ * Oozie's existing security configuration parameters (b) use/convert every
znode under the namespace (including the namespace
+ * itself) to have ACLs such that only Oozie servers have access (i.e. if
"service/host@REALM" is the Kerberos principal, then
+ * "service" will be used for the ACLs).
*/
public class ZKUtils {
/**
@@ -70,8 +89,13 @@ public class ZKUtils {
*/
public static final String OOZIE_INSTANCE_ID = "oozie.instance.id";
+ /**
+ * oozie-site property for specifying that ZooKeeper is secure.
+ */
+ public static final String ZK_SECURE = "oozie.zookeeper.secure";
+
private static final String ZK_OOZIE_SERVICE = "servers";
- private static final String ZK_BASE_PATH = "/services";
+ private static final String ZK_BASE_SERVICES_PATH = "/services";
private static Set<Object> users = new HashSet<Object>();
private CuratorFramework client = null;
@@ -79,6 +103,7 @@ public class ZKUtils {
private long zkRegTime;
private ServiceDiscovery<Map> sDiscovery;
private ServiceCache<Map> sCache;
+ private List<ACL> saslACL;
private XLog log;
private static ZKUtils zk = null;
@@ -93,6 +118,7 @@ public class ZKUtils {
zkId = System.getProperty(OOZIE_INSTANCE_ID);
createClient();
advertiseService();
+ checkAndSetACLs();
}
/**
@@ -130,20 +156,33 @@ public class ZKUtils {
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
String zkConnectionString =
Services.get().getConf().get(ZK_CONNECTION_STRING, "localhost:2181");
String zkNamespace = Services.get().getConf().get(ZK_NAMESPACE,
"oozie");
+ ACLProvider aclProvider;
+ if (Services.get().getConf().getBoolean(ZK_SECURE, false)) {
+ log.info("Connecting to ZooKeeper with SASL/Kerberos and using
'sasl' ACLs");
+ setJaasConfiguration();
+ System.setProperty(ZooKeeperSaslClient.LOGIN_CONTEXT_NAME_KEY,
"Client");
+ System.setProperty("zookeeper.authProvider.1",
"org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+ saslACL = Collections.singletonList(new ACL(Perms.ALL, new
Id("sasl", getServicePrincipal())));
+ aclProvider = new SASLOwnerACLProvider();
+ } else {
+ log.info("Connecting to ZooKeeper without authentication");
+ aclProvider = new DefaultACLProvider(); // open to everyone
+ }
client = CuratorFrameworkFactory.builder()
.namespace(zkNamespace)
.connectString(zkConnectionString)
.retryPolicy(retryPolicy)
+ .aclProvider(aclProvider)
.build();
client.start();
}
private void advertiseService() throws Exception {
// Advertise on the service discovery
- new EnsurePath(ZK_BASE_PATH).ensure(client.getZookeeperClient());
+ new
EnsurePath(ZK_BASE_SERVICES_PATH).ensure(client.getZookeeperClient());
InstanceSerializer<Map> instanceSerializer = new
FixedJsonInstanceSerializer<Map>(Map.class);
sDiscovery = ServiceDiscoveryBuilder.builder(Map.class)
- .basePath(ZK_BASE_PATH)
+
.basePath(ZK_BASE_SERVICES_PATH)
.client(client)
.serializer(instanceSerializer)
.build();
@@ -196,7 +235,6 @@ public class ZKUtils {
* or two stale.
*
* @return a List of the metadata provided by all of the Oozie Servers.
- * @throws Exception
*/
public List<ServiceInstance<Map>> getAllMetaData() {
List<ServiceInstance<Map>> instances = null;
@@ -245,6 +283,59 @@ public class ZKUtils {
return index;
}
+ private void checkAndSetACLs() throws Exception {
+ if (Services.get().getConf().getBoolean(ZK_SECURE, false)) {
+ // If znodes were previously created without security enabled, and
now it is, we need to go through all existing znodes
+ // and set the ACLs for them
+ // We can't get the namespace znode through curator; have to go
through zk client
+ String namespace = "/" + client.getNamespace();
+ if (client.getZookeeperClient().getZooKeeper().exists(namespace,
null) != null) {
+ List<ACL> acls =
client.getZookeeperClient().getZooKeeper().getACL(namespace, new Stat());
+ if (!acls.get(0).getId().getScheme().equals("sasl")) {
+ log.info("'sasl' ACLs not set; setting...");
+ List<String> children =
client.getZookeeperClient().getZooKeeper().getChildren(namespace, null);
+ for (String child : children) {
+ checkAndSetACLs(child);
+ }
+
client.getZookeeperClient().getZooKeeper().setACL(namespace, saslACL, -1);
+ }
+ }
+ }
+ }
+
+ private void checkAndSetACLs(String path) throws Exception {
+ List<String> children = client.getChildren().forPath(path);
+ for (String child : children) {
+ checkAndSetACLs(path + "/" + child);
+ }
+ client.setACL().withACL(saslACL).forPath(path);
+ }
+
+ // This gets ignored during most tests, see
ZKXTestCaseWithSecurity#setupZKServer()
+ private void setJaasConfiguration() throws ServiceException, IOException {
+ String keytabFile = Services.get().getConf().get(KERBEROS_KEYTAB,
System.getProperty("user.home") + "/oozie.keytab").trim();
+ if (keytabFile.length() == 0) {
+ throw new ServiceException(ErrorCode.E0026, KERBEROS_KEYTAB);
+ }
+ String principal = Services.get().getConf().get(KERBEROS_PRINCIPAL,
"oozie/localhost@LOCALHOST");
+ if (principal.length() == 0) {
+ throw new ServiceException(ErrorCode.E0026, KERBEROS_PRINCIPAL);
+ }
+
+ // This is equivalent to writing a jaas.conf file and setting the
system property, "java.security.auth.login.config", to
+ // point to it (but this way we don't have to write a file, and it
works better for the tests)
+ JaasConfiguration.addEntry("Client", principal, keytabFile);
+ Configuration.setConfiguration(JaasConfiguration.getInstance());
+ }
+
+ private String getServicePrincipal() throws ServiceException {
+ String principal = Services.get().getConf().get(KERBEROS_PRINCIPAL,
"oozie/localhost@LOCALHOST");
+ if (principal.length() == 0) {
+ throw new ServiceException(ErrorCode.E0026, KERBEROS_PRINCIPAL);
+ }
+ return principal.split("[/@]")[0];
+ }
+
/**
* Useful for tests to get the registered classes
*
@@ -268,4 +359,20 @@ public class ZKUtils {
*/
public static final String OOZIE_URL = "OOZIE_URL";
}
+
+ /**
+ * Simple implementation of an {@link ACLProvider} that simply returns
{@link #saslACL}.
+ */
+ public class SASLOwnerACLProvider implements ACLProvider {
+
+ @Override
+ public List<ACL> getDefaultAcl() {
+ return saslACL;
+ }
+
+ @Override
+ public List<ACL> getAclForPath(String path) {
+ return saslACL;
+ }
+ }
}
Modified: oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java
(original)
+++ oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCase.java Tue
Dec 3 01:41:59 2013
@@ -32,6 +32,7 @@ import org.apache.curator.x.discovery.Se
import org.apache.curator.x.discovery.details.InstanceSerializer;
import org.apache.oozie.service.Services;
import org.apache.oozie.util.FixedJsonInstanceSerializer;
+import org.apache.oozie.util.ZKUtils;
/**
* Provides a version of XTestCase that also runs a ZooKeeper server and
provides some utilities for interacting and simulating ZK
@@ -43,11 +44,16 @@ import org.apache.oozie.util.FixedJsonIn
* To simulate another Oozie server, the DummyZKOozie object can be used; you
can specify a ZooKeeper ID and Oozie URL for it in
* the constructor. Unlike this test class, it will advertise on the ZK
service discovery, so it will appear as another Oozie
* Server to anything using ZKUtils (though it does not use ZKUtils itself so
it can have different information).
+ * To simulate another ZK-aware class, DummyUser can be used, which will use
ZKUtils for interacting with ZK, including advertising
+ * on the service discovery; it also provides access to its ZKUtils instance.
+ * <p>
+ * To use security, see {@link ZKXTestCaseWithSecurity}.
*/
public abstract class ZKXTestCase extends XTestCase {
private TestingServer zkServer;
private CuratorFramework client = null;
private ServiceDiscovery<Map> sDiscovery = null;
+
/**
* The ZooKeeper ID for "this" Oozie server
*/
@@ -57,8 +63,7 @@ public abstract class ZKXTestCase extend
protected void setUp() throws Exception {
super.setUp();
new Services().init();
- // Start the ZooKeeper server and set Oozie ZK properties
- zkServer = new TestingServer();
+ zkServer = setupZKServer();
Services.get().getConf().set("oozie.zookeeper.connection.string",
zkServer.getConnectString());
setSystemProperty("oozie.instance.id", ZK_ID);
createClient();
@@ -78,6 +83,16 @@ public abstract class ZKXTestCase extend
}
/**
+ * Creates and sets up the embedded ZooKeeper server. Test subclasses
should have no reason to override this method.
+ *
+ * @return the embedded ZooKeeper server
+ * @throws Exception
+ */
+ protected TestingServer setupZKServer() throws Exception {
+ return new TestingServer();
+ }
+
+ /**
* Returns the connection string for ZooKeeper.
*
* @return the conection string for ZooKeeeper
@@ -209,5 +224,47 @@ public abstract class ZKXTestCase extend
.build();
}
}
+
+ /**
+ * Provides a class that can can register/unregister with the ZKUtils. It
also provides access to the ZKUtils object. This is
+ * useful for testing features of the of ZKUtils class. It will register
when {@link DummyUser#register()} is called. Make
+ * sure to call {@link DummyUser#unregister()} when done using it.
+ */
+ protected class DummyUser {
+
+ public DummyUser() {
+ }
+ private ZKUtils zk = null;
+
+ /**
+ * Registers with ZKUtils.
+ *
+ * @throws Exception
+ */
+ public void register() throws Exception {
+ zk = ZKUtils.register(this);
+ sleep(1000); // Sleep to allow ZKUtils ServiceCache to update
+ }
+
+ /**
+ * Unregisters with ZKUtils.
+ */
+ public void unregister() {
+ if (zk != null) {
+ zk.unregister(this);
+ sleep(1000); // Sleep to allow ZKUtils ServiceCache to
update
+ }
+ zk = null;
+ }
+
+ /**
+ * Accessor for the ZKUtils object used by this class.
+ *
+ * @return The ZKUtils object
+ */
+ public ZKUtils getZKUtils() {
+ return zk;
+ }
+ }
}
Added:
oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java?rev=1547259&view=auto
==============================================================================
---
oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java
(added)
+++
oozie/trunk/core/src/test/java/org/apache/oozie/test/ZKXTestCaseWithSecurity.java
Tue Dec 3 01:41:59 2013
@@ -0,0 +1,133 @@
+/**
+ * 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.oozie.test;
+
+import java.io.File;
+import javax.security.auth.login.Configuration;
+
+import org.apache.curator.test.TestingServer;
+import org.apache.hadoop.minikdc.MiniKdc;
+import org.apache.oozie.service.HadoopAccessorService;
+import org.apache.oozie.service.Services;
+import org.apache.oozie.util.JaasConfiguration;
+import org.apache.zookeeper.server.ZooKeeperSaslServer;
+
+/**
+ * Provides a version of {@link ZKXTestCase} with security. A MiniKdc will be
started (so no special outside setup is needed) and
+ * the embedded ZooKeeper provided by this class will support connecting to it
with SASL/Kerberos authentication. However,
+ * currently, the client returned by {@link #getClient()) and the client used
by DummyZKOozie do not authenticate, so they won't
+ * have full access to any znodes with "sasl" ACLs (this is not always true,
see {@link #setupZKServer()).
+ * <p>
+ * Anything using {@link ZKUtils} can connect using authentication by simply
setting "oozie.zookeeper.secure" to "true" before
+ * creating the first thing that uses ZKUtils. Make sure to set it back to
false when done.
+ */
+public abstract class ZKXTestCaseWithSecurity extends ZKXTestCase {
+ private MiniKdc kdc = null;
+ private File keytabFile;
+ private String originalKeytabLoc;
+ private String originalPrincipal;
+
+ /**
+ * The primary part of the principal name for the Kerberos user
+ */
+ protected static final String PRIMARY_PRINCIPAL = "oozie";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Set the keytab location and principal to the miniKDC values
+ originalKeytabLoc =
Services.get().getConf().get(HadoopAccessorService.KERBEROS_KEYTAB);
+ originalPrincipal =
Services.get().getConf().get(HadoopAccessorService.KERBEROS_PRINCIPAL);
+ Services.get().getConf().set(HadoopAccessorService.KERBEROS_KEYTAB,
keytabFile.getAbsolutePath());
+ Services.get().getConf().set(HadoopAccessorService.KERBEROS_PRINCIPAL,
getPrincipal());
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ // Restore these values
+ Services.get().getConf().set(HadoopAccessorService.KERBEROS_KEYTAB,
originalKeytabLoc);
+ Services.get().getConf().set(HadoopAccessorService.KERBEROS_PRINCIPAL,
originalPrincipal);
+ // Just in case the test forgets to set this back
+ Services.get().getConf().set("oozie.zookeeper.secure", "false");
+ super.tearDown();
+ if (kdc != null) {
+ kdc.stop();
+ }
+ }
+
+ /**
+ * Creates and sets up the embedded ZooKeeper server. Test subclasses
should have no reason to override this method.
+ * <p>
+ * Here we override it to start the MiniKdc, set the jaas configuration,
configure ZooKeeper for SASL/Kerberos authentication
+ * and ACLs, and to start the ZooKeeper server.
+ * <p>
+ * Unfortunately, ZooKeeper security requires setting the security for the
entire JVM. And for the tests, we're running the
+ * ZK server and one or more clients from the same JVM, so things get
messy. There are two ways to tell ZooKeeper to
+ * authenticate: (1) set the system property,
"java.security.auth.login.config", to a jaas.conf file and (2) create a
+ * javax.security.auth.login.Configuration object with the same info as
the jaas.conf and set it. In either case, once set and
+ * something has authenticated, it seems that it can't be unset or
changed, and there's no way to log out. By setting the
+ * system property, "javax.security.auth.useSubjectCredsOnly", to "false"
we can sort-of change the jaas Configuration, but its
+ * kind of funny about it. Another effect of this is that we have to add
jaas entries for the "Server" and "Client" here
+ * instead of just the "Server" here and the "Client" in the normal place
({@link ZKUtils}) or it will be unable to find the
+ * "Client" info. Also, because there is no way to logout, once any
client has authenticated once, all subsequent clients will
+ * automatically connect using the same authentication; trying to stop
this is futile and either results in an error or has no
+ * effect. This means that there's no way to do any tests with an
unauthenticated client. Also, if any tests using secure
+ * ZooKeeper get run before tests not using secure ZooKeeper, they will
likely fail because it will try to use authentication:
+ * so they should be run separately.
+ *
+ * @return the embedded ZooKeeper server
+ * @throws Exception
+ */
+ @Override
+ protected TestingServer setupZKServer() throws Exception {
+ // Not entirely sure exactly what
"javax.security.auth.useSubjectCredsOnly=false" does, but it has something to
do with
+ // re-authenticating in cases where it otherwise wouldn't. One of the
sections on this page briefly mentions it:
+ //
http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/tutorials/Troubleshooting.html
+ setSystemProperty("javax.security.auth.useSubjectCredsOnly", "false");
+
+ // Setup KDC and principal
+ kdc = new MiniKdc(MiniKdc.createConf(), new File(getTestCaseDir()));
+ kdc.start();
+ keytabFile = new File(getTestCaseDir(), "test.keytab");
+ String serverPrincipal = "zookeeper/" + kdc.getHost();
+ kdc.createPrincipal(keytabFile, getPrincipal(), serverPrincipal);
+
+ setSystemProperty("zookeeper.authProvider.1",
"org.apache.zookeeper.server.auth.SASLAuthenticationProvider");
+ setSystemProperty("zookeeper.kerberos.removeHostFromPrincipal",
"true");
+ setSystemProperty("zookeeper.kerberos.removeRealmFromPrincipal",
"true");
+
+ JaasConfiguration.addEntry("Server", serverPrincipal,
keytabFile.getAbsolutePath());
+ // Here's where we add the "Client" to the jaas configuration, even
though we'd like not to
+ JaasConfiguration.addEntry("Client", getPrincipal(),
keytabFile.getAbsolutePath());
+ Configuration.setConfiguration(JaasConfiguration.getInstance());
+
+ setSystemProperty(ZooKeeperSaslServer.LOGIN_CONTEXT_NAME_KEY,
"Server");
+
+ return new TestingServer();
+ }
+
+ /**
+ * Returns the principal of the Kerberos user. This would be {@link
#PRIMARY_PRINCIPAL}/_host_
+ *
+ * @return the principal of the Kerberos user
+ */
+ protected String getPrincipal() {
+ return PRIMARY_PRINCIPAL + "/" + kdc.getHost();
+ }
+}
+
Added:
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestJaasConfiguration.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/util/TestJaasConfiguration.java?rev=1547259&view=auto
==============================================================================
---
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestJaasConfiguration.java
(added)
+++
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestJaasConfiguration.java
Tue Dec 3 01:41:59 2013
@@ -0,0 +1,81 @@
+/**
+ * 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.oozie.util;
+
+import java.util.Map;
+import javax.security.auth.login.AppConfigurationEntry;
+import org.apache.oozie.test.XTestCase;
+
+public class TestJaasConfiguration extends XTestCase {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ // We won't test actually using it to authenticate because that gets messy
and may conflict with other tests; but we can test
+ // that it otherwise behaves correctly
+ public void test() throws Exception {
+ String krb5LoginModuleName;
+ if (System.getProperty("java.vendor").contains("IBM")) {
+ krb5LoginModuleName =
"com.ibm.security.auth.module.Krb5LoginModule";
+ }
+ else {
+ krb5LoginModuleName =
"com.sun.security.auth.module.Krb5LoginModule";
+ }
+
+ JaasConfiguration.clearEntries();
+ assertTrue(JaasConfiguration.getEntries().isEmpty());
+
+ JaasConfiguration.addEntry("foo", "foo/localhost",
"/some/location/foo");
+ assertEquals(1, JaasConfiguration.getEntries().size());
+ JaasConfiguration.addEntry("bar", "bar/localhost",
"/some/location/bar");
+ assertEquals(2, JaasConfiguration.getEntries().size());
+ JaasConfiguration.addEntry("zoo", "zoo/localhost",
"/some/location/zoo");
+ assertEquals(3, JaasConfiguration.getEntries().size());
+ checkEntry(krb5LoginModuleName, "foo", "foo/localhost",
"/some/location/foo");
+ checkEntry(krb5LoginModuleName, "bar", "bar/localhost",
"/some/location/bar");
+ checkEntry(krb5LoginModuleName, "zoo", "zoo/localhost",
"/some/location/zoo");
+
+ JaasConfiguration.removeEntry("bar");
+ assertEquals(2, JaasConfiguration.getEntries().size());
+ checkEntry(krb5LoginModuleName, "foo", "foo/localhost",
"/some/location/foo");
+ checkEntry(krb5LoginModuleName, "zoo", "zoo/localhost",
"/some/location/zoo");
+
+ JaasConfiguration.clearEntries();
+ assertTrue(JaasConfiguration.getEntries().isEmpty());
+ }
+
+ private void checkEntry(String loginModuleName, String name, String
principal, String keytab) {
+ AppConfigurationEntry entry = JaasConfiguration.getEntries().get(name);
+ assertEquals(loginModuleName, entry.getLoginModuleName());
+ assertEquals(AppConfigurationEntry.LoginModuleControlFlag.REQUIRED,
entry.getControlFlag());
+ Map<String, ?> options = entry.getOptions();
+ assertEquals(keytab, options.get("keyTab"));
+ assertEquals(principal, options.get("principal"));
+ assertEquals("true", options.get("useKeyTab"));
+ assertEquals("true", options.get("storeKey"));
+ assertEquals("false", options.get("useTicketCache"));
+ assertEquals(5, options.size());
+ }
+}
Modified: oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtils.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtils.java?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtils.java
(original)
+++ oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtils.java Tue
Dec 3 01:41:59 2013
@@ -39,26 +39,6 @@ public class TestZKUtils extends ZKXTest
super.tearDown();
}
- private class DummyUser {
- private ZKUtils zk = null;
- void register() throws Exception {
- zk = ZKUtils.register(this);
- sleep(1000); // Sleep to allow ZKUtils ServiceCache to update
- }
-
- void unregister() {
- if (zk != null) {
- zk.unregister(this);
- sleep(1000); // Sleep to allow ZKUtils ServiceCache to
update
- }
- zk = null;
- }
-
- ZKUtils getZKUtils() {
- return zk;
- }
- }
-
public void testRegisterAdvertiseUnadvertiseUnregister() throws Exception {
CuratorFramework client = getClient();
ServiceDiscovery<Map> sDiscovery = getServiceDiscovery();
Added:
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java
URL:
http://svn.apache.org/viewvc/oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java?rev=1547259&view=auto
==============================================================================
---
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java
(added)
+++
oozie/trunk/core/src/test/java/org/apache/oozie/util/TestZKUtilsWithSecurity.java
Tue Dec 3 01:41:59 2013
@@ -0,0 +1,187 @@
+/**
+ * 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.oozie.util;
+
+import java.util.List;
+import static junit.framework.Assert.assertEquals;
+
+import org.apache.oozie.lock.LockToken;
+import org.apache.oozie.service.Services;
+import org.apache.oozie.service.ZKLocksService;
+import org.apache.oozie.test.ZKXTestCaseWithSecurity;
+import org.apache.zookeeper.ZooDefs;
+import org.apache.zookeeper.data.ACL;
+import org.apache.zookeeper.data.Stat;
+
+public class TestZKUtilsWithSecurity extends ZKXTestCaseWithSecurity {
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testCheckAndSetACLs() throws Exception {
+ // We want to verify the ACLs on locks and the service discovery;
ZKUtils does the service discovery and starting
+ // ZKLocksService will use ZKUtils which will start advertising on the
service discovery. We can also acquire a lock so
+ // it will create a lock znode.
+ ZKLocksService zkls = new ZKLocksService();
+ try {
+ zkls.init(Services.get());
+ LockToken lock = zkls.getWriteLock("foo", 3);
+ lock.release();
+ List<ACL> acls =
getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat());
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks/foo");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers/" + ZK_ID);
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ }
+ finally {
+ // unregistering all users of ZKUtils (i.e. ZKLocksService) will
cause it to disconnect so when we set
+ // "oozie.zookeeper.secure" to true, it will again connect but
using SASL/Kerberos
+ zkls.destroy();
+ }
+
+ // Verify that the expected paths created above still exist with the
"world" ACLs
+ List<ACL> acls =
getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat());
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks/foo");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("world", acls.get(0).getId().getScheme());
+ assertEquals("anyone", acls.get(0).getId().getId());
+
+ zkls = new ZKLocksService();
+ try {
+ Services.get().getConf().set("oozie.zookeeper.secure", "true");
+ // Now that security is enabled, it will trigger the
checkAndSetACLs() code to go through and set all of the previously
+ // created znodes to have "sasl" ACLs
+ zkls.init(Services.get());
+ acls =
getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat());
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks/foo");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers/" + ZK_ID);
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ }
+ finally {
+ zkls.destroy();
+ Services.get().getConf().set("oozie.zookeeper.secure", "false");
+ }
+ }
+
+ public void testNewUsingACLs() throws Exception {
+ // We want to verify the ACLs on new locks and the service discovery;
ZKUtils does the service discovery and starting
+ // ZKLocksService will use ZKUtils which will start advertising on the
service discovery. We can also acquire a lock so
+ // it will create a lock znode.
+ ZKLocksService zkls = new ZKLocksService();
+ try {
+ Services.get().getConf().set("oozie.zookeeper.secure", "true");
+ // Verify that the znodes don't already exist
+
assertNull(getClient().getZookeeperClient().getZooKeeper().exists("/oozie",
null));
+ assertNull(getClient().checkExists().forPath("/locks"));
+ assertNull(getClient().checkExists().forPath("/services"));
+ // Check that new znodes will use the ACLs
+ zkls.init(Services.get());
+ LockToken lock = zkls.getWriteLock("foo", 3);
+ lock.release();
+ List<ACL> acls =
getClient().getZookeeperClient().getZooKeeper().getACL("/oozie", new Stat());
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/locks/foo");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers");
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ acls = getClient().getACL().forPath("/services/servers/" + ZK_ID);
+ assertEquals(ZooDefs.Perms.ALL, acls.get(0).getPerms());
+ assertEquals("sasl", acls.get(0).getId().getScheme());
+ assertEquals(PRIMARY_PRINCIPAL, acls.get(0).getId().getId());
+ }
+ finally {
+ zkls.destroy();
+ Services.get().getConf().set("oozie.zookeeper.secure", "false");
+ }
+ }
+}
Modified: oozie/trunk/docs/src/site/twiki/AG_Install.twiki
URL:
http://svn.apache.org/viewvc/oozie/trunk/docs/src/site/twiki/AG_Install.twiki?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/docs/src/site/twiki/AG_Install.twiki (original)
+++ oozie/trunk/docs/src/site/twiki/AG_Install.twiki Tue Dec 3 01:41:59 2013
@@ -790,9 +790,37 @@ be missing information until that server
---++++ Security
-Oozie HA works with the existing Oozie security framework and settings. There
are no additional security settings specific to HA;
-however, for log streaming to work properly in a secure setup,
=oozie.authentication.type= must be set properly on each server
-(though this is already required if using security in the first place).
+Oozie HA works with the existing Oozie security framework and settings. For
log streaming to work properly in a secure
+setup =oozie.authentication.type= must be set properly on each server (though
this is already required if using security in the
+first place).
+
+(Optional) To prevent unauthorized users or programs from interacting with or
reading the znodes used by Oozie in ZooKeeper,
+you can tell Oozie to use Kerberos-backed ACLs. To enforce this for all of
the Oozie-related znodes, simply add the following
+property to oozie-site.xml in all Oozie servers and set it to =true=. The
default is =false=.
+
+<verbatim>
+<property>
+ <name>oozie.zookeeper.secure</name>
+ <value>true</value>
+</property>
+</verbatim>
+
+Note: The Kerberos principals of each of the Oozie servers should have the
same primary name (i.e. in =primary/instance@REALM=, each
+server should have the same value for =primary=).
+
+*Important:* Once this property is set to =true=, it will set the ACLs on all
existing Oozie-related znodes to only allow Kerberos
+authenticated users with a principal that has the same primary as described
above (also for any subsequently created new znodes).
+This means that if you ever want to turn this feature off, you will have to
manually connect to ZooKeeper using a Kerberos principal
+with the same primary and either delete all znodes under and including the
namespace (i.e. if =oozie.zookeeper.namespace= = =oozie=
+then that would be =/oozie=); alternatively, instead of deleting them all, you
can manually set all of their ACLs to =world:anyone=.
+In either case, make sure that no Oozie servers are running while this is
being done.
+
+Also, in your zoo.cfg for ZooKeeper, make sure to set the following properties:
+<verbatim>
+authProvider.1=org.apache.zookeeper.server.auth.SASLAuthenticationProvider
+kerberos.removeHostFromPrincipal=true
+kerberos.removeRealmFromPrincipal=true
+</verbatim>
---++ Starting and Stopping Oozie
Modified: oozie/trunk/pom.xml
URL:
http://svn.apache.org/viewvc/oozie/trunk/pom.xml?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/pom.xml (original)
+++ oozie/trunk/pom.xml Tue Dec 3 01:41:59 2013
@@ -248,6 +248,13 @@
<type>war</type>
</dependency>
+ <dependency>
+ <groupId>org.apache.hadoop</groupId>
+ <artifactId>hadoop-minikdc</artifactId>
+ <!-- TODO: Replace version once MiniKdc is in a released
version of Hadoop -->
+ <version>2.3.0-SNAPSHOT</version>
+ </dependency>
+
<!-- client -->
<dependency>
<groupId>org.apache.hadoop</groupId>
@@ -862,6 +869,11 @@
<artifactId>openjpa-maven-plugin</artifactId>
<version>${openjpa.version}</version>
</plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <version>2.4.0</version>
+ </plugin>
</plugins>
</pluginManagement>
@@ -982,6 +994,11 @@
Requires at least Hadoop 1.2.0 or 2.2.0.
-->
<exclude>**/TestMapReduceActionExecutorUberJar.java</exclude>
+
+ <!-- Explictly use -Dtest=TestZKUtilsWithSecurity to
test the ZKUtils with security.
+ It can conflict with other non-secure tests that
use zookeeper
+ -->
+ <exclude>**/TestZKUtilsWithSecurity.java</exclude>
</excludes>
<!-- DO NOT CHANGE THIS VALUES, TESTCASES CANNOT RUN IN
PARALLEL -->
<parallel>classes</parallel>
Modified: oozie/trunk/release-log.txt
URL:
http://svn.apache.org/viewvc/oozie/trunk/release-log.txt?rev=1547259&r1=1547258&r2=1547259&view=diff
==============================================================================
--- oozie/trunk/release-log.txt (original)
+++ oozie/trunk/release-log.txt Tue Dec 3 01:41:59 2013
@@ -1,5 +1,6 @@
-- Oozie 4.1.0 release (trunk - unreleased)
+OOZIE-1491 Make sure HA works with a secure ZooKeeper (rkanter)
OOZIE-1615 shell action cannot find script file and fails in uber mode (ryota)
OOZIE-1605 Add common custom filter applied to Wf/Coord/Bundle jobs on oozie
UI (ryota)
OOZIE-1474 Fix logging issues - latency, accurate job ids, coord Job UI to
show job logs (mona)