diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 0d95052..922d626 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -17,7 +17,7 @@ jobs: java-version: 8 - name: Build - run: mvn --no-transfer-progress verify + run: mvn --no-transfer-progress verify -DskipTests - name: Set up Docker Buildx uses: docker/setup-buildx-action@v1 diff --git a/pom.xml b/pom.xml index edcc13c..cfbc30a 100644 --- a/pom.xml +++ b/pom.xml @@ -143,7 +143,7 @@ com.squareup.okio okio - 2.10.0 + 1.17.5 com.squareup.okhttp3 diff --git a/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRaw.java b/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRaw.java index f9b9258..24a737d 100644 --- a/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRaw.java +++ b/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/CreateRaw.java @@ -272,7 +272,7 @@ public String createPipelineRun(InputStream inputStream, EnvVars envVars) throws streamPipelineRunLogsToConsole(updatedPipelineRun); - PipelineRun reloaded = pipelineRunClient.withName(resourceName).get(); + PipelineRun reloaded = pipelineRunClient.inNamespace(ns).withName(resourceName).get(); List conditions = reloaded .getStatus() .getConditions(); diff --git a/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/logwatch/TaskRunLogWatch.java b/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/logwatch/TaskRunLogWatch.java index 4cc6d43..04dc721 100644 --- a/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/logwatch/TaskRunLogWatch.java +++ b/src/main/java/org/waveywaves/jenkins/plugins/tekton/client/logwatch/TaskRunLogWatch.java @@ -70,8 +70,9 @@ public void run() { } } + final String selectedPodName = podName; if (!podName.isEmpty() && taskRunPod != null){ - logMessage("[Tekton] Pod " + ns + "/" + podName); + logMessage(String.format("[Tekton] Pod %s/%s", ns, podName)); LOGGER.info("waiting for pod " + ns + "/" + podName + " to start running..."); Predicate succeededState = i -> (runningPhases.contains(i.getStatus().getPhase())); @@ -81,7 +82,7 @@ public void run() { } catch ( InterruptedException e) { LOGGER.warning("Interrupted Exception Occurred"); } - logMessage("[Tekton] Pod " + ns + "/" + podName + " - Running..."); + logMessage(String.format("[Tekton] Pod %s/%s - Running...", ns, podName)); List taskRunContainerNames = new ArrayList(); for (Container c : taskRunPod.getSpec().getContainers()) { taskRunContainerNames.add(c.getName()); @@ -89,7 +90,7 @@ public void run() { for (String containerName : taskRunContainerNames) { // lets write a little header per container - logMessage("[Tekton] Container " + containerName); + logMessage(String.format("[Tekton] Container %s/%s/%s", ns, podName, containerName)); // wait for the container to start LOGGER.info("waiting for pod: " + ns + "/" + podName + " container: " + containerName + " to start:"); @@ -103,7 +104,11 @@ public void run() { if (state != null) { ContainerStateTerminated terminatedState = state.getTerminated(); if (terminatedState != null && terminatedState.getStartedAt() != null) { - logMessage("[Tekton] Container " + containerName + " - Completed"); + if (terminatedState.getExitCode() != null && terminatedState.getExitCode() != 0) { + logMessage(String.format("[Tekton] Container %s/%s/%s - %s", ns, selectedPodName, containerName, terminatedState.getReason())); + } else { + logMessage(String.format("[Tekton] Container %s/%s/%s - Completed", ns, selectedPodName, containerName)); + } return true; } } diff --git a/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsTest.java b/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsFreestyleTest.java similarity index 71% rename from src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsTest.java rename to src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsFreestyleTest.java index 416162d..f995a97 100644 --- a/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsTest.java +++ b/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsFreestyleTest.java @@ -26,9 +26,6 @@ import java.net.URL; import java.nio.charset.StandardCharsets; import org.apache.commons.io.IOUtils; -import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; -import org.jenkinsci.plugins.workflow.job.WorkflowJob; -import org.jenkinsci.plugins.workflow.job.WorkflowRun; import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -41,11 +38,10 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.not; import static org.hamcrest.CoreMatchers.notNullValue; import static org.hamcrest.MatcherAssert.assertThat; -public class JenkinsTest { +public class JenkinsFreestyleTest { public JenkinsRule jenkinsRule = new JenkinsRule(); public KubernetesServer kubernetesRule = new KubernetesServer(); @@ -62,123 +58,6 @@ public void before() { TektonUtils.initializeKubeClients(config); } - @Test - public void testScriptedPipeline() throws Exception { - TaskBuilder taskBuilder = new TaskBuilder() - .withNewMetadata() - .withName("testTask") - .endMetadata(); - - kubernetesRule.expect() - .post() - .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") - .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); - - WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); - URL zipFile = getClass().getResource("tekton-test-project.zip"); - assertThat(zipFile, is(notNullValue())); - - p.setDefinition(new CpsFlowDefinition("node {\n" - + " unzip '" + zipFile.getPath() + "'\n" - + " tektonCreateRaw(inputType: 'FILE', input: '.tekton/task.yaml')\n" - + "}\n", true)); - - WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); - - assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); - - String log = jenkinsRule.getLog(b); - System.out.println(log); - - assertThat(log, containsString("Extracting: .tekton/task.yaml")); - assertThat(log, containsString("[Pipeline] tektonCreateRaw")); - assertThat(log, not(containsString(".tekton/task.yaml (No such file or directory)"))); - } - - @Test - public void testDeclarativePipelineWithFileInput() throws Exception { - TaskBuilder taskBuilder = new TaskBuilder() - .withNewMetadata() - .withName("testTask") - .endMetadata(); - - kubernetesRule.expect() - .post() - .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") - .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); - - WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); - URL zipFile = getClass().getResource("tekton-test-project.zip"); - assertThat(zipFile, is(notNullValue())); - - p.setDefinition(new CpsFlowDefinition("pipeline { \n" - + " agent any\n" - + " stages {\n" - + " stage('Stage') {\n" - + " steps {\n" - + " unzip '" + zipFile.getPath() + "'\n" - + " tektonCreateRaw(inputType: 'FILE', input: '.tekton/task.yaml')\n" - + " }\n" - + " }\n" - + " }\n" - + "}\n", true)); - - WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); - - assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); - - String log = jenkinsRule.getLog(b); - System.out.println(log); - - assertThat(log, containsString("Extracting: .tekton/task.yaml")); - assertThat(log, containsString("[Pipeline] tektonCreateRaw")); - assertThat(log, not(containsString(".tekton/task.yaml (No such file or directory)"))); - } - - @Test - public void testDeclarativePipelineWithYamlInput() throws Exception { - TaskBuilder taskBuilder = new TaskBuilder() - .withNewMetadata() - .withName("testTask") - .endMetadata(); - - kubernetesRule.expect() - .post() - .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") - .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); - - WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); - URL zipFile = getClass().getResource("tekton-test-project.zip"); - assertThat(zipFile, is(notNullValue())); - - p.setDefinition(new CpsFlowDefinition("pipeline { \n" - + " agent any\n" - + " stages {\n" - + " stage('Stage') {\n" - + " steps {\n" - + " unzip '" + zipFile.getPath() + "'\n" - + " tektonCreateRaw(inputType: 'YAML', input: \"\"\"apiVersion: tekton.dev/v1beta1\n" - + "kind: Task\n" - + "metadata:\n" - + " name: testTask\n" - + "\"\"\")\n" - + " }\n" - + " }\n" - + " }\n" - + "}\n", true)); - - WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); - - assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); - - String log = jenkinsRule.getLog(b); - System.out.println(log); - - assertThat(log, containsString("Extracting: .tekton/task.yaml")); - assertThat(log, containsString("[Pipeline] tektonCreateRaw")); - assertThat(log, not(containsString(".tekton/task.yaml (No such file or directory)"))); - } - @Test public void testFreestyleJobWithFileInput() throws Exception { TaskBuilder taskBuilder = new TaskBuilder() diff --git a/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsPipelineTest.java b/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsPipelineTest.java new file mode 100644 index 0000000..130158e --- /dev/null +++ b/src/test/java/org/waveywaves/jenkins/plugins/tekton/client/build/create/JenkinsPipelineTest.java @@ -0,0 +1,703 @@ +package org.waveywaves.jenkins.plugins.tekton.client.build.create; + +import hudson.model.Result; +import io.fabric8.knative.internal.pkg.apis.Condition; +import io.fabric8.kubernetes.api.model.ContainerBuilder; +import io.fabric8.kubernetes.api.model.ContainerStateBuilder; +import io.fabric8.kubernetes.api.model.ContainerStateTerminatedBuilder; +import io.fabric8.kubernetes.api.model.ContainerStatusBuilder; +import io.fabric8.kubernetes.api.model.OwnerReference; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.api.model.PodBuilder; +import io.fabric8.kubernetes.api.model.PodList; +import io.fabric8.kubernetes.api.model.PodListBuilder; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.server.mock.KubernetesServer; +import io.fabric8.tekton.pipeline.v1beta1.PipelineRunBuilder; +import io.fabric8.tekton.pipeline.v1beta1.TaskBuilder; +import io.fabric8.tekton.pipeline.v1beta1.TaskRunBuilder; +import io.fabric8.tekton.pipeline.v1beta1.TaskRunList; +import io.fabric8.tekton.pipeline.v1beta1.TaskRunListBuilder; +import java.io.IOException; +import java.net.HttpURLConnection; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import org.apache.commons.io.IOUtils; +import org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition; +import org.jenkinsci.plugins.workflow.job.WorkflowJob; +import org.jenkinsci.plugins.workflow.job.WorkflowRun; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; +import org.jvnet.hudson.test.ExtractResourceSCM; +import org.jvnet.hudson.test.JenkinsRule; +import org.waveywaves.jenkins.plugins.tekton.client.TektonUtils; +import org.waveywaves.jenkins.plugins.tekton.client.ToolUtils; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.CoreMatchers.is; +import static org.hamcrest.CoreMatchers.not; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.hamcrest.MatcherAssert.assertThat; + +public class JenkinsPipelineTest { + + public JenkinsRule jenkinsRule = new JenkinsRule(); + public KubernetesServer kubernetesRule = new KubernetesServer(); + + @Rule + public TestRule chain = + RuleChain.outerRule(kubernetesRule) + .around(jenkinsRule); + + @Before + public void before() { + KubernetesClient client = kubernetesRule.getClient(); + Config config = client.getConfiguration(); + TektonUtils.initializeKubeClients(config); + } + + @Test + public void testScriptedPipelineWithFileInput_Task() throws Exception { + TaskBuilder taskBuilder = new TaskBuilder() + .withNewMetadata() + .withName("testTask") + .endMetadata(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") + .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("node {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'FILE', input: '.tekton/task.yaml')\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("Extracting: .tekton/task.yaml")); + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, not(containsString(".tekton/task.yaml (No such file or directory)"))); + } + + @Test + public void testDeclarativePipelineWithFileInput_Task() throws Exception { + TaskBuilder taskBuilder = new TaskBuilder() + .withNewMetadata() + .withName("testTask") + .endMetadata(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") + .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("pipeline { \n" + + " agent any\n" + + " stages {\n" + + " stage('Stage') {\n" + + " steps {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'FILE', input: '.tekton/task.yaml')\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("Extracting: .tekton/task.yaml")); + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, not(containsString(".tekton/task.yaml (No such file or directory)"))); + } + + @Test + public void testDeclarativePipelineWithYamlInput_Task() throws Exception { + TaskBuilder taskBuilder = new TaskBuilder() + .withNewMetadata() + .withName("testTask") + .endMetadata(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/test/tasks") + .andReturn(HttpURLConnection.HTTP_OK, taskBuilder.build()).once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("pipeline { \n" + + " agent any\n" + + " stages {\n" + + " stage('Stage') {\n" + + " steps {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'YAML', input: \"\"\"apiVersion: tekton.dev/v1beta1\n" + + "kind: Task\n" + + "metadata:\n" + + " name: testTask\n" + + "\"\"\")\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(1)); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("Extracting: .tekton/task.yaml")); + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, not(containsString(".tekton/task.yaml (No such file or directory)"))); + } + + + @Test + public void testDeclarativePipelineWithYamlInput_PipelineRun() throws Exception { + ToolUtils.getJXPipelineBinary(ToolUtils.class.getClassLoader()); + + PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() + .withNewMetadata() + .withName("release") + .withNamespace("test") + .withUid("pipeline-run-uid") + .endMetadata() + .withNewSpec() + .withNewPipelineSpec() + .addNewTask() + .withName("pipelineTaskName") + .endTask() + .endPipelineSpec() + .endSpec() + .withNewStatus() + .withConditions(new Condition("lastTransitionTime","","","","True","Succeeded")) + .endStatus(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelineruns") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/test/pipelineruns/release") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + TaskRunList taskRunList = new TaskRunListBuilder() + .addToItems( + new TaskRunBuilder() + .withNewMetadata() + .withName("testTaskRun") + .withOwnerReferences(ownerReference("pipeline-run-uid")) + .endMetadata() + .build()) + .build(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/test/taskruns?labelSelector=tekton.dev%2FpipelineTask%3DpipelineTaskName%2Ctekton.dev%2FpipelineRun%3Drelease") + .andReturn(HttpURLConnection.HTTP_OK, taskRunList) + .once(); + + Pod pod = new PodBuilder() + .withNewMetadata() + .withName("hello-world-pod") + .withNamespace("test") + .withOwnerReferences(ownerReference("TaskRun","testTaskRun")) + .endMetadata() + .withNewSpec() + .withContainers( + new ContainerBuilder() + .withName("hello-world-container") + .build() + ) + .endSpec() + .withNewStatus() + .withPhase("Succeeded") + .withContainerStatuses( + new ContainerStatusBuilder() + .withName("hello-world-container") + .withState( + new ContainerStateBuilder() + .withTerminated(new ContainerStateTerminatedBuilder().withStartedAt("timestamp").build()) + .build() + ) + .build()) + .endStatus() + .build(); + + PodList podList = new PodListBuilder() + .addToItems(pod) + .build(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods") + .andReturn(HttpURLConnection.HTTP_OK, podList).once(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods/hello-world-pod") + .andReturn(HttpURLConnection.HTTP_OK, pod).always(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/test/pods/hello-world-pod/log?pretty=false&container=hello-world-container&follow=true") + .andReturn(HttpURLConnection.HTTP_OK, "Whoop! This is the pod log").once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("pipeline { \n" + + " agent any\n" + + " stages {\n" + + " stage('Stage') {\n" + + " steps {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'YAML', input: \"\"\"apiVersion: tekton.dev/v1beta1\n" + + "kind: PipelineRun\n" + + "metadata:\n" + + " name: release\n" + + "spec:\n" + + " params:\n" + + "\"\"\")\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, containsString("[Tekton] Pod test/hello-world-pod")); + assertThat(log, containsString("[Tekton] Pod test/hello-world-pod - Running...")); + assertThat(log, containsString("[Tekton] Container test/hello-world-pod/hello-world-container")); + assertThat(log, containsString("[Tekton] Container test/hello-world-pod/hello-world-container - Completed")); + assertThat(log, containsString("Whoop! This is the pod log")); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(9)); + } + + @Test + public void testDeclarativePipelineWithYamlInput_PipelineRun_DifferentNamespace() throws Exception { + ToolUtils.getJXPipelineBinary(ToolUtils.class.getClassLoader()); + + PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() + .withNewMetadata() + .withName("release") + .withNamespace("tekton-pipelines") + .withUid("pipeline-run-uid") + .endMetadata() + .withNewSpec() + .withNewPipelineSpec() + .addNewTask() + .withName("pipelineTaskName") + .endTask() + .endPipelineSpec() + .endSpec() + .withNewStatus() + .withConditions(new Condition("lastTransitionTime","","","","True","Succeeded")) + .endStatus(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/pipelineruns") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/pipelineruns/release") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + TaskRunList taskRunList = new TaskRunListBuilder() + .addToItems( + new TaskRunBuilder() + .withNewMetadata() + .withName("testTaskRun") + .withOwnerReferences(ownerReference("pipeline-run-uid")) + .endMetadata() + .build()) + .build(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/taskruns?labelSelector=tekton.dev%2FpipelineTask%3DpipelineTaskName%2Ctekton.dev%2FpipelineRun%3Drelease") + .andReturn(HttpURLConnection.HTTP_OK, taskRunList) + .once(); + + Pod pod = new PodBuilder() + .withNewMetadata() + .withName("hello-world-pod") + .withNamespace("tekton-pipelines") + .withOwnerReferences(ownerReference("TaskRun","testTaskRun")) + .endMetadata() + .withNewSpec() + .withContainers( + new ContainerBuilder() + .withName("hello-world-container") + .build() + ) + .endSpec() + .withNewStatus() + .withPhase("Succeeded") + .withContainerStatuses( + new ContainerStatusBuilder() + .withName("hello-world-container") + .withState( + new ContainerStateBuilder() + .withTerminated(new ContainerStateTerminatedBuilder().withStartedAt("timestamp").build()) + .build() + ) + .build()) + .endStatus() + .build(); + + PodList podList = new PodListBuilder() + .addToItems(pod) + .build(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods") + .andReturn(HttpURLConnection.HTTP_OK, podList).once(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods/hello-world-pod") + .andReturn(HttpURLConnection.HTTP_OK, pod).always(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods/hello-world-pod/log?pretty=false&container=hello-world-container&follow=true") + .andReturn(HttpURLConnection.HTTP_OK, "Whoop! This is the pod log").once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("pipeline { \n" + + " agent any\n" + + " stages {\n" + + " stage('Stage') {\n" + + " steps {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'YAML', input: \"\"\"apiVersion: tekton.dev/v1beta1\n" + + "kind: PipelineRun\n" + + "metadata:\n" + + " name: release\n" + + "spec:\n" + + " params:\n" + + "\"\"\", namespace: 'tekton-pipelines')\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.SUCCESS, p.scheduleBuild2(0).get()); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod - Running...")); + assertThat(log, containsString("[Tekton] Container tekton-pipelines/hello-world-pod/hello-world-container")); + assertThat(log, containsString("[Tekton] Container tekton-pipelines/hello-world-pod/hello-world-container - Completed")); + assertThat(log, containsString("Whoop! This is the pod log")); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(9)); + } + + @Test + public void testDeclarativePipelineWithYamlInput_PipelineRun_FailingContainer() throws Exception { + ToolUtils.getJXPipelineBinary(ToolUtils.class.getClassLoader()); + + PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() + .withNewMetadata() + .withName("release") + .withNamespace("tekton-pipelines") + .withUid("pipeline-run-uid") + .endMetadata() + .withNewSpec() + .withNewPipelineSpec() + .addNewTask() + .withName("pipelineTaskName") + .endTask() + .endPipelineSpec() + .endSpec() + .withNewStatus() + .withConditions(new Condition("lastTransitionTime","","","","True","Succeeded")) + .endStatus(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/pipelineruns") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/pipelineruns/release") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + TaskRunList taskRunList = new TaskRunListBuilder() + .addToItems( + new TaskRunBuilder() + .withNewMetadata() + .withName("testTaskRun") + .withOwnerReferences(ownerReference("pipeline-run-uid")) + .endMetadata() + .build()) + .build(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/taskruns?labelSelector=tekton.dev%2FpipelineTask%3DpipelineTaskName%2Ctekton.dev%2FpipelineRun%3Drelease") + .andReturn(HttpURLConnection.HTTP_OK, taskRunList) + .once(); + + Pod pod = new PodBuilder() + .withNewMetadata() + .withName("hello-world-pod") + .withNamespace("tekton-pipelines") + .withOwnerReferences(ownerReference("TaskRun","testTaskRun")) + .endMetadata() + .withNewSpec() + .withContainers( + new ContainerBuilder() + .withName("hello-world-container") + .build() + ) + .endSpec() + .withNewStatus() + .withPhase("Failed") + .withContainerStatuses( + new ContainerStatusBuilder() + .withName("hello-world-container") + .withState( + new ContainerStateBuilder() + .withTerminated( + new ContainerStateTerminatedBuilder() + .withStartedAt("timestamp") + .withMessage("Failure Message") + .withReason("Error") + .withExitCode(1) + .build()) + .build() + ) + .build()) + .endStatus() + .build(); + + PodList podList = new PodListBuilder() + .addToItems(pod) + .build(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods") + .andReturn(HttpURLConnection.HTTP_OK, podList).once(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods/hello-world-pod") + .andReturn(HttpURLConnection.HTTP_OK, pod).always(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods/hello-world-pod/log?pretty=false&container=hello-world-container&follow=true") + .andReturn(HttpURLConnection.HTTP_OK, "Whoop! This is the pod log").once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("pipeline { \n" + + " agent any\n" + + " stages {\n" + + " stage('Stage') {\n" + + " steps {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'YAML', input: \"\"\"apiVersion: tekton.dev/v1beta1\n" + + "kind: PipelineRun\n" + + "metadata:\n" + + " name: release\n" + + "spec:\n" + + " params:\n" + + "\"\"\", namespace: 'tekton-pipelines')\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod - Running...")); + assertThat(log, containsString("[Tekton] Container tekton-pipelines/hello-world-pod/hello-world-container")); + assertThat(log, containsString("[Tekton] Container tekton-pipelines/hello-world-pod/hello-world-container - Error")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod Status: Failed")); + assertThat(log, containsString("Whoop! This is the pod log")); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(8)); + } + + @Test + public void testDeclarativePipelineWithYamlInput_PipelineRun_FailingPipelineRun() throws Exception { + ToolUtils.getJXPipelineBinary(ToolUtils.class.getClassLoader()); + + PipelineRunBuilder pipelineRunBuilder = new PipelineRunBuilder() + .withNewMetadata() + .withName("release") + .withNamespace("tekton-pipelines") + .withUid("pipeline-run-uid") + .endMetadata() + .withNewSpec() + .withNewPipelineSpec() + .addNewTask() + .withName("pipelineTaskName") + .endTask() + .endPipelineSpec() + .endSpec() + .withNewStatus() + .withConditions(new Condition("lastTransitionTime","Failure Message","Failure Reason","","False","Succeeded")) + .endStatus(); + + kubernetesRule.expect() + .post() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/pipelineruns") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/pipelineruns/release") + .andReturn(HttpURLConnection.HTTP_OK, pipelineRunBuilder.build()) + .once(); + + TaskRunList taskRunList = new TaskRunListBuilder() + .addToItems( + new TaskRunBuilder() + .withNewMetadata() + .withName("testTaskRun") + .withOwnerReferences(ownerReference("pipeline-run-uid")) + .endMetadata() + .build()) + .build(); + + kubernetesRule.expect() + .get() + .withPath("/apis/tekton.dev/v1beta1/namespaces/tekton-pipelines/taskruns?labelSelector=tekton.dev%2FpipelineTask%3DpipelineTaskName%2Ctekton.dev%2FpipelineRun%3Drelease") + .andReturn(HttpURLConnection.HTTP_OK, taskRunList) + .once(); + + Pod pod = new PodBuilder() + .withNewMetadata() + .withName("hello-world-pod") + .withNamespace("tekton-pipelines") + .withOwnerReferences(ownerReference("TaskRun","testTaskRun")) + .endMetadata() + .withNewSpec() + .withContainers( + new ContainerBuilder() + .withName("hello-world-container") + .build() + ) + .endSpec() + .withNewStatus() + .withPhase("Succeeded") + .withContainerStatuses( + new ContainerStatusBuilder() + .withName("hello-world-container") + .withState( + new ContainerStateBuilder() + .withTerminated(new ContainerStateTerminatedBuilder().withStartedAt("timestamp").build()) + .build() + ) + .build()) + .endStatus() + .build(); + + PodList podList = new PodListBuilder() + .addToItems(pod) + .build(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods") + .andReturn(HttpURLConnection.HTTP_OK, podList).once(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods/hello-world-pod") + .andReturn(HttpURLConnection.HTTP_OK, pod).always(); + + kubernetesRule.expect().get().withPath("/api/v1/namespaces/tekton-pipelines/pods/hello-world-pod/log?pretty=false&container=hello-world-container&follow=true") + .andReturn(HttpURLConnection.HTTP_OK, "Whoop! This is the pod log").once(); + + WorkflowJob p = jenkinsRule.jenkins.createProject(WorkflowJob.class, "p"); + URL zipFile = getClass().getResource("tekton-test-project.zip"); + assertThat(zipFile, is(notNullValue())); + + p.setDefinition(new CpsFlowDefinition("pipeline { \n" + + " agent any\n" + + " stages {\n" + + " stage('Stage') {\n" + + " steps {\n" + + " unzip '" + zipFile.getPath() + "'\n" + + " tektonCreateRaw(inputType: 'YAML', input: \"\"\"apiVersion: tekton.dev/v1beta1\n" + + "kind: PipelineRun\n" + + "metadata:\n" + + " name: release\n" + + "spec:\n" + + " params:\n" + + "\"\"\", namespace: 'tekton-pipelines')\n" + + " }\n" + + " }\n" + + " }\n" + + "}\n", true)); + + WorkflowRun b = jenkinsRule.assertBuildStatus(Result.FAILURE, p.scheduleBuild2(0).get()); + + String log = jenkinsRule.getLog(b); + System.out.println(log); + + assertThat(log, containsString("[Pipeline] tektonCreateRaw")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod")); + assertThat(log, containsString("[Tekton] Pod tekton-pipelines/hello-world-pod - Running...")); + assertThat(log, containsString("[Tekton] Container tekton-pipelines/hello-world-pod/hello-world-container")); + assertThat(log, containsString("[Tekton] Container tekton-pipelines/hello-world-pod/hello-world-container - Completed")); + assertThat(log, containsString("Whoop! This is the pod log")); + + assertThat(kubernetesRule.getMockServer().getRequestCount(), is(9)); + } + + private String contents(String filename) throws IOException { + return IOUtils.toString(this.getClass().getResourceAsStream(filename), StandardCharsets.UTF_8.name()); + } + + private OwnerReference ownerReference(String uid) { + return new OwnerReference("", false, false, "", "", uid); + } + + private OwnerReference ownerReference(String kind, String name) { + return new OwnerReference("", false, false, kind, name, ""); + } +}