You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by wu...@apache.org on 2022/11/04 03:14:57 UTC
[ambari] branch trunk updated: AMBARI-25332: Kerberos keytab regeneration working slow (#3436)
This is an automated email from the ASF dual-hosted git repository.
wuzhiguo pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/ambari.git
The following commit(s) were added to refs/heads/trunk by this push:
new 309a61def9 AMBARI-25332: Kerberos keytab regeneration working slow (#3436)
309a61def9 is described below
commit 309a61def98059b50d61efe6a9464053883820ce
Author: Zhiguo Wu <wu...@apache.org>
AuthorDate: Fri Nov 4 11:14:49 2022 +0800
AMBARI-25332: Kerberos keytab regeneration working slow (#3436)
---
.../ambari/server/configuration/Configuration.java | 21 ++-
.../ambari/server/controller/KerberosHelper.java | 60 ++++---
.../server/controller/KerberosHelperImpl.java | 83 +++++++--
.../HostKerberosIdentityResourceProvider.java | 11 +-
.../ambari/server/orm/dao/KerberosKeytabDAO.java | 14 +-
.../server/orm/dao/KerberosKeytabPrincipalDAO.java | 22 ++-
.../server/orm/dao/KerberosPrincipalDAO.java | 6 +-
.../server/orm/entities/KerberosKeytabEntity.java | 6 +-
.../entities/KerberosKeytabPrincipalEntity.java | 27 ++-
.../ConfigureAmbariIdentitiesServerAction.java | 37 ++--
.../kerberos/CreateKeytabFilesServerAction.java | 190 +++++++++++----------
.../kerberos/CreatePrincipalsServerAction.java | 63 ++++---
.../kerberos/FinalizeKerberosServerAction.java | 17 +-
.../kerberos/KerberosServerAction.java | 65 ++++++-
.../stageutils/KerberosKeytabController.java | 40 ++++-
.../server/controller/KerberosHelperTest.java | 18 +-
.../HostKerberosIdentityResourceProviderTest.java | 18 +-
.../ConfigureAmbariIdentitiesServerActionTest.java | 13 +-
18 files changed, 524 insertions(+), 187 deletions(-)
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
index e7dc5a62de..c6e8404d9f 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/configuration/Configuration.java
@@ -2611,7 +2611,14 @@ public class Configuration {
*/
@Markdown(description = "The timeout, in seconds, when finalizing Kerberos enable/disable/regenerate commands.")
public static final ConfigurationProperty<Integer> KERBEROS_SERVER_ACTION_FINALIZE_SECONDS = new ConfigurationProperty<>(
- "server.kerberos.finalize.timeout", 600);
+ "server.kerberos.finalize.timeout", 600);
+
+ /**
+ * The number of threads to use when executing server-side Kerberos commands, such as generate keytabs.
+ */
+ @Markdown(description = "The number of threads to use when executing server-side Kerberos commands, such as generate keytabs.")
+ public static final ConfigurationProperty<Integer> KERBEROS_SERVER_ACTION_THREADPOOL_SIZE = new ConfigurationProperty<>(
+ "server.kerberos.action.threadpool.size", 1);
/**
* Fully qualified class name of the strategy used to form host groups for add service request layout recommendation.
@@ -3041,7 +3048,7 @@ public class Configuration {
writeConfigFile(existingProperties, false);
// reloading properties
- this.properties = readConfigFile();
+ properties = readConfigFile();
}
/**
@@ -5562,6 +5569,16 @@ public class Configuration {
return Integer.parseInt(getProperty(DEFAULT_MAX_DEGREE_OF_PARALLELISM_FOR_UPGRADES));
}
+ /**
+ * Gets the number of threads to use when executing server-side Kerberos
+ * commands, such as generate keytabs.
+ *
+ * @return the threadpool size, defaulting to 1
+ */
+ public int getKerberosServerActionThreadpoolSize() {
+ return Integer.parseInt(getProperty(KERBEROS_SERVER_ACTION_THREADPOOL_SIZE));
+ }
+
/**
* Get the timeout, in seconds, when finalizing Kerberos
* enable/disable/regenerate commands.
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
index d3c291d7e7..7fe54e6734 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java
@@ -713,32 +713,52 @@ public interface KerberosHelper {
Map<String, Map<String, String>> getIdentityConfigurations(List<KerberosIdentityDescriptor> identityDescriptors);
/**
- * Returns the active identities for the named cluster. Results are filtered by host, service,
- * and/or component; and grouped by host.
+ * Returns the active identities for the named cluster. Results are filtered
+ * by host, service, and/or component; and grouped by host.
* <p/>
- * The cluster name is mandatory; however the active identities may be filtered by one or more of
- * host, service, or component. A <code>null</code> value for any of these filters indicates no
- * filter for that parameter.
+ * The cluster name is mandatory; however the active identities may be
+ * filtered by one or more of host, service, or component. A <code>null</code>
+ * value for any of these filters indicates no filter for that parameter.
* <p/>
- * The return values are grouped by host and optionally <code>_HOST</code> in principals will be
- * replaced with the relevant hostname if specified to do so.
- *
- * @param clusterName the name of the relevant cluster (mandatory)
- * @param hostName the name of a host for which to find results, null indicates all hosts
- * @param serviceName the name of a service for which to find results, null indicates all
- * services
- * @param componentName the name of a component for which to find results, null indicates all
- * components
- * @param replaceHostNames if true, _HOST in principals will be replace with the relevant host
- * name
+ * The return values are grouped by host and optionally <code>_HOST</code> in
+ * principals will be replaced with the relevant hostname if specified to do
+ * so.
+ *
+ * @param clusterName
+ * the name of the relevant cluster (mandatory)
+ * @param hostName
+ * the name of a host for which to find results, null indicates all
+ * hosts
+ * @param serviceName
+ * the name of a service for which to find results, null indicates
+ * all services
+ * @param componentName
+ * the name of a component for which to find results, null indicates
+ * all components
+ * @param replaceHostNames
+ * if true, _HOST in principals will be replace with the relevant
+ * host name
+ * @param hostConfigurations
+ * a mapping of hostname to configurations for that host. Fetching
+ * this ahead of time for every host in the cluster will ensure that
+ * this method doesn't need to do it inside of a loop. If
+ * {@code null} or empty, then this method will do the lookup itself,
+ * at considerable cost.
+ * @param kerberosDescriptor
+ * the kerberos descriptor to use when looking up identities. If
+ * {@code null}, then this method will deserialize the descriptor
+ * inside of a loop at considerable cost.
* @return a map of host names to kerberos identities
- * @throws AmbariException if an error occurs processing the cluster's active identities
+ * @throws AmbariException
+ * if an error occurs processing the cluster's active identities
*/
Map<String, Collection<KerberosIdentityDescriptor>> getActiveIdentities(String clusterName,
String hostName,
String serviceName,
String componentName,
- boolean replaceHostNames)
+ boolean replaceHostNames,
+ Map<String, Map<String, Map<String, String>>> hostConfigurations,
+ KerberosDescriptor kerberosDescriptor)
throws AmbariException;
/**
@@ -823,7 +843,7 @@ public interface KerberosHelper {
* @return a map of configuration types to sets of property names
*/
Map<String, Set<String>> translateConfigurationSpecifications(Collection<String> configurationSpecifications);
-
+
/**
* Gathers the Kerberos-related data from configurations and stores it in a new KerberosDetails
* instance.
@@ -836,7 +856,7 @@ public interface KerberosHelper {
* @throws AmbariException
*/
KerberosDetails getKerberosDetails(Cluster cluster, Boolean manageIdentities)
- throws KerberosInvalidConfigurationException, AmbariException;
+ throws KerberosInvalidConfigurationException, AmbariException;
/**
* Types of Kerberos descriptors related to where the data is stored.
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
index 9473344222..b8b613ba5c 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java
@@ -37,6 +37,7 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;
+import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import org.apache.ambari.annotations.Experimental;
@@ -63,6 +64,7 @@ import org.apache.ambari.server.orm.dao.ArtifactDAO;
import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.dao.KerberosKeytabDAO;
import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO.KeytabPrincipalFindOrCreateResult;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.ArtifactEntity;
import org.apache.ambari.server.orm.entities.HostEntity;
@@ -137,6 +139,7 @@ import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Stopwatch;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
@@ -1739,7 +1742,9 @@ public class KerberosHelperImpl implements KerberosHelper {
String hostName,
String serviceName,
String componentName,
- boolean replaceHostNames)
+ boolean replaceHostNames,
+ Map<String, Map<String, Map<String, String>>> hostConfigurations,
+ KerberosDescriptor kerberosDescriptor)
throws AmbariException {
if ((clusterName == null) || clusterName.isEmpty()) {
@@ -1784,8 +1789,15 @@ public class KerberosHelperImpl implements KerberosHelper {
hosts = Collections.singleton(hostName);
}
+ if (null == hostConfigurations) {
+ hostConfigurations = new HashMap<>();
+ }
+
if (!hosts.isEmpty()) {
- KerberosDescriptor kerberosDescriptor = getKerberosDescriptor(cluster, false);
+
+ if (null == kerberosDescriptor) {
+ kerberosDescriptor = getKerberosDescriptor(cluster, false);
+ }
if (kerberosDescriptor != null) {
Set<String> existingServices = cluster.getServices().keySet();
@@ -1793,12 +1805,17 @@ public class KerberosHelperImpl implements KerberosHelper {
for (String host : hosts) {
// Calculate the current host-specific configurations. These will be used to replace
// variables within the Kerberos descriptor data
- Map<String, Map<String, String>> configurations = calculateConfigurations(cluster,
+ Map<String, Map<String, String>> configurations = hostConfigurations.get(host);
+ if (configurations == null) {
+ configurations = calculateConfigurations(cluster,
(ambariServerHostnameIsForced && ambariServerHostname.equals(host)) ? null : host,
kerberosDescriptor,
false,
false);
+ hostConfigurations.put(host, configurations);
+ }
+
// Create the context to use for filtering Kerberos Identities based on the state of the cluster
Map<String, Object> filterContext = new HashMap<>();
filterContext.put("configurations", configurations);
@@ -1945,15 +1962,27 @@ public class KerberosHelperImpl implements KerberosHelper {
}
/**
- * Creates and saves underlying {@link org.apache.ambari.server.orm.entities.KerberosPrincipalEntity},
- * {@link org.apache.ambari.server.orm.entities.KerberosKeytabEntity} entities in JPA storage.
+ * Creates and saves underlying
+ * {@link org.apache.ambari.server.orm.entities.KerberosPrincipalEntity},
+ * {@link org.apache.ambari.server.orm.entities.KerberosKeytabEntity} entities
+ * in JPA storage.
+ * <p>
+ * This method has to be very, very careful WRT how and when it merges
+ * bidirectional associations.For larger cluster, merging the
+ * {@link KerberosKeytabEntity} and {@link KerberosPrincipalEntity} even when
+ * a {@link KerberosKeytabPrincipalEntity} will result in major performance
+ * problems.
*
- * @param resolvedKerberosKeytab kerberos keytab to be persisted
+ * @param resolvedKerberosKeytab
+ * kerberos keytab to be persisted
*/
@Override
public void createResolvedKeytab(ResolvedKerberosKeytab resolvedKerberosKeytab) {
- if (kerberosKeytabDAO.find(resolvedKerberosKeytab.getFile()) == null) {
- KerberosKeytabEntity kke = new KerberosKeytabEntity(resolvedKerberosKeytab.getFile());
+ Stopwatch stopwatch = Stopwatch.createStarted();
+
+ KerberosKeytabEntity kke = kerberosKeytabDAO.find(resolvedKerberosKeytab.getFile());
+ if (null == kke) {
+ kke = new KerberosKeytabEntity(resolvedKerberosKeytab.getFile());
kke.setAmbariServerKeytab(resolvedKerberosKeytab.isAmbariServerKeytab());
kke.setWriteAmbariJaasFile(resolvedKerberosKeytab.isMustWriteAmbariJaasFile());
kke.setOwnerName(resolvedKerberosKeytab.getOwnerName());
@@ -1962,24 +1991,43 @@ public class KerberosHelperImpl implements KerberosHelper {
kke.setGroupAccess(resolvedKerberosKeytab.getGroupAccess());
kerberosKeytabDAO.create(kke);
}
+
for (ResolvedKerberosPrincipal principal : resolvedKerberosKeytab.getPrincipals()) {
- if (!kerberosPrincipalDAO.exists(principal.getPrincipal())) {
- kerberosPrincipalDAO.create(principal.getPrincipal(), principal.isService());
+ KerberosPrincipalEntity kpe = kerberosPrincipalDAO.find(principal.getPrincipal());
+
+ if (null == kpe) {
+ kpe = kerberosPrincipalDAO.create(principal.getPrincipal(), principal.isService());
}
+
+ // only need to merge the kke and kpe if a new kkp is created/added to their lists
+ boolean mergeBidirectionalAssociatedEntities = false;
for (Map.Entry<String, String> mappingEntry : principal.getServiceMapping().entries()) {
String serviceName = mappingEntry.getKey();
HostEntity hostEntity = principal.getHostId() != null ? hostDAO.findById(principal.getHostId()) : null;
- KerberosKeytabEntity kke = kerberosKeytabDAO.find(resolvedKerberosKeytab.getFile());
- KerberosPrincipalEntity kpe = kerberosPrincipalDAO.find(principal.getPrincipal());
- KerberosKeytabPrincipalEntity kkp = kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe);
+ KeytabPrincipalFindOrCreateResult result = kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe);
+ KerberosKeytabPrincipalEntity kkp = result.kkp;
+ mergeBidirectionalAssociatedEntities = mergeBidirectionalAssociatedEntities || result.created;
+
+ // updating the kkp service mappings does not affect kke/kpe bidirectional relationships
if (kkp.putServiceMapping(serviceName, mappingEntry.getValue())) {
kerberosKeytabPrincipalDAO.merge(kkp);
}
- kerberosKeytabDAO.merge(kke);
- kerberosPrincipalDAO.merge(kpe);
+ }
+
+ // merge the keytab and the principal IFF at least one keytabprincipal was
+ // created causing the bi-directional lists associations to need updating
+ if(mergeBidirectionalAssociatedEntities) {
+ Stopwatch mergeStockwatch = Stopwatch.createStarted();
+ kke = kerberosKeytabDAO.merge(kke);
+ kpe = kerberosPrincipalDAO.merge(kpe);
+ LOG.info("Merging bidirectional associated entities for this keytab took {}ms"
+ + mergeStockwatch.elapsed(TimeUnit.MILLISECONDS));
}
}
+
+ LOG.info("Resolving this keytab and all associated principals took {}ms ",
+ stopwatch.elapsed(TimeUnit.MILLISECONDS));
}
@Override
@@ -2386,7 +2434,10 @@ public class KerberosHelperImpl implements KerberosHelper {
kerberosPrincipalDAO.create(kpe);
}
- KerberosKeytabPrincipalEntity kkp = kerberosKeytabPrincipalDAO.findOrCreate(kke, hostDAO.findById(sch.getHost().getHostId()), kpe);
+ KeytabPrincipalFindOrCreateResult result = kerberosKeytabPrincipalDAO.findOrCreate(
+ kke, hostDAO.findById(sch.getHost().getHostId()), kpe);
+
+ KerberosKeytabPrincipalEntity kkp = result.kkp;
if (kkp.putServiceMapping(sch.getServiceName(), sch.getServiceComponentName())) {
kerberosKeytabPrincipalDAO.merge(kkp);
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
index d90d5bf65c..b4f6def212 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProvider.java
@@ -40,6 +40,9 @@ import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.HostEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
@@ -158,9 +161,15 @@ public class HostKerberosIdentityResourceProvider extends ReadOnlyResourceProvid
String clusterName = (String) propertyMap.get(KERBEROS_IDENTITY_CLUSTER_NAME_PROPERTY_ID);
String hostName = (String) propertyMap.get(KERBEROS_IDENTITY_HOST_NAME_PROPERTY_ID);
+ Clusters clusters = getManagementController().getClusters();
+ Cluster cluster = clusters.getCluster(clusterName);
+
+ KerberosDescriptor kerberosDescriptor = kerberosHelper.getKerberosDescriptor(cluster, false);
+
// Retrieve the active identities for the cluster filtered and grouped by hostname
Map<String, Collection<KerberosIdentityDescriptor>> hostDescriptors =
- kerberosHelper.getActiveIdentities(clusterName, hostName, null, null, true);
+ kerberosHelper.getActiveIdentities(clusterName, hostName, null, null, true, null,
+ kerberosDescriptor);
if (hostDescriptors != null) {
for (Map.Entry<String, Collection<KerberosIdentityDescriptor>> entry : hostDescriptors.entrySet()) {
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
index 60ec754f8f..c999f9b711 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabDAO.java
@@ -21,6 +21,7 @@ package org.apache.ambari.server.orm.dao;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.concurrent.TimeUnit;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
@@ -32,6 +33,7 @@ import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.base.Stopwatch;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
@@ -88,9 +90,15 @@ public class KerberosKeytabDAO {
@RequiresSession
public List<KerberosKeytabEntity> findByPrincipalAndHost(String principalName, Long hostId) {
+ Stopwatch stopwatch = Stopwatch.createStarted();
if(hostId == null) {
- return findByPrincipalAndNullHost(principalName);
+ List<KerberosKeytabEntity> result = findByPrincipalAndNullHost(principalName);
+ LOG.debug("Loading keytabs by principal name took {}ms",
+ stopwatch.elapsed(TimeUnit.MILLISECONDS));
+
+ return result;
}
+
TypedQuery<KerberosKeytabEntity> query = entityManagerProvider.get().
createNamedQuery("KerberosKeytabEntity.findByPrincipalAndHost", KerberosKeytabEntity.class);
query.setParameter("hostId", hostId);
@@ -99,6 +107,10 @@ public class KerberosKeytabDAO {
if(result == null) {
return Collections.emptyList();
}
+
+ LOG.debug("Loading keytabs by principal name and host took {}ms",
+ stopwatch.elapsed(TimeUnit.MILLISECONDS));
+
return result;
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
index 7a44f2cd6f..bb8ac6cfa3 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosKeytabPrincipalDAO.java
@@ -74,20 +74,28 @@ public class KerberosKeytabPrincipalDAO {
* @param kerberosPrincipalEntity {@link KerberosPrincipalEntity} which related to this principal
* @return evaluated entity
*/
- public KerberosKeytabPrincipalEntity findOrCreate(KerberosKeytabEntity kerberosKeytabEntity, HostEntity hostEntity, KerberosPrincipalEntity kerberosPrincipalEntity) {
+ public KeytabPrincipalFindOrCreateResult findOrCreate(KerberosKeytabEntity kerberosKeytabEntity, HostEntity hostEntity, KerberosPrincipalEntity kerberosPrincipalEntity) {
+ KeytabPrincipalFindOrCreateResult result = new KeytabPrincipalFindOrCreateResult();
+ result.created = false;
+
Long hostId = hostEntity == null ? null : hostEntity.getHostId();
KerberosKeytabPrincipalEntity kkp = findByNaturalKey(hostId, kerberosKeytabEntity.getKeytabPath(), kerberosPrincipalEntity.getPrincipalName());
if (kkp == null) {
+ result.created = true;
+
kkp = new KerberosKeytabPrincipalEntity(
kerberosKeytabEntity,
hostEntity,
kerberosPrincipalEntity
);
create(kkp);
+
kerberosKeytabEntity.addKerberosKeytabPrincipal(kkp);
kerberosPrincipalEntity.addKerberosKeytabPrincipal(kkp);
}
- return kkp;
+
+ result.kkp = kkp;
+ return result;
}
@Transactional
@@ -345,4 +353,14 @@ public class KerberosKeytabPrincipalDAO {
principalNames);
}
}
+
+ /**
+ * Used to return a keytab principal and whether or not it was created. This
+ * is required so that callers know whether associated keytabs and principals
+ * need bi-direcitonal merges.
+ */
+ public static class KeytabPrincipalFindOrCreateResult {
+ public KerberosKeytabPrincipalEntity kkp;
+ public boolean created;
+ }
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
index 8774ca8911..5db85909d4 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/KerberosPrincipalDAO.java
@@ -69,8 +69,10 @@ public class KerberosPrincipalDAO {
* @param service a boolean value declaring whether the principal represents a service (true) or not )false).
*/
@Transactional
- public void create(String principalName, boolean service) {
- create(new KerberosPrincipalEntity(principalName, service, null));
+ public KerberosPrincipalEntity create(String principalName, boolean service) {
+ KerberosPrincipalEntity kpe = new KerberosPrincipalEntity(principalName, service, null);
+ create(kpe);
+ return kpe;
}
/**
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
index 4fb4f96d36..d9a60ba038 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabEntity.java
@@ -29,6 +29,7 @@ import javax.persistence.Id;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
+import javax.persistence.QueryHint;
import javax.persistence.Table;
@Entity
@@ -37,7 +38,10 @@ import javax.persistence.Table;
@NamedQuery(name = "KerberosKeytabEntity.findAll", query = "SELECT kk FROM KerberosKeytabEntity kk"),
@NamedQuery(
name = "KerberosKeytabEntity.findByPrincipalAndHost",
- query = "SELECT kk FROM KerberosKeytabEntity kk JOIN kk.kerberosKeytabPrincipalEntities kkp WHERE kkp.hostId=:hostId AND kkp.principalName=:principalName"
+ query = "SELECT kk FROM KerberosKeytabEntity kk, KerberosKeytabPrincipalEntity kkp WHERE kkp.hostId=:hostId AND kkp.principalName=:principalName AND kkp.keytabPath = kk.keytabPath",
+ hints = {
+ @QueryHint(name = "eclipselink.query-results-cache", value = "true"),
+ @QueryHint(name = "eclipselink.query-results-cache.size", value = "500") }
),
@NamedQuery(
name = "KerberosKeytabEntity.findByPrincipalAndNullHost",
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
index 676b3e69e9..49bf42b617 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/KerberosKeytabPrincipalEntity.java
@@ -24,6 +24,7 @@ import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
+import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@@ -32,9 +33,12 @@ import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
+import javax.persistence.QueryHint;
import javax.persistence.Table;
import javax.persistence.TableGenerator;
+import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
+
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
@@ -71,6 +75,13 @@ import com.google.common.collect.Multimap;
name = "KerberosKeytabPrincipalEntity.findByHost",
query = "SELECT kkpe FROM KerberosKeytabPrincipalEntity kkpe WHERE kkpe.hostId=:hostId"
),
+ @NamedQuery(
+ name = "KerberosKeytabPrincipalEntity.findByPrincipalAndHost",
+ query = "SELECT kkpe FROM KerberosKeytabPrincipalEntity kkpe WHERE kkpe.hostId=:hostId AND kkpe.principalName=:principalName",
+ hints = {
+ @QueryHint(name = "eclipselink.query-results-cache", value = "true"),
+ @QueryHint(name = "eclipselink.query-results-cache.size", value = "500") }
+ ),
@NamedQuery(
name = "KerberosKeytabPrincipalEntity.findByHostKeytabAndPrincipal",
query = "SELECT kkpe FROM KerberosKeytabPrincipalEntity kkpe WHERE kkpe.hostId=:hostId AND kkpe.keytabPath=:keytabPath AND kkpe.principalName=:principalName"
@@ -98,7 +109,12 @@ public class KerberosKeytabPrincipalEntity {
@Column(name = "is_distributed", nullable = false)
private Integer isDistributed = 0;
- @ManyToOne
+ /**
+ * The assocaited keytab entity must be fetched {@link FetchType#EAGER} to
+ * ensure that it can be discovered when only the principal and host are
+ * known.
+ */
+ @ManyToOne(fetch = FetchType.EAGER)
@JoinColumn(name = "keytab_path", referencedColumnName = "keytab_path", updatable = false, nullable = false, insertable = false)
private KerberosKeytabEntity kerberosKeytabEntity;
@@ -110,7 +126,12 @@ public class KerberosKeytabPrincipalEntity {
@JoinColumn(name = "principal_name", referencedColumnName = "principal_name", updatable = false, nullable = false, insertable = false)
private KerberosPrincipalEntity kerberosPrincipalEntity;
- @OneToMany(cascade = CascadeType.ALL, mappedBy = "kerberosKeytabPrincipalEntity", orphanRemoval = true)
+ /**
+ * Service mappings must be fetched {@link FetchType#EAGER} since they are
+ * required when processing {@link KerberosKeytabPrincipalEntity} instances
+ * and turning them into {@link ResolvedKerberosPrincipal}.
+ */
+ @OneToMany(cascade = CascadeType.ALL, mappedBy = "kerberosKeytabPrincipalEntity", orphanRemoval = true, fetch = FetchType.EAGER)
private List<KerberosKeytabServiceMappingEntity> serviceMapping = new ArrayList<>();
public KerberosKeytabPrincipalEntity() {
@@ -144,7 +165,7 @@ public class KerberosKeytabPrincipalEntity {
}
public void setKerberosKeytabEntity(KerberosKeytabEntity kke) {
- this.kerberosKeytabEntity = kke;
+ kerberosKeytabEntity = kke;
if (kke != null) {
keytabPath = kke.getKeytabPath();
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
index a2ddf1239f..1b4f2069cc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerAction.java
@@ -23,6 +23,7 @@ import java.io.IOException;
import java.nio.charset.Charset;
import java.util.Map;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.agent.CommandReport;
@@ -46,6 +47,7 @@ import org.apache.commons.io.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Striped;
import com.google.inject.Inject;
/**
@@ -78,6 +80,11 @@ public class ConfigureAmbariIdentitiesServerAction extends KerberosServerAction
@Inject
private HostDAO hostDAO;
+ /**
+ * Used to prevent multiple threads from working with the same keytab.
+ */
+ private Striped<Lock> m_locksByKeytab = Striped.lazyWeakLock(25);
+
/**
* Called to execute this action. Upon invocation, calls
* {@link KerberosServerAction#processIdentities(Map)} )}
@@ -136,18 +143,24 @@ public class ConfigureAmbariIdentitiesServerAction extends KerberosServerAction
File hostDirectory = new File(dataDirectory, hostName);
File srcKeytabFile = new File(hostDirectory, DigestUtils.sha256Hex(destKeytabFilePath));
- if (srcKeytabFile.exists()) {
- String ownerAccess = keytab.getOwnerAccess();
- String groupAccess = keytab.getGroupAccess();
-
- installAmbariServerIdentity(resolvedPrincipal, srcKeytabFile.getAbsolutePath(), destKeytabFilePath,
- keytab.getOwnerName(), ownerAccess,
- keytab.getGroupName(), groupAccess, actionLog);
-
- if (serviceMappingEntry.getValue().contains("AMBARI_SERVER_SELF")) {
- // Create/update the JAASFile...
- configureJAAS(resolvedPrincipal.getPrincipal(), destKeytabFilePath, actionLog);
+ Lock lock = m_locksByKeytab.get(destKeytabFilePath);
+ lock.lock();
+ try {
+ if (srcKeytabFile.exists()) {
+ String ownerAccess = keytab.getOwnerAccess();
+ String groupAccess = keytab.getGroupAccess();
+
+ installAmbariServerIdentity(resolvedPrincipal, srcKeytabFile.getAbsolutePath(), destKeytabFilePath,
+ keytab.getOwnerName(), ownerAccess,
+ keytab.getGroupName(), groupAccess, actionLog);
+
+ if (serviceMappingEntry.getValue().contains("AMBARI_SERVER_SELF")) {
+ // Create/update the JAASFile...
+ configureJAAS(resolvedPrincipal.getPrincipal(), destKeytabFilePath, actionLog);
+ }
}
+ } finally {
+ lock.unlock();
}
}
}
@@ -215,7 +228,7 @@ public class ConfigureAmbariIdentitiesServerAction extends KerberosServerAction
for(Map.Entry<String, String> mapping : principal.getServiceMapping().entries()) {
String serviceName = mapping.getKey();
String componentName = mapping.getValue();
- KerberosKeytabPrincipalEntity entity = kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe);
+ KerberosKeytabPrincipalEntity entity = kerberosKeytabPrincipalDAO.findOrCreate(kke, hostEntity, kpe).kkp;
entity.setDistributed(true);
entity.putServiceMapping(serviceName, componentName);
kerberosKeytabPrincipalDAO.merge(entity);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
index 02f7616689..7d73dc81f2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreateKeytabFilesServerAction.java
@@ -20,11 +20,12 @@ package org.apache.ambari.server.serveraction.kerberos;
import java.io.File;
import java.io.IOException;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
@@ -32,7 +33,6 @@ import org.apache.ambari.server.agent.CommandReport;
import org.apache.ambari.server.audit.event.kerberos.CreateKeyTabKerberosAuditEvent;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.KerberosHelper;
-import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
import org.apache.ambari.server.serveraction.ActionLog;
@@ -44,6 +44,7 @@ import org.apache.directory.server.kerberos.shared.keytab.Keytab;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Striped;
import com.google.inject.Inject;
/**
@@ -71,20 +72,19 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
@Inject
private Configuration configuration;
- /**
- * HostDAO used to retrieveHost Entity object
- */
- @Inject
- private HostDAO hostDAO;
-
@Inject
private KerberosKeytabController kerberosKeytabController;
+ /**
+ * Used to prevent multiple threads from working with the same keytab.
+ */
+ private Striped<Lock> m_locksByKeytab = Striped.lazyWeakLock(25);
+
/**
* A map of data used to track what has been processed in order to optimize the creation of keytabs
* such as knowing when to create a cached keytab file or use a cached keytab file.
*/
- Map<String, Set<String>> visitedIdentities = new HashMap<>();
+ Map<String, Set<String>> visitedIdentities = new ConcurrentHashMap<>();
/**
* Called to execute this action. Upon invocation, calls
@@ -175,114 +175,122 @@ public class CreateKeytabFilesServerAction extends KerberosServerAction {
} else {
Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext);
+
for (ResolvedKerberosKeytab rkk : keytabsToCreate) {
String hostName = resolvedPrincipal.getHostName();
String keytabFilePath = rkk.getFile();
if ((hostName != null) && !hostName.isEmpty() && (keytabFilePath != null) && !keytabFilePath.isEmpty()) {
- Set<String> visitedPrincipalKeys = visitedIdentities.get(resolvedPrincipal.getPrincipal());
- String visitationKey = String.format("%s|%s", hostName, keytabFilePath);
-
- if ((visitedPrincipalKeys == null) || !visitedPrincipalKeys.contains(visitationKey)) {
- // Look up the current evaluatedPrincipal's password.
- // If found create the keytab file, else try to find it in the cache.
- String password = principalPasswordMap.get(resolvedPrincipal.getPrincipal());
- Integer keyNumber = principalKeyNumberMap.get(resolvedPrincipal.getPrincipal());
-
- message = String.format("Creating keytab file for %s on host %s", resolvedPrincipal.getPrincipal(), hostName);
- LOG.info(message);
- actionLog.writeStdOut(message);
- auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(keytabFilePath);
-
- // Determine where to store the keytab file. It should go into a host-specific
- // directory under the previously determined data directory.
- File hostDirectory = new File(dataDirectory, hostName);
-
- // Ensure the host directory exists...
- if (!hostDirectory.exists() && hostDirectory.mkdirs()) {
- // Make sure only Ambari has access to this directory.
- ensureAmbariOnlyAccess(hostDirectory);
- }
+ Lock lock = m_locksByKeytab.get(keytabFilePath);
+ lock.lock();
+
+ try {
+ Set<String> visitedPrincipalKeys = visitedIdentities.get(resolvedPrincipal.getPrincipal());
+ String visitationKey = String.format("%s|%s", hostName, keytabFilePath);
+
+ if ((visitedPrincipalKeys == null) || !visitedPrincipalKeys.contains(visitationKey)) {
+ // Look up the current evaluatedPrincipal's password.
+ // If found create the keytab file, else try to find it in the cache.
+ String password = principalPasswordMap.get(resolvedPrincipal.getPrincipal());
+ Integer keyNumber = principalKeyNumberMap.get(resolvedPrincipal.getPrincipal());
+
+ message = String.format("Creating keytab file for %s on host %s", resolvedPrincipal.getPrincipal(), hostName);
+ LOG.info(message);
+ actionLog.writeStdOut(message);
+ auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(keytabFilePath);
+
+ // Determine where to store the keytab file. It should go into a host-specific
+ // directory under the previously determined data directory.
+ File hostDirectory = new File(dataDirectory, hostName);
+
+ // Ensure the host directory exists...
+ if (!hostDirectory.exists() && hostDirectory.mkdirs()) {
+ // Make sure only Ambari has access to this directory.
+ ensureAmbariOnlyAccess(hostDirectory);
+ }
- if (hostDirectory.exists()) {
- File destinationKeytabFile = new File(hostDirectory, DigestUtils.sha256Hex(keytabFilePath));
+ if (hostDirectory.exists()) {
+ File destinationKeytabFile = new File(hostDirectory, DigestUtils.sha256Hex(keytabFilePath));
- boolean regenerateKeytabs = getOperationType(getCommandParameters()) == OperationType.RECREATE_ALL;
+ boolean regenerateKeytabs = getOperationType(getCommandParameters()) == OperationType.RECREATE_ALL;
- if(!includedInFilter) {
- // If this principal is to be filtered out, skip... unless is has not yet been created...
- regenerateKeytabs = false;
- }
+ if(!includedInFilter) {
+ // If this principal is to be filtered out, skip... unless is has not yet been created...
+ regenerateKeytabs = false;
+ }
- KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
- String cachedKeytabPath = (principalEntity == null) ? null : principalEntity.getCachedKeytabPath();
+ KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
+ String cachedKeytabPath = (principalEntity == null) ? null : principalEntity.getCachedKeytabPath();
- if (password == null) {
- if (!regenerateKeytabs && hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) {
- // There is nothing to do for this since it must already exist and we don't want to
- // regenerate the keytab
- message = String.format("Skipping keytab file for %s, missing password indicates nothing to do", resolvedPrincipal.getPrincipal());
- LOG.info(message);
- } else {
- if (cachedKeytabPath == null) {
- message = String.format("Failed to create keytab for %s, missing cached file", resolvedPrincipal.getPrincipal());
- actionLog.writeStdErr(message);
- LOG.error(message);
- commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+ if (password == null) {
+ if (!regenerateKeytabs && hostName.equalsIgnoreCase(KerberosHelper.AMBARI_SERVER_HOST_NAME)) {
+ // There is nothing to do for this since it must already exist and we don't want to
+ // regenerate the keytab
+ message = String.format("Skipping keytab file for %s, missing password indicates nothing to do", resolvedPrincipal.getPrincipal());
+ LOG.info(message);
} else {
- try {
- operationHandler.createKeytabFile(new File(cachedKeytabPath), destinationKeytabFile);
- } catch (KerberosOperationException e) {
- message = String.format("Failed to create keytab file for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
+ if (cachedKeytabPath == null) {
+ message = String.format("Failed to create keytab for %s, missing cached file", resolvedPrincipal.getPrincipal());
actionLog.writeStdErr(message);
- LOG.error(message, e);
+ LOG.error(message);
commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+ } else {
+ try {
+ operationHandler.createKeytabFile(new File(cachedKeytabPath), destinationKeytabFile);
+ } catch (KerberosOperationException e) {
+ message = String.format("Failed to create keytab file for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
+ actionLog.writeStdErr(message);
+ LOG.error(message, e);
+ commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+ }
}
}
- }
- } else {
- Keytab keytab = createKeytab(resolvedPrincipal.getPrincipal(), password, keyNumber, operationHandler, visitedPrincipalKeys != null, true, actionLog);
-
- if (keytab != null) {
- try {
- if (operationHandler.createKeytabFile(keytab, destinationKeytabFile)) {
- ensureAmbariOnlyAccess(destinationKeytabFile);
+ } else {
+ Keytab keytab = createKeytab(resolvedPrincipal.getPrincipal(), password, keyNumber, operationHandler, visitedPrincipalKeys != null, true, actionLog);
- message = String.format("Successfully created keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
- LOG.info(message);
- auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
- } else {
- message = String.format("Failed to create keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
+ if (keytab != null) {
+ try {
+ if (operationHandler.createKeytabFile(keytab, destinationKeytabFile)) {
+ ensureAmbariOnlyAccess(destinationKeytabFile);
+
+ message = String.format("Successfully created keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
+ LOG.info(message);
+ auditEventBuilder.withPrincipal(resolvedPrincipal.getPrincipal()).withHostName(hostName).withKeyTabFilePath(destinationKeytabFile.getAbsolutePath());
+ } else {
+ message = String.format("Failed to create keytab file for %s at %s", resolvedPrincipal.getPrincipal(), destinationKeytabFile.getAbsolutePath());
+ actionLog.writeStdErr(message);
+ LOG.error(message);
+ commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+ }
+ } catch (KerberosOperationException e) {
+ message = String.format("Failed to create keytab file for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
actionLog.writeStdErr(message);
- LOG.error(message);
+ LOG.error(message, e);
commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
}
- } catch (KerberosOperationException e) {
- message = String.format("Failed to create keytab file for %s - %s", resolvedPrincipal.getPrincipal(), e.getMessage());
- actionLog.writeStdErr(message);
- LOG.error(message, e);
+ } else {
commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
}
- } else {
- commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
- }
- if (visitedPrincipalKeys == null) {
- visitedPrincipalKeys = new HashSet<>();
- visitedIdentities.put(resolvedPrincipal.getPrincipal(), visitedPrincipalKeys);
- }
+ if (visitedPrincipalKeys == null) {
+ visitedPrincipalKeys = new HashSet<>();
+ visitedIdentities.put(resolvedPrincipal.getPrincipal(), visitedPrincipalKeys);
+ }
- visitedPrincipalKeys.add(visitationKey);
+ visitedPrincipalKeys.add(visitationKey);
+ }
+ } else {
+ message = String.format("Failed to create keytab file for %s, the container directory does not exist: %s",
+ resolvedPrincipal.getPrincipal(), hostDirectory.getAbsolutePath());
+ actionLog.writeStdErr(message);
+ LOG.error(message);
+ commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
}
} else {
- message = String.format("Failed to create keytab file for %s, the container directory does not exist: %s",
- resolvedPrincipal.getPrincipal(), hostDirectory.getAbsolutePath());
- actionLog.writeStdErr(message);
- LOG.error(message);
- commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+ LOG.debug("Skipping previously processed keytab for {} on host {}", resolvedPrincipal.getPrincipal(), hostName);
}
- } else {
- LOG.debug("Skipping previously processed keytab for {} on host {}", resolvedPrincipal.getPrincipal(), hostName);
+ } finally {
+ lock.unlock();
}
}
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
index 175b0ac7c3..a62af40b14 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/CreatePrincipalsServerAction.java
@@ -23,6 +23,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
@@ -39,6 +40,7 @@ import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Striped;
import com.google.inject.Inject;
/**
@@ -67,6 +69,11 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
@Inject
private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
+ /**
+ * Used to prevent multiple threads from working with the same principal.
+ */
+ private Striped<Lock> locksByPrincipal = Striped.lazyWeakLock(25);
+
/**
* A set of visited principal names used to prevent unnecessary processing on already processed
* principal names
@@ -160,32 +167,40 @@ public class CreatePrincipalsServerAction extends KerberosServerAction {
if (processPrincipal) {
Map<String, String> principalPasswordMap = getPrincipalPasswordMap(requestSharedDataContext);
- String password = principalPasswordMap.get(resolvedPrincipal.getPrincipal());
-
- if (password == null) {
- CreatePrincipalResult result = createPrincipal(resolvedPrincipal.getPrincipal(), servicePrincipal, kerberosConfiguration, operationHandler, regenerateKeytabs, actionLog);
- if (result == null) {
- commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
- } else {
- Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext);
-
- principalPasswordMap.put(resolvedPrincipal.getPrincipal(), result.getPassword());
- principalKeyNumberMap.put(resolvedPrincipal.getPrincipal(), result.getKeyNumber());
- // invalidate given principal for all keytabs to make them redistributed again
- for (KerberosKeytabPrincipalEntity kkpe: kerberosKeytabPrincipalDAO.findByPrincipal(resolvedPrincipal.getPrincipal())) {
- kkpe.setDistributed(false);
- kerberosKeytabPrincipalDAO.merge(kkpe);
- }
- // invalidate principal cache
- KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
- try {
- new File(principalEntity.getCachedKeytabPath()).delete();
- } catch (Exception e) {
- LOG.debug("Failed to delete cache file '{}'", principalEntity.getCachedKeytabPath());
+ String principal = resolvedPrincipal.getPrincipal();
+ Lock lock = locksByPrincipal.get(principal);
+ lock.lock();
+
+ String password = principalPasswordMap.get(principal);
+
+ try {
+ if (password == null) {
+ CreatePrincipalResult result = createPrincipal(resolvedPrincipal.getPrincipal(), servicePrincipal, kerberosConfiguration, operationHandler, regenerateKeytabs, actionLog);
+ if (result == null) {
+ commandReport = createCommandReport(1, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(), actionLog.getStdErr());
+ } else {
+ Map<String, Integer> principalKeyNumberMap = getPrincipalKeyNumberMap(requestSharedDataContext);
+
+ principalPasswordMap.put(resolvedPrincipal.getPrincipal(), result.getPassword());
+ principalKeyNumberMap.put(resolvedPrincipal.getPrincipal(), result.getKeyNumber());
+ // invalidate given principal for all keytabs to make them redistributed again
+ for (KerberosKeytabPrincipalEntity kkpe: kerberosKeytabPrincipalDAO.findByPrincipal(resolvedPrincipal.getPrincipal())) {
+ kkpe.setDistributed(false);
+ kerberosKeytabPrincipalDAO.merge(kkpe);
+ }
+ // invalidate principal cache
+ KerberosPrincipalEntity principalEntity = kerberosPrincipalDAO.find(resolvedPrincipal.getPrincipal());
+ try {
+ new File(principalEntity.getCachedKeytabPath()).delete();
+ } catch (Exception e) {
+ LOG.debug("Failed to delete cache file '{}'", principalEntity.getCachedKeytabPath());
+ }
+ principalEntity.setCachedKeytabPath(null);
+ kerberosPrincipalDAO.merge(principalEntity);
}
- principalEntity.setCachedKeytabPath(null);
- kerberosPrincipalDAO.merge(principalEntity);
}
+ } finally {
+ lock.unlock();
}
}
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
index b3b3082b67..12369c6057 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/FinalizeKerberosServerAction.java
@@ -22,6 +22,7 @@ import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.locks.Lock;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
@@ -35,6 +36,7 @@ import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.google.common.util.concurrent.Striped;
import com.google.inject.Inject;
public class FinalizeKerberosServerAction extends KerberosServerAction {
@@ -42,6 +44,11 @@ public class FinalizeKerberosServerAction extends KerberosServerAction {
private final TopologyHolder topologyHolder;
+ /**
+ * Used to prevent multiple threads from working with the same keytab.
+ */
+ private Striped<Lock> m_locksByKeytab = Striped.lazyWeakLock(25);
+
@Inject
public FinalizeKerberosServerAction(TopologyHolder topologyHolder) {
this.topologyHolder = topologyHolder;
@@ -91,7 +98,13 @@ public class FinalizeKerberosServerAction extends KerberosServerAction {
String keytabFilePath = resolvedPrincipal.getKeytabPath();
- if (!StringUtils.isEmpty(keytabFilePath)) {
+ if (StringUtils.isEmpty(keytabFilePath)) {
+ return null;
+ }
+
+ Lock lock = m_locksByKeytab.get(keytabFilePath);
+ lock.lock();
+ try {
Set<String> visited = (Set<String>) requestSharedDataContext.get(this.getClass().getName() + "_visited");
if (!visited.contains(keytabFilePath)) {
@@ -154,6 +167,8 @@ public class FinalizeKerberosServerAction extends KerberosServerAction {
visited.add(keytabFilePath);
}
+ } finally {
+ lock.unlock();
}
}
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
index c635ccae64..397914a2dc 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/KerberosServerAction.java
@@ -21,15 +21,24 @@ package org.apache.ambari.server.serveraction.kerberos;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Type;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.CompletionService;
+import java.util.concurrent.ExecutorCompletionService;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ThreadFactory;
import org.apache.ambari.server.AmbariException;
import org.apache.ambari.server.actionmanager.HostRoleStatus;
import org.apache.ambari.server.agent.CommandReport;
import org.apache.ambari.server.agent.ExecutionCommand;
+import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.UpdateConfigurationPolicy;
import org.apache.ambari.server.orm.dao.HostDAO;
@@ -50,6 +59,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.reflect.TypeToken;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
import com.google.inject.Inject;
/**
@@ -189,6 +199,9 @@ public abstract class KerberosServerAction extends AbstractServerAction {
@Inject
private KerberosKeytabController kerberosKeytabController;
+ @Inject
+ private Configuration configuration;
+
/**
* Given a (command parameter) Map and a property name, attempts to safely retrieve the requested
* data.
@@ -415,6 +428,9 @@ public abstract class KerberosServerAction extends AbstractServerAction {
* Using {@link #getHostFilter()}, {@link #getIdentityFilter()} and {@link #getServiceComponentFilter()} it retrieve
* list of filtered keytabs and their principals and process each principal using
* {@link #processIdentity(ResolvedKerberosPrincipal, KerberosOperationHandler, Map, boolean, Map)}.
+ * The configuration option {@link Configuration#getKerberosServerActionThreadpoolSize()} defines
+ * how many threads will handle {@link #processIdentity(ResolvedKerberosPrincipal, KerberosOperationHandler, Map, boolean, Map)}.
+ * The default is {@code 1}, but this method must be thread-safe in the event that concurrent threads are used.
*
* @param requestSharedDataContext a Map to be used a shared data among all ServerActions related
* to a given request
@@ -449,20 +465,67 @@ public abstract class KerberosServerAction extends AbstractServerAction {
}
try {
+ // create the thread factory, executor, and completion service for
+ // running the identity processing in parallel
+ String factoryName = "process-identity-%d";
+ ExecutionCommand executionCommand = getExecutionCommand();
+ if( null != executionCommand ) {
+ factoryName = "process-identity-task-" + executionCommand.getTaskId() + "-thread-%d";
+ }
+
+ ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat(factoryName).build();
+
+ int threadCount = configuration.getKerberosServerActionThreadpoolSize();
+ ExecutorService executorService = Executors.newFixedThreadPool(threadCount, threadFactory);
+ CompletionService<CommandReport> completionService = new ExecutorCompletionService<>(executorService);
+
Map<String, Collection<String>> serviceComponentFilter = getServiceComponentFilter();
if (serviceComponentFilter != null && pruneServiceFilter()) {
kerberosKeytabController.adjustServiceComponentFilter(clusters.getCluster(getClusterName()), true, serviceComponentFilter);
}
final Collection<KerberosIdentityDescriptor> serviceIdentities = serviceComponentFilter == null ? null : kerberosKeytabController.getServiceIdentities(getClusterName(), serviceComponentFilter.keySet());
+ List<Future<CommandReport>> futures = new ArrayList<>();
for (ResolvedKerberosKeytab rkk : kerberosKeytabController.getFilteredKeytabs(serviceIdentities, getHostFilter(),getIdentityFilter())) {
for (ResolvedKerberosPrincipal principal : rkk.getPrincipals()) {
- commandReport = processIdentity(principal, handler, kerberosConfiguration, isRelevantIdentity(serviceIdentities, principal), requestSharedDataContext);
+ // submit this method to the service to be processed concurrently
+ Future<CommandReport> future = completionService.submit(() -> {
+ try {
+ return processIdentity(principal, handler, kerberosConfiguration,
+ isRelevantIdentity(serviceIdentities, principal), requestSharedDataContext);
+ } catch (AmbariException ambariException) {
+ throw new RuntimeException(ambariException);
+ }
+ });
+
+ // keep track of futures for total count and ability to cancel later
+ futures.add(future);
+ }
+ }
+
+ LOG.info("Processing {} identities concurrently...", futures.size());
+
+ // get each future as it completes (out of order is OK), cancelling if
+ // an error is found
+ try {
+ for( int i = 0; i < futures.size(); i++ ) {
+ Future<CommandReport> future = completionService.take();
+ commandReport = future.get();
+
// If the principal processor returns a CommandReport, than it is time to stop
// since an error condition has probably occurred, else all is assumed to be well.
if (commandReport != null) {
break;
}
}
+ } catch (Exception exception) {
+ LOG.error("Unable to process identities asynchronously", exception);
+ return createCommandReport(0, HostRoleStatus.FAILED, "{}", actionLog.getStdOut(),actionLog.getStdErr());
+ } finally {
+ futures.stream()
+ .filter(x -> !x.isCancelled() && !x.isDone())
+ .forEach(x -> x.cancel(true));
+
+ executorService.shutdown();
}
} finally {
// The KerberosOperationHandler needs to be closed, if it fails to close ignore the
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
index ec01310f36..cac811e1fa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/kerberos/stageutils/KerberosKeytabController.java
@@ -35,8 +35,12 @@ import org.apache.ambari.server.orm.entities.KerberosKeytabEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
import org.apache.ambari.server.orm.entities.KerberosPrincipalEntity;
import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.Service;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
+import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.collections.MapUtils;
import com.google.common.collect.ImmutableSet;
@@ -56,6 +60,9 @@ public class KerberosKeytabController {
@Inject
private KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO;
+ @Inject
+ private Clusters clusters;
+
//TODO: due to circular dependencies in Guice this field cannot be injected with Guice's @Inject annotation; for now we should statically inject in AmbariServer
private static KerberosHelper kerberosHelper;
@@ -100,7 +107,10 @@ public class KerberosKeytabController {
* @return set of keytabs found
*/
public Set<ResolvedKerberosKeytab> getFromPrincipal(ResolvedKerberosPrincipal rkp) {
- return fromKeytabEntities(kerberosKeytabDAO.findByPrincipalAndHost(rkp.getPrincipal(), rkp.getHostId()));
+ List<KerberosKeytabEntity> keytabs = kerberosKeytabDAO.findByPrincipalAndHost(
+ rkp.getPrincipal(), rkp.getHostId());
+
+ return fromKeytabEntities(keytabs);
}
/**
@@ -224,6 +234,7 @@ public class KerberosKeytabController {
ImmutableSet.Builder<ResolvedKerberosPrincipal> builder = ImmutableSet.builder();
for (KerberosKeytabPrincipalEntity kkpe : principalEntities) {
KerberosPrincipalEntity kpe = kkpe.getKerberosPrincipalEntity();
+
if(kpe != null) {
ResolvedKerberosPrincipal rkp = new ResolvedKerberosPrincipal(
kkpe.getHostId(),
@@ -281,8 +292,33 @@ public class KerberosKeytabController {
public Collection<KerberosIdentityDescriptor> getServiceIdentities(String clusterName, Collection<String> services) throws AmbariException {
final Collection<KerberosIdentityDescriptor> serviceIdentities = new ArrayList<>();
+ Cluster cluster = clusters.getCluster(clusterName);
+ KerberosDescriptor kerberosDescriptor = kerberosHelper.getKerberosDescriptor(cluster, false);
+ Map<String, Map<String, Map<String, String>>> hostConfigurations = new HashMap<>();
+ Map<String, Host> hostMap = clusters.getHostsForCluster(clusterName);
+ Set<String> hosts = new HashSet<>(hostMap.keySet());
+
+ String ambariServerHostname = StageUtils.getHostName();
+ if (!hosts.contains(ambariServerHostname)) {
+ hosts.add(ambariServerHostname);
+ }
+ for( String hostName : hosts ) {
+ Map<String, Map<String, String>> configurations = kerberosHelper.calculateConfigurations(
+ cluster, hostName, kerberosDescriptor, false, false);
+ hostConfigurations.put(hostName, configurations);
+ }
for (String service : services) {
- for (Collection<KerberosIdentityDescriptor> activeIdentities : kerberosHelper.getActiveIdentities(clusterName, null, service, null, true).values()) {
+ Collection<Collection<KerberosIdentityDescriptor>> identities = kerberosHelper.getActiveIdentities(
+ clusterName,
+ null,
+ service,
+ null,
+ true,
+ hostConfigurations,
+ kerberosDescriptor
+ ).values();
+
+ for (Collection<KerberosIdentityDescriptor> activeIdentities : identities) {
serviceIdentities.addAll(activeIdentities);
}
}
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
index f4e86ab8be..9fcb38af62 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java
@@ -86,6 +86,7 @@ import org.apache.ambari.server.orm.DBAccessor;
import org.apache.ambari.server.orm.dao.ArtifactDAO;
import org.apache.ambari.server.orm.dao.HostRoleCommandDAO;
import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO.KeytabPrincipalFindOrCreateResult;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
import org.apache.ambari.server.scheduler.ExecutionScheduler;
@@ -3564,7 +3565,12 @@ public class KerberosHelperTest extends EasyMockSupport {
private void testCreateTestIdentity(final PrincipalKeyCredential PrincipalKeyCredential, Boolean manageIdentities) throws Exception {
KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = injector.getInstance(KerberosKeytabPrincipalDAO.class);
- expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), anyObject())).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).anyTimes();
+ KerberosKeytabPrincipalEntity kkp = createNiceMock(KerberosKeytabPrincipalEntity.class);
+ KeytabPrincipalFindOrCreateResult result = new KeytabPrincipalFindOrCreateResult();
+ result.created = true;
+ result.kkp = kkp;
+
+ expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), anyObject())).andReturn(result).anyTimes();
boolean managingIdentities = !Boolean.FALSE.equals(manageIdentities);
final Map<String, String> kerberosEnvProperties = new HashMap<>();
@@ -3754,7 +3760,12 @@ public class KerberosHelperTest extends EasyMockSupport {
private void testDeleteTestIdentity(final PrincipalKeyCredential PrincipalKeyCredential) throws Exception {
KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = injector.getInstance(KerberosKeytabPrincipalDAO.class);
- expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), anyObject())).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).anyTimes();
+ KerberosKeytabPrincipalEntity kkp = createNiceMock(KerberosKeytabPrincipalEntity.class);
+ KeytabPrincipalFindOrCreateResult result = new KeytabPrincipalFindOrCreateResult();
+ result.created = true;
+ result.kkp = kkp;
+
+ expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), anyObject(), anyObject())).andReturn(result).anyTimes();
Host host1 = createMock(Host.class);
expect(host1.getHostId()).andReturn(1l).anyTimes();
@@ -4185,7 +4196,8 @@ public class KerberosHelperTest extends EasyMockSupport {
injector.getInstance(AmbariMetaInfo.class).init();
Map<String, Collection<KerberosIdentityDescriptor>> identities;
- identities = kerberosHelper.getActiveIdentities(clusterName, hostName, serviceName, componentName, replaceHostNames);
+ identities = kerberosHelper.getActiveIdentities(clusterName, hostName, serviceName,
+ componentName, replaceHostNames, null, null);
verifyAll();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
index e271932d94..c9143a96ca 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/HostKerberosIdentityResourceProviderTest.java
@@ -43,10 +43,14 @@ import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
import org.apache.ambari.server.orm.dao.KerberosPrincipalDAO;
import org.apache.ambari.server.orm.entities.HostEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.kerberos.KerberosDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosKeytabDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosPrincipalDescriptor;
import org.apache.ambari.server.state.kerberos.KerberosPrincipalType;
+import org.easymock.EasyMock;
import org.easymock.EasyMockSupport;
import org.junit.Assert;
import org.junit.Test;
@@ -131,8 +135,12 @@ public class HostKerberosIdentityResourceProviderTest extends EasyMockSupport {
@Test
public void testGetResources() throws Exception {
+ Clusters clusters = createNiceMock(Clusters.class);
+ expect(clusters.getCluster(EasyMock.anyString())).andReturn(
+ createNiceMock(Cluster.class)).once();
AmbariManagementController managementController = createMock(AmbariManagementController.class);
+ expect(managementController.getClusters()).andReturn(clusters).atLeastOnce();
KerberosPrincipalDescriptor principalDescriptor1 = createStrictMock(KerberosPrincipalDescriptor.class);
expect(principalDescriptor1.getValue()).andReturn("principal1@EXAMPLE.COM");
@@ -214,9 +222,15 @@ public class HostKerberosIdentityResourceProviderTest extends EasyMockSupport {
activeIdentities.put("Host100", identities);
KerberosHelper kerberosHelper = createStrictMock(KerberosHelper.class);
- expect(kerberosHelper.getActiveIdentities("Cluster100", "Host100", null, null, true))
+ KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class);
+ expect(kerberosHelper.getKerberosDescriptor(
+ EasyMock.anyObject(Cluster.class),
+ EasyMock.eq(false))).andReturn(kerberosDescriptor).atLeastOnce();
+
+ expect(kerberosHelper.getActiveIdentities("Cluster100", "Host100", null, null, true, null,
+ kerberosDescriptor))
.andReturn(activeIdentities)
- .times(1);
+ .once();
// replay
replayAll();
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
index beeac825a6..191b6cbe37 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/serveraction/kerberos/ConfigureAmbariIdentitiesServerActionTest.java
@@ -36,11 +36,13 @@ import org.apache.ambari.server.controller.RootService;
import org.apache.ambari.server.orm.DBAccessor;
import org.apache.ambari.server.orm.dao.HostDAO;
import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO;
+import org.apache.ambari.server.orm.dao.KerberosKeytabPrincipalDAO.KeytabPrincipalFindOrCreateResult;
import org.apache.ambari.server.orm.entities.HostEntity;
import org.apache.ambari.server.orm.entities.KerberosKeytabPrincipalEntity;
import org.apache.ambari.server.serveraction.ActionLog;
import org.apache.ambari.server.serveraction.kerberos.stageutils.ResolvedKerberosPrincipal;
import org.apache.ambari.server.state.Clusters;
+import org.apache.ambari.server.state.stack.OsFamily;
import org.apache.ambari.server.utils.StageUtils;
import org.apache.commons.io.FileUtils;
import org.easymock.EasyMockSupport;
@@ -94,9 +96,13 @@ public class ConfigureAmbariIdentitiesServerActionTest extends EasyMockSupport {
expect(hostDAO.findByName(StageUtils.getHostName())).andReturn(hostEntity).once();
KerberosKeytabPrincipalDAO kerberosKeytabPrincipalDAO = injector.getInstance(KerberosKeytabPrincipalDAO.class);
- KerberosKeytabPrincipalEntity kke = createNiceMock(KerberosKeytabPrincipalEntity.class);
- expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), eq(hostEntity), anyObject())).andReturn(kke).once();
- expect(kerberosKeytabPrincipalDAO.merge(kke)).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).once();
+ KerberosKeytabPrincipalEntity kkp = createNiceMock(KerberosKeytabPrincipalEntity.class);
+ KeytabPrincipalFindOrCreateResult result = new KeytabPrincipalFindOrCreateResult();
+ result.created = true;
+ result.kkp = kkp;
+
+ expect(kerberosKeytabPrincipalDAO.findOrCreate(anyObject(), eq(hostEntity), anyObject())).andReturn(result).once();
+ expect(kerberosKeytabPrincipalDAO.merge(kkp)).andReturn(createNiceMock(KerberosKeytabPrincipalEntity.class)).once();
// Mock the methods that do the actual file manipulation to avoid having to deal with ambari-sudo.sh used in
// ShellCommandUtil#mkdir, ShellCommandUtil#copyFile, etc..
@@ -215,6 +221,7 @@ public class ConfigureAmbariIdentitiesServerActionTest extends EasyMockSupport {
bind(AuditLogger.class).toInstance(createNiceMock(AuditLogger.class));
bind(Clusters.class).toInstance(createNiceMock(Clusters.class));
bind(KerberosHelper.class).toInstance(createNiceMock(KerberosHelper.class));
+ bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
bind(HostDAO.class).toInstance(createMock(HostDAO.class));
bind(KerberosKeytabPrincipalDAO.class).toInstance(createMock(KerberosKeytabPrincipalDAO.class));
---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ambari.apache.org
For additional commands, e-mail: commits-help@ambari.apache.org