Édouard Lopez
06/10/2022, 10:34 AMjson
06/10/2022, 12:39 PMÉdouard Lopez
06/10/2022, 2:01 PMconsumer feature branch which creates the contract must verify the contract before it can be merged into the master branch of that consumerI tend to only use "_verifiaction"_ to talk about the provider to avoid confusion, so I reckon you mean consumer test pass and generate the pact file, right?
json
06/10/2022, 2:07 PMjson
06/10/2022, 2:08 PMÉdouard Lopez
06/13/2022, 1:16 PMjson
06/13/2022, 1:20 PMjson
06/13/2022, 1:21 PMÉdouard Lopez
06/13/2022, 1:23 PMjson
06/13/2022, 1:26 PMstage("RUN UNIT TESTS") {
steps {
script {
sh "$POETRY run pytest tests --cov=${job.sourceName}"
IS_PACT_CONSUMER = library.exists('./tests/*.json')
IS_PACT_PROVIDER = library.exists('./tests/pact_provider')
}
}
}
We have a library method which is just calling a shell script to check for the existence of a file or directory and convert it to a boolean. Since the Pact
fixture in pytest automatically produces a pact json file for each provider this service consumes when it's done, we can check if this is a consumer if it has any pact files, and we're using a convention that if this service is a provider, it has its provider state setup logic in ./tests/pact_provider
so we can check for the existence of that directory to see if this is a provider (it can also be both or neither, since this is a general-use pipeline script)json
06/13/2022, 1:29 PMstage("UPLOAD PACT FILE") {
when {
expression { return IS_PACT_CONSUMER }
}
steps {
script {
sh "docker run --network host -v \$(pwd)/tests:/tests " +
"pactfoundation/pact-cli broker publish " +
"/tests/ " +
"--broker-base-url=\"${PACT_BROKER_URL}\" " +
"--consumer-app-version=\"${job.abbrevCommitHash}\" "
}
}
}
Next, if the current project is a consumer, it uploads the pact file using the pact CLI (it's running it through the published docker container so we don't have to worry about installing it on our jenkins agents). We're creating our pact contract files into the tests
directory, so we pass that directory into this script and it is smart enough to find all the pact json files. It uploads them to our broker (at PACT_BROKER_URL
) and tags the contracts with the consumer version provided by job.abbrevCommitHash
.json
06/13/2022, 1:33 PM{
"uuid": "a0beeef6-b69e-421a-8033-d9212c274426",
"description": "Automatically trigger pact verification on contract change.",
"enabled": true,
"request": {
"method": "POST",
"url": "http://<jenkins-url>.com/job/Pact%20Verify%20Provider/buildWithParameters",
"headers": {
"Content-Type": "application/json"
},
"body": {
"project": "${pactbroker.providerName}",
"version": "${pactbroker.providerVersionNumber}",
"pactfile_url": "${pactbroker.pactUrl}"
},
"username": "**********",
"password": "**********"
},
"events": [
{
"name": "contract_requiring_verification_published"
}
],
"createdAt": "2022-06-01T20:32:58+00:00"
}
This webhook is configured to call another jenkins job, passing it the name of the provider that needs to be verified, the version of that provider that needs to be verified (the git commit hash) and the URL of the pactfile that it needs to verify.json
06/13/2022, 1:37 PMstage("VERIFY") {
steps {
script {
cmd = "docker run " +
"--network host pactfoundation/pact-ref-verifier " +
"--broker-url \"${PACT_BROKER_URL}\" " +
"--provider-name \"${project}\" " +
"--provider-version ${version} " +
"--publish " +
"--hostname \"localhost\" " +
"--port \"$PROVIDER_PORT\" " +
"--state-change-url \"<http://localhost>:$PROVIDER_PORT/_pact/provider_states\" "
if (pactfile_url != "false") {
cmd += "--url \"${pactfile_url}\" "
} else {
cmd += "--consumer-version-selectors \"{ 'environment': '${environment}' }\" "
}
sh cmd
}
}
}
(the "else" clause in there is because this job can alternatively be supplied with an environment name instead of a pact url, and it will verify the provider against all pacts tagged with that environment)json
06/13/2022, 1:40 PMstage ("VERIFY CONTRACTS") {
when {
expression { return IS_PACT_PROVIDER }
}
steps{
script {
echo 'Verify pacts where self is provider...'
build job: "Pact Verify Provider",
parameters: [
string(name: 'project', value: job.projectName),
string(name: 'version', value: job.abbrevCommitHash),
string(name: 'environment', value: 'master')
],
wait: true
}
}
}
We are treating "master" like an environment since we are on a trunk-based development model and simply want to verify all new changes against the head of master before we allow them to merge.json
06/13/2022, 1:42 PMstage("CHECK PACT VERIFICATIONS") {
when {
expression { return IS_PACT_PROVIDER || IS_PACT_CONSUMER }
}
steps {
script {
sh "docker run --network host pactfoundation/pact-cli broker " +
"can-i-deploy " +
"--broker-base-url=\"${PACT_BROKER_URL}\" " +
"--retry-while-unknown=30 " +
"--retry-interval=10 " +
"--pacticipant=\"${job.projectName}\" " +
"--version=\"${job.abbrevCommitHash}\" " +
"--to-environment=master "
}
}
}
json
06/13/2022, 1:44 PMstage("TAG PACT BROKER") {
when {
branch 'master'
expression { return IS_PACT_PROVIDER || IS_PACT_CONSUMER }
}
steps {
script {
sh "docker run --network host pactfoundation/pact-cli broker " +
"create-version-tag " +
"--broker-base-url=\"${PACT_BROKER_URL}\" " +
"--pacticipant=\"${job.projectName}\" " +
"--version=\"${job.abbrevCommitHash}\" " +
"--tag=\"${job.publishedVersion}\" "
sh "docker run --network host pactfoundation/pact-cli broker " +
"record-deployment " +
"--broker-base-url=\"${PACT_BROKER_URL}\" " +
"--pacticipant=\"${job.projectName}\" " +
"--version=\"${job.abbrevCommitHash}\" " +
"--environment=\"master\" "
}
}
}
json
06/13/2022, 1:44 PM