Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IJMP-1875 - failOnExist options #84

Merged
merged 34 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
4785cf7
feat: implement failOnExist option
callbacksin Jun 25, 2024
105f574
fix: pass failOnNotExist default values
callbacksin Jun 25, 2024
070e58f
clarify URL template hint
callbacksin Jun 25, 2024
bbd1e9f
sign-off
Apr 7, 2023
1f2f174
IJMP-1389 Added GitHub Action to automate builds
Nov 2, 2023
29220f2
Added hpi-builder.yml GitHub workflow
Apr 25, 2024
eae92d1
Optimized workflow execution time and removed the old one
Apr 25, 2024
6939964
Added archive unzip with tests execution
May 7, 2024
6bf7b69
sign-off
May 7, 2024
88d2b40
Test run without mvn verify stage
May 7, 2024
8377c6f
Two tests fixed
May 17, 2024
13da9be
Added declarative pipelines execution within Groove pipeline sandbox …
May 21, 2024
80e0dc4
Added Maven Wrapper into plugin .hpi build
May 21, 2024
2f0b419
Fix try for ./mvnw: Permission denied error
May 21, 2024
4bd1da5
Upgraded mvnw version due to:"The plugin org.jenkins-ci.tools:maven-h…
May 21, 2024
b2b29e3
Test fix
May 23, 2024
68addae
Test fix
May 23, 2024
70304c6
Added mvnw wrapper for unit tests execution
May 23, 2024
a9c69f8
Added fix for Permission denied
May 24, 2024
3a0ee39
Skip artifact compression
May 24, 2024
1cebb15
Some fixes and name change
May 24, 2024
09e042d
Try without chmod +x
May 24, 2024
a564028
Updated execution rights for mvnw
KUGDev May 24, 2024
6501957
Added CHANGELOG.md with versions history
Jun 17, 2024
fab061f
Added 0.2.0 changelog
Jun 19, 2024
8ae85fd
Fixed URLs and commits
Jun 20, 2024
4b01292
Bracket correction
Jun 20, 2024
bacea45
fix templates
callbacksin Jun 25, 2024
69857d3
remove validation error
callbacksin Jul 25, 2024
faf8093
fix tests
callbacksin Jul 26, 2024
369a85e
delete 'returnedCode' (required on my machine, but fail compilation o…
callbacksin Jul 26, 2024
211de1a
fix tests
callbacksin Jul 26, 2024
7e4e122
merge release/v0.2.0 into feature/IJMP-1627-failOnExist-options
callbacksin Sep 4, 2024
6febb36
add tests for failOnExist
callbacksin Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ constructor(
val primary: Int = 1,
var secondary: Int,
var recFm: RecordFormat,
var failOnExist: Boolean = false,
) : AbstractBuildStep(connectionName){

private var volser: String? = null
Expand Down Expand Up @@ -177,7 +178,8 @@ constructor(
dataClass,
avgBlk,
dsnType,
dsModel
dsModel,
failOnExist,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import org.zowe.zdevops.Messages
import org.zowe.zdevops.classic.AbstractBuildStep
import org.zowe.zdevops.logic.deleteDatasetOrMember
import org.zowe.zdevops.utils.validateDatasetName
import org.zowe.zdevops.utils.validateFieldIsNotEmpty
import org.zowe.zdevops.utils.validateMemberName

class DeleteDatasetStep
Expand All @@ -38,6 +37,7 @@ constructor(
connectionName: String,
val dsn: String,
val member: String?,
val failOnNotExist: Boolean = false ,
) : AbstractBuildStep(connectionName) {

override fun perform(
Expand All @@ -46,7 +46,7 @@ constructor(
listener: BuildListener,
zosConnection: ZOSConnection
) {
deleteDatasetOrMember(dsn, member, zosConnection, listener)
deleteDatasetOrMember(dsn, member, zosConnection, listener, failOnNotExist)
}

@Extension
Expand All @@ -67,10 +67,14 @@ constructor(
* Checks if the member name is valid
*
* @param member The dataset member name
* @return FormValidation.ok() if the member name is valid, or an error message otherwise
* @return FormValidation.ok() if either the member name is valid or is not provided, or an error message otherwise
*/
fun doCheckMember(@QueryParameter member: String): FormValidation? {
return validateMemberName(member)?: validateFieldIsNotEmpty(member)
return if (member.isNotBlank()) {
validateMemberName(member)
} else {
FormValidation.ok()
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class DeleteDatasetsByMaskStep
constructor(
connectionName: String,
val dsnMask: String,
val failOnNotExist: Boolean = false,
) : AbstractBuildStep(connectionName) {

override fun perform(
Expand All @@ -39,7 +40,7 @@ constructor(
listener: BuildListener,
zosConnection: ZOSConnection
) {
deleteDatasetsByMask(dsnMask, zosConnection, listener)
deleteDatasetsByMask(dsnMask, zosConnection, listener, failOnNotExist)
}

@Extension
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ constructor(
val linkBuilder: (String?, String, String) -> String = { jobUrl, jobName, jobId ->
"${jobUrl}ws/${jobName}.${jobId}/*view*/"
}
val returnCode = submitJobSync(jobName, zosConnection, listener, workspace, build.getEnvironment(listener)["JOB_URL"], linkBuilder)
if (checkRC && !returnCode.equals("CC 0000")) {
val jobResult = submitJobSync(jobName, zosConnection, listener, workspace, build.getEnvironment(listener)["JOB_URL"], linkBuilder)
if (checkRC && !jobResult.equals("CC 0000")) {
throw AbortException("Job RC code is not 0000")
}
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ constructor(
private val dsOrg: DatasetOrganization,
private val primary: Int,
private var secondary: Int,
private var recFm: RecordFormat) :
private var recFm: RecordFormat,
private var failOnExist: Boolean = false) :
AbstractZosmfAction() {

private var volser: String? = null
Expand Down Expand Up @@ -126,7 +127,8 @@ constructor(
dataClass,
avgBlk,
dsnType,
dsModel
dsModel,
failOnExist,
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,17 @@ class DeleteDatasetDeclarative @DataBoundConstructor constructor(

private var dsn: String = ""
private var member: String = ""
private var failOnNotExist: Boolean = false

@DataBoundSetter
fun setDsn(dsn: String) { this.dsn = dsn }

@DataBoundSetter
fun setMember(member: String) { this.member = member }

@DataBoundSetter
fun setFailOnNotExist(failOnNotExist: Boolean) { this.failOnNotExist = failOnNotExist }

override val exceptionMessage: String = zMessages.zdevops_deleting_ds_fail()

override fun perform(
Expand All @@ -83,7 +87,7 @@ class DeleteDatasetDeclarative @DataBoundConstructor constructor(
listener: TaskListener,
zosConnection: ZOSConnection
) {
deleteDatasetOrMember(dsn, member, zosConnection, listener)
deleteDatasetOrMember(dsn, member, zosConnection, listener, failOnNotExist)
}

@Symbol("deleteDataset")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,14 @@ class DeleteDatasetsByMaskDeclarative @DataBoundConstructor constructor(
) : AbstractZosmfAction() {

private var mask: String = ""
private var failOnNotExist: Boolean = false

@DataBoundSetter
fun setMask(mask: String) { this.mask = mask }

@DataBoundSetter
fun setFailOnNotExist(failOnNotExist: Boolean) { this.failOnNotExist = failOnNotExist }

override val exceptionMessage: String = zMessages.zdevops_deleting_ds_fail()

override fun perform(
Expand All @@ -70,7 +74,7 @@ class DeleteDatasetsByMaskDeclarative @DataBoundConstructor constructor(
listener: TaskListener,
zosConnection: ZOSConnection
) {
deleteDatasetsByMask(mask, zosConnection,listener)
deleteDatasetsByMask(mask, zosConnection,listener, failOnNotExist)
}


Expand Down
16 changes: 12 additions & 4 deletions src/main/kotlin/org/zowe/zdevops/logic/AllocateOperation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ fun allocateDataset(listener: TaskListener,
dataClass: String?,
avgBlk: Int?,
dsnType: DsnameType?,
dsModel: String?

dsModel: String?,
failOnExist: Boolean,
) {
listener.logger.println(Messages.zdevops_declarative_DSN_allocating(dsn, zosConnection.host, zosConnection.zosmfPort))
val alcParms = CreateDataset(
Expand All @@ -79,6 +79,14 @@ fun allocateDataset(listener: TaskListener,
dsnType,
dsModel
)
ZosDsn(zosConnection).createDsn(dsn, alcParms)
listener.logger.println(Messages.zdevops_declarative_DSN_allocated_success(dsn))
try {
ZosDsn(zosConnection).createDsn(dsn, alcParms)
listener.logger.println(Messages.zdevops_declarative_DSN_allocated_success(dsn))
} catch (allocateDsEx: Exception) {
listener.logger.println("Dataset allocation failed. Reason: $allocateDsEx")
if(failOnExist) {
throw allocateDsEx
}
listener.logger.println("The `failOnExist` option is set to false. Continuing with execution.")
}
}
41 changes: 28 additions & 13 deletions src/main/kotlin/org/zowe/zdevops/logic/DeleteOperation.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,22 +29,31 @@ private val successMessage: String = zMessages.zdevops_deleting_ds_success()
* @param listener The task listener to log information and handle exceptions.
* @throws AbortException If the mask is empty or no matching datasets are found.
*/
fun deleteDatasetsByMask(mask: String, zosConnection: ZOSConnection, listener: TaskListener) {
fun deleteDatasetsByMask(mask: String, zosConnection: ZOSConnection, listener: TaskListener, failOnNotExist: Boolean) {
if (mask.isEmpty()) {
throw AbortException(zMessages.zdevops_deleting_datasets_by_mask_but_mask_is_empty())
}
listener.logger.println(zMessages.zdevops_deleting_ds_by_mask(mask))
val dsnList = ZosDsnList(zosConnection).listDsn(mask, ListParams())
if (dsnList.items.isEmpty()) {
throw AbortException(zMessages.zdevops_deleting_ds_fail_no_matching_mask())
}
dsnList.items.forEach {
runMFTryCatchWrappedQuery(listener) {
listener.logger.println(zMessages.zdevops_deleting_ds(it.name, zosConnection.host, zosConnection.zosmfPort))
ZosDsn(zosConnection).deleteDsn(it.name)
try {
val dsnList = ZosDsnList(zosConnection).listDsn(mask, ListParams())
if (dsnList.items.isEmpty()) {
throw AbortException(zMessages.zdevops_deleting_ds_fail_no_matching_mask())
}
dsnList.items.forEach {
runMFTryCatchWrappedQuery(listener) {
listener.logger.println(zMessages.zdevops_deleting_ds(it.name, zosConnection.host, zosConnection.zosmfPort))
ZosDsn(zosConnection).deleteDsn(it.name)
}
}
listener.logger.println(successMessage)
} catch (doesNotExistEx: Exception) {
if(failOnNotExist) {
throw doesNotExistEx
}
listener.logger.println("Reason: $doesNotExistEx")
// TODO I wanna have the dataset name here - it's inside exception message?
listener.logger.println("Dataset deletion failed, but the `failOnNotExist` option is set to false. Continuing with execution.")
}
listener.logger.println(successMessage)
}

/**
Expand All @@ -56,22 +65,28 @@ fun deleteDatasetsByMask(mask: String, zosConnection: ZOSConnection, listener: T
* @param listener The task listener to log information and handle exceptions.
* @throws AbortException If the dataset name is empty or the member name is invalid.
*/
fun deleteDatasetOrMember(dsn: String, member: String?, zosConnection: ZOSConnection, listener: TaskListener) {
fun deleteDatasetOrMember(dsn: String, member: String?, zosConnection: ZOSConnection, listener: TaskListener, failOnNotExist: Boolean) {
if (dsn.isEmpty()) {
throw AbortException(zMessages.zdevops_deleting_ds_fail_dsn_param_empty())
}
val logMessage = if (!member.isNullOrEmpty()) zMessages.zdevops_deleting_ds_member(member, dsn, zosConnection.host, zosConnection.zosmfPort)
else zMessages.zdevops_deleting_ds(dsn, zosConnection.host, zosConnection.zosmfPort)
listener.logger.println(logMessage)
runMFTryCatchWrappedQuery(listener) {
try {
if (!member.isNullOrEmpty()) {
isMemberNameValid(member)
ZosDsn(zosConnection).deleteDsn(dsn, member)
} else {
ZosDsn(zosConnection).deleteDsn(dsn)
}
listener.logger.println(successMessage)
} catch (doesNotExistEx: Exception) {
if(failOnNotExist) {
throw doesNotExistEx
}
listener.logger.println("Reason: $doesNotExistEx")
listener.logger.println("Dataset deletion failed, but the `failOnNotExist` option is set to false. Continuing with execution.")
}
listener.logger.println(successMessage)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import hudson.AbortException
import hudson.model.TaskListener
import org.zowe.kotlinsdk.zowe.client.sdk.core.ZOSConnection
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 @@ -36,12 +37,12 @@ fun performTsoCommand(
command: String,
) {
listener.logger.println(Messages.zdevops_issue_TSO_command(command))
runCatching {
val tsoCommandResponse = IssueTso(zosConnection).issueTsoCommand(acct, command)
try {
val tsoCommandResponse = IssueTso(zosConnection).issueTsoCommand(acct, command, StartTsoParams(), failOnPrompt = true)
listener.logger.println(tsoCommandResponse.commandResponses)
}.onFailure {
} catch (ex: Exception) {
listener.logger.println(Messages.zdevops_TSO_command_fail())
throw AbortException(Messages.zdevops_TSO_command_fail())
throw ex
}
listener.logger.println(Messages.zdevops_TSO_command_success())
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,4 +86,7 @@
</td>
</tr>
</table>
<form:entry field="failOnExist" title="failOnExist?" description="If the dataset already exists and the option is enabled, execution will halt.">
<form:checkbox/>
</form:entry>
</jelly:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,7 @@
<form:entry field="member" title="${%zdevops.classic.member.title}">
<form:textbox/>
</form:entry>
<form:entry field="failOnNotExist" title="failOnNotExist?" description="If the dataset does not exists and the option is enabled, execution will halt.">
<form:checkbox/>
</form:entry>
</jelly:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,7 @@
<form:entry field="dsnMask" title="${%zdevops.classic.dsnMask.title}">
<form:textbox/>
</form:entry>
<form:entry field="failOnNotExist" title="failOnNotExist?" description="If the dataset does not exists and the option is enabled, execution will halt.">
<form:checkbox/>
</form:entry>
</jelly:jelly>
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<form:entry field="name" title="${%zdevops.config.ZOSConnection.name.title}">
<form:textbox/>
</form:entry>
<form:entry field="url" title="${%zdevops.config.ZOSConnection.url.title}">
<form:entry field="url" title="${%zdevops.config.ZOSConnection.url.title}" description="Enter a z/OS URL in the format https://url:port">
<form:textbox/>
</form:entry>
<form:entry field="credentialsId" title="${%zdevops.config.ZOSConnection.credentialsId.title}">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,74 @@ class AllocateDatasetStepSpec : ShouldSpec({
assertSoftly { isDatasetAllocating shouldBe true }
assertSoftly { isDatasetAllocated shouldBe true }
}

should("fail as such dataset already exists and failOnExist is set to true") {
var isDatasetAllocating = false
var isFailedToAllocate = false
val taskListener = object : TestBuildListener() {
override fun getLogger(): PrintStream {
val logger = mockk<PrintStream>()
every {
logger.println(any<String>())
} answers {
if (firstArg<String>().contains("Allocating dataset")) {
isDatasetAllocating = true
} else if (firstArg<String>().contains("Dataset allocation failed.")) {
isFailedToAllocate = true
} else {
fail("Unexpected logger message: ${firstArg<String>()}")
}
}
return logger
}
}
val launcher = TestLauncher(taskListener, virtualChannel)

responseDispatcher.injectEndpoint(
this.testCase.name.testName,
{ it?.requestLine?.contains("/zosmf/restfiles/ds/") ?: false },
{ MockResponse().setResponseCode(409) }
)

val allocateDatasetStepInst = spyk(
AllocateDatasetStep(
"test",
"TEST.IJMP.DATASET1",
DatasetOrganization.PS,
1,
0,
RecordFormat.F,
failOnExist = true
)
)
allocateDatasetStepInst.setAlcUnit(AllocationUnit.CYL)
allocateDatasetStepInst.setStorClass("")
allocateDatasetStepInst.setStorClass("TEST")
allocateDatasetStepInst.setMgntClass("")
allocateDatasetStepInst.setMgntClass("TEST")
allocateDatasetStepInst.setDataClass("")
allocateDatasetStepInst.setDataClass("TEST")
allocateDatasetStepInst.setVolser("")
allocateDatasetStepInst.setVolser("TEST")
allocateDatasetStepInst.setUnit("")
allocateDatasetStepInst.setUnit("TEST")
allocateDatasetStepInst.setLrecl(3120)
allocateDatasetStepInst.setBlkSize(3120)
allocateDatasetStepInst.setDsnType(DsnameType.BASIC)
try {
allocateDatasetStepInst.perform(
build,
launcher,
taskListener,
zosConnection
)
} catch (ex: Exception) {
isFailedToAllocate = true
}
assertSoftly { isDatasetAllocating shouldBe true }
assertSoftly { isFailedToAllocate shouldBe true }
}

}

val descriptor = AllocateDatasetStep.DescriptorImpl()
Expand Down
Loading