package com.sequenceiq.it.cloudbreak.recipes; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.apache.commons.lang3.tuple.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.testng.Assert; import org.testng.annotations.Parameters; import org.testng.annotations.Test; import com.sequenceiq.cloudbreak.api.endpoint.StackEndpoint; import com.sequenceiq.cloudbreak.api.model.InstanceGroupResponse; import com.sequenceiq.cloudbreak.api.model.InstanceMetaDataJson; import com.sequenceiq.cloudbreak.client.CloudbreakClient; import com.sequenceiq.it.IntegrationTestContext; import com.sequenceiq.it.cloudbreak.AbstractCloudbreakIntegrationTest; import com.sequenceiq.it.cloudbreak.CloudbreakITContextConstants; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.common.IOUtils; import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.transport.verification.PromiscuousVerifier; public class CountRecipeResultsTest extends AbstractCloudbreakIntegrationTest { private static final Logger LOGGER = LoggerFactory.getLogger(CountRecipeResultsTest.class); @Value("${integrationtest.defaultPrivateKeyFile}") private String defaultPrivateKeyFile; @Test @Parameters({"searchRecipesOnHosts", "lookingFor", "require"}) public void testFetchRecipeResult(String searchRecipesOnHosts, String lookingFor, Integer require) throws Exception { //GIVEN Assert.assertEquals(new File(defaultPrivateKeyFile).exists(), true, "Private cert file not found: " + defaultPrivateKeyFile); Assert.assertFalse(lookingFor.isEmpty()); IntegrationTestContext itContext = getItContext(); String stackId = itContext.getContextParam(CloudbreakITContextConstants.STACK_ID); StackEndpoint stackEndpoint = itContext.getContextParam(CloudbreakITContextConstants.CLOUDBREAK_CLIENT, CloudbreakClient.class).stackEndpoint(); List<InstanceGroupResponse> instanceGroups = stackEndpoint.get(Long.valueOf(stackId)).getInstanceGroups(); String[] files = lookingFor.split(","); List<String> publicIps = getPublicIps(instanceGroups, Arrays.asList(searchRecipesOnHosts.split(","))); List<Future> futures = new ArrayList<>(publicIps.size() * files.length); ExecutorService executorService = Executors.newFixedThreadPool(publicIps.size()); final AtomicInteger count = new AtomicInteger(0); //WHEN try { for (final String file : files) { for (final String ip : publicIps) { futures.add(executorService.submit(() -> { if (findFile(ip, file)) { count.incrementAndGet(); } })); } } for (Future<Boolean> future : futures) { future.get(); } } finally { executorService.shutdown(); } //THEN Assert.assertEquals(count.get(), require.intValue(), "The number of existing files is different than required."); } private List<String> getPublicIps(List<InstanceGroupResponse> instanceGroups, List<String> hostGroupsWithRecipe) { List<String> ips = new ArrayList<>(); for (InstanceGroupResponse instanceGroup : instanceGroups) { if (hostGroupsWithRecipe.contains(instanceGroup.getGroup())) { for (InstanceMetaDataJson metaData : instanceGroup.getMetadata()) { ips.add(metaData.getPublicIp()); } } } return ips; } private Boolean findFile(String host, String file) { SSHClient sshClient = null; boolean result = false; try { sshClient = createSSHClient(host, 22, "cloudbreak", defaultPrivateKeyFile); Pair<Integer, String> cmdOut = executeSshCommand(sshClient, "sudo ls " + file); result = cmdOut.getLeft() == 0 && cmdOut.getRight().startsWith(file); } catch (Exception ex) { LOGGER.error("Error during remote command execution", ex); } finally { try { if (sshClient != null) { sshClient.disconnect(); } } catch (Exception ex) { LOGGER.error("Error during ssh disconnect", ex); } } return result; } private SSHClient createSSHClient(String host, int port, String user, String privateKeyFile) throws IOException { SSHClient sshClient = new SSHClient(); sshClient.addHostKeyVerifier(new PromiscuousVerifier()); sshClient.connect(host, port); sshClient.authPublickey(user, privateKeyFile); return sshClient; } private Session startSshSession(SSHClient ssh) throws IOException { Session sshSession = ssh.startSession(); sshSession.allocateDefaultPTY(); return sshSession; } private Pair<Integer, String> executeSshCommand(SSHClient ssh, String command) throws IOException { Session session = null; Session.Command cmd = null; try { session = startSshSession(ssh); cmd = session.exec(command); String stdout = IOUtils.readFully(cmd.getInputStream()).toString(); cmd.join(10, TimeUnit.SECONDS); return Pair.of(cmd.getExitStatus(), stdout); } finally { if (cmd != null) { cmd.close(); } if (session != null) { session.close(); } } } }