Repository: ambari Updated Branches: refs/heads/trunk 0b397cdff -> e767aa44d
AMBARI-21392. Cleanup relevant Kerberos identities when a service is removed (amagyar) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/e767aa44 Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/e767aa44 Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/e767aa44 Branch: refs/heads/trunk Commit: e767aa44d872bab9ac0c416684f80b2b662347e5 Parents: 0b397cd Author: Attila Magyar <amag...@hortonworks.com> Authored: Tue Jul 11 20:10:12 2017 +0200 Committer: Attila Magyar <amag...@hortonworks.com> Committed: Tue Jul 11 20:10:12 2017 +0200 ---------------------------------------------------------------------- .../controller/DeleteIdentityHandler.java | 77 ++++++++-- .../server/controller/KerberosHelper.java | 2 +- .../server/controller/KerberosHelperImpl.java | 5 +- .../utilities/KerberosIdentityCleaner.java | 88 +++-------- .../utilities/RemovableIdentities.java | 145 +++++++++++++++++++ .../controller/utilities/UsedIdentities.java | 101 +++++++++++++ .../ServiceComponentUninstalledEvent.java | 6 + .../server/events/ServiceRemovedEvent.java | 29 ++-- .../ambari/server/orm/dao/ClusterDAO.java | 15 ++ .../orm/entities/ClusterConfigEntity.java | 3 + .../org/apache/ambari/server/state/Cluster.java | 7 + .../apache/ambari/server/state/ServiceImpl.java | 14 +- .../server/state/cluster/ClusterImpl.java | 9 ++ .../AbstractKerberosDescriptorContainer.java | 12 ++ .../kerberos/KerberosComponentDescriptor.java | 15 -- .../kerberos/KerberosIdentityDescriptor.java | 14 +- .../utilities/KerberosIdentityCleanerTest.java | 102 +++++++++++-- .../server/orm/dao/ServiceConfigDAOTest.java | 12 ++ 18 files changed, 520 insertions(+), 136 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/controller/DeleteIdentityHandler.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/DeleteIdentityHandler.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/DeleteIdentityHandler.java index aa098b6..3329e76 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/DeleteIdentityHandler.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/DeleteIdentityHandler.java @@ -17,12 +17,13 @@ */ package org.apache.ambari.server.controller; -import static com.google.common.collect.Sets.newHashSet; +import static java.util.Collections.singleton; +import static java.util.stream.Collectors.toSet; import static org.apache.ambari.server.controller.KerberosHelperImpl.BASE_LOG_DIR; import java.io.File; +import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -45,10 +46,15 @@ import org.apache.ambari.server.serveraction.kerberos.KDCType; import org.apache.ambari.server.serveraction.kerberos.KerberosOperationHandler; import org.apache.ambari.server.serveraction.kerberos.KerberosServerAction; import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Config; +import org.apache.ambari.server.state.StackId; import org.apache.ambari.server.state.kerberos.KerberosDescriptor; import org.apache.ambari.server.state.svccomphost.ServiceComponentHostServerActionEvent; import org.apache.ambari.server.utils.StageUtils; +import com.google.gson.reflect.TypeToken; + + /** * I delete kerberos identities (principals and keytabs) of a given component. */ @@ -78,7 +84,7 @@ class DeleteIdentityHandler { if (manageIdentities) { addPrepareDeleteIdentity(cluster, hostParamsJson, event, commandParameters, stageContainer); addDestroyPrincipals(cluster, hostParamsJson, event, commandParameters, stageContainer); - addDeleteKeytab(cluster, newHashSet(commandParameters.component.getHostName()), hostParamsJson, commandParameters, stageContainer); + addDeleteKeytab(cluster, commandParameters.getAffectedHostNames(), hostParamsJson, commandParameters, stageContainer); } addFinalize(cluster, hostParamsJson, event, stageContainer, commandParameters); } @@ -172,15 +178,15 @@ class DeleteIdentityHandler { public static class CommandParams { - private final Component component; - private final List<String> identities; + private final List<Component> components; + private final Set<String> identities; private final String authName; private final File dataDirectory; private final String defaultRealm; private final KDCType kdcType; - public CommandParams(Component component, List<String> identities, String authName, File dataDirectory, String defaultRealm, KDCType kdcType) { - this.component = component; + public CommandParams(List<Component> components, Set<String> identities, String authName, File dataDirectory, String defaultRealm, KDCType kdcType) { + this.components = components; this.identities = identities; this.authName = authName; this.dataDirectory = dataDirectory; @@ -194,11 +200,15 @@ class DeleteIdentityHandler { commandParameters.put(KerberosServerAction.DEFAULT_REALM, defaultRealm); commandParameters.put(KerberosServerAction.KDC_TYPE, kdcType.name()); commandParameters.put(KerberosServerAction.IDENTITY_FILTER, StageUtils.getGson().toJson(identities)); - commandParameters.put(KerberosServerAction.COMPONENT_FILTER, StageUtils.getGson().toJson(component)); + commandParameters.put(KerberosServerAction.COMPONENT_FILTER, StageUtils.getGson().toJson(components)); commandParameters.put(KerberosServerAction.DATA_DIRECTORY, dataDirectory.getAbsolutePath()); return commandParameters; } + public Set<String> getAffectedHostNames() { + return components.stream().map(Component::getHostName).collect(toSet()); + } + public String asJson() { return StageUtils.getGson().toJson(asMap()); } @@ -211,22 +221,57 @@ class DeleteIdentityHandler { processServiceComponents( getCluster(), kerberosDescriptor, - Collections.singletonList(getComponentFilter()), + componentFilter(), getIdentityFilter(), dataDirectory(), - calculateConfig(kerberosDescriptor), - new HashMap<String, Map<String, String>>(), + calculateConfig(kerberosDescriptor, serviceNames()), + new HashMap<>(), false, - new HashMap<String, Set<String>>()); + new HashMap<>()); return createCommandReport(0, HostRoleStatus.COMPLETED, "{}", actionLog.getStdOut(), actionLog.getStdErr()); } - protected Component getComponentFilter() { - return StageUtils.getGson().fromJson(getCommandParameterValue(KerberosServerAction.COMPONENT_FILTER), Component.class); + private Set<String> serviceNames() { + return componentFilter().stream().map(component -> component.getServiceName()).collect(toSet()); + } + + private List<Component> componentFilter() { + Type jsonType = new TypeToken<List<Component>>() {}.getType(); + return StageUtils.getGson().fromJson(getCommandParameterValue(KerberosServerAction.COMPONENT_FILTER), jsonType); + } + + /** + * Cleaning identities is asynchronous, it can happen that the service and its configuration is already deleted at this point. + * We're extending the actual config with the properties of the latest deleted configuration of the service. + * The service configuration is needed because principal names may contain placeholder variables which are replaced based on the service configuration. + */ + private Map<String, Map<String, String>> calculateConfig(KerberosDescriptor kerberosDescriptor, Set<String> serviceNames) throws AmbariException { + Map<String, Map<String, String>> actualConfig = getKerberosHelper().calculateConfigurations(getCluster(), null, kerberosDescriptor.getProperties()); + extendWithDeletedConfigOfService(actualConfig, serviceNames); + return actualConfig; + } + + private void extendWithDeletedConfigOfService(Map<String, Map<String, String>> configToBeExtended, Set<String> serviceNames) throws AmbariException { + Set<String> deletedConfigTypes = serviceNames.stream() + .flatMap(serviceName -> configTypesOfService(serviceName).stream()) + .collect(toSet()); + for (Config deletedConfig : getCluster().getLatestConfigsWithTypes(deletedConfigTypes)) { + configToBeExtended.put(deletedConfig.getType(), deletedConfig.getProperties()); + } } - private Map<String, Map<String, String>> calculateConfig(KerberosDescriptor kerberosDescriptor) throws AmbariException { - return getKerberosHelper().calculateConfigurations(getCluster(), null, kerberosDescriptor.getProperties()); + private Set<String> configTypesOfService(String serviceName) { + try { + StackId stackId = getCluster().getCurrentStackVersion(); + StackServiceRequest stackServiceRequest = new StackServiceRequest(stackId.getStackName(), stackId.getStackVersion(), serviceName); + return AmbariServer.getController().getStackServices(singleton(stackServiceRequest)).stream() + .findFirst() + .orElseThrow(() -> new IllegalArgumentException("Could not find stack service " + serviceName)) + .getConfigTypes() + .keySet(); + } catch (AmbariException e) { + throw new RuntimeException(e); + } } private String dataDirectory() { http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelper.java ---------------------------------------------------------------------- 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 cc0c048..3819863 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 @@ -233,7 +233,7 @@ public interface KerberosHelper { RequestStageContainer requestStageContainer, Boolean manageIdentities) throws AmbariException, KerberosOperationException; - void deleteIdentity(Cluster cluster, Component component, List<String> identities) throws AmbariException, KerberosOperationException; + void deleteIdentities(Cluster cluster, List<Component> components, Set<String> identities) throws AmbariException, KerberosOperationException; /** * Updates the relevant configurations for the components specified in the service filter. http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/controller/KerberosHelperImpl.java ---------------------------------------------------------------------- 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 b30f8f6..e5b7afd 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 @@ -301,17 +301,18 @@ public class KerberosHelperImpl implements KerberosHelper { * Deletes the kerberos identities of the given component, even if the component is already deleted. */ @Override - public void deleteIdentity(Cluster cluster, Component component, List<String> identities) throws AmbariException, KerberosOperationException { + public void deleteIdentities(Cluster cluster, List<Component> components, Set<String> identities) throws AmbariException, KerberosOperationException { if (identities.isEmpty()) { return; } + LOG.info("Deleting identities: ", identities); KerberosDetails kerberosDetails = getKerberosDetails(cluster, null); validateKDCCredentials(kerberosDetails, cluster); File dataDirectory = createTemporaryDirectory(); RoleCommandOrder roleCommandOrder = ambariManagementController.getRoleCommandOrder(cluster); DeleteIdentityHandler handler = new DeleteIdentityHandler(customCommandExecutionHelper, configuration.getDefaultServerTaskTimeout(), stageFactory, ambariManagementController); DeleteIdentityHandler.CommandParams commandParameters = new DeleteIdentityHandler.CommandParams( - component, + components, identities, ambariManagementController.getAuthName(), dataDirectory, http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleaner.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleaner.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleaner.java index 0a8462f..7ec4a6e 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleaner.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleaner.java @@ -17,26 +17,12 @@ */ package org.apache.ambari.server.controller.utilities; -import static org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor.nullToEmpty; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.controller.KerberosHelper; import org.apache.ambari.server.events.ServiceComponentUninstalledEvent; +import org.apache.ambari.server.events.ServiceRemovedEvent; import org.apache.ambari.server.events.publishers.AmbariEventPublisher; -import org.apache.ambari.server.serveraction.kerberos.Component; import org.apache.ambari.server.serveraction.kerberos.KerberosMissingAdminCredentialsException; -import org.apache.ambari.server.state.Cluster; import org.apache.ambari.server.state.Clusters; -import org.apache.ambari.server.state.SecurityType; -import org.apache.ambari.server.state.Service; -import org.apache.ambari.server.state.kerberos.KerberosComponentDescriptor; -import org.apache.ambari.server.state.kerberos.KerberosDescriptor; -import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor; -import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -69,67 +55,29 @@ public class KerberosIdentityCleaner { @Subscribe public void componentRemoved(ServiceComponentUninstalledEvent event) throws KerberosMissingAdminCredentialsException { try { - Cluster cluster = clusters.getCluster(event.getClusterId()); - if (cluster.getSecurityType() != SecurityType.KERBEROS) { - return; - } - KerberosComponentDescriptor descriptor = componentDescriptor(cluster, event.getServiceName(), event.getComponentName()); - if (descriptor == null) { - LOG.info("No kerberos descriptor for {}", event); - return; - } - List<String> identitiesToRemove = identityNames(skipSharedIdentities(descriptor.getIdentitiesSkipReferences(), cluster, event)); - LOG.info("Deleting identities {} after an event {}", identitiesToRemove, event); - kerberosHelper.deleteIdentity(cluster, new Component(event.getHostName(), event.getServiceName(), event.getComponentName()), identitiesToRemove); + LOG.info("Removing identities after {}", event); + RemovableIdentities + .ofComponent(clusters.getCluster(event.getClusterId()), event, kerberosHelper) + .remove(kerberosHelper); } catch (Exception e) { LOG.error("Error while deleting kerberos identity after an event: " + event, e); } } - private KerberosComponentDescriptor componentDescriptor(Cluster cluster, String serviceName, String componentName) throws AmbariException { - KerberosServiceDescriptor serviceDescriptor = kerberosHelper.getKerberosDescriptor(cluster).getService(serviceName); - return serviceDescriptor == null ? null : serviceDescriptor.getComponent(componentName); - } - - private List<String> identityNames(List<KerberosIdentityDescriptor> identities) { - List<String> result = new ArrayList<>(); - for (KerberosIdentityDescriptor each : identities) { result.add(each.getName()); } - return result; - } - - private List<KerberosIdentityDescriptor> skipSharedIdentities(List<KerberosIdentityDescriptor> candidates, Cluster cluster, ServiceComponentUninstalledEvent event) throws AmbariException { - List<KerberosIdentityDescriptor> activeIdentities = activeIdentities(cluster, kerberosHelper.getKerberosDescriptor(cluster), event); - List<KerberosIdentityDescriptor> result = new ArrayList<>(); - for (KerberosIdentityDescriptor candidate : candidates) { - if (!candidate.isShared(activeIdentities)) { - result.add(candidate); - } else { - LOG.debug("Skip removing shared identity: {}", candidate.getName()); - } - } - return result; - } - - private List<KerberosIdentityDescriptor> activeIdentities(Cluster cluster, KerberosDescriptor root, ServiceComponentUninstalledEvent event) { - List<KerberosIdentityDescriptor> result = new ArrayList<>(); - result.addAll(nullToEmpty(root.getIdentities())); - for (Map.Entry<String, Service> serviceEntry : cluster.getServices().entrySet()) { - KerberosServiceDescriptor serviceDescriptor = root.getService(serviceEntry.getKey()); - if (serviceDescriptor == null) { - continue; - } - result.addAll(nullToEmpty(serviceDescriptor.getIdentities())); - for (String componentName : serviceEntry.getValue().getServiceComponents().keySet()) { - if (!sameComponent(event, componentName, serviceEntry.getKey())) { - result.addAll(serviceDescriptor.getComponentIdentities(componentName)); - } - } + /** + * Removes kerberos identities (principals and keytabs) after a service was uninstalled. + * Keeps the identity if either the principal or the keytab is used by an other service + */ + @Subscribe + public void serviceRemoved(ServiceRemovedEvent event) { + try { + LOG.info("Removing identities after {}", event); + RemovableIdentities + .ofService(clusters.getCluster(event.getClusterId()), event, kerberosHelper) + .remove(kerberosHelper); + } catch (Exception e) { + LOG.error("Error while deleting kerberos identity after an event: " + event, e); } - return result; - } - - private boolean sameComponent(ServiceComponentUninstalledEvent event, String componentName, String serviceName) { - return event.getServiceName().equals(serviceName) && event.getComponentName().equals(componentName); } } http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/RemovableIdentities.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/RemovableIdentities.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/RemovableIdentities.java new file mode 100644 index 0000000..d4bb501 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/RemovableIdentities.java @@ -0,0 +1,145 @@ +/* + * 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.controller.utilities; + +import static java.util.Collections.emptyList; +import static java.util.Collections.singletonList; +import static java.util.stream.Collectors.toList; +import static java.util.stream.Collectors.toSet; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.controller.utilities.UsedIdentities.ComponentExclude; +import org.apache.ambari.server.controller.utilities.UsedIdentities.ServiceExclude; +import org.apache.ambari.server.events.ServiceComponentUninstalledEvent; +import org.apache.ambari.server.events.ServiceRemovedEvent; +import org.apache.ambari.server.serveraction.kerberos.Component; +import org.apache.ambari.server.serveraction.kerberos.KerberosOperationException; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.SecurityType; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor; +import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor; + +/** + * I represent a group of kerberos identities which are to be deleted after a service or a component was removed. + * My instances provide methods for removing the candidates, excluding those that are still used by other components or services. + */ +public class RemovableIdentities { + private final List<KerberosIdentityDescriptor> candidateIdentities; + private final UsedIdentities usedIdentities; + private final Cluster cluster; + private final List<Component> components; + + /** + * Populate the identities with the identities of the removed service and its components + */ + public static RemovableIdentities ofService(Cluster cluster, ServiceRemovedEvent event, KerberosHelper kerberosHelper) throws AmbariException { + if (cluster.getSecurityType() != SecurityType.KERBEROS) { + return RemovableIdentities.none(); + } + KerberosServiceDescriptor serviceDescriptor = kerberosHelper.getKerberosDescriptor(cluster).getService(event.getServiceName()); + if (serviceDescriptor == null) { + return RemovableIdentities.none(); + } + UsedIdentities usedIdentities = UsedIdentities.populate(cluster, excludeService(event.getServiceName()), ComponentExclude.NONE, kerberosHelper); + return new RemovableIdentities( + serviceDescriptor.getIdentitiesSkipReferences(), + usedIdentities, + cluster, + event.getComponents()); + } + + /** + * Populate the identities with the identities of the removed component + */ + public static RemovableIdentities ofComponent(Cluster cluster, ServiceComponentUninstalledEvent event, KerberosHelper kerberosHelper) throws AmbariException { + if (cluster.getSecurityType() != SecurityType.KERBEROS) { + return RemovableIdentities.none(); + } + KerberosServiceDescriptor serviceDescriptor = kerberosHelper.getKerberosDescriptor(cluster).getService(event.getServiceName()); + if (serviceDescriptor == null) { + return RemovableIdentities.none(); + } + UsedIdentities usedIdentities = UsedIdentities.populate( + cluster, + ServiceExclude.NONE, + excludeComponent(event.getServiceName(), event.getComponentName(), event.getHostName()), + kerberosHelper); + return new RemovableIdentities( + componentIdentities(singletonList(event.getComponentName()), serviceDescriptor), + usedIdentities, + cluster, + singletonList(event.getComponent())); + } + + /** + * Populates the identities with an empty list + */ + public static RemovableIdentities none() throws AmbariException { + return new RemovableIdentities(emptyList(), UsedIdentities.none(), null, null); + } + + private static ServiceExclude excludeService(String excludedServiceName) { + return serviceName -> excludedServiceName.equals(serviceName); + } + + private static ComponentExclude excludeComponent(String excludedServiceName, String excludedComponentName, String excludedHostName) { + return (serviceName, componentName, hosts) -> excludedServiceName.equals(serviceName) + && excludedComponentName.equals(componentName) + && hostNames(hosts).equals(singletonList(excludedHostName)); + } + + private static List<String> hostNames(Collection<ServiceComponentHost> hosts) { + return hosts.stream().map(ServiceComponentHost::getHostName).collect(toList()); + } + + private static List<KerberosIdentityDescriptor> componentIdentities(List<String> componentNames, KerberosServiceDescriptor serviceDescriptor) throws AmbariException { + return componentNames.stream() + .map(componentName -> serviceDescriptor.getComponent(componentName)) + .filter(Objects::nonNull) + .flatMap(componentDescriptor -> componentDescriptor.getIdentitiesSkipReferences().stream()) + .collect(toList()); + } + + private RemovableIdentities(List<KerberosIdentityDescriptor> candidateIdentities, UsedIdentities usedIdentities, Cluster cluster, List<Component> components) { + this.candidateIdentities = candidateIdentities; + this.usedIdentities = usedIdentities; + this.cluster = cluster; + this.components = components; + } + + /** + * Remove all identities which are not used by other services or components + */ + public void remove(KerberosHelper kerberosHelper) throws AmbariException, KerberosOperationException { + Set<String> identitiesToRemove = skipUsed().stream().map(KerberosIdentityDescriptor::getName).collect(toSet()); + if (!identitiesToRemove.isEmpty()) { + kerberosHelper.deleteIdentities(cluster, components, identitiesToRemove); + } + } + + private List<KerberosIdentityDescriptor> skipUsed() throws AmbariException { + return candidateIdentities.stream().filter(each -> !usedIdentities.contains(each)).collect(toList()); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/UsedIdentities.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/UsedIdentities.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/UsedIdentities.java new file mode 100644 index 0000000..46f5642 --- /dev/null +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/utilities/UsedIdentities.java @@ -0,0 +1,101 @@ +/* + * 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.controller.utilities; + +import static java.util.Collections.emptyList; +import static java.util.stream.Collectors.toList; +import static org.apache.ambari.server.state.kerberos.AbstractKerberosDescriptor.nullToEmpty; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.apache.ambari.server.AmbariException; +import org.apache.ambari.server.controller.KerberosHelper; +import org.apache.ambari.server.state.Cluster; +import org.apache.ambari.server.state.Service; +import org.apache.ambari.server.state.ServiceComponent; +import org.apache.ambari.server.state.ServiceComponentHost; +import org.apache.ambari.server.state.kerberos.KerberosDescriptor; +import org.apache.ambari.server.state.kerberos.KerberosIdentityDescriptor; +import org.apache.ambari.server.state.kerberos.KerberosServiceDescriptor; + +/** + * I represent a group of identities that are still used by any non-excluded component or service + */ +public class UsedIdentities { + private final List<KerberosIdentityDescriptor> used; + + public static UsedIdentities none() throws AmbariException { + return new UsedIdentities(emptyList()); + } + + /** + * Get all identities of the installed services and components. Skip service or component that is excluded. + */ + public static UsedIdentities populate(Cluster cluster, ServiceExclude serviceExclude, ComponentExclude componentExclude, KerberosHelper kerberosHelper) throws AmbariException { + List<KerberosIdentityDescriptor> result = new ArrayList<>(); + KerberosDescriptor root = kerberosHelper.getKerberosDescriptor(cluster); + result.addAll(nullToEmpty(root.getIdentities())); + for (Service service : cluster.getServices().values()) { + if (serviceExclude.shouldExclude(service.getName())) { + continue; + } + KerberosServiceDescriptor serviceDescriptor = root.getService(service.getName()); + if (serviceDescriptor != null) { + result.addAll(nullToEmpty(serviceDescriptor.getIdentities())); + result.addAll(nullToEmpty(componentIdentities(serviceDescriptor, service, componentExclude))); + } + } + return new UsedIdentities(result); + } + + private static List<KerberosIdentityDescriptor> componentIdentities(KerberosServiceDescriptor serviceDescriptor, Service service, ComponentExclude componentExclude) { + return service.getServiceComponents().values() + .stream() + .filter(component -> !isComponentExcluded(service, componentExclude, component)) + .flatMap(component -> serviceDescriptor.getComponentIdentities(component.getName()).stream()) + .collect(toList()); + } + + private static boolean isComponentExcluded(Service service, ComponentExclude componentExclude, ServiceComponent component) { + return component.getServiceComponentHosts().isEmpty() + || componentExclude.shouldExclude(service.getName(), component.getName(), component.getServiceComponentHosts().values()); + } + + private UsedIdentities(List<KerberosIdentityDescriptor> used) { + this.used = used; + } + + /** + * @return true if there is an identity in the used list with the same keytab or principal name than the given identity + */ + public boolean contains(KerberosIdentityDescriptor identity) { + return used.stream().anyMatch(each -> identity.isShared(each)); + } + + public interface ServiceExclude { + boolean shouldExclude(String serviceName); + ServiceExclude NONE = serviceName -> false; // default implementation, exclude nothing + } + + public interface ComponentExclude { + boolean shouldExclude(String serviceName, String componentName, Collection<ServiceComponentHost> hosts); + ComponentExclude NONE = (serviceName, componentName, hosts) -> false; // default implementation, exclude nothing + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceComponentUninstalledEvent.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceComponentUninstalledEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceComponentUninstalledEvent.java index 5b55339..8acc401 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceComponentUninstalledEvent.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceComponentUninstalledEvent.java @@ -17,6 +17,8 @@ */ package org.apache.ambari.server.events; +import org.apache.ambari.server.serveraction.kerberos.Component; + /** * The {@link ServiceComponentUninstalledEvent} class is fired when a service * component is successfully uninstalled. @@ -85,4 +87,8 @@ public class ServiceComponentUninstalledEvent extends ServiceEvent { buffer.append("}"); return buffer.toString(); } + + public Component getComponent() { + return new Component(getHostName(), getServiceName(), getComponentName()); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java index aca00a8..de96342 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/events/ServiceRemovedEvent.java @@ -17,23 +17,24 @@ */ package org.apache.ambari.server.events; +import static java.util.stream.Collectors.toList; + +import java.util.List; + +import org.apache.ambari.server.serveraction.kerberos.Component; + /** * The {@link ServiceRemovedEvent} class is fired when a service is successfully * removed. */ public class ServiceRemovedEvent extends ServiceEvent { - /** - * Constructor. - * - * @param clusterId - * @param stackName - * @param stackVersion - * @param serviceName - */ + private final List<Component> components; + public ServiceRemovedEvent(long clusterId, String stackName, - String stackVersion, String serviceName) { + String stackVersion, String serviceName, List<Component> components) { super(AmbariEventType.SERVICE_REMOVED_SUCCESS, clusterId, stackName, - stackVersion, serviceName); + stackVersion, serviceName); + this.components = components; } /** @@ -49,4 +50,12 @@ public class ServiceRemovedEvent extends ServiceEvent { buffer.append("}"); return buffer.toString(); } + + public List<Component> getComponents() { + return components; + } + + public List<String> getComponentNames() { + return components.stream().map(Component::getServiceComponentName).collect(toList()); + } } http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java index a23b914..d0f8d0b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/dao/ClusterDAO.java @@ -217,6 +217,21 @@ public class ClusterDAO { } /** + * Gets the latest configurations for a given stack with any of the given config types. + * This method does not take into account the configuration being enabled. + */ + @RequiresSession + public List<ClusterConfigEntity> getLatestConfigurationsWithTypes(long clusterId, StackId stackId, Collection<String> configTypes) { + StackEntity stackEntity = stackDAO.find(stackId.getStackName(), stackId.getStackVersion()); + return daoUtils.selectList( + entityManagerProvider.get() + .createNamedQuery("ClusterConfigEntity.findLatestConfigsByStackWithTypes", ClusterConfigEntity.class) + .setParameter("clusterId", clusterId) + .setParameter("stack", stackEntity) + .setParameter("types", configTypes)); + } + + /** * Gets the latest configurations for a given stack for all of the * configurations of the specified cluster. * http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java index 34f3034..3a74367 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/orm/entities/ClusterConfigEntity.java @@ -62,6 +62,9 @@ import org.apache.commons.lang.builder.EqualsBuilder; name = "ClusterConfigEntity.findLatestConfigsByStack", query = "SELECT clusterConfig FROM ClusterConfigEntity clusterConfig WHERE clusterConfig.clusterId = :clusterId AND clusterConfig.stack = :stack AND clusterConfig.selectedTimestamp = (SELECT MAX(clusterConfig2.selectedTimestamp) FROM ClusterConfigEntity clusterConfig2 WHERE clusterConfig2.clusterId=:clusterId AND clusterConfig2.stack=:stack AND clusterConfig2.type = clusterConfig.type)"), @NamedQuery( + name = "ClusterConfigEntity.findLatestConfigsByStackWithTypes", + query = "SELECT clusterConfig FROM ClusterConfigEntity clusterConfig WHERE clusterConfig.type IN :types AND clusterConfig.clusterId = :clusterId AND clusterConfig.stack = :stack AND clusterConfig.selectedTimestamp = (SELECT MAX(clusterConfig2.selectedTimestamp) FROM ClusterConfigEntity clusterConfig2 WHERE clusterConfig2.clusterId=:clusterId AND clusterConfig2.stack=:stack AND clusterConfig2.type = clusterConfig.type)"), + @NamedQuery( name = "ClusterConfigEntity.findNotMappedClusterConfigsToService", query = "SELECT clusterConfig FROM ClusterConfigEntity clusterConfig WHERE clusterConfig.serviceConfigEntities IS EMPTY AND clusterConfig.type != 'cluster-env'"), @NamedQuery( http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java index b4f7120..9597ba1 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/Cluster.java @@ -267,6 +267,13 @@ public interface Cluster { Config getConfig(String configType, String versionTag); /** + * Get latest (including inactive ones) configurations with any of the given types. + * This method does not take into account the configuration being enabled. + * @return the list of configurations with the given types + */ + List<Config> getLatestConfigsWithTypes(Collection<String> types); + + /** * Gets the specific config that matches the specified type and version. This not * necessarily a DESIRED configuration that applies to a cluster. * @param configType the config type to find http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java index 5084703..74d79c8 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/ServiceImpl.java @@ -51,6 +51,7 @@ import org.apache.ambari.server.orm.entities.ServiceConfigEntity; import org.apache.ambari.server.orm.entities.ServiceDesiredStateEntity; import org.apache.ambari.server.orm.entities.ServiceDesiredStateEntityPK; import org.apache.ambari.server.orm.entities.StackEntity; +import org.apache.ambari.server.serveraction.kerberos.Component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -588,6 +589,7 @@ public class ServiceImpl implements Service { @Override @Transactional public void delete() throws AmbariException { + List<Component> components = getComponents(); // XXX temporal coupling, need to call this BEFORE deletingAllComponents deleteAllComponents(); deleteAllServiceConfigs(); @@ -601,11 +603,21 @@ public class ServiceImpl implements Service { } ServiceRemovedEvent event = new ServiceRemovedEvent(getClusterId(), stackId.getStackName(), - stackId.getStackVersion(), getName()); + stackId.getStackVersion(), getName(), components); eventPublisher.publish(event); } + private List<Component> getComponents() { + List<Component> result = new ArrayList<>(); + for (ServiceComponent component : getServiceComponents().values()) { + for (ServiceComponentHost host : component.getServiceComponentHosts().values()) { + result.add(new Component(host.getHostName(), getName(), component.getName())); + } + } + return result; + } + @Transactional protected void removeEntities() throws AmbariException { serviceDesiredStateDAO.removeByPK(serviceDesiredStateEntityPK); http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java index 06b6217..c950d67 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/cluster/ClusterImpl.java @@ -35,6 +35,7 @@ import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.locks.ReadWriteLock; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.persistence.EntityManager; @@ -1125,6 +1126,14 @@ public class ClusterImpl implements Cluster { } @Override + public List<Config> getLatestConfigsWithTypes(Collection<String> types) { + return clusterDAO.getLatestConfigurationsWithTypes(clusterId, getDesiredStackVersion(), types) + .stream() + .map(clusterConfigEntity -> configFactory.createExisting(this, clusterConfigEntity)) + .collect(Collectors.toList()); + } + + @Override public Config getConfigByVersion(String configType, Long configVersion) { clusterGlobalLock.readLock().lock(); try { http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java index 0a89c1d..5658133 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/AbstractKerberosDescriptorContainer.java @@ -18,6 +18,8 @@ package org.apache.ambari.server.state.kerberos; +import static java.util.stream.Collectors.toList; + import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -777,6 +779,16 @@ public abstract class AbstractKerberosDescriptorContainer extends AbstractKerber return map; } + /** + * @return identities which are not references to other identities + */ + public List<KerberosIdentityDescriptor> getIdentitiesSkipReferences() { + return nullToEmpty(getIdentities()) + .stream() + .filter(identity -> !identity.getReferencedServiceName().isPresent() && identity.getName() != null && !identity.getName().startsWith("/")) + .collect(toList()); + } + @Override public int hashCode() { return super.hashCode() + http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java index 41d1f65..768a17e 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosComponentDescriptor.java @@ -17,9 +17,7 @@ */ package org.apache.ambari.server.state.kerberos; -import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -113,19 +111,6 @@ public class KerberosComponentDescriptor extends AbstractKerberosDescriptorConta return null; } - /** - * @return identities which are not references to other identities - */ - public List<KerberosIdentityDescriptor> getIdentitiesSkipReferences() { - List<KerberosIdentityDescriptor> result = new ArrayList<>(); - for (KerberosIdentityDescriptor each : nullToEmpty(getIdentities())) { - if (!each.getReferencedServiceName().isPresent() && each.getName() != null && !each.getName().startsWith("/")) { - result.add(each); - } - } - return result; - } - @Override public int hashCode() { return 35 * super.hashCode(); http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java index 2023793..911723b 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/state/kerberos/KerberosIdentityDescriptor.java @@ -17,10 +17,8 @@ */ package org.apache.ambari.server.state.kerberos; -import java.util.List; import java.util.Map; -import org.apache.ambari.server.AmbariException; import org.apache.ambari.server.collections.Predicate; import org.apache.ambari.server.collections.PredicateUtils; @@ -371,16 +369,12 @@ public class KerberosIdentityDescriptor extends AbstractKerberosDescriptor { } } + /** - * @return true if this identity either has the same principal or keytab as any of the given identities. + * @return true if the given identity has the same principal or keytab as me */ - public boolean isShared(List<KerberosIdentityDescriptor> identities) throws AmbariException { - for (KerberosIdentityDescriptor each : identities) { - if (hasSamePrincipal(each) || hasSameKeytab(each)) { - return true; - } - } - return false; + public boolean isShared(KerberosIdentityDescriptor that) { + return hasSamePrincipal(that) || hasSameKeytab(that); } private boolean hasSameKeytab(KerberosIdentityDescriptor that) { http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/test/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleanerTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleanerTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleanerTest.java index d22c92e..027f339 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleanerTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/utilities/KerberosIdentityCleanerTest.java @@ -18,15 +18,20 @@ package org.apache.ambari.server.controller.utilities; import static com.google.common.collect.Lists.newArrayList; +import static com.google.common.collect.Sets.newHashSet; +import static java.util.Collections.singletonList; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.expectLastCall; import static org.easymock.EasyMock.reset; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.apache.ambari.server.controller.KerberosHelper; import org.apache.ambari.server.events.ServiceComponentUninstalledEvent; +import org.apache.ambari.server.events.ServiceRemovedEvent; import org.apache.ambari.server.events.publishers.AmbariEventPublisher; import org.apache.ambari.server.serveraction.kerberos.Component; import org.apache.ambari.server.serveraction.kerberos.KerberosMissingAdminCredentialsException; @@ -35,6 +40,7 @@ import org.apache.ambari.server.state.Clusters; 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; import org.apache.ambari.server.state.kerberos.KerberosDescriptor; import org.apache.ambari.server.state.kerberos.KerberosDescriptorFactory; import org.easymock.EasyMockRule; @@ -47,6 +53,7 @@ import org.junit.Test; public class KerberosIdentityCleanerTest extends EasyMockSupport { @Rule public EasyMockRule mocks = new EasyMockRule(this); private static final String HOST = "c6401"; + private static final String HOST2 = "c6402"; private static final String OOZIE = "OOZIE"; private static final String OOZIE_SERVER = "OOZIE_SERVER"; private static final String OOZIE_2 = "OOZIE2"; @@ -55,6 +62,9 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { private static final String RESOURCE_MANAGER_2 = "RESOURCE_MANAGER2"; private static final String YARN = "YARN"; private static final String RESOURCE_MANAGER = "RESOURCE_MANAGER"; + private static final String HDFS = "HDFS"; + private static final String NAMENODE = "NAMENODE"; + private static final String DATANODE = "DATANODE"; private static final long CLUSTER_ID = 1; @Mock private KerberosHelper kerberosHelper; @Mock private Clusters clusters; @@ -66,8 +76,8 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { @Test public void removesAllKerberosIdentitesOfComponentAfterComponentWasUninstalled() throws Exception { - installComponent(OOZIE, OOZIE_SERVER); - kerberosHelper.deleteIdentity(cluster, new Component(HOST, OOZIE, OOZIE_SERVER), newArrayList("oozie_server1", "oozie_server2")); + installComponent(OOZIE, OOZIE_SERVER, HOST); + kerberosHelper.deleteIdentities(cluster, singletonList(new Component(HOST, OOZIE, OOZIE_SERVER)), newHashSet("oozie_server1", "oozie_server2")); expectLastCall().once(); replayAll(); uninstallComponent(OOZIE, OOZIE_SERVER, HOST); @@ -83,9 +93,9 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { @Test public void skipsRemovingIdentityThatIsSharedByPrincipalName() throws Exception { - installComponent(OOZIE, OOZIE_SERVER); - installComponent(OOZIE_2, OOZIE_SERVER_2); - kerberosHelper.deleteIdentity(cluster, new Component(HOST, OOZIE, OOZIE_SERVER), newArrayList("oozie_server1")); + installComponent(OOZIE, OOZIE_SERVER, HOST); + installComponent(OOZIE_2, OOZIE_SERVER_2, HOST); + kerberosHelper.deleteIdentities(cluster, singletonList(new Component(HOST, OOZIE, OOZIE_SERVER)), newHashSet("oozie_server1")); expectLastCall().once(); replayAll(); uninstallComponent(OOZIE, OOZIE_SERVER, HOST); @@ -94,9 +104,9 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { @Test public void skipsRemovingIdentityThatIsSharedByKeyTabFilePath() throws Exception { - installComponent(YARN, RESOURCE_MANAGER); - installComponent(YARN_2, RESOURCE_MANAGER_2); - kerberosHelper.deleteIdentity(cluster, new Component(HOST, YARN, RESOURCE_MANAGER), newArrayList("rm_unique")); + installComponent(YARN, RESOURCE_MANAGER, HOST); + installComponent(YARN_2, RESOURCE_MANAGER_2, HOST); + kerberosHelper.deleteIdentities(cluster, singletonList(new Component(HOST, YARN, RESOURCE_MANAGER)), newHashSet("rm_unique")); expectLastCall().once(); replayAll(); uninstallComponent(YARN, RESOURCE_MANAGER, HOST); @@ -112,11 +122,43 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { verifyAll(); } - private void installComponent(String serviceName, final String componentName) { + @Test + public void skipsRemovingIdentityIfComponentIsStillInstalledOnADifferentHost() throws Exception { + installComponent(OOZIE, OOZIE_SERVER, HOST, HOST2); + replayAll(); + uninstallComponent(OOZIE, OOZIE_SERVER, HOST); + verifyAll(); + } + + @Test + public void removesServiceIdentitiesSkipComponentIdentitiesAfterServiceWasUninstalled() throws Exception { + installComponent(OOZIE, OOZIE_SERVER, HOST); + kerberosHelper.deleteIdentities(cluster, hdfsComponents(), newHashSet("hdfs-service")); + expectLastCall().once(); + replayAll(); + uninstallService(HDFS, hdfsComponents()); + verifyAll(); + } + + private ArrayList<Component> hdfsComponents() { + return newArrayList(new Component(HOST, HDFS, NAMENODE), new Component(HOST, HDFS, DATANODE)); + } + + private void installComponent(String serviceName, String componentName, String... hostNames) { Service service = createMock(serviceName + "_" + componentName, Service.class); + ServiceComponent component = createMock(componentName, ServiceComponent.class); + expect(component.getName()).andReturn(componentName).anyTimes(); + Map<String, ServiceComponentHost> hosts = new HashMap<>(); + expect(component.getServiceComponentHosts()).andReturn(hosts).anyTimes(); + for (String hostName : hostNames) { + ServiceComponentHost host = createMock(hostName, ServiceComponentHost.class); + expect(host.getHostName()).andReturn(hostName).anyTimes(); + hosts.put(hostName, host); + } installedServices.put(serviceName, service); + expect(service.getName()).andReturn(serviceName).anyTimes(); expect(service.getServiceComponents()).andReturn(new HashMap<String, ServiceComponent>() {{ - put(componentName, null); + put(componentName, component); }}).anyTimes(); } @@ -124,6 +166,10 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { kerberosIdentityCleaner.componentRemoved(new ServiceComponentUninstalledEvent(CLUSTER_ID, "any", "any", service, component, host, false)); } + private void uninstallService(String service, List<Component> components) throws KerberosMissingAdminCredentialsException { + kerberosIdentityCleaner.serviceRemoved(new ServiceRemovedEvent(CLUSTER_ID, "any", "any", service, components)); + } + @Before public void setUp() throws Exception { kerberosIdentityCleaner = new KerberosIdentityCleaner(new AmbariEventPublisher(), kerberosHelper, clusters); @@ -139,7 +185,8 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { " 'name': '/HDFS/NAMENODE/hdfs'" + " }," + " {" + - " 'name': 'oozie_server1'" + + " 'name': 'oozie_server1'," + + " 'principal': { 'value': 'oozie1/_h...@example.com' }" + " }," +"" + " {" + " 'name': 'oozie_server2'," + @@ -193,6 +240,39 @@ public class KerberosIdentityCleanerTest extends EasyMockSupport { " ]" + " }" + " ]" + + " }," + + " {" + + " 'name': 'HDFS'," + + " 'identities': [" + + " {" + + " 'name': 'hdfs-service'" + + " }," + + " {" + + " 'name': 'shared'," + + " 'principal': { 'value': 'oozie/_h...@example.com' }" + + " }," + + " {" + + " 'name': '/YARN/RESOURCE_MANAGER/rm'" + + " }," + + " ]," + + " 'components': [" + + " {" + + " 'name': 'NAMENODE'," + + " 'identities': [" + + " {" + + " 'name': 'namenode'" + + " }" + + " ]" + + " }," + + " {" + + " 'name': 'DATANODE'," + + " 'identities': [" + + " {" + + " 'name': 'datanode'" + + " }" + + " ]" + + " }" + + " ]" + " }" + " ]" + "}"); http://git-wip-us.apache.org/repos/asf/ambari/blob/e767aa44/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java index 406349a..80cb4dc 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/orm/dao/ServiceConfigDAOTest.java @@ -17,6 +17,8 @@ */ package org.apache.ambari.server.orm.dao; +import static java.util.Arrays.asList; + import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; @@ -482,6 +484,16 @@ public class ServiceConfigDAOTest { Assert.assertTrue(entity.isSelected()); } + @Test + public void testGetLatestClusterConfigsWithTypes() throws Exception { + initClusterEntities(); + ClusterEntity clusterEntity = clusterDAO.findByName("c1"); + List<ClusterConfigEntity> entities = clusterDAO.getLatestConfigurationsWithTypes(clusterEntity.getClusterId(), HDP_01, asList("oozie-site")); + Assert.assertEquals(1, entities.size()); + entities = clusterDAO.getLatestConfigurationsWithTypes(clusterEntity.getClusterId(), HDP_01, asList("no-such-type")); + Assert.assertTrue(entities.isEmpty()); + } + /** * Tests getting latest and enabled configurations when there is a * configuration group. Configurations for configuration groups are not