You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ambari.apache.org by jo...@apache.org on 2018/05/23 19:45:20 UTC
[ambari] branch trunk updated: [AMBARI-23930]q - Provide a
Framework For Regenerating Keytabs During Upgrade (#1355)
This is an automated email from the ASF dual-hosted git repository.
jonathanhurley 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 7b3aca8 [AMBARI-23930]q - Provide a Framework For Regenerating Keytabs During Upgrade (#1355)
7b3aca8 is described below
commit 7b3aca83483cbc3cad777ba2eadf7015c1234d0d
Author: Jonathan Hurley <jo...@apache.org>
AuthorDate: Wed May 23 15:45:14 2018 -0400
[AMBARI-23930]q - Provide a Framework For Regenerating Keytabs During Upgrade (#1355)
---
.../ambari/server/controller/KerberosHelper.java | 7 +
.../server/controller/KerberosHelperImpl.java | 144 ++++++++++++++-------
.../internal/UpgradeResourceProvider.java | 110 ++++++++++++----
.../serveraction/upgrades/AddComponentAction.java | 2 +-
.../org/apache/ambari/server/stack/HostsType.java | 20 ++-
.../ambari/server/stack/MasterHostResolver.java | 14 +-
.../state/stack/upgrade/ClusterGrouping.java | 34 ++++-
.../server/state/stack/upgrade/Grouping.java | 3 +
.../state/stack/upgrade/RegenerateKeytabsTask.java | 81 ++++++++++++
.../server/state/stack/upgrade/StageWrapper.java | 3 +-
.../ambari/server/state/stack/upgrade/Task.java | 12 +-
.../server/state/stack/upgrade/TaskWrapper.java | 4 +-
.../stack-hooks/before-SET_KEYTAB/scripts/hook.py | 38 ++++++
ambari-server/src/main/resources/upgrade-pack.xsd | 9 +-
.../server/controller/KerberosHelperTest.java | 45 ++++++-
.../internal/UpgradeResourceProviderTest.java | 48 ++++++-
.../before-SET_KEYTAB/test_before_set_keytab.py | 41 ++++++
.../stacks/2.2/KERBEROS/test_kerberos_client.py | 2 +-
.../upgrades/upgrade_test_regenerate_keytabs.xml | 41 ++++++
19 files changed, 558 insertions(+), 100 deletions(-)
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 297fc3c..38701e2 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
@@ -132,6 +132,13 @@ public interface KerberosHelper {
String PRECONFIGURE_SERVICES = "preconfigure_services";
/**
+ * If {@code true}, then this will create the stages and tasks as being
+ * retry-able. A failure during Kerberos operations will not cause the entire
+ * request to be aborted.
+ */
+ String ALLOW_RETRY = "allow_retry_on_failure";
+
+ /**
* Toggles Kerberos security to enable it or remove it depending on the state of the cluster.
* <p/>
* The cluster "security_type" property is used to determine the security state of the cluster.
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 c492173..0fc8165 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
@@ -279,6 +279,12 @@ public class KerberosHelperImpl implements KerberosHelper {
throw new AmbariException(String.format("Custom operation %s can only be requested with the security type cluster property: %s", operation.name(), SecurityType.KERBEROS.name()));
}
+ boolean retryAllowed = false;
+ if (requestProperties.containsKey(ALLOW_RETRY)) {
+ String allowRetryString = requestProperties.get(ALLOW_RETRY);
+ retryAllowed = Boolean.parseBoolean(allowRetryString);
+ }
+
CreatePrincipalsAndKeytabsHandler handler = null;
Set<String> hostFilter = parseHostFilter(requestProperties);
@@ -296,6 +302,8 @@ public class KerberosHelperImpl implements KerberosHelper {
}
if (handler != null) {
+ handler.setRetryAllowed(retryAllowed);
+
requestStageContainer = handle(cluster, getKerberosDetails(cluster, manageIdentities),
serviceComponentFilter, hostFilter, null, null, requestStageContainer, handler);
} else {
@@ -473,7 +481,7 @@ public class KerberosHelperImpl implements KerberosHelper {
// If Ambari is managing it own identities then add AMBARI to the set of installed service so
// that its Kerberos descriptor entries will be included.
if (createAmbariIdentities(existingConfigurations.get(KERBEROS_ENV))) {
- installedServices = new HashMap<String, Set<String>>(installedServices);
+ installedServices = new HashMap<>(installedServices);
installedServices.put(RootService.AMBARI.name(), Collections.singleton(RootComponent.AMBARI_SERVER.name()));
}
@@ -2533,6 +2541,7 @@ public class KerberosHelperImpl implements KerberosHelper {
* @return a File pointing to the new temporary directory, or null if one was not created
* @throws AmbariException if a new temporary directory cannot be created
*/
+ @Override
public File createTemporaryDirectory() throws AmbariException {
try {
File temporaryDirectory = getConfiguredTemporaryDirectory();
@@ -2721,41 +2730,6 @@ public class KerberosHelperImpl implements KerberosHelper {
}
/**
- * Creates a new stage with a single task describing the ServerAction class to invoke and the other
- * task-related information.
- *
- * @param id the new stage's id
- * @param cluster the relevant Cluster
- * @param requestId the relevant request Id
- * @param requestContext a String describing the stage
- * @param commandParams JSON-encoded command parameters
- * @param hostParams JSON-encoded host parameters
- * @param actionClass The ServeAction class that implements the action to invoke
- * @param event The relevant ServiceComponentHostServerActionEvent
- * @param commandParameters a Map of command parameters to attach to the task added to the new
- * stage
- * @param commandDetail a String declaring a descriptive name to pass to the action - null or an
- * empty string indicates no value is to be set
- * @param timeout the timeout for the task/action @return a newly created Stage
- */
- private Stage createServerActionStage(long id, Cluster cluster, long requestId,
- String requestContext,
- String commandParams, String hostParams,
- Class<? extends ServerAction> actionClass,
- ServiceComponentHostServerActionEvent event,
- Map<String, String> commandParameters, String commandDetail,
- Integer timeout) throws AmbariException {
-
- Stage stage = createNewStage(id, cluster, requestId, requestContext, commandParams, hostParams);
- stage.addServerActionCommand(actionClass.getName(), null, Role.AMBARI_SERVER_ACTION,
- RoleCommand.EXECUTE, cluster.getClusterName(), event, commandParameters, commandDetail,
- ambariManagementController.findConfigurationTagsWithOverrides(cluster, null), timeout,
- false, false);
-
- return stage;
- }
-
- /**
* Given a Collection of ServiceComponentHosts generates a unique list of hosts.
*
* @param serviceComponentHosts a Collection of ServiceComponentHosts from which to to retrieve host names
@@ -3330,6 +3304,23 @@ public class KerberosHelperImpl implements KerberosHelper {
*/
private abstract class Handler {
+ /**
+ * If (@code true}, allows stages and tasks created with the handler to be
+ * retried instead of outright failing a task.
+ *
+ * @see KerberosHelper#ALLOW_RETRY
+ */
+ protected boolean retryAllowed = false;
+
+ /**
+ * Sets whether tasks created as part of this handler can be retry if they fail. If a task
+ * cannot be retried it will fail the entire request.
+ *
+ * @param retryAllowed
+ */
+ void setRetryAllowed(boolean retryAllowed) {
+ this.retryAllowed = retryAllowed;
+ }
/**
* Creates the necessary stages to complete the relevant task and stores them in the supplied
@@ -3551,12 +3542,14 @@ public class KerberosHelperImpl implements KerberosHelper {
if (!hosts.isEmpty()) {
Map<String, String> requestParams = new HashMap<>();
+
+ ActionExecutionContext actionExecContext = createActionExecutionContext(
+ cluster.getClusterName(),
+ SET_KEYTAB,
+ createRequestResourceFilters(hosts),
+ requestParams,
+ retryAllowed);
- ActionExecutionContext actionExecContext = new ActionExecutionContext(
- cluster.getClusterName(),
- SET_KEYTAB,
- createRequestResourceFilters(hosts),
- requestParams);
customCommandExecutionHelper.addExecutionCommandsToStage(actionExecContext, stage,
requestParams, null);
}
@@ -3586,11 +3579,13 @@ public class KerberosHelperImpl implements KerberosHelper {
if (!hostsToInclude.isEmpty()) {
Map<String, String> requestParams = new HashMap<>();
- ActionExecutionContext actionExecContext = new ActionExecutionContext(
- cluster.getClusterName(),
- CHECK_KEYTABS,
- createRequestResourceFilters(hostsToInclude),
- requestParams);
+ ActionExecutionContext actionExecContext = createActionExecutionContext(
+ cluster.getClusterName(),
+ CHECK_KEYTABS,
+ createRequestResourceFilters(hostsToInclude),
+ requestParams,
+ retryAllowed);
+
customCommandExecutionHelper.addExecutionCommandsToStage(actionExecContext, stage, requestParams, null);
}
RoleGraph roleGraph = roleGraphFactory.createNew(roleCommandOrder);
@@ -3797,6 +3792,63 @@ public class KerberosHelperImpl implements KerberosHelper {
requestResourceFilters.add(reqResFilter);
return requestResourceFilters;
}
+
+ /**
+ * Creates a new stage with a single task describing the ServerAction class to invoke and the other
+ * task-related information.
+ *
+ * @param id the new stage's id
+ * @param cluster the relevant Cluster
+ * @param requestId the relevant request Id
+ * @param requestContext a String describing the stage
+ * @param commandParams JSON-encoded command parameters
+ * @param hostParams JSON-encoded host parameters
+ * @param actionClass The ServeAction class that implements the action to invoke
+ * @param event The relevant ServiceComponentHostServerActionEvent
+ * @param commandParameters a Map of command parameters to attach to the task added to the new
+ * stage
+ * @param commandDetail a String declaring a descriptive name to pass to the action - null or an
+ * empty string indicates no value is to be set
+ * @param timeout the timeout for the task/action @return a newly created Stage
+ */
+ private Stage createServerActionStage(long id, Cluster cluster, long requestId,
+ String requestContext,
+ String commandParams, String hostParams,
+ Class<? extends ServerAction> actionClass,
+ ServiceComponentHostServerActionEvent event,
+ Map<String, String> commandParameters, String commandDetail,
+ Integer timeout) throws AmbariException {
+
+ Stage stage = createNewStage(id, cluster, requestId, requestContext, commandParams, hostParams);
+ stage.addServerActionCommand(actionClass.getName(), null, Role.AMBARI_SERVER_ACTION,
+ RoleCommand.EXECUTE, cluster.getClusterName(), event, commandParameters, commandDetail,
+ ambariManagementController.findConfigurationTagsWithOverrides(cluster, null), timeout,
+ retryAllowed, false);
+
+ return stage;
+ }
+
+ /**
+ * Creates an {@link ActionExecutionContext} where some of the common values are pre-initialized.
+ *
+ * @param clusterName
+ * @param commandName
+ * @param resourceFilters
+ * @param parameters
+ * @param retryAllowed
+ * @return
+ */
+ private ActionExecutionContext createActionExecutionContext(String clusterName,
+ String commandName, List<RequestResourceFilter> resourceFilters,
+ Map<String, String> parameters, boolean retryAllowed) {
+
+ ActionExecutionContext actionExecContext = new ActionExecutionContext(clusterName, SET_KEYTAB,
+ resourceFilters, parameters);
+
+ actionExecContext.setRetryAllowed(retryAllowed);
+
+ return actionExecContext;
+ }
}
/**
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
index 52425b4..c00121b 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/internal/UpgradeResourceProvider.java
@@ -50,6 +50,8 @@ import org.apache.ambari.server.controller.AmbariActionExecutionHelper;
import org.apache.ambari.server.controller.AmbariCustomCommandExecutionHelper;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.ExecuteCommandJson;
+import org.apache.ambari.server.controller.KerberosHelper;
+import org.apache.ambari.server.controller.KerberosHelperImpl.SupportedCustomOperation;
import org.apache.ambari.server.controller.spi.NoSuchParentResourceException;
import org.apache.ambari.server.controller.spi.NoSuchResourceException;
import org.apache.ambari.server.controller.spi.Predicate;
@@ -78,6 +80,7 @@ import org.apache.ambari.server.security.authorization.AuthorizationException;
import org.apache.ambari.server.security.authorization.AuthorizationHelper;
import org.apache.ambari.server.security.authorization.ResourceType;
import org.apache.ambari.server.security.authorization.RoleAuthorization;
+import org.apache.ambari.server.serveraction.kerberos.KerberosOperationException;
import org.apache.ambari.server.state.Cluster;
import org.apache.ambari.server.state.Clusters;
import org.apache.ambari.server.state.ConfigHelper;
@@ -261,6 +264,12 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
@Inject
private RequestDAO requestDAO;
+ /**
+ * Used for creating keytab regeneration stage inside of an upgrade request.
+ */
+ @Inject
+ private static Provider<KerberosHelper> s_kerberosHelper;
+
static {
// properties
PROPERTY_IDS.add(UPGRADE_CLUSTER_NAME);
@@ -798,41 +807,86 @@ public class UpgradeResourceProvider extends AbstractControllerResourceProvider
for (StageWrapper wrapper : group.items) {
RepositoryVersionEntity effectiveRepositoryVersion = upgradeContext.getRepositoryVersion();
- if (wrapper.getType() == StageWrapper.Type.SERVER_SIDE_ACTION) {
- // !!! each stage is guaranteed to be of one type. but because there
- // is a bug that prevents one stage with multiple tasks assigned for
- // the same host, break them out into individual stages.
- for (TaskWrapper taskWrapper : wrapper.getTasks()) {
- for (Task task : taskWrapper.getTasks()) {
- if (upgradeContext.isManualVerificationAutoSkipped()
- && task.getType() == Task.Type.MANUAL) {
- continue;
+ switch(wrapper.getType()) {
+ case SERVER_SIDE_ACTION:{
+ // !!! each stage is guaranteed to be of one type. but because there
+ // is a bug that prevents one stage with multiple tasks assigned for
+ // the same host, break them out into individual stages.
+ for (TaskWrapper taskWrapper : wrapper.getTasks()) {
+ for (Task task : taskWrapper.getTasks()) {
+ if (upgradeContext.isManualVerificationAutoSkipped()
+ && task.getType() == Task.Type.MANUAL) {
+ continue;
+ }
+
+ UpgradeItemEntity itemEntity = new UpgradeItemEntity();
+
+ itemEntity.setText(wrapper.getText());
+ itemEntity.setTasks(wrapper.getTasksJson());
+ itemEntity.setHosts(wrapper.getHostsJson());
+
+ injectVariables(configHelper, cluster, itemEntity);
+ if (makeServerSideStage(group, upgradeContext, effectiveRepositoryVersion, req,
+ itemEntity, (ServerSideActionTask) task, configUpgradePack)) {
+ itemEntities.add(itemEntity);
+ }
}
-
- UpgradeItemEntity itemEntity = new UpgradeItemEntity();
-
- itemEntity.setText(wrapper.getText());
- itemEntity.setTasks(wrapper.getTasksJson());
- itemEntity.setHosts(wrapper.getHostsJson());
-
- injectVariables(configHelper, cluster, itemEntity);
- if (makeServerSideStage(group, upgradeContext, effectiveRepositoryVersion, req,
- itemEntity, (ServerSideActionTask) task, configUpgradePack)) {
+ }
+ break;
+ }
+ case REGENERATE_KEYTABS: {
+ try {
+ // remmeber how many stages we had before adding keytab stuff
+ int stageCount = req.getStages().size();
+
+ // build a map of request properties which say to
+ // - only regenerate missing tabs
+ // - allow all tasks which fail to be retried (so the upgrade doesn't abort)
+ Map<String, String> requestProperties = new HashMap<>();
+ requestProperties.put(SupportedCustomOperation.REGENERATE_KEYTABS.name().toLowerCase(), "missing");
+ requestProperties.put(KerberosHelper.ALLOW_RETRY, Boolean.TRUE.toString().toLowerCase());
+
+ // add stages to the upgrade which will regenerate missing keytabs only
+ req = s_kerberosHelper.get().executeCustomOperations(cluster, requestProperties, req, null);
+
+ // for every stage which was added for kerberos stuff create an
+ // associated upgrade item for it
+ List<Stage> stages = req.getStages();
+ int newStageCount = stages.size();
+ for (int i = stageCount; i < newStageCount; i++) {
+ Stage stage = stages.get(i);
+ stage.setSkippable(group.skippable);
+ stage.setAutoSkipFailureSupported(group.supportsAutoSkipOnFailure);
+
+ UpgradeItemEntity itemEntity = new UpgradeItemEntity();
+ itemEntity.setStageId(stage.getStageId());
+ itemEntity.setText(stage.getRequestContext());
+ itemEntity.setTasks(wrapper.getTasksJson());
+ itemEntity.setHosts(wrapper.getHostsJson());
itemEntities.add(itemEntity);
+ injectVariables(configHelper, cluster, itemEntity);
}
+ } catch (KerberosOperationException kerberosOperationException) {
+ throw new AmbariException("Unable to build keytab regeneration stage",
+ kerberosOperationException);
}
+
+ break;
}
- } else {
- UpgradeItemEntity itemEntity = new UpgradeItemEntity();
- itemEntity.setText(wrapper.getText());
- itemEntity.setTasks(wrapper.getTasksJson());
- itemEntity.setHosts(wrapper.getHostsJson());
- itemEntities.add(itemEntity);
+ default: {
+ UpgradeItemEntity itemEntity = new UpgradeItemEntity();
+ itemEntity.setText(wrapper.getText());
+ itemEntity.setTasks(wrapper.getTasksJson());
+ itemEntity.setHosts(wrapper.getHostsJson());
+ itemEntities.add(itemEntity);
+
+ injectVariables(configHelper, cluster, itemEntity);
- injectVariables(configHelper, cluster, itemEntity);
+ // upgrade items match a stage
+ createStage(group, upgradeContext, effectiveRepositoryVersion, req, itemEntity, wrapper);
- // upgrade items match a stage
- createStage(group, upgradeContext, effectiveRepositoryVersion, req, itemEntity, wrapper);
+ break;
+ }
}
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AddComponentAction.java b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AddComponentAction.java
index ee3150e..9a339d2 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AddComponentAction.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/serveraction/upgrades/AddComponentAction.java
@@ -81,7 +81,7 @@ public class AddComponentAction extends AbstractUpgradeServerAction {
if (candidates.isEmpty()) {
return createCommandReport(0, HostRoleStatus.FAILED, "{}", "", String.format(
"Unable to add a new component to the cluster as there are no hosts which contain %s's %s",
- task.service, task.component));
+ task.hostService, task.hostComponent));
}
Service service = cluster.getService(task.service);
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/HostsType.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/HostsType.java
index 530c9ee..7dbfff8 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/HostsType.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/HostsType.java
@@ -30,6 +30,9 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
+import org.apache.ambari.server.state.Cluster;
+import org.apache.ambari.server.state.Host;
+import org.apache.ambari.server.state.MaintenanceState;
import org.apache.ambari.server.state.ServiceComponentHost;
/**
@@ -76,7 +79,7 @@ public class HostsType {
* For example: [sec1, sec2, master1, sec3, sec4, master2]
*/
public void arrangeHostSecondariesFirst() {
- this.hosts = getHighAvailabilityHosts().stream()
+ hosts = getHighAvailabilityHosts().stream()
.flatMap(each -> Stream.concat(each.getSecondaries().stream(), Stream.of(each.getMaster())))
.collect(toCollection(LinkedHashSet::new));
}
@@ -170,6 +173,21 @@ public class HostsType {
return HostsType.normal(host);
}
+ /**
+ * Create an instance with all healthy hosts.
+ */
+ public static HostsType healthy(Cluster cluster) {
+ LinkedHashSet<String> hostNames = new LinkedHashSet<>();
+ for (Host host : cluster.getHosts()) {
+ MaintenanceState maintenanceState = host.getMaintenanceState(cluster.getClusterId());
+ if (maintenanceState == MaintenanceState.OFF) {
+ hostNames.add(host.getHostName());
+ }
+ }
+
+ return normal(hostNames);
+ }
+
private HostsType(List<HighAvailabilityHosts> highAvailabilityHosts, LinkedHashSet<String> hosts) {
this.highAvailabilityHosts = highAvailabilityHosts;
this.hosts = hosts;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java b/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
index 9042d2e..b018277 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/stack/MasterHostResolver.java
@@ -176,7 +176,7 @@ public class MasterHostResolver {
String serviceName, String componentName) {
Collection<Host> candidates = cluster.getHosts();
if (StringUtils.isNotBlank(serviceName) && StringUtils.isNotBlank(componentName)) {
- List<ServiceComponentHost> schs = cluster.getServiceComponentHosts(serviceName,componentName);
+ List<ServiceComponentHost> schs = cluster.getServiceComponentHosts(serviceName,componentName);
candidates = schs.stream().map(sch -> sch.getHost()).collect(Collectors.toList());
}
@@ -185,23 +185,23 @@ public class MasterHostResolver {
}
// figure out where to add the new component
- List<Host> hosts = Lists.newArrayList();
+ List<Host> winners = Lists.newArrayList();
switch (executeHostType) {
case ALL:
- hosts.addAll(candidates);
+ winners.addAll(candidates);
break;
case FIRST:
- hosts.add(candidates.iterator().next());
+ winners.add(candidates.iterator().next());
break;
case MASTER:
- hosts.add(candidates.iterator().next());
+ winners.add(candidates.iterator().next());
break;
case ANY:
- hosts.add(candidates.iterator().next());
+ winners.add(candidates.iterator().next());
break;
}
- return candidates;
+ return winners;
}
/**
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
index e013d18..fcc7e57 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/ClusterGrouping.java
@@ -222,6 +222,10 @@ public class ClusterGrouping extends Grouping {
wrapper = getExecuteStageWrapper(upgradeContext, execution);
break;
+ case REGENERATE_KEYTABS:
+ wrapper = getRegenerateKeytabsWrapper(upgradeContext, execution);
+ break;
+
default:
break;
}
@@ -274,10 +278,32 @@ public class ClusterGrouping extends Grouping {
}
/**
- * Return a Stage Wrapper for a task meant to execute code, typically on Ambari Server.
- * @param ctx Upgrade Context
- * @param execution Execution Stage
- * @return Returns a Stage Wrapper, or null if a valid one could not be created.
+ * Return a {@link StageWrapper} for regeneration of keytabs.
+ *
+ * @param ctx
+ * Upgrade Context
+ * @param execution
+ * Execution Stage
+ * @return a {@link StageWrapper} which will regenerate keytabs on all hosts.
+ */
+ private StageWrapper getRegenerateKeytabsWrapper(UpgradeContext ctx, ExecuteStage execution) {
+ Task task = execution.task;
+ HostsType hosts = HostsType.healthy(ctx.getCluster());
+
+ return new StageWrapper(StageWrapper.Type.REGENERATE_KEYTABS, execution.title,
+ new TaskWrapper(null, null, hosts.getHosts(), task));
+ }
+
+ /**
+ * Return a Stage Wrapper for a task meant to execute code, typically on
+ * Ambari Server.
+ *
+ * @param ctx
+ * Upgrade Context
+ * @param execution
+ * Execution Stage
+ * @return Returns a Stage Wrapper, or null if a valid one could not be
+ * created.
*/
private StageWrapper getExecuteStageWrapper(UpgradeContext ctx, ExecuteStage execution) {
String service = execution.service;
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
index cc42d65..150c9fa 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Grouping.java
@@ -349,6 +349,9 @@ public class Grouping {
case SERVICE_CHECK:
type = StageWrapper.Type.SERVICE_CHECK;
break;
+ case REGENERATE_KEYTABS:
+ type = StageWrapper.Type.REGENERATE_KEYTABS;
+ break;
}
tasks.add(initial);
}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RegenerateKeytabsTask.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RegenerateKeytabsTask.java
new file mode 100644
index 0000000..704235f
--- /dev/null
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/RegenerateKeytabsTask.java
@@ -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.ambari.server.state.stack.upgrade;
+
+import javax.xml.bind.annotation.XmlAccessType;
+import javax.xml.bind.annotation.XmlAccessorType;
+import javax.xml.bind.annotation.XmlRootElement;
+import javax.xml.bind.annotation.XmlTransient;
+import javax.xml.bind.annotation.XmlType;
+
+import com.google.gson.annotations.Expose;
+
+/**
+ * The {@link RegenerateKeytabsTask} is used for injection Kerberos tasks into
+ * an upgrade which will regenerate keytabs. The regeneration is always partial,
+ * opting to only regenerate missing keytabs.
+ */
+@XmlRootElement
+@XmlAccessorType(XmlAccessType.FIELD)
+@XmlType(name = "regenerate_keytabs")
+public class RegenerateKeytabsTask extends Task {
+
+ @Expose
+ @XmlTransient
+ private Task.Type type = Type.REGENERATE_KEYTABS;
+
+ /**
+ * Constructor.
+ *
+ */
+ public RegenerateKeytabsTask() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Task.Type getType() {
+ return type;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public StageWrapper.Type getStageWrapperType() {
+ return StageWrapper.Type.REGENERATE_KEYTABS;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getActionVerb() {
+ return "Regenerating Keytabs";
+ }
+
+ /**
+ * Gets a JSON representation of this task.
+ *
+ * @return a JSON representation of this task.
+ */
+ public String toJson() {
+ return GSON.toJson(this);
+ }
+}
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
index db8f849..81c7ade 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/StageWrapper.java
@@ -159,7 +159,8 @@ public class StageWrapper {
SERVICE_CHECK,
STOP,
START,
- CONFIGURE
+ CONFIGURE,
+ REGENERATE_KEYTABS;
}
/**
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
index 8b141a8..a77fceb 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/Task.java
@@ -34,7 +34,8 @@ import com.google.gson.annotations.Expose;
@XmlSeeAlso(
value = { ExecuteTask.class, CreateAndConfigureTask.class, ConfigureTask.class,
ManualTask.class, RestartTask.class, StartTask.class, StopTask.class,
- ServerActionTask.class, ConfigureFunction.class, AddComponentTask.class })
+ ServerActionTask.class, ConfigureFunction.class, AddComponentTask.class,
+ RegenerateKeytabsTask.class })
public abstract class Task {
/**
@@ -161,7 +162,12 @@ public abstract class Task {
/**
* A task which adds new components to the cluster during the upgrade.
*/
- ADD_COMPONENT;
+ ADD_COMPONENT,
+
+ /**
+ * Create keytab regeneration steps as part of the upgrade.
+ */
+ REGENERATE_KEYTABS;
/**
* Commands which run on the server.
@@ -173,7 +179,7 @@ public abstract class Task {
* Commands which are run on agents.
*/
public static final EnumSet<Type> COMMANDS = EnumSet.of(RESTART, START, CONFIGURE_FUNCTION,
- STOP, SERVICE_CHECK);
+ STOP, SERVICE_CHECK, REGENERATE_KEYTABS);
/**
* @return {@code true} if the task is manual or automated.
diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
index 56047d7..b13c9978 100644
--- a/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
+++ b/ambari-server/src/main/java/org/apache/ambari/server/state/stack/upgrade/TaskWrapper.java
@@ -26,7 +26,7 @@ import java.util.Set;
import org.apache.commons.lang.StringUtils;
-import com.google.common.base.Objects;
+import com.google.common.base.MoreObjects;
/**
* Aggregates all upgrade tasks for a HostComponent into one wrapper.
@@ -112,7 +112,7 @@ public class TaskWrapper {
*/
@Override
public String toString() {
- return Objects.toStringHelper(this).add("service", service)
+ return MoreObjects.toStringHelper(this).add("service", service)
.add("component", component)
.add("tasks", tasks)
.add("hosts", hosts)
diff --git a/ambari-server/src/main/resources/stack-hooks/before-SET_KEYTAB/scripts/hook.py b/ambari-server/src/main/resources/stack-hooks/before-SET_KEYTAB/scripts/hook.py
new file mode 100644
index 0000000..4d028fc
--- /dev/null
+++ b/ambari-server/src/main/resources/stack-hooks/before-SET_KEYTAB/scripts/hook.py
@@ -0,0 +1,38 @@
+"""
+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.
+
+"""
+
+from resource_management import *
+
+class BeforeSetKeytabHook(Hook):
+
+ def hook(self, env):
+ """
+ This will invoke the before-ANY hook which contains all of the user and group creation logic.
+ Keytab regeneration requires all users are already created, which is usually done by the
+ before-INSTALL hook. However, if the keytab regeneration is executed as part of an upgrade,
+ then the before-INSTALL hook never ran.
+
+ :param env:
+ :return:
+ """
+ self.run_custom_hook('before-ANY')
+
+if __name__ == "__main__":
+ BeforeSetKeytabHook().execute()
+
diff --git a/ambari-server/src/main/resources/upgrade-pack.xsd b/ambari-server/src/main/resources/upgrade-pack.xsd
index c77e29b..f9e6b37 100644
--- a/ambari-server/src/main/resources/upgrade-pack.xsd
+++ b/ambari-server/src/main/resources/upgrade-pack.xsd
@@ -374,7 +374,14 @@
</xs:extension>
</xs:complexContent>
</xs:complexType>
-
+
+ <xs:complexType name="regenerate_keytabs">
+ <xs:complexContent>
+ <xs:extension base="abstract-server-task-type">
+ </xs:extension>
+ </xs:complexContent>
+ </xs:complexType>
+
<xs:complexType name="order-type">
<xs:sequence>
<xs:element name="group" minOccurs="1" maxOccurs="unbounded" />
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 5c6f693..09d5d4c 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
@@ -167,6 +167,7 @@ public class KerberosHelperTest extends EasyMockSupport {
private final AmbariMetaInfo metaInfo = createMock(AmbariMetaInfo.class);
private final TopologyManager topologyManager = createMock(TopologyManager.class);
private final Configuration configuration = createMock(Configuration.class);
+ private final AmbariCustomCommandExecutionHelper customCommandExecutionHelperMock = createNiceMock(AmbariCustomCommandExecutionHelper.class);
@Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
@@ -249,7 +250,7 @@ public class KerberosHelperTest extends EasyMockSupport {
bind(DBAccessor.class).toInstance(createNiceMock(DBAccessor.class));
bind(SecurityHelper.class).toInstance(createNiceMock(SecurityHelper.class));
bind(OsFamily.class).toInstance(createNiceMock(OsFamily.class));
- bind(AmbariCustomCommandExecutionHelper.class).toInstance(createNiceMock(AmbariCustomCommandExecutionHelper.class));
+ bind(AmbariCustomCommandExecutionHelper.class).toInstance(customCommandExecutionHelperMock);
bind(AmbariManagementController.class).toInstance(createNiceMock(AmbariManagementController.class));
bind(AmbariMetaInfo.class).toInstance(metaInfo);
bind(ActionManager.class).toInstance(createNiceMock(ActionManager.class));
@@ -498,6 +499,33 @@ public class KerberosHelperTest extends EasyMockSupport {
testRegenerateKeytabs(new PrincipalKeyCredential("principal", "password"), false, false);
}
+ /**
+ * Tests that when regenerating keytabs for an upgrade, that the retry allowed
+ * boolean is set on the tasks created.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testRegenerateKeytabsWithRetryAllowed() throws Exception {
+ Capture<ActionExecutionContext> captureContext = Capture.newInstance();
+ customCommandExecutionHelperMock.addExecutionCommandsToStage(capture(captureContext),
+ anyObject(Stage.class), anyObject(), eq(null));
+
+ expectLastCall().atLeastOnce();
+
+ Map<String, String> requestMap = new HashMap<>();
+ requestMap.put(KerberosHelper.DIRECTIVE_REGENERATE_KEYTABS, "true");
+ requestMap.put(KerberosHelper.ALLOW_RETRY, "true");
+
+ RequestStageContainer requestStageContainer = testRegenerateKeytabs(
+ new PrincipalKeyCredential("principal", "password"), requestMap, false, false);
+
+ assertNotNull(requestStageContainer);
+
+ ActionExecutionContext capturedContext = captureContext.getValue();
+ assertTrue(capturedContext.isRetryAllowed());
+ }
+
@Test
public void testDisableKerberos() throws Exception {
testDisableKerberos(new PrincipalKeyCredential("principal", "password"));
@@ -1309,7 +1337,11 @@ public class KerberosHelperTest extends EasyMockSupport {
verifyAll();
}
- private void testRegenerateKeytabs(final PrincipalKeyCredential PrincipalKeyCredential, boolean mockRequestStageContainer, final boolean testInvalidHost) throws Exception {
+ private RequestStageContainer testRegenerateKeytabs(final PrincipalKeyCredential principalKeyCredential, boolean mockRequestStageContainer, final boolean testInvalidHost) throws Exception {
+ return testRegenerateKeytabs(principalKeyCredential, Collections.singletonMap(KerberosHelper.DIRECTIVE_REGENERATE_KEYTABS, "true"), mockRequestStageContainer, testInvalidHost);
+ }
+
+ private RequestStageContainer testRegenerateKeytabs(final PrincipalKeyCredential PrincipalKeyCredential, Map<String,String> requestMap, boolean mockRequestStageContainer, final boolean testInvalidHost) throws Exception {
KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class);
@@ -1492,9 +1524,14 @@ public class KerberosHelperTest extends EasyMockSupport {
credentialStoreService.setCredential(cluster.getClusterName(), KerberosHelper.KDC_ADMINISTRATOR_CREDENTIAL_ALIAS,
PrincipalKeyCredential, CredentialStoreType.TEMPORARY);
- Assert.assertNotNull(kerberosHelper.executeCustomOperations(cluster, Collections.singletonMap(KerberosHelper.DIRECTIVE_REGENERATE_KEYTABS, "true"), requestStageContainer, true));
+ RequestStageContainer returnValue = kerberosHelper.executeCustomOperations(cluster,
+ requestMap, requestStageContainer, true);
+
+ Assert.assertNotNull(returnValue);
verifyAll();
+
+ return returnValue;
}
@Test
@@ -2860,7 +2897,7 @@ public class KerberosHelperTest extends EasyMockSupport {
put(DIRECTIVE_COMPONENTS, "SERVICE1:COMPONENT1;COMPONENT2,SERVICE2:COMPONENT1;COMPONENT2;COMPONENT3");
}};
- Set<String> expectedHosts = new HashSet<String>(Arrays.asList("host1", "host2", "host3"));
+ Set<String> expectedHosts = new HashSet<>(Arrays.asList("host1", "host2", "host3"));
Set<String> hosts = KerberosHelperImpl.parseHostFilter(requestProperties);
assertEquals(expectedHosts, hosts);
diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
index 72d364b..f577264 100644
--- a/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
+++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/internal/UpgradeResourceProviderTest.java
@@ -17,6 +17,7 @@
*/
package org.apache.ambari.server.controller.internal;
+import static org.easymock.EasyMock.eq;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.junit.Assert.assertEquals;
@@ -56,6 +57,7 @@ import org.apache.ambari.server.audit.AuditLogger;
import org.apache.ambari.server.configuration.Configuration;
import org.apache.ambari.server.controller.AmbariManagementController;
import org.apache.ambari.server.controller.AmbariServer;
+import org.apache.ambari.server.controller.KerberosHelper;
import org.apache.ambari.server.controller.ResourceProviderFactory;
import org.apache.ambari.server.controller.spi.Predicate;
import org.apache.ambari.server.controller.spi.Request;
@@ -100,6 +102,7 @@ import org.apache.ambari.server.state.ConfigHelper;
import org.apache.ambari.server.state.Host;
import org.apache.ambari.server.state.HostState;
import org.apache.ambari.server.state.RepositoryType;
+import org.apache.ambari.server.state.SecurityType;
import org.apache.ambari.server.state.Service;
import org.apache.ambari.server.state.ServiceComponent;
import org.apache.ambari.server.state.ServiceComponentHost;
@@ -109,6 +112,7 @@ import org.apache.ambari.server.state.UpgradeHelper;
import org.apache.ambari.server.state.UpgradeState;
import org.apache.ambari.server.state.stack.upgrade.ConfigureTask;
import org.apache.ambari.server.state.stack.upgrade.Direction;
+import org.apache.ambari.server.state.stack.upgrade.RegenerateKeytabsTask;
import org.apache.ambari.server.state.stack.upgrade.UpgradeType;
import org.apache.ambari.server.topology.TopologyManager;
import org.apache.ambari.server.utils.StageUtils;
@@ -147,6 +151,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
private RepositoryVersionDAO repoVersionDao = null;
private Injector injector;
private Clusters clusters;
+ private Cluster cluster;
private AmbariManagementController amc;
private ConfigHelper configHelper;
private AgentConfigsHolder agentConfigsHolder = createNiceMock(AgentConfigsHolder.class);
@@ -154,6 +159,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
private TopologyManager topologyManager;
private ConfigFactory configFactory;
private HostRoleCommandDAO hrcDAO;
+ private KerberosHelper kerberosHelperMock = createNiceMock(KerberosHelper.class);
RepositoryVersionEntity repoVersionEntity2110;
RepositoryVersionEntity repoVersionEntity2111;
@@ -256,7 +262,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
clusters = injector.getInstance(Clusters.class);
clusters.addCluster("c1", stack211);
- Cluster cluster = clusters.getCluster("c1");
+ cluster = clusters.getCluster("c1");
clusters.addHost("h1");
Host host = clusters.getHost("h1");
@@ -2081,6 +2087,45 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
}
/**
+ * Tests that a {@link RegenerateKeytabsTask} causes the upgrade to inject the
+ * correct stages.
+ *
+ * @throws Exception
+ */
+ @Test
+ public void testCreateRegenerateKeytabStages() throws Exception {
+ Map<String, Object> requestProps = new HashMap<>();
+ requestProps.put(UpgradeResourceProvider.UPGRADE_CLUSTER_NAME, "c1");
+ requestProps.put(UpgradeResourceProvider.UPGRADE_REPO_VERSION_ID, String.valueOf(repoVersionEntity2200.getId()));
+ requestProps.put(UpgradeResourceProvider.UPGRADE_PACK, "upgrade_test_regenerate_keytabs");
+ requestProps.put(UpgradeResourceProvider.UPGRADE_SKIP_PREREQUISITE_CHECKS, "true");
+ requestProps.put(UpgradeResourceProvider.UPGRADE_DIRECTION, Direction.UPGRADE.name());
+
+ cluster.setSecurityType(SecurityType.KERBEROS);
+
+ RequestStageContainer requestStageContainer = createNiceMock(RequestStageContainer.class);
+ expect(requestStageContainer.getStages()).andReturn(Lists.newArrayList()).once();
+
+ expect(kerberosHelperMock.executeCustomOperations(eq(cluster), EasyMock.anyObject(),
+ EasyMock.anyObject(RequestStageContainer.class), eq(null))).andReturn(
+ requestStageContainer).once();
+
+ replayAll();
+
+ ResourceProvider upgradeResourceProvider = createProvider(amc);
+ Request request = PropertyHelper.getCreateRequest(Collections.singleton(requestProps), null);
+
+ try {
+ upgradeResourceProvider.createResources(request);
+ Assert.fail("The mock request stage container should have caused a problem in JPA");
+ } catch (IllegalArgumentException illegalArgumentException) {
+ // ignore
+ }
+
+ verifyAll();
+ }
+
+ /**
*
*/
private class MockModule implements Module {
@@ -2091,6 +2136,7 @@ public class UpgradeResourceProviderTest extends EasyMockSupport {
public void configure(Binder binder) {
binder.bind(ConfigHelper.class).toInstance(configHelper);
binder.bind(AgentConfigsHolder.class).toInstance(agentConfigsHolder);
+ binder.bind(KerberosHelper.class).toInstance(kerberosHelperMock);
}
}
}
diff --git a/ambari-server/src/test/python/stacks/2.0.6/hooks/before-SET_KEYTAB/test_before_set_keytab.py b/ambari-server/src/test/python/stacks/2.0.6/hooks/before-SET_KEYTAB/test_before_set_keytab.py
new file mode 100644
index 0000000..a706d9f
--- /dev/null
+++ b/ambari-server/src/test/python/stacks/2.0.6/hooks/before-SET_KEYTAB/test_before_set_keytab.py
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+
+'''
+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.
+'''
+
+from stacks.utils.RMFTestCase import *
+from mock.mock import MagicMock, patch
+from resource_management import Hook
+import itertools
+
+@patch("platform.linux_distribution", new = MagicMock(return_value="Linux"))
+@patch("os.path.exists", new = MagicMock(return_value=True))
+@patch.object(Hook, "run_custom_hook")
+class TestHookBeforeSetKeytab(RMFTestCase):
+ STACK_VERSION = '2.0.6'
+ def test_hook_default(self, run_custom_hook_mock):
+ self.executeScript("before-SET_KEYTAB/scripts/hook.py",
+ classname="BeforeSetKeytabHook",
+ command="hook",
+ stack_version = self.STACK_VERSION,
+ target=RMFTestCase.TARGET_STACK_HOOKS,
+ config_file="default.json",
+ call_mocks=itertools.cycle([(0, "1000")])
+ )
+
+ run_custom_hook_mock.assert_called_with('before-ANY')
\ No newline at end of file
diff --git a/ambari-server/src/test/python/stacks/2.2/KERBEROS/test_kerberos_client.py b/ambari-server/src/test/python/stacks/2.2/KERBEROS/test_kerberos_client.py
index 8d96707..8464526 100644
--- a/ambari-server/src/test/python/stacks/2.2/KERBEROS/test_kerberos_client.py
+++ b/ambari-server/src/test/python/stacks/2.2/KERBEROS/test_kerberos_client.py
@@ -18,7 +18,7 @@ limitations under the License.
"""
import json
-from mock.mock import MagicMock, patch
+from mock.mock import patch
import os
import sys
import use_cases
diff --git a/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_regenerate_keytabs.xml b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_regenerate_keytabs.xml
new file mode 100644
index 0000000..df23ffd
--- /dev/null
+++ b/ambari-server/src/test/resources/stacks/HDP/2.1.1/upgrades/upgrade_test_regenerate_keytabs.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<upgrade xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="upgrade-pack.xsd">
+ <target>2.2.*.*</target>
+ <target-stack>HDP-2.2.0</target-stack>
+ <type>ROLLING</type>
+ <prerequisite-checks/>
+
+ <order>
+ <group xsi:type="cluster" name="REGENERATE_KEYTABS" title="Regenerate Missing Keytabs">
+ <condition xsi:type="security" type="kerberos"/>
+ <direction>UPGRADE</direction>
+ <execute-stage title="Regenerate Missing Keytabs">
+ <task xsi:type="regenerate_keytabs"/>
+ </execute-stage>
+ </group>
+ </order>
+
+ <processing>
+ <service name="ZOOKEEPER">
+ <component name="ZOOKEEPER_SERVER">
+ <upgrade />
+ </component>
+ </service>
+ </processing>
+</upgrade>
--
To stop receiving notification emails like this one, please contact
jonathanhurley@apache.org.