package com.cloudbees.jenkins.plugins.sshagent; import com.cloudbees.jenkins.plugins.sshcredentials.SSHUserPrivateKey; import com.cloudbees.jenkins.plugins.sshcredentials.impl.BasicSSHUserPrivateKey; import com.cloudbees.plugins.credentials.CredentialsProvider; import com.cloudbees.plugins.credentials.CredentialsScope; import com.cloudbees.plugins.credentials.SystemCredentialsProvider; import com.cloudbees.plugins.credentials.domains.Domain; import hudson.Util; import hudson.model.Fingerprint; import hudson.util.Secret; import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; import org.jenkinsci.plugins.workflow.cps.CpsFlowExecution; import org.jenkinsci.plugins.workflow.job.WorkflowJob; import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.jenkinsci.plugins.workflow.test.steps.SemaphoreStep; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.junit.runners.model.Statement; import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.RestartableJenkinsRule; import java.util.ArrayList; import java.util.List; import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.core.IsCollectionContaining.hasItem; import static org.hamcrest.core.IsNull.notNullValue; import static org.hamcrest.core.IsNull.nullValue; import static org.junit.Assert.*; public class SSHAgentStepWorkflowTest extends SSHAgentBase { @Rule public RestartableJenkinsRule story = new RestartableJenkinsRule(); @ClassRule public static BuildWatcher buildWatcher = new BuildWatcher(); @Test public void sshAgentAvailable() throws Exception { story.addStep(new Statement() { @Override public void evaluate() throws Throwable { startMockSSHServer(); List<String> credentialIds = new ArrayList<String>(); credentialIds.add(CREDENTIAL_ID); SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees", new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test"); SystemCredentialsProvider.getInstance().getCredentials().add(key); SystemCredentialsProvider.getInstance().save(); WorkflowJob job = story.j.jenkins.createProject(WorkflowJob.class, "sshAgentAvailable"); job.setDefinition(new CpsFlowDefinition("" + "node('" + story.j.createSlave().getNodeName() + "') {\n" + " sshagent (credentials: ['" + CREDENTIAL_ID + "']) {\n" + " sh 'ls -l $SSH_AUTH_SOCK && ssh -o StrictHostKeyChecking=no -p " + getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST + "'\n" + " }\n" + "}\n", true) ); story.j.assertBuildStatusSuccess(job.scheduleBuild2(0)); stopMockSSHServer(); } }); } /** * This test verifies: * * 1. The Job is executed successfully * 2. SSH_AUTH_SOCK is available before and after Jenkins was restarted * 3. SSH_AUTH_SOCK has different values before and after Jenkins was restarted * * It verifies that {@link SSHAgentStepExecution#onResume()} method is invoked and a new SSH Agent is launched after Jenkins is restarted. * * @throws Exception */ @Test public void sshAgentAvailableAfterRestart() throws Exception { story.addStep(new Statement() { @Override public void evaluate() throws Throwable { startMockSSHServer(); List<String> credentialIds = new ArrayList<String>(); credentialIds.add(CREDENTIAL_ID); SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees", new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test"); SystemCredentialsProvider.getInstance().getCredentials().add(key); SystemCredentialsProvider.getInstance().save(); WorkflowJob p = story.j.jenkins.createProject(WorkflowJob.class, "sshAgentAvailableAfterRestart"); p.setDefinition(new CpsFlowDefinition("" + "node {\n" + " sshagent (credentials: ['" + CREDENTIAL_ID + "']) {\n" + " sh 'ssh -o StrictHostKeyChecking=no -p " + getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST + "'\n" + " echo \"SSH Agent before restart ${env.SSH_AUTH_SOCK}\"\n" + " semaphore 'sshAgentAvailableAfterRestart'\n" + " sh 'ssh -o StrictHostKeyChecking=no -p " + getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST + "'\n" + " echo \"SSH Agent after restart ${env.SSH_AUTH_SOCK}\"\n" + " }\n" + "}\n", true)); // get the build going WorkflowRun b = p.scheduleBuild2(0).getStartCondition().get(); CpsFlowExecution e = (CpsFlowExecution) b.getExecutionPromise().get(); // wait until the executor gets assigned and the execution pauses SemaphoreStep.waitForStart("sshAgentAvailableAfterRestart/1", b); assertTrue(JenkinsRule.getLog(b), b.isBuilding()); } }); story.addStep(new Statement() { @Override public void evaluate() throws Throwable { WorkflowJob p = story.j.jenkins.getItemByFullName("sshAgentAvailableAfterRestart", WorkflowJob.class); WorkflowRun b = p.getBuildByNumber(1); SemaphoreStep.success("sshAgentAvailableAfterRestart/1", null); story.j.assertBuildStatusSuccess(story.j.waitForCompletion(b)); Pattern pattern = Pattern.compile("(?:SSH Agent (?:before|after) restart )/.+/ssh-.+/agent.(\\d)+"); Scanner sc = new Scanner(b.getLogFile()); List<String> socketFile = new ArrayList<String>(); while (sc.hasNextLine()) { String match = sc.findInLine(pattern); if (match != null) { socketFile.add(match); } else { sc.nextLine(); } } sc.close(); assertEquals(socketFile.toString(), 2, socketFile.size()); assertNotEquals(socketFile.get(0), socketFile.get(1)); stopMockSSHServer(); } }); } @Issue("JENKINS-38830") @Test public void testTrackingOfCredential() { story.addStep(new Statement() { @Override public void evaluate() throws Throwable { startMockSSHServer(); List<String> credentialIds = new ArrayList<String>(); credentialIds.add(CREDENTIAL_ID); SSHUserPrivateKey key = new BasicSSHUserPrivateKey(CredentialsScope.GLOBAL, credentialIds.get(0), "cloudbees", new BasicSSHUserPrivateKey.DirectEntryPrivateKeySource(getPrivateKey()), "cloudbees", "test"); SystemCredentialsProvider.getInstance().getCredentials().add(key); SystemCredentialsProvider.getInstance().save(); Fingerprint fingerprint = CredentialsProvider.getFingerprintOf(key); WorkflowJob job = story.j.jenkins.createProject(WorkflowJob.class, "sshAgentAvailable"); job.setDefinition(new CpsFlowDefinition("" + "node {\n" + " sshagent (credentials: ['" + CREDENTIAL_ID + "']) {\n" + " sh 'ls -l $SSH_AUTH_SOCK && ssh -o StrictHostKeyChecking=no -p " + getAssignedPort() + " -v -l cloudbees " + SSH_SERVER_HOST + "'\n" + " }\n" + "}\n", true) ); assertThat("No fingerprint created until first use", fingerprint, nullValue()); story.j.assertBuildStatusSuccess(job.scheduleBuild2(0)); fingerprint = CredentialsProvider.getFingerprintOf(key); assertThat(fingerprint, notNullValue()); assertThat(fingerprint.getJobs(), hasItem(is(job.getFullName()))); stopMockSSHServer(); } }); } }