ENT-1821 - Doorman failed with parallel requests (#827)

* attempt to fix race condition by introducing thread lock
This commit is contained in:
Patrick Kuo 2018-05-21 16:32:28 +01:00 committed by GitHub
parent 2a07537f7b
commit 6a783124bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 119 additions and 113 deletions

View File

@ -98,7 +98,7 @@ dependencies {
// Unit testing helpers.
testCompile "junit:junit:$junit_version"
testCompile "org.assertj:assertj-core:${assertj_version}"
testCompile "com.nhaarman:mockito-kotlin:0.6.1"
testCompile "com.nhaarman:mockito-kotlin:1.5.0"
testCompile "com.spotify:docker-client:8.9.1"
compile('com.atlassian.jira:jira-rest-java-client-core:5.0.4') {

View File

@ -12,22 +12,11 @@ package com.r3.corda.networkmanage.doorman
import com.atlassian.jira.rest.client.api.JiraRestClient
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.contextLogger
class CrrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClient(restClient, projectCode) {
companion object {
val logger = contextLogger()
}
fun createCertificateRevocationRequestTicket(revocationRequest: CertificateRevocationRequestData) {
// Check there isn't already a ticket for this request.
if (getIssueById(revocationRequest.requestId) != null) {
logger.warn("There is already a ticket corresponding to request Id ${revocationRequest.requestId}, not creating a new one.")
return
}
val ticketDescription = "Legal name: ${revocationRequest.legalName}\n" +
"Certificate serial number: ${revocationRequest.certificateSerialNumber}\n" +
"Revocation reason: ${revocationRequest.reason.name}\n" +
@ -46,16 +35,8 @@ class CrrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClien
.setDescription(ticketDescription)
.setSummary(ticketSummary)
.setFieldValue(requestIdField.id, revocationRequest.requestId)
// This will block until the issue is created.
restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim().key
val createdIssue = checkNotNull(getIssueById(revocationRequest.requestId)) { "Missing the JIRA ticket for the request ID: ${revocationRequest.requestId}" }
restClient.issueClient.addAttachment(createdIssue.attachmentsUri, revocationRequest.certificate.encoded.inputStream(), "${revocationRequest.certificateSerialNumber}.cer")
.fail { logger.error("Error processing request '${createdIssue.key}' : Exception when uploading attachment to JIRA.", it) }.claim()
}
fun updateDoneCertificateRevocationRequest(requestId: String) {
logger.debug("Marking JIRA ticket with `request ID` = $requestId as DONE.")
val issue = requireNotNull(getIssueById(requestId)) { "Missing the JIRA ticket for the request ID: $requestId" }
restClient.issueClient.transition(issue, TransitionInput(getTransitionId(DONE_TRANSITION_KEY, issue))).fail { logger.error("Exception when transiting JIRA status.", it) }.claim()
val attachment = Pair("${revocationRequest.certificateSerialNumber}.cer", revocationRequest.certificate.encoded.inputStream())
createJiraTicket(revocationRequest.requestId, issue.build(), attachment)
}
}

View File

@ -12,44 +12,22 @@ package com.r3.corda.networkmanage.doorman
import com.atlassian.jira.rest.client.api.JiraRestClient
import com.atlassian.jira.rest.client.api.domain.input.IssueInputBuilder
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
import com.r3.corda.networkmanage.common.utils.getCertRole
import com.r3.corda.networkmanage.common.utils.getEmail
import net.corda.core.identity.CordaX500Name
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.bouncycastle.openssl.jcajce.JcaPEMWriter
import org.bouncycastle.pkcs.PKCS10CertificationRequest
import org.bouncycastle.util.io.pem.PemObject
import java.io.StringWriter
import java.security.cert.CertPath
import javax.security.auth.x500.X500Principal
class CsrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClient(restClient, projectCode) {
companion object {
val logger = contextLogger()
}
// TODO: Pass in a parsed object instead of raw PKCS10 request.
fun createCertificateSigningRequestTicket(requestId: String, signingRequest: PKCS10CertificationRequest) {
// Check there isn't already a ticket for this request.
if (getIssueById(requestId) != null) {
logger.warn("There is already a ticket corresponding to request Id $requestId, not creating a new one.")
return
}
// Make sure request has been accepted.
val request = StringWriter()
JcaPEMWriter(request).use {
it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded))
}
fun createCertificateSigningRequestTicket(requestData: CertificationRequestData) {
val (requestId, signingRequest) = requestData
// TODO The subject of the signing request has already been validated and parsed into a CordaX500Name. We shouldn't
// have to do it again here.
val subject = CordaX500Name.build(X500Principal(signingRequest.subject.encoded))
val email = signingRequest.getEmail()
val certRole = signingRequest.getCertRole()
val ticketSummary = if (subject.organisationUnit != null) {
"${subject.organisationUnit}, ${subject.organisation}"
@ -57,33 +35,36 @@ class CsrJiraClient(restClient: JiraRestClient, projectCode: String) : JiraClien
subject.organisation
}
val data = mapOf("Requested Role Type" to certRole.name,
val data = mapOf(
"Requested Role Type" to signingRequest.getCertRole().name,
"Common Name" to subject.commonName,
"Organisation" to subject.organisation,
"Organisation Unit" to subject.organisationUnit,
"State" to subject.state,
"Nearest City" to subject.locality,
"Country" to subject.country,
"Email" to email,
"Email" to signingRequest.getEmail(),
"X500 Name" to subject.toString())
val ticketDescription = data.filter { it.value != null }.map { "${it.key}: ${it.value}" }.joinToString("\n") + "\n\n{code}$request{code}"
val requestPemString = StringWriter().apply {
JcaPEMWriter(this).use {
it.writeObject(PemObject("CERTIFICATE REQUEST", signingRequest.encoded))
}
}.toString()
val ticketDescription = data.filter { it.value != null }.map { "${it.key}: ${it.value}" }.joinToString("\n") + "\n\n{code}$requestPemString{code}"
val issue = IssueInputBuilder().setIssueTypeId(taskIssueType.id)
.setProjectKey(projectCode)
.setDescription(ticketDescription)
.setSummary(ticketSummary)
.setFieldValue(requestIdField.id, requestId)
// This will block until the issue is created.
restClient.issueClient.createIssue(issue.build()).fail { logger.error("Exception when creating JIRA issue.", it) }.claim()
}
fun updateDoneCertificateSigningRequest(requestId: String, certPath: CertPath) {
// Retrieving certificates for signed CSRs to attach to the jira tasks.
val certificate = certPath.certificates.first()
val issue = requireNotNull(getIssueById(requestId)) { "Cannot find the JIRA ticket `request ID` = $requestId" }
restClient.issueClient.transition(issue, TransitionInput(getTransitionId(DONE_TRANSITION_KEY, issue))).fail { logger.error("Exception when transiting JIRA status.", it) }.claim()
restClient.issueClient.addAttachment(issue.attachmentsUri, certificate.encoded.inputStream(), "${X509Utilities.CORDA_CLIENT_CA}.cer")
.fail { logger.error("Error processing request '${issue.key}' : Exception when uploading attachment to JIRA.", it) }.claim()
createJiraTicket(requestId, issue.build())
}
}
// TODO: Parse PKCS10 request.
data class CertificationRequestData(val requestId: String, val rawRequest: PKCS10CertificationRequest)

View File

@ -16,70 +16,109 @@ import com.atlassian.jira.rest.client.api.domain.Comment
import com.atlassian.jira.rest.client.api.domain.Field
import com.atlassian.jira.rest.client.api.domain.Issue
import com.atlassian.jira.rest.client.api.domain.IssueType
import com.atlassian.jira.rest.client.api.domain.input.IssueInput
import com.atlassian.jira.rest.client.api.domain.input.TransitionInput
import com.r3.corda.networkmanage.doorman.JiraConstant.DONE_TRANSITION_KEY
import com.r3.corda.networkmanage.doorman.JiraConstant.START_TRANSITION_KEY
import com.r3.corda.networkmanage.doorman.JiraConstant.STOP_TRANSITION_KEY
import net.corda.core.internal.ConcurrentBox
import net.corda.core.utilities.contextLogger
import org.slf4j.Logger
import java.io.InputStream
abstract class JiraClient(protected val restClient: JiraRestClient, protected val projectCode: String) {
/**
* The Jira client class manages concurrent access to the [JiraRestClient] to ensure atomic read and write for some operations to prevent race condition.
*/
abstract class JiraClient(restClient: JiraRestClient, protected val projectCode: String) {
companion object {
val logger = contextLogger()
const val DONE_TRANSITION_KEY = "Done"
const val START_TRANSITION_KEY = "Start Progress"
const val STOP_TRANSITION_KEY = "Stop Progress"
}
private val restClientLock = ConcurrentBox(restClient)
// The JIRA project must have a Request ID and reject reason field, and the Task issue type.
protected val requestIdField: Field = requireNotNull(restClient.metadataClient.fields.claim().find { it.name == "Request ID" }) { "Request ID field not found in JIRA '$projectCode'" }
protected val taskIssueType: IssueType = requireNotNull(restClient.metadataClient.issueTypes.claim().find { it.name == "Task" }) { "Task issue type field not found in JIRA '$projectCode'" }
protected val rejectReasonField: Field = requireNotNull(restClient.metadataClient.fields.claim().find { it.name == "Reject Reason" }) { "Reject Reason field not found in JIRA '$projectCode'" }
private val rejectReasonField: Field = requireNotNull(restClient.metadataClient.fields.claim().find { it.name == "Reject Reason" }) { "Reject Reason field not found in JIRA '$projectCode'" }
private val transitions = mutableMapOf<String, Int>()
fun getApprovedRequests(): List<ApprovedRequest> {
val issues = restClient.searchClient.searchJql("project = $projectCode AND status = Approved").claim().issues
return issues.mapNotNull { issue ->
val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." }
// Issue retrieved via search doesn't contain change logs.
val fullIssue = restClient.issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim()
val approvedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Approved" } }
ApprovedRequest(requestId, approvedBy?.author?.displayName ?: "Unknown")
return restClientLock.concurrent {
val issues = searchClient.searchJql("project = $projectCode AND status = Approved").claim().issues
issues.mapNotNull { issue ->
val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." }
// Issue retrieved via search doesn't contain change logs.
val fullIssue = issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim()
val approvedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Approved" } }
ApprovedRequest(requestId, approvedBy?.author?.displayName ?: "Unknown")
}
}
}
fun getRejectedRequests(): List<RejectedRequest> {
val issues = restClient.searchClient.searchJql("project = $projectCode AND status = Rejected").claim().issues
return issues.mapNotNull { issue ->
val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." }
val rejectedReason = issue.getField(rejectReasonField.id)?.value?.toString()
// Issue retrieved via search doesn't contain comments.
val fullIssue = restClient.issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim()
val rejectedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Rejected" } }
RejectedRequest(requestId, rejectedBy?.author?.displayName ?: "Unknown", rejectedReason)
return restClientLock.concurrent {
val issues = searchClient.searchJql("project = $projectCode AND status = Rejected").claim().issues
issues.mapNotNull { issue ->
val requestId = requireNotNull(issue.getField(requestIdField.id)?.value?.toString()) { "Error processing request '${issue.key}' : RequestId cannot be null." }
val rejectedReason = issue.getField(rejectReasonField.id)?.value?.toString()
// Issue retrieved via search doesn't contain comments.
val fullIssue = issueClient.getIssue(issue.key, listOf(IssueRestClient.Expandos.CHANGELOG)).claim()
val rejectedBy = fullIssue.changelog?.last { it.items.any { it.field == "status" && it.toString == "Rejected" } }
RejectedRequest(requestId, rejectedBy?.author?.displayName ?: "Unknown", rejectedReason)
}
}
}
fun updateRejectedRequest(requestId: String) {
val issue = requireNotNull(getIssueById(requestId)) { "Issue with the `request ID` = $requestId does not exist." }
// Move status to in progress.
restClient.issueClient.transition(issue, TransitionInput(getTransitionId(START_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim()
// Move status to stopped.
restClient.issueClient.transition(issue, TransitionInput(getTransitionId(STOP_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim()
restClient.issueClient.addComment(issue.commentsUri, Comment.valueOf("Request cancelled by doorman.")).claim()
restClientLock.exclusive {
val issue = requireNotNull(getIssueById(requestId)) { "Issue with the `request ID` = $requestId does not exist." }
// Move status to in progress.
issueClient.transition(issue, TransitionInput(getTransitionId(START_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim()
// Move status to stopped.
issueClient.transition(issue, TransitionInput(getTransitionId(STOP_TRANSITION_KEY, issue))).fail { logger.error("Error processing request '${issue.key}' : Exception when transiting JIRA status.", it) }.claim()
issueClient.addComment(issue.commentsUri, Comment.valueOf("Request cancelled by doorman.")).claim()
}
}
protected fun getIssueById(requestId: String): Issue? {
private fun JiraRestClient.getIssueById(requestId: String): Issue? {
// Jira only support ~ (contains) search for custom textfield.
return restClient.searchClient.searchJql("'Request ID' ~ $requestId").claim().issues.firstOrNull()
return searchClient.searchJql("'Request ID' ~ $requestId").claim().issues.firstOrNull()
}
protected fun getTransitionId(transitionKey: String, issue: Issue): Int {
private fun getTransitionId(transitionKey: String, issue: Issue): Int {
return transitions.computeIfAbsent(transitionKey, { key ->
restClient.issueClient.getTransitions(issue.transitionsUri).claim().single { it.name == key }.id
restClientLock.concurrent {
issueClient.getTransitions(issue.transitionsUri).claim().single { it.name == key }.id
}
})
}
protected fun createJiraTicket(requestId: String, issue: IssueInput, attachment: Pair<String, InputStream>? = null) {
restClientLock.exclusive {
if (getIssueById(requestId) != null) {
logger.warn("There is already a ticket corresponding to request Id $requestId, not creating a new one.")
return
}
// This will block until the issue is created.
issueClient.createIssue(issue).fail { logger.error("Exception when creating JIRA issue for request: '$requestId'.", it) }.claim()
attachment?.let { addAttachment(requestId, it) }
}
}
fun transitRequestStatusToDone(requestId: String, attachment: Pair<String, InputStream>? = null) {
restClientLock.exclusive {
val issue = requireNotNull(getIssueById(requestId)) { "Cannot find the JIRA ticket `request ID` = $requestId" }
issueClient.transition(issue, TransitionInput(getTransitionId(DONE_TRANSITION_KEY, issue))).fail { logger.error("Exception when transiting JIRA status.", it) }.claim()
attachment?.let { addAttachment(requestId, it) }
}
}
private fun JiraRestClient.addAttachment(requestId: String, attachment: Pair<String, InputStream>) {
val createdIssue = checkNotNull(getIssueById(requestId)) { "Missing the JIRA ticket for the request ID: $requestId" }
issueClient.addAttachment(createdIssue.attachmentsUri, attachment.second, attachment.first)
.fail { logger.error("Error processing request '${createdIssue.key}' : Exception when uploading attachment to JIRA.", it) }.claim()
}
}
data class ApprovedRequest(val requestId: String, val approvedBy: String)
@ -95,3 +134,9 @@ inline fun <T : Any> Iterable<T>.forEachWithExceptionLogging(logger: Logger, act
}
}
}
object JiraConstant{
const val DONE_TRANSITION_KEY = "Done"
const val START_TRANSITION_KEY = "Start Progress"
const val STOP_TRANSITION_KEY = "Stop Progress"
}

View File

@ -13,10 +13,7 @@ package com.r3.corda.networkmanage.doorman.signer
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestData
import com.r3.corda.networkmanage.common.persistence.CertificateRevocationRequestStorage
import com.r3.corda.networkmanage.common.persistence.RequestStatus
import com.r3.corda.networkmanage.doorman.ApprovedRequest
import com.r3.corda.networkmanage.doorman.CrrJiraClient
import com.r3.corda.networkmanage.doorman.RejectedRequest
import com.r3.corda.networkmanage.doorman.forEachWithExceptionLogging
import com.r3.corda.networkmanage.doorman.*
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.network.CertificateRevocationRequest
@ -62,7 +59,7 @@ class JiraCrrHandler(private val jiraClient: CrrJiraClient,
logger.debug("Updating JIRA tickets: `approved` = $approvedRequest, `rejected` = $rejectedRequest")
approvedRequest.mapNotNull { crrStorage.getRevocationRequest(it.requestId) }
.filter { it.status == RequestStatus.DONE }
.forEachWithExceptionLogging(logger) { jiraClient.updateDoneCertificateRevocationRequest(it.requestId) }
.forEachWithExceptionLogging(logger) { jiraClient.transitRequestStatusToDone(it.requestId) }
rejectedRequest.mapNotNull { crrStorage.getRevocationRequest(it.requestId) }
.filter { it.status == RequestStatus.REJECTED }
.forEachWithExceptionLogging(logger) { jiraClient.updateRejectedRequest(it.requestId) }

View File

@ -14,11 +14,9 @@ import com.r3.corda.networkmanage.common.persistence.CertificateResponse
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequest
import com.r3.corda.networkmanage.common.persistence.CertificateSigningRequestStorage
import com.r3.corda.networkmanage.common.persistence.RequestStatus
import com.r3.corda.networkmanage.doorman.ApprovedRequest
import com.r3.corda.networkmanage.doorman.CsrJiraClient
import com.r3.corda.networkmanage.doorman.RejectedRequest
import com.r3.corda.networkmanage.doorman.forEachWithExceptionLogging
import com.r3.corda.networkmanage.doorman.*
import net.corda.core.utilities.contextLogger
import net.corda.nodeapi.internal.crypto.X509Utilities
import org.bouncycastle.pkcs.PKCS10CertificationRequest
class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage: CertificateSigningRequestStorage, private val delegate: CsrHandler) : CsrHandler by delegate {
@ -31,7 +29,7 @@ class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage:
// Make sure request has been accepted.
try {
if (delegate.getResponse(requestId) !is CertificateResponse.Unauthorised) {
jiraClient.createCertificateSigningRequestTicket(requestId, rawRequest)
jiraClient.createCertificateSigningRequestTicket(CertificationRequestData(requestId, rawRequest))
storage.markRequestTicketCreated(requestId)
}
} catch (e: Exception) {
@ -66,7 +64,10 @@ class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage:
approvedRequest.mapNotNull { storage.getRequest(it.requestId) }
.filter { it.status == RequestStatus.DONE && it.certData != null }
.forEachWithExceptionLogging(logger) {
jiraClient.updateDoneCertificateSigningRequest(it.requestId, it.certData!!.certPath)
val attachment = it.certData?.certPath?.certificates?.firstOrNull()?.let {
Pair("${X509Utilities.CORDA_CLIENT_CA}.cer", it.encoded.inputStream())
}
jiraClient.transitRequestStatusToDone(it.requestId, attachment)
}
rejectedRequest.mapNotNull { storage.getRequest(it.requestId) }
.filter { it.status == RequestStatus.REJECTED }
@ -88,7 +89,7 @@ class JiraCsrHandler(private val jiraClient: CsrJiraClient, private val storage:
}
private fun createTicket(signingRequest: CertificateSigningRequest) {
jiraClient.createCertificateSigningRequestTicket(signingRequest.requestId, signingRequest.request)
jiraClient.createCertificateSigningRequestTicket(CertificationRequestData(signingRequest.requestId, signingRequest.request))
storage.markRequestTicketCreated(signingRequest.requestId)
}
}

View File

@ -34,7 +34,7 @@ class CsrJiraClientTest {
@Test
fun createRequestTicket() {
val request = X509Utilities.createCertificateSigningRequest(CordaX500Name("JiraAPITest", "R3 Ltd 3", "London", "GB").x500Principal, "test@test.com", Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME))
jiraClient.createCertificateSigningRequestTicket(SecureHash.randomSHA256().toString(), request)
jiraClient.createCertificateSigningRequestTicket(CertificationRequestData(SecureHash.randomSHA256().toString(), request))
}
@Test
@ -54,7 +54,8 @@ class CsrJiraClientTest {
X500Principal("O=test,L=london,C=GB"),
Crypto.generateKeyPair(X509Utilities.DEFAULT_TLS_SIGNATURE_SCHEME)))
jiraClient.getApprovedRequests().forEach {
jiraClient.updateDoneCertificateSigningRequest(it.requestId, selfSignedCaCertPath)
val attachment = Pair("${X509Utilities.CORDA_CLIENT_CA}.cer", selfSignedCaCertPath.certificates.first().encoded.inputStream())
jiraClient.transitRequestStatusToDone(it.requestId, attachment)
}
}

View File

@ -14,6 +14,7 @@ import com.nhaarman.mockito_kotlin.*
import com.r3.corda.networkmanage.TestBase
import com.r3.corda.networkmanage.common.persistence.*
import com.r3.corda.networkmanage.doorman.ApprovedRequest
import com.r3.corda.networkmanage.doorman.CertificationRequestData
import com.r3.corda.networkmanage.doorman.CsrJiraClient
import com.r3.corda.networkmanage.doorman.RejectedRequest
import net.corda.core.crypto.Crypto
@ -66,7 +67,7 @@ class JiraCsrHandlerTest : TestBase() {
fun `If jira connection fails we don't mark the ticket as created`() {
whenever(defaultCsrHandler.saveRequest(any())).thenReturn(requestId)
whenever(defaultCsrHandler.getResponse(requestId)).thenReturn(certificateResponse)
whenever(jiraClient.createCertificateSigningRequestTicket(eq(requestId), any())).thenThrow(IllegalStateException("something broke"))
whenever(jiraClient.createCertificateSigningRequestTicket(any())).thenThrow(IllegalStateException("something broke"))
// Test
jiraCsrHandler.saveRequest(pkcS10CertificationRequest)
@ -93,11 +94,10 @@ class JiraCsrHandlerTest : TestBase() {
status = RequestStatus.NEW,
request = pkcS10CertificationRequest)
whenever(certificationRequestStorage.getRequests(RequestStatus.NEW)).thenReturn(listOf(csr))
// Test
jiraCsrHandler.processRequests()
verify(jiraClient).createCertificateSigningRequestTicket(requestId, csr.request)
verify(jiraClient).createCertificateSigningRequestTicket(CertificationRequestData(requestId, csr.request))
verify(certificationRequestStorage).markRequestTicketCreated(requestId)
}
@ -133,8 +133,8 @@ class JiraCsrHandlerTest : TestBase() {
// Test.
jiraCsrHandler.processRequests()
verify(jiraClient).createCertificateSigningRequestTicket(id1, csr1.request)
verify(jiraClient).createCertificateSigningRequestTicket(id2, csr2.request)
verify(jiraClient).createCertificateSigningRequestTicket(CertificationRequestData(id1, csr1.request))
verify(jiraClient).createCertificateSigningRequestTicket(CertificationRequestData(id2, csr2.request))
verify(certificationRequestStorage).markRequestTicketCreated(id1)
verify(certificationRequestStorage).markRequestTicketCreated(id2)
@ -145,7 +145,7 @@ class JiraCsrHandlerTest : TestBase() {
// Verify jira client get the correct call.
verify(jiraClient).updateRejectedRequest(id2)
verify(jiraClient, never()).updateDoneCertificateSigningRequest(any(), any())
verify(jiraClient, never()).transitRequestStatusToDone(any(), anyOrNull())
// Sign request 1
val certPath = mock<CertPath>()
@ -156,6 +156,6 @@ class JiraCsrHandlerTest : TestBase() {
jiraCsrHandler.processRequests()
// Update signed request should be called.
verify(jiraClient).updateDoneCertificateSigningRequest(id1, certPath)
verify(jiraClient).transitRequestStatusToDone(eq(id1), anyOrNull())
}
}