package com.sequenceiq.it.cloudbreak.mock; import static com.sequenceiq.it.spark.ITResponse.AMBARI_API_ROOT; import static com.sequenceiq.it.spark.ITResponse.SALT_API_ROOT; import static com.sequenceiq.it.spark.ITResponse.SALT_BOOT_ROOT; import static spark.Spark.get; import static spark.Spark.post; import static spark.Spark.put; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.junit.Before; import org.springframework.http.HttpStatus; import org.testng.Assert; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Optional; import org.testng.annotations.Parameters; import org.testng.annotations.Test; import com.fasterxml.jackson.annotation.JsonAutoDetect; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.common.collect.ImmutableList; import com.sequenceiq.cloudbreak.api.endpoint.ClusterEndpoint; import com.sequenceiq.cloudbreak.api.model.ClusterRequest; import com.sequenceiq.cloudbreak.api.model.ConstraintJson; import com.sequenceiq.cloudbreak.api.model.GatewayJson; import com.sequenceiq.cloudbreak.api.model.HostGroupRequest; import com.sequenceiq.cloudbreak.api.model.KerberosRequest; import com.sequenceiq.cloudbreak.cloud.model.CloudVmMetaDataStatus; import com.sequenceiq.cloudbreak.orchestrator.model.GenericResponse; import com.sequenceiq.cloudbreak.orchestrator.model.GenericResponses; import com.sequenceiq.it.IntegrationTestContext; import com.sequenceiq.it.cloudbreak.AbstractMockIntegrationTest; import com.sequenceiq.it.cloudbreak.CloudbreakITContextConstants; import com.sequenceiq.it.cloudbreak.CloudbreakUtil; import com.sequenceiq.it.cloudbreak.HostGroup; import com.sequenceiq.it.spark.ITResponse; import com.sequenceiq.it.spark.ambari.AmbariCheckResponse; import com.sequenceiq.it.spark.ambari.AmbariClusterRequestsResponse; import com.sequenceiq.it.spark.ambari.AmbariClusterResponse; import com.sequenceiq.it.spark.ambari.AmbariClustersHostsResponse; import com.sequenceiq.it.spark.ambari.AmbariHostsResponse; import com.sequenceiq.it.spark.ambari.AmbariServicesComponentsResponse; import com.sequenceiq.it.spark.ambari.AmbariStatusResponse; import com.sequenceiq.it.spark.ambari.EmptyAmbariClusterResponse; import com.sequenceiq.it.spark.ambari.EmptyAmbariResponse; import com.sequenceiq.it.spark.salt.SaltApiRunPostResponse; import com.sequenceiq.it.util.HostNameUtil; import com.sequenceiq.it.verification.Verification; public class MockClusterCreationWithSaltSuccessTest extends AbstractMockIntegrationTest { private boolean clusterCreated; @Before public void setup() { clusterCreated = false; } @BeforeMethod public void setContextParameters() { IntegrationTestContext itContext = getItContext(); Assert.assertNotNull(itContext.getContextParam(CloudbreakITContextConstants.BLUEPRINT_ID), "Blueprint id is mandatory."); Assert.assertNotNull(itContext.getContextParam(CloudbreakITContextConstants.STACK_ID), "Stack id is mandatory."); } @Test @Parameters({"clusterName", "ambariPort", "ambariUser", "ambariPassword", "emailNeeded", "enableSecurity", "kerberosMasterKey", "kerberosAdmin", "kerberosPassword", "runRecipesOnHosts", "checkAmbari", "mockPort"}) public void testClusterCreation(@Optional("it-cluster") String clusterName, @Optional("8080") String ambariPort, @Optional("admin") String ambariUser, @Optional("admin123!@#") String ambariPassword, @Optional("false") boolean emailNeeded, @Optional("false") boolean enableSecurity, @Optional String kerberosMasterKey, @Optional String kerberosAdmin, @Optional String kerberosPassword, @Optional("") String runRecipesOnHosts, @Optional("true") boolean checkAmbari, @Optional("9443") int mockPort) throws Exception { // GIVEN IntegrationTestContext itContext = getItContext(); String stackIdStr = itContext.getContextParam(CloudbreakITContextConstants.STACK_ID); Integer stackId = Integer.valueOf(stackIdStr); Integer blueprintId = Integer.valueOf(itContext.getContextParam(CloudbreakITContextConstants.BLUEPRINT_ID)); List<HostGroup> hostgroups = itContext.getContextParam(CloudbreakITContextConstants.HOSTGROUP_ID, List.class); Set<HostGroupRequest> hostGroupJsons1 = convertHostGroups(hostgroups, runRecipesOnHosts); itContext.putContextParam(CloudbreakITContextConstants.AMBARI_USER_ID, ambariUser); itContext.putContextParam(CloudbreakITContextConstants.AMBARI_PASSWORD_ID, ambariPassword); // WHEN ClusterRequest clusterRequest = new ClusterRequest(); clusterRequest.setName(clusterName); clusterRequest.setDescription("Cluster for integration test"); clusterRequest.setEmailNeeded(emailNeeded); clusterRequest.setEnableSecurity(enableSecurity); clusterRequest.setPassword(ambariPassword); clusterRequest.setUserName(ambariUser); clusterRequest.setBlueprintId(Long.valueOf(blueprintId)); clusterRequest.setHostGroups(hostGroupJsons1); KerberosRequest kerberosRequest = new KerberosRequest(); kerberosRequest.setAdmin(kerberosAdmin); kerberosRequest.setPassword(kerberosPassword); kerberosRequest.setMasterKey(kerberosMasterKey); clusterRequest.setKerberos(kerberosRequest); GatewayJson gatewayJson = new GatewayJson(); gatewayJson.setEnableGateway(Boolean.TRUE); gatewayJson.setExposedServices(ImmutableList.of("ALL")); clusterRequest.setGateway(gatewayJson); initSpark(); Map<String, CloudVmMetaDataStatus> instanceMap = itContext.getContextParam(CloudbreakITContextConstants.MOCK_INSTANCE_MAP, Map.class); if (instanceMap == null || instanceMap.size() == 0) { throw new IllegalStateException("instance map should not be empty!"); } addSaltMappings(instanceMap); addAmbariMappings(instanceMap); ClusterEndpoint clusterEndpoint = getCloudbreakClient().clusterEndpoint(); Long clusterId = clusterEndpoint.post(Long.valueOf(stackId), clusterRequest).getId(); // THEN Assert.assertNotNull(clusterId); CloudbreakUtil.waitAndCheckStackStatus(getCloudbreakClient(), stackIdStr, "AVAILABLE"); CloudbreakUtil.checkClusterAvailability(getCloudbreakClient().stackEndpoint(), ambariPort, stackIdStr, ambariUser, ambariPassword, checkAmbari); verifyCalls(instanceMap, clusterName); } private void verifyCalls(Map<String, CloudVmMetaDataStatus> instanceMap, String clusterName) { verify(SALT_BOOT_ROOT + "/health", "GET").exactTimes(1).verify(); Verification distributeVerification = verify(SALT_BOOT_ROOT + "/salt/action/distribute", "POST").exactTimes(1); for (String instanceId : instanceMap.keySet()) { CloudVmMetaDataStatus cloudVmMetaDataStatus = instanceMap.get(instanceId); distributeVerification.bodyContains("address\":\"" + cloudVmMetaDataStatus.getMetaData().getPrivateIp()); } distributeVerification.verify(); verify(AMBARI_API_ROOT + "/services/AMBARI/components/AMBARI_SERVER", "GET").exactTimes(1).verify(); verify(AMBARI_API_ROOT + "/clusters", "GET").exactTimes(2).verify(); verify(AMBARI_API_ROOT + "/check", "GET").exactTimes(1).verify(); verify(AMBARI_API_ROOT + "/users/admin", "PUT").exactTimes(1).bodyContains("Users/password").bodyContains("Users/old_password").verify(); verify(AMBARI_API_ROOT + "/blueprints/bp", "POST").exactTimes(1) .bodyContains("blueprint_name").bodyContains("stack_name").bodyContains("stack_version").bodyContains("host_groups") .exactTimes(1).verify(); verify(AMBARI_API_ROOT + "/clusters/" + clusterName, "POST").exactTimes(1).bodyContains("blueprint").bodyContains("default_password") .bodyContains("host_groups").verify(); verify(AMBARI_API_ROOT + "/clusters/ambari_cluster/requests/1", "GET").atLeast(1).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=saltutil.sync_grains").atLeast(1).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=mine.update").atLeast(1).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=state.highstate").atLeast(2).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=jobs.lookup_jid").bodyContains("jid=1").atLeast(2).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=grains.append").bodyContains("ambari_agent").exactTimes(1).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=grains.append").bodyContains("ambari_server").exactTimes(1).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=grains.remove").bodyContains("recipes").exactTimes(2).verify(); verify(SALT_API_ROOT + "/run", "POST").bodyContains("fun=jobs.active").atLeast(2).verify(); verify(SALT_BOOT_ROOT + "/file", "POST").exactTimes(0).verify(); verify(SALT_BOOT_ROOT + "/file/distribute", "POST").exactTimes(4).verify(); } private void addAmbariMappings(Map<String, CloudVmMetaDataStatus> instanceMap) { get(AMBARI_API_ROOT + "/clusters/:cluster/requests/:request", new AmbariStatusResponse()); post(AMBARI_API_ROOT + "/views/:view/versions/1.0.0/instances/*", new EmptyAmbariResponse()); get(AMBARI_API_ROOT + "/clusters", (req, resp) -> { ITResponse itResp = clusterCreated ? new AmbariClusterResponse(instanceMap) : new EmptyAmbariClusterResponse(); return itResp.handle(req, resp); }); post(AMBARI_API_ROOT + "/clusters/:cluster/requests", new AmbariClusterRequestsResponse()); post(AMBARI_API_ROOT + "/clusters/:cluster", (req, resp) -> { clusterCreated = true; return new EmptyAmbariResponse().handle(req, resp); }, gson()::toJson); get(AMBARI_API_ROOT + "/clusters", new AmbariClusterResponse(instanceMap)); post(AMBARI_API_ROOT + "/clusters/:cluster/requests", new AmbariClusterRequestsResponse()); post(AMBARI_API_ROOT + "/clusters/:cluster", new EmptyAmbariResponse()); get(AMBARI_API_ROOT + "/services/AMBARI/components/AMBARI_SERVER", new AmbariServicesComponentsResponse(), gson()::toJson); get(AMBARI_API_ROOT + "/hosts", new AmbariHostsResponse(instanceMap), gson()::toJson); get(AMBARI_API_ROOT + "/blueprints/*", (request, response) -> responseFromJsonFile("blueprint/hdp-small-default.bp")); post(AMBARI_API_ROOT + "/blueprints/*", new EmptyAmbariResponse()); put(AMBARI_API_ROOT + "/users/admin", new EmptyAmbariResponse()); get(AMBARI_API_ROOT + "/check", new AmbariCheckResponse()); post(AMBARI_API_ROOT + "/users", new EmptyAmbariResponse()); get(AMBARI_API_ROOT + "/clusters/:cluster/hosts", new AmbariClustersHostsResponse(instanceMap, "SUCCESSFUL")); put(AMBARI_API_ROOT + "/stacks/HDP/versions/:version/operating_systems/:os/repositories/:hdpversion", new EmptyAmbariResponse()); } private void addSaltMappings(Map<String, CloudVmMetaDataStatus> instanceMap) { ObjectMapper objectMapper = new ObjectMapper(); get(SALT_BOOT_ROOT + "/health", (request, response) -> { GenericResponse genericResponse = new GenericResponse(); genericResponse.setStatusCode(HttpStatus.OK.value()); return genericResponse; }, gson()::toJson); objectMapper.setVisibility(objectMapper.getVisibilityChecker().withGetterVisibility(JsonAutoDetect.Visibility.NONE)); post(SALT_API_ROOT + "/run", new SaltApiRunPostResponse(instanceMap)); post(SALT_BOOT_ROOT + "/file", (request, response) -> { response.status(HttpStatus.CREATED.value()); return response; }); post(SALT_BOOT_ROOT + "/salt/server/pillar", (request, response) -> { GenericResponse genericResponse = new GenericResponse(); genericResponse.setStatusCode(HttpStatus.OK.value()); return genericResponse; }, gson()::toJson); post(SALT_BOOT_ROOT + "/salt/action/distribute", (request, response) -> { GenericResponses genericResponses = new GenericResponses(); genericResponses.setResponses(new ArrayList<>()); return genericResponses; }, gson()::toJson); post(SALT_BOOT_ROOT + "/hostname/distribute", (request, response) -> { GenericResponses genericResponses = new GenericResponses(); ArrayList<GenericResponse> responses = new ArrayList<>(); for (String instanceId : instanceMap.keySet()) { CloudVmMetaDataStatus cloudVmMetaDataStatus = instanceMap.get(instanceId); GenericResponse genericResponse = new GenericResponse(); genericResponse.setAddress(cloudVmMetaDataStatus.getMetaData().getPrivateIp()); genericResponse.setStatus(HostNameUtil.generateHostNameByIp(cloudVmMetaDataStatus.getMetaData().getPrivateIp())); genericResponse.setStatusCode(HttpStatus.OK.value()); responses.add(genericResponse); } genericResponses.setResponses(responses); return genericResponses; }, gson()::toJson); post(SALT_BOOT_ROOT + "/file/distribute", (request, response) -> { GenericResponses genericResponses = new GenericResponses(); GenericResponse genericResponse = new GenericResponse(); genericResponse.setStatusCode(HttpStatus.CREATED.value()); genericResponses.setResponses(Collections.singletonList(genericResponse)); return genericResponses; }, gson()::toJson); post(SALT_BOOT_ROOT + "/salt/server/pillar/distribute", (request, response) -> { GenericResponses genericResponses = new GenericResponses(); GenericResponse genericResponse = new GenericResponse(); genericResponse.setStatusCode(HttpStatus.OK.value()); genericResponses.setResponses(Collections.singletonList(genericResponse)); return genericResponses; }, gson()::toJson); } private Set<HostGroupRequest> convertHostGroups(List<HostGroup> hostGroups, String runRecipesOnHosts) { Set<Long> recipeIds = Collections.emptySet(); List<String> hostGroupsWithRecipe = Collections.emptyList(); if (!runRecipesOnHosts.isEmpty()) { recipeIds = getItContext().getContextParam(CloudbreakITContextConstants.RECIPE_ID, Set.class); Assert.assertFalse(recipeIds == null || recipeIds.isEmpty()); hostGroupsWithRecipe = Arrays.asList(runRecipesOnHosts.split(",")); } Set<HostGroupRequest> hgMaps = new HashSet<>(); for (HostGroup hostgroup : hostGroups) { HostGroupRequest hostGroupBase = new HostGroupRequest(); hostGroupBase.setName(hostgroup.getName()); ConstraintJson constraintJson = new ConstraintJson(); constraintJson.setInstanceGroupName(hostgroup.getInstanceGroupName()); constraintJson.setHostCount(hostgroup.getHostCount()); hostGroupBase.setConstraint(constraintJson); if (hostGroupsWithRecipe.contains(hostgroup.getName())) { hostGroupBase.setRecipeIds(recipeIds); } hgMaps.add(hostGroupBase); } return hgMaps; } }