Repository: ambari Updated Branches: refs/heads/trunk 4e80ff992 -> a116498e9
AMBARI-9385. Implement Keytab regeneration (rlevas) Project: http://git-wip-us.apache.org/repos/asf/ambari/repo Commit: http://git-wip-us.apache.org/repos/asf/ambari/commit/a116498e Tree: http://git-wip-us.apache.org/repos/asf/ambari/tree/a116498e Diff: http://git-wip-us.apache.org/repos/asf/ambari/diff/a116498e Branch: refs/heads/trunk Commit: a116498e92babd241f2bc083bb1ef2325f4ca3d7 Parents: 4e80ff9 Author: Robert Levas <rle...@hortonworks.com> Authored: Fri Jan 30 10:57:59 2015 -0500 Committer: Robert Levas <rle...@hortonworks.com> Committed: Fri Jan 30 12:46:44 2015 -0500 ---------------------------------------------------------------------- .../resources/ClusterResourceDefinition.java | 10 + .../AmbariManagementControllerImpl.java | 13 +- .../server/controller/KerberosHelper.java | 96 ++++++- .../AmbariManagementControllerImplTest.java | 66 ++++- .../server/controller/KerberosHelperTest.java | 268 ++++++++++++++++++- 5 files changed, 435 insertions(+), 18 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ambari/blob/a116498e/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java index 9b744d0..980a40f 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/api/resources/ClusterResourceDefinition.java @@ -18,6 +18,7 @@ package org.apache.ambari.server.api.resources; +import java.util.Collection; import java.util.HashSet; import java.util.Set; @@ -75,4 +76,13 @@ public class ClusterResourceDefinition extends BaseStacksResourceDefinition { return setChildren; } + + @Override + public Collection<String> getUpdateDirectives() { + Collection<String> directives = super.getUpdateDirectives(); + directives.add("regenerate_keytabs"); + + return directives; + } + } http://git-wip-us.apache.org/repos/asf/ambari/blob/a116498e/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java index e867f99..b6dd5c4 100644 --- a/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java +++ b/ambari-server/src/main/java/org/apache/ambari/server/controller/AmbariManagementControllerImpl.java @@ -1153,12 +1153,12 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle // // *************************************************** - response = updateCluster(request); + response = updateCluster(request, requestProperties); } return response; } - private synchronized RequestStatusResponse updateCluster(ClusterRequest request) + private synchronized RequestStatusResponse updateCluster(ClusterRequest request, Map<String, String> requestProperties) throws AmbariException { RequestStageContainer requestStageContainer = null; @@ -1331,7 +1331,13 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle // set the new security type of the cluster if change is requested SecurityType securityType = request.getSecurityType(); - if((securityType != null) && (cluster.getSecurityType() != securityType) ){ + + if(securityType != null) { + // if any custom operations are valid and requested, the process of executing them should be initiated, + // most of the validation logic will be left to the KerberosHelper to avoid polluting the controller + if (kerberosHelper.shouldExecuteCustomOperations(securityType, requestProperties)) { + requestStageContainer = kerberosHelper.executeCustomOperations(cluster, request.getKerberosDescriptor(), requestProperties, requestStageContainer); + } else if (cluster.getSecurityType() != securityType) { LOG.info("Received cluster security type change request from {} to {}", cluster.getSecurityType().name(), securityType.name()); @@ -1345,6 +1351,7 @@ public class AmbariManagementControllerImpl implements AmbariManagementControlle } cluster.setSecurityType(securityType); + } } if (requestStageContainer != null) { http://git-wip-us.apache.org/repos/asf/ambari/blob/a116498e/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 6bb9bf1..fd1fb57 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 @@ -136,6 +136,7 @@ public class KerberosHelper { */ private static ClusterController clusterController = null; + /** * The Handler implementation that provides the logic to enable Kerberos */ @@ -189,11 +190,6 @@ public class KerberosHelper { // Update KerberosDetails with the new security type - the current one in the cluster is the "old" value kerberosDetails.setSecurityType(securityType); - //todo: modify call from cluster state transition to not include descriptor - if (kerberosDescriptor == null) { - kerberosDescriptor = getClusterDescriptor(cluster); - } - if (securityType == SecurityType.KERBEROS) { LOG.info("Configuring Kerberos for realm {} on cluster, {}", kerberosDetails.getDefaultRealm(), cluster.getClusterName()); requestStageContainer = handle(cluster, kerberosDescriptor, kerberosDetails, null, null, requestStageContainer, enableKerberosHandler); @@ -208,6 +204,52 @@ public class KerberosHelper { } /** + * Used to execute custom security operations which are sent as directives in URI + * + * @param cluster the relevant Cluster + * @param kerberosDescriptor a KerberosDescriptor containing updates to the descriptor already + * configured for the cluster + * @param requestProperties this structure is expected to hold already supported and validated directives + * for the 'Cluster' resource. See ClusterResourceDefinition#getUpdateDirectives + * @param requestStageContainer a RequestStageContainer to place generated stages, if needed - + * if null a new RequestStageContainer will be created. @return the updated or a new RequestStageContainer containing the stages that need to be + * executed to complete this task; or null if no stages need to be executed. + * @throws AmbariException + */ + public RequestStageContainer executeCustomOperations(Cluster cluster, KerberosDescriptor kerberosDescriptor, + Map<String, String> requestProperties, RequestStageContainer requestStageContainer) + throws AmbariException { + + if (requestProperties != null) { + + for (SupportedCustomOperation operation : SupportedCustomOperation.values()) { + if (requestProperties.containsKey(operation.name().toLowerCase())) { + String value = requestProperties.get(operation.name().toLowerCase()); + + // The operation specific logic is kept in one place and described here + switch (operation) { + case REGENERATE_KEYTABS: + if (cluster.getSecurityType() != SecurityType.KERBEROS) { + throw new AmbariException(String.format("Custom operation %s can only be requested with the security type cluster property: %s", operation.name(), SecurityType.KERBEROS.name())); + } + + if ("true".equalsIgnoreCase(value)) { + handle(cluster, kerberosDescriptor, getKerberosDetails(cluster), null, null, requestStageContainer, createPrincipalsAndKeytabsHandler); + } + break; + + default: // No other operations are currently supported + throw new AmbariException(String.format("Custom operation not supported: %s", operation.name())); + } + } + } + } + + return requestStageContainer; + } + + + /** * Ensures the set of filtered principals and keytabs exist on the cluster. * <p/> * No configurations will be altered as a result of this operation, however principals and keytabs @@ -279,6 +321,11 @@ public class KerberosHelper { Map<String, Service> services = cluster.getServices(); + //todo: modify call from cluster state transition to not include descriptor + if (kerberosDescriptor == null) { + kerberosDescriptor = getClusterDescriptor(cluster); + } + if ((services != null) && !services.isEmpty()) { SecurityState desiredSecurityState = handler.getNewServiceSecurityState(); String clusterName = cluster.getClusterName(); @@ -696,8 +743,7 @@ public class KerberosHelper { * is available from the endpoint * stacks/:stackName/versions/:version/artifacts/kerberos_descriptor. * - * @param cluster cluster instance - * + * @param cluster cluster instance * @return the kerberos descriptor associated with the specified cluster * @throws AmbariException if unable to obtain the descriptor */ @@ -733,11 +779,11 @@ public class KerberosHelper { // parent cluster doesn't exist. shouldn't happen since we have the cluster instance e.printStackTrace(); throw new AmbariException("An unknown error occurred while trying to obtain the cluster kerberos descriptor", e); - } catch (NoSuchResourceException e) { + } catch (NoSuchResourceException e) { // no descriptor registered, use the default from the stack } - if (response != null && ! response.isEmpty()) { + if (response != null && !response.isEmpty()) { Resource descriptorResource = response.iterator().next(); String descriptor_data = (String) descriptorResource.getPropertyValue( ArtifactResourceProvider.ARTIFACT_DATA_PROPERTY); @@ -1165,7 +1211,7 @@ public class KerberosHelper { /** * Determine if a cluster has kerberos enabled. * - * @param cluster cluster to test + * @param cluster cluster to test * @return true if the provided cluster has kerberos enabled; false otherwise */ public boolean isClusterKerberosEnabled(Cluster cluster) { @@ -1173,6 +1219,13 @@ public class KerberosHelper { } /** + * A enumeration of the supported custom operations + */ + public static enum SupportedCustomOperation { + REGENERATE_KEYTABS + } + + /** * Handler is an interface that needs to be implemented by toggle handler classes to do the * "right" thing for the task at hand. */ @@ -1620,6 +1673,29 @@ public class KerberosHelper { } } + /** + * Method used to externally peek if weather we have supported operations to execute or not + * <p/> + * It is required that the SecurityType from the request is wither KERBEROS or NONE and that at least one + * directive in the requestProperties map is supported. + * + * @param requestSecurityType the SecurityType from the request + * @param requestProperties A Map of request directives and their values + * @return true if custom operations should be executed; false otherwise + */ + public boolean shouldExecuteCustomOperations(SecurityType requestSecurityType, Map<String, String> requestProperties) { + + if (((requestSecurityType == SecurityType.KERBEROS) || (requestSecurityType == SecurityType.NONE)) && + (requestProperties != null) && !requestProperties.isEmpty()) { + for (SupportedCustomOperation type : SupportedCustomOperation.values()) { + if (requestProperties.containsKey(type.name().toLowerCase())) { + return true; + } + } + } + return false; + } + /** * KerberosDetails is a helper class to hold the details of the relevant Kerberos-specific http://git-wip-us.apache.org/repos/asf/ambari/blob/a116498e/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java ---------------------------------------------------------------------- diff --git a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java index 1a66cd9..e0667da 100644 --- a/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java +++ b/ambari-server/src/test/java/org/apache/ambari/server/controller/AmbariManagementControllerImplTest.java @@ -536,6 +536,55 @@ public class AmbariManagementControllerImplTest { } /** + * Ensure that when the appropriate request directives are set, KerberosHelper#executeCustomOperations + * is invoked + */ + @Test + public void testUpdateClustersKerberosCustomOperationsInvoked() throws Exception { + // member state mocks + Capture<AmbariManagementController> controllerCapture = new Capture<AmbariManagementController>(); + Injector injector = createStrictMock(Injector.class); + Cluster cluster = createNiceMock(Cluster.class); + ActionManager actionManager = createNiceMock(ActionManager.class); + ClusterRequest clusterRequest = createNiceMock(ClusterRequest.class); + + // requests + Set<ClusterRequest> requests = Collections.singleton(clusterRequest); + + // request properties (aka directives) + Map<String, String> requestProperties = Collections.singletonMap("regenerate_keytabs", "true"); + + KerberosHelper kerberosHelper = createMockBuilder(KerberosHelper.class) + .addMockedMethod("executeCustomOperations", Cluster.class, KerberosDescriptor.class, Map.class, RequestStageContainer.class) + .createStrictMock(); + + // expectations + injector.injectMembers(capture(controllerCapture)); + expect(injector.getInstance(Gson.class)).andReturn(null); + expect(injector.getInstance(MaintenanceStateHelper.class)).andReturn(null); + expect(injector.getInstance(KerberosHelper.class)).andReturn(kerberosHelper); + expect(clusterRequest.getClusterId()).andReturn(1L).times(6); + expect(clusterRequest.getSecurityType()).andReturn(SecurityType.KERBEROS).anyTimes(); + expect(clusters.getClusterById(1L)).andReturn(cluster).times(2); + expect(cluster.getClusterName()).andReturn("cluster").times(2); + + expect(kerberosHelper.executeCustomOperations(cluster, null, requestProperties, null)) + .andReturn(null) + .once(); + + // replay mocks + replay(actionManager, cluster, clusters, injector, clusterRequest, sessionManager, kerberosHelper); + + // test + AmbariManagementController controller = new AmbariManagementControllerImpl(actionManager, clusters, injector); + controller.updateClusters(requests, requestProperties); + + // assert and verify + assertSame(controller, controllerCapture.getValue()); + verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager, kerberosHelper); + } + + /** * Ensure that when the cluster is updated KerberosHandler.toggleKerberos is not invoked unless * the security type is altered */ @@ -573,7 +622,7 @@ public class AmbariManagementControllerImplTest { // assert and verify assertSame(controller, controllerCapture.getValue()); - verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager); + verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager, kerberosHelper); } /** @@ -607,6 +656,9 @@ public class AmbariManagementControllerImplTest { cluster.addSessionAttributes(anyObject(Map.class)); expectLastCall().once(); + expect(kerberosHelper.shouldExecuteCustomOperations(SecurityType.KERBEROS, null)) + .andReturn(false) + .once(); expect(kerberosHelper.toggleKerberos(anyObject(Cluster.class), anyObject(SecurityType.class), anyObject(KerberosDescriptor.class), anyObject(RequestStageContainer.class))) .andReturn(null) .once(); @@ -620,7 +672,7 @@ public class AmbariManagementControllerImplTest { // assert and verify assertSame(controller, controllerCapture.getValue()); - verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager); + verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager, kerberosHelper); } /** @@ -654,6 +706,9 @@ public class AmbariManagementControllerImplTest { cluster.addSessionAttributes(anyObject(Map.class)); expectLastCall().once(); + expect(kerberosHelper.shouldExecuteCustomOperations(SecurityType.NONE, null)) + .andReturn(false) + .once(); expect(kerberosHelper.toggleKerberos(anyObject(Cluster.class), anyObject(SecurityType.class), anyObject(KerberosDescriptor.class), anyObject(RequestStageContainer.class))) .andReturn(null) .once(); @@ -667,7 +722,7 @@ public class AmbariManagementControllerImplTest { // assert and verify assertSame(controller, controllerCapture.getValue()); - verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager); + verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager, kerberosHelper); } /** @@ -710,6 +765,9 @@ public class AmbariManagementControllerImplTest { cluster.addSessionAttributes(anyObject(Map.class)); expectLastCall().once(); + expect(kerberosHelper.shouldExecuteCustomOperations(SecurityType.NONE, null)) + .andReturn(false) + .once(); expect(kerberosHelper.toggleKerberos(anyObject(Cluster.class), anyObject(SecurityType.class), anyObject(KerberosDescriptor.class), anyObject(RequestStageContainer.class))) .andThrow(new IllegalArgumentException("bad args!")) .once(); @@ -730,7 +788,7 @@ public class AmbariManagementControllerImplTest { // assert and verify assertSame(controller, controllerCapture.getValue()); - verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager); + verify(actionManager, cluster, clusters, injector, clusterRequest, sessionManager, kerberosHelper); } /** http://git-wip-us.apache.org/repos/asf/ambari/blob/a116498e/ambari-server/src/test/java/org/apache/ambari/server/controller/KerberosHelperTest.java ---------------------------------------------------------------------- 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 e976d81..3532e69 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 @@ -109,7 +109,7 @@ import static org.powermock.api.easymock.PowerMock.verifyAll; @RunWith(PowerMockRunner.class) @PrepareForTest(KerberosDescriptor.class) -@PowerMockIgnore({"javax.crypto.*" }) +@PowerMockIgnore({"javax.crypto.*"}) @SuppressWarnings("unchecked") public class KerberosHelperTest { @@ -309,6 +309,35 @@ public class KerberosHelperTest { } } + @Test + public void testExecuteCustomOperationsInvalidOperation() throws Exception { + KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class); + final Cluster cluster = createNiceMock(Cluster.class); + + try { + kerberosHelper.executeCustomOperations(cluster, createNiceMock(KerberosDescriptor.class), + Collections.singletonMap("invalid_operation", "false"), null); + } + catch(Throwable t) { + Assert.fail("Exception should not have been thrown"); + } + } + + @Test(expected = AmbariException.class) + public void testRegenerateKeytabsInvalidValue() throws Exception { + KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class); + final Cluster cluster = createNiceMock(Cluster.class); + + kerberosHelper.executeCustomOperations(cluster, createNiceMock(KerberosDescriptor.class), + Collections.singletonMap("regenerate_keytabs", "false"), null); + Assert.fail("AmbariException should have failed"); + } + + @Test + public void testRegenerateKeytabs() throws Exception { + testRegenerateKeytabs(new KerberosCredential("principal", "password", "keytab"), false, false); + } + private void testEnableKerberos(final KerberosCredential kerberosCredential, boolean getClusterDescriptor, boolean getStackDescriptor) throws Exception { @@ -548,6 +577,243 @@ public class KerberosHelperTest { verifyAll(); } + private void testRegenerateKeytabs(final KerberosCredential kerberosCredential, + boolean getClusterDescriptor, + boolean getStackDescriptor) throws Exception { + + KerberosHelper kerberosHelper = injector.getInstance(KerberosHelper.class); + + final StackId stackVersion = createNiceMock(StackId.class); + + final ServiceComponentHost sch1 = createMock(ServiceComponentHost.class); + expect(sch1.getServiceName()).andReturn("SERVICE1").once(); + expect(sch1.getServiceComponentName()).andReturn("COMPONENT1").once(); + expect(sch1.getSecurityState()).andReturn(SecurityState.UNSECURED).anyTimes(); + expect(sch1.getDesiredSecurityState()).andReturn(SecurityState.UNSECURED).anyTimes(); + expect(sch1.getStackVersion()).andReturn(stackVersion).anyTimes(); + expect(sch1.getHostName()).andReturn("host1").anyTimes(); + + final ServiceComponentHost sch2 = createMock(ServiceComponentHost.class); + expect(sch2.getServiceName()).andReturn("SERVICE2").once(); + expect(sch2.getServiceComponentName()).andReturn("COMPONENT2").once(); + expect(sch2.getSecurityState()).andReturn(SecurityState.UNSECURED).anyTimes(); + expect(sch2.getDesiredSecurityState()).andReturn(SecurityState.UNSECURED).anyTimes(); + expect(sch2.getStackVersion()).andReturn(stackVersion).anyTimes(); + expect(sch2.getHostName()).andReturn("host1").anyTimes(); + + final Host host = createNiceMock(Host.class); + expect(host.getHostName()).andReturn("host1").once(); + expect(host.getState()).andReturn(HostState.HEALTHY).once(); + + final Service service1 = createStrictMock(Service.class); + expect(service1.getName()).andReturn("SERVICE1").anyTimes(); + expect(service1.getServiceComponents()) + .andReturn(Collections.<String, ServiceComponent>emptyMap()) + .once(); + + final Service service2 = createStrictMock(Service.class); + expect(service2.getName()).andReturn("SERVICE2").anyTimes(); + expect(service2.getServiceComponents()) + .andReturn(Collections.<String, ServiceComponent>emptyMap()) + .once(); + + final Map<String, String> kerberosEnvProperties = createNiceMock(Map.class); + // TODO: (rlevas) Add when AMBARI 9121 is complete + // expect(kerberosEnvProperties.get("kdc_type")).andReturn("mit-kdc").once(); + + final Config kerberosEnvConfig = createNiceMock(Config.class); + expect(kerberosEnvConfig.getProperties()).andReturn(kerberosEnvProperties).once(); + + final Map<String, String> krb5ConfProperties = createNiceMock(Map.class); + expect(krb5ConfProperties.get("kdc_type")).andReturn("mit-kdc").once(); + expect(krb5ConfProperties.get("realm")).andReturn("FOOBAR.COM").once(); + + final Config krb5ConfConfig = createNiceMock(Config.class); + // TODO: (rlevas) Remove when AMBARI 9121 is complete + expect(krb5ConfConfig.getProperties()).andReturn(krb5ConfProperties).once(); + + final MaintenanceStateHelper maintenanceStateHelper = injector.getInstance(MaintenanceStateHelper.class); + expect(maintenanceStateHelper.getEffectiveState(anyObject(ServiceComponentHost.class))) + .andReturn(MaintenanceState.OFF).anyTimes(); + + final Cluster cluster = createNiceMock(Cluster.class); + expect(cluster.getSecurityType()).andReturn(SecurityType.KERBEROS).once(); + expect(cluster.getDesiredConfigByType("krb5-conf")).andReturn(krb5ConfConfig).once(); + expect(cluster.getDesiredConfigByType("kerberos-env")).andReturn(kerberosEnvConfig).once(); + expect(cluster.getClusterName()).andReturn("c1").anyTimes(); + expect(cluster.getServices()) + .andReturn(new HashMap<String, Service>() { + { + put("SERVICE1", service1); + put("SERVICE2", service2); + } + }) + .anyTimes(); + expect(cluster.getServiceComponentHosts("host1")) + .andReturn(new ArrayList<ServiceComponentHost>() { + { + add(sch1); + add(sch2); + } + }) + .once(); + expect(cluster.getCurrentStackVersion()) + .andReturn(new StackId("HDP", "2.2")) + .anyTimes(); + expect(cluster.getSessionAttributes()).andReturn(new HashMap<String, Object>() {{ + if (kerberosCredential != null) { + put("kerberos_admin/" + KerberosCredential.KEY_NAME_PRINCIPAL, kerberosCredential.getPrincipal()); + put("kerberos_admin/" + KerberosCredential.KEY_NAME_PASSWORD, kerberosCredential.getPassword()); + put("kerberos_admin/" + KerberosCredential.KEY_NAME_KEYTAB, kerberosCredential.getKeytab()); + } + }}).anyTimes(); + + final Clusters clusters = injector.getInstance(Clusters.class); + expect(clusters.getHostsForCluster("c1")) + .andReturn(new HashMap<String, Host>() { + { + put("host1", host); + } + }) + .once(); + + final AmbariManagementController ambariManagementController = injector.getInstance(AmbariManagementController.class); + expect(ambariManagementController.findConfigurationTagsWithOverrides(cluster, "host1")) + .andReturn(Collections.<String, Map<String, String>>emptyMap()) + .once(); + expect(ambariManagementController.getRoleCommandOrder(cluster)) + .andReturn(createNiceMock(RoleCommandOrder.class)) + .once(); + + final ConfigHelper configHelper = injector.getInstance(ConfigHelper.class); + expect(configHelper.getEffectiveConfigProperties(anyObject(Cluster.class), anyObject(Map.class))) + .andReturn(new HashMap<String, Map<String, String>>() { + { + put("cluster-env", new HashMap<String, String>() {{ + put("kerberos_domain", "FOOBAR.COM"); + }}); + } + }) + .once(); + expect(configHelper.getEffectiveConfigAttributes(anyObject(Cluster.class), anyObject(Map.class))) + .andReturn(Collections.<String, Map<String, Map<String, String>>>emptyMap()) + .once(); + + final KerberosPrincipalDescriptor principalDescriptor1 = createNiceMock(KerberosPrincipalDescriptor.class); + expect(principalDescriptor1.getValue()).andReturn("component1/_HOST@${realm}").once(); + expect(principalDescriptor1.getType()).andReturn(KerberosPrincipalType.SERVICE).once(); + expect(principalDescriptor1.getConfiguration()).andReturn("service1-site/component1.kerberos.principal").once(); + + final KerberosPrincipalDescriptor principalDescriptor2 = createNiceMock(KerberosPrincipalDescriptor.class); + expect(principalDescriptor2.getValue()).andReturn("component2/${host}@${realm}").once(); + expect(principalDescriptor2.getType()).andReturn(KerberosPrincipalType.SERVICE).once(); + expect(principalDescriptor2.getConfiguration()).andReturn("service2-site/component2.kerberos.principal").once(); + + final KerberosKeytabDescriptor keytabDescriptor1 = createNiceMock(KerberosKeytabDescriptor.class); + expect(keytabDescriptor1.getFile()).andReturn("${keytab_dir}/service1.keytab").once(); + expect(keytabDescriptor1.getOwnerName()).andReturn("service1").once(); + expect(keytabDescriptor1.getOwnerAccess()).andReturn("rw").once(); + expect(keytabDescriptor1.getGroupName()).andReturn("hadoop").once(); + expect(keytabDescriptor1.getGroupAccess()).andReturn("").once(); + expect(keytabDescriptor1.getConfiguration()).andReturn("service1-site/component1.keytab.file").once(); + + final KerberosKeytabDescriptor keytabDescriptor2 = createNiceMock(KerberosKeytabDescriptor.class); + expect(keytabDescriptor2.getFile()).andReturn("${keytab_dir}/service2.keytab").once(); + expect(keytabDescriptor2.getOwnerName()).andReturn("service2").once(); + expect(keytabDescriptor2.getOwnerAccess()).andReturn("rw").once(); + expect(keytabDescriptor2.getGroupName()).andReturn("hadoop").once(); + expect(keytabDescriptor2.getGroupAccess()).andReturn("").once(); + expect(keytabDescriptor2.getConfiguration()).andReturn("service2-site/component2.keytab.file").once(); + + final KerberosIdentityDescriptor identityDescriptor1 = createNiceMock(KerberosIdentityDescriptor.class); + expect(identityDescriptor1.getPrincipalDescriptor()).andReturn(principalDescriptor1).once(); + expect(identityDescriptor1.getKeytabDescriptor()).andReturn(keytabDescriptor1).once(); + + final KerberosIdentityDescriptor identityDescriptor2 = createNiceMock(KerberosIdentityDescriptor.class); + expect(identityDescriptor2.getPrincipalDescriptor()).andReturn(principalDescriptor2).once(); + expect(identityDescriptor2.getKeytabDescriptor()).andReturn(keytabDescriptor2).once(); + + final KerberosComponentDescriptor componentDescriptor1 = createNiceMock(KerberosComponentDescriptor.class); + expect(componentDescriptor1.getIdentities(true)). + andReturn(new ArrayList<KerberosIdentityDescriptor>() {{ + add(identityDescriptor1); + }}).once(); + + final KerberosComponentDescriptor componentDescriptor2 = createNiceMock(KerberosComponentDescriptor.class); + expect(componentDescriptor2.getIdentities(true)). + andReturn(new ArrayList<KerberosIdentityDescriptor>() {{ + add(identityDescriptor2); + }}).once(); + + final KerberosServiceDescriptor serviceDescriptor1 = createNiceMock(KerberosServiceDescriptor.class); + expect(serviceDescriptor1.getComponent("COMPONENT1")).andReturn(componentDescriptor1).once(); + + final KerberosServiceDescriptor serviceDescriptor2 = createNiceMock(KerberosServiceDescriptor.class); + expect(serviceDescriptor2.getComponent("COMPONENT2")).andReturn(componentDescriptor2).once(); + + final KerberosDescriptor kerberosDescriptor = createNiceMock(KerberosDescriptor.class); + expect(kerberosDescriptor.getService("SERVICE1")).andReturn(serviceDescriptor1).once(); + expect(kerberosDescriptor.getService("SERVICE2")).andReturn(serviceDescriptor2).once(); + + //todo: extract method? + if (getClusterDescriptor) { + // needed to mock the static method fromJson() + setupGetDescriptorFromCluster(kerberosDescriptor); + } else if (getStackDescriptor) { + setupGetDescriptorFromStack(kerberosDescriptor); + } + final StageFactory stageFactory = injector.getInstance(StageFactory.class); + expect(stageFactory.createNew(anyLong(), anyObject(String.class), anyObject(String.class), + anyLong(), anyObject(String.class), anyObject(String.class), anyObject(String.class), + anyObject(String.class))) + .andAnswer(new IAnswer<Stage>() { + @Override + public Stage answer() throws Throwable { + Stage stage = createNiceMock(Stage.class); + + expect(stage.getHostRoleCommands()) + .andReturn(Collections.<String, Map<String, HostRoleCommand>>emptyMap()) + .anyTimes(); + replay(stage); + return stage; + } + }) + .anyTimes(); + + // This is a STRICT mock to help ensure that the end result is what we want. + final RequestStageContainer requestStageContainer = createStrictMock(RequestStageContainer.class); + // Create Principals Stage + expect(requestStageContainer.getLastStageId()).andReturn(-1L).anyTimes(); + expect(requestStageContainer.getId()).andReturn(1L).once(); + requestStageContainer.addStages(anyObject(List.class)); + expectLastCall().once(); + // Create Keytabs Stage + expect(requestStageContainer.getLastStageId()).andReturn(0L).anyTimes(); + expect(requestStageContainer.getId()).andReturn(1L).once(); + requestStageContainer.addStages(anyObject(List.class)); + expectLastCall().once(); + // Distribute Keytabs Stage + expect(requestStageContainer.getLastStageId()).andReturn(1L).anyTimes(); + expect(requestStageContainer.getId()).andReturn(1L).once(); + requestStageContainer.addStages(anyObject(List.class)); + expectLastCall().once(); + // Clean-up/Finalize Stage + expect(requestStageContainer.getLastStageId()).andReturn(3L).anyTimes(); + expect(requestStageContainer.getId()).andReturn(1L).once(); + requestStageContainer.addStages(anyObject(List.class)); + expectLastCall().once(); + + replayAll(); + + // Needed by infrastructure + metaInfo.init(); + + kerberosHelper.executeCustomOperations(cluster, !(getClusterDescriptor || getStackDescriptor) ? + kerberosDescriptor : null, Collections.singletonMap("regenerate_keytabs", "true"), requestStageContainer); + + verifyAll(); + } + private void setupGetDescriptorFromCluster(KerberosDescriptor kerberosDescriptor) throws Exception { mockStatic(KerberosDescriptor.class); ResourceProvider resourceProvider = createStrictMock(ResourceProvider.class);