Skip to content

Commit

Permalink
Merge pull request #85 from zowe/feature/IJMP-1714-handle-job-errors
Browse files Browse the repository at this point in the history
Add response handling for SubmitJobSync and PerformTsoCommand declarative methods
  • Loading branch information
KUGDev authored Oct 8, 2024
2 parents 2351c29 + 2f2bfdd commit 663d4ca
Show file tree
Hide file tree
Showing 11 changed files with 500 additions and 108 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* Copyright (c) 2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.declarative

import hudson.EnvVars
import hudson.FilePath
import hudson.model.Run
import hudson.model.TaskListener
import org.jenkinsci.plugins.workflow.steps.*
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.zdevops.utils.getZoweZosConnection
import java.nio.charset.StandardCharsets

/**
* Abstract class that represents an action that performs some work with a String result in a Jenkins pipeline.
* This class is designed to be extended by other classes that need to perform specific actions
* and return a result. In order to return the result, the method must be wrapped in `script` tag.
*/
abstract class AbstractZosmfActionWithResult : Step() {

/**
* Executes the action using the provided context and returns the result as a String.
*
* @param workspace the workspace where the action is executed.
* @param listener the listener used to log messages during execution.
* @param envVars the environment variables that may influence the execution.
* @param zoweZOSConnection the connection to z/OSMF used for the action execution.
* @return the result of the action as a String.
*/
abstract fun run(workspace: FilePath, listener: TaskListener, envVars: EnvVars, zoweZOSConnection: ZOSConnection): String

/**
* Starts the execution of this step.
*
* @param context the context in which the step is executed.
* @return a StepExecution instance that will manage the execution of this step.
*/
override fun start(context: StepContext): StepExecution {
return Execution(this, context)
}

companion object {
/**
* Default descriptor for steps that extend AbstractZosmfActionWithResult.
* Provides metadata about the step for Jenkins, which in our case is a name for the method in declarative pipeline.
*/
open class DefaultStepDescriptor(private val functionName: String): StepDescriptor() {

/**
* Provides the context to the Step.
*/
override fun getRequiredContext(): Set<Class<*>> {
return setOf<Class<*>>(
Run::class.java,
FilePath::class.java,
TaskListener::class.java,
EnvVars::class.java
)
}

/**
* The name for a declarative method returning result.
*/
override fun getFunctionName(): String = functionName

/**
* We do not expect a block argument as an input for a declarative method returning result.
*/
override fun takesImplicitBlockArgument(): Boolean = false
}

/**
* Synchronous step execution class for actions that extend AbstractZosmfActionWithResult.
* Manages the execution of the step and retrieves the necessary context information.
*/
class Execution(@Transient private val step: AbstractZosmfActionWithResult, context: StepContext)
: SynchronousNonBlockingStepExecution<String>(context) {
/**
* Prepares everything for the actual step run.
*/
override fun run(): String {
val workspace: FilePath = getClassFromContext(context, FilePath::class.java)
val listener = getClassFromContext(context, TaskListener::class.java)
val env = getClassFromContext(context, EnvVars::class.java)

val connectionName: String = workspace.read().readBytes().toString(StandardCharsets.UTF_8)
val zoweConnection = getZoweZosConnection(connectionName, listener)

return step.run(workspace, listener, env, zoweConnection)
}

/**
* Gets necessary context classes for the execution and ensures they are not empty.
*/
private fun <T> getClassFromContext(context: StepContext, clazz: Class<T>): T {
return context.get(clazz) ?: throw RuntimeException("Couldn't get ${clazz.simpleName}")
}

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@ import org.kohsuke.stapler.DataBoundConstructor
import org.zowe.zdevops.utils.getZoweZosConnection
import org.zowe.zdevops.utils.validateConnection


class ZosmfStepDeclarative @DataBoundConstructor constructor(private val connectionName: String) : Step() {
override fun start(context: StepContext): StepExecution {
val listener: TaskListener? = context.get(TaskListener::class.java)
val zosConnection = getZoweZosConnection(connectionName, listener)
val zosConnection = getZoweZosConnection(connectionName, listener)

validateConnection(zosConnection)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,74 +1,54 @@
/*
* Copyright (c) 2023-2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright IBA Group 2023
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.declarative.jobs

import hudson.*
import hudson.model.Run
import hudson.AbortException
import hudson.EnvVars
import hudson.Extension
import hudson.FilePath
import hudson.model.TaskListener
import org.jenkinsci.Symbol
import org.kohsuke.stapler.DataBoundConstructor
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.zdevops.declarative.AbstractZosmfAction
import org.zowe.zdevops.declarative.AbstractZosmfActionWithResult
import org.zowe.zdevops.logic.performTsoCommand

/**
* A Jenkins Pipeline step for performing a TSO (Time Sharing Option) command on a z/OS system
* using the Declarative Pipeline syntax.
* Class that represents an action to perform a TSO command with a result in a declarative pipeline.
* This class extends {@code AbstractZosmfActionWithResult} and is designed to execute a TSO command
* via Zowe z/OSMF and return the command's output.
*
* This step allows you to execute TSO commands on a z/OS system and provides integration with
* Jenkins Pipelines for mainframe automation.
* @param acct the TSO account number.
* @param command the TSO command to be executed.
*/
class PerformTsoCommandDeclarative
/**
* Data-bound constructor for the {@code PerformTsoCommandDeclarative} step.
*
* @param acct The z/OS account under which to run the TSO command.
* @param command The TSO command to be executed.
*/
@DataBoundConstructor
constructor(
val acct: String,
val command: String,
) : AbstractZosmfAction() {

override val exceptionMessage: String = zMessages.zdevops_TSO_command_fail()
val acct: String,
val command: String,
) : AbstractZosmfActionWithResult() {

/**
* Performs the TSO command execution step within a Jenkins Pipeline.
*
* @param run The current Jenkins build run.
* @param workspace The workspace where the build is executed.
* @param env The environment variables for the build.
* @param launcher The build launcher.
* @param listener The build listener.
* @param zosConnection The z/OS connection to execute the TSO command.
*/
override fun perform(
run: Run<*, *>,
workspace: FilePath,
env: EnvVars,
launcher: Launcher,
listener: TaskListener,
zosConnection: ZOSConnection
) {
performTsoCommand(zosConnection, listener, acct, command)
}
override fun run(
workspace: FilePath,
listener: TaskListener,
envVars: EnvVars,
zoweZOSConnection: ZOSConnection
): String {
return performTsoCommand(zoweZOSConnection, listener, acct, command)
?: throw AbortException("TSO command execution returned an empty result")
}

/**
* Descriptor for the {@code PerformTsoCommandDeclarative} step.
*
* This descriptor provides information about the step and makes it available for use
* within Jenkins Pipelines.
*/
@Symbol("performTsoCommand")
@Extension
class DescriptorImpl : Companion.DefaultBuildDescriptor("Perform TSO command Declarative")
}
@Extension
class DescriptorImpl : Companion.DefaultStepDescriptor(functionName = "performTsoCommandWithResult")
}
Original file line number Diff line number Diff line change
@@ -1,45 +1,54 @@
/*
* Copyright (c) 2022-2024 IBA Group.
*
* This program and the accompanying materials are made available under the terms of the
* Eclipse Public License v2.0 which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-v20.html
*
* SPDX-License-Identifier: EPL-2.0
*
* Copyright IBA Group 2022
* Contributors:
* IBA Group
* Zowe Community
*/

package org.zowe.zdevops.declarative.jobs

import hudson.*
import hudson.model.Run
import hudson.EnvVars
import hudson.Extension
import hudson.FilePath
import hudson.model.TaskListener
import org.jenkinsci.Symbol
import org.kohsuke.stapler.DataBoundConstructor
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.zdevops.declarative.AbstractZosmfAction
import org.zowe.zdevops.declarative.AbstractZosmfActionWithResult
import org.zowe.zdevops.logic.submitJobSync

class SubmitJobSyncStepDeclarative @DataBoundConstructor constructor(private val fileToSubmit: String):
AbstractZosmfAction() {

override val exceptionMessage: String = zMessages.zdevops_declarative_ZOSJobs_submitted_fail(fileToSubmit)
/**
* Class that represents an action to submit a z/OS job and retrieve the return code in a declarative pipeline.
* This class extends {@code AbstractZosmfActionWithResult} and is designed to submit a job via Zowe z/OSMF
* and return the job's return code.
*
* @param fileToSubmit the path to the file containing the JCL to be submitted.
*/
class SubmitJobSyncStepDeclarative
@DataBoundConstructor
constructor(val fileToSubmit: String)
: AbstractZosmfActionWithResult() {

override fun perform(
run: Run<*, *>,
override fun run(
workspace: FilePath,
env: EnvVars,
launcher: Launcher,
listener: TaskListener,
zosConnection: ZOSConnection
) {
val workspacePath = FilePath(null, workspace.remote.replace(workspace.name,""))
val linkBuilder: (String?, String, String) -> String = { buildUrl, jobName, jobId ->
"$buildUrl/execution/node/3/ws/${jobName}.${jobId}/*view*/"
}
submitJobSync(fileToSubmit, zosConnection, listener, workspacePath, env["BUILD_URL"], linkBuilder)
envVars: EnvVars,
zoweZOSConnection: ZOSConnection
): String {
val workspacePath = FilePath(null, workspace.remote.replace(workspace.name,""))
val linkBuilder: (String?, String, String) -> String = { buildUrl, jobName, jobId ->
"$buildUrl/execution/node/3/ws/${jobName}.${jobId}/*view*/"
}
return submitJobSync(fileToSubmit, zoweZOSConnection,
listener, workspacePath, envVars["BUILD_URL"], linkBuilder)
}

@Symbol("submitJobSync")
@Extension
class DescriptorImpl : Companion.DefaultBuildDescriptor("Submit Job Synchronously Declarative")
class DescriptorImpl : Companion.DefaultStepDescriptor(functionName = "submitJobSyncWithResult")
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ package org.zowe.zdevops.logic
import hudson.AbortException
import hudson.model.TaskListener
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
import org.zowe.kotlinsdk.zowe.client.sdk.zostso.IssueResponse
import org.zowe.kotlinsdk.zowe.client.sdk.zostso.IssueTso
import org.zowe.kotlinsdk.zowe.client.sdk.zostso.input.StartTsoParams
import org.zowe.zdevops.Messages
Expand All @@ -26,7 +27,7 @@ import org.zowe.zdevops.Messages
* @param listener The Jenkins build listener for logging and monitoring the execution.
* @param acct The z/OS account number.
* @param command The TSO command to be executed.
*
* @return the command output.
* @throws AbortException if the TSO command execution fails, with the error message indicating
* the reason for the failure.
*/
Expand All @@ -35,14 +36,16 @@ fun performTsoCommand(
listener: TaskListener,
acct: String,
command: String,
) {
): String? {
listener.logger.println(Messages.zdevops_issue_TSO_command(command))
val tsoCommandResponse: IssueResponse
try {
val tsoCommandResponse = IssueTso(zosConnection).issueTsoCommand(acct, command, StartTsoParams(), failOnPrompt = true)
tsoCommandResponse = IssueTso(zosConnection).issueTsoCommand(acct, command, StartTsoParams(), failOnPrompt = true)
listener.logger.println(tsoCommandResponse.commandResponses)
} catch (ex: Exception) {
listener.logger.println(Messages.zdevops_TSO_command_fail())
throw ex
}
listener.logger.println(Messages.zdevops_TSO_command_success())
return tsoCommandResponse.commandResponses
}
4 changes: 2 additions & 2 deletions src/main/kotlin/org/zowe/zdevops/logic/SubmitJobOperation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ fun submitJobSync(
workspacePath: FilePath,
buildUrl: String?,
linkBuilder: (String?, String, String) -> String
): String? {
): String {
val submitJobRsp = submitJob(fileToSubmit, zosConnection, listener)
listener.logger.println(Messages.zdevops_declarative_ZOSJobs_submitted_waiting())

Expand Down Expand Up @@ -103,5 +103,5 @@ fun submitJobSync(
listener.logger.println(Messages.zdevops_no_spool_files(submitJobRsp.jobid))
}

return finalResult?.returnedCode
return finalResult.returnedCode ?: throw AbortException("Couldn't get the job $jobId")
}
Loading

0 comments on commit 663d4ca

Please sign in to comment.