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

[DR-3282] Add Pact provider support for integration with WDS consumer #1528

Merged
merged 36 commits into from
Nov 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4196768
first pass at some verification
calypsomatic Oct 16, 2023
ba9ede7
test update and gradle task
calypsomatic Oct 16, 2023
3059d38
some updates
calypsomatic Oct 19, 2023
8f80483
use more anys
calypsomatic Oct 20, 2023
0a65873
Create verify_consumer_pacts.yaml
calypsomatic Oct 20, 2023
e490cfc
Update verify_consumer_pacts.yaml
calypsomatic Oct 24, 2023
059c7e6
rename tdr-provider
calypsomatic Oct 24, 2023
efb8a13
Show standard stream logging output for Pact verification tests
okotsopoulos Oct 24, 2023
fda6b67
Categorize Pact tests with @Tag
okotsopoulos Oct 24, 2023
3178763
Spotless
okotsopoulos Oct 24, 2023
5e56bb3
Debug why Pact verification isn't running
okotsopoulos Oct 24, 2023
0f47268
Set up Pact test with Spring boot test slicing
okotsopoulos Oct 25, 2023
2c1be11
Try to enable Gradle build scans for Pact tests
okotsopoulos Oct 25, 2023
b3c807c
Pact broker URL shouldn't specify https prefix
okotsopoulos Oct 25, 2023
d74be35
Use HTTPS when resolving Pact Broker
okotsopoulos Oct 25, 2023
04f8b09
Instruct Pact context to use MockMvc as its target
okotsopoulos Oct 25, 2023
6e19d3e
GlobalExceptionHandler must be loaded in Pact verification test slicing
okotsopoulos Oct 26, 2023
1efd0e3
Pass Pact username and password to Gradle verifyTests target
okotsopoulos Oct 26, 2023
fcdf54d
Rename provider to match Helm chart name: datarepo
okotsopoulos Oct 27, 2023
805ea19
Update documentation to include Pact broker credential management
okotsopoulos Oct 27, 2023
ee1072e
Update au.com.dius.pact.provider:junit5spring to latest
okotsopoulos Oct 27, 2023
67fa417
Update au.com.dius.pact.provider:junit5 to latest
okotsopoulos Oct 27, 2023
69ba23d
Downgrade au.com.dius.pact.provider to previously working version
okotsopoulos Oct 27, 2023
a02f63c
Simplify Provider states to the minimum required
okotsopoulos Nov 1, 2023
5ca6609
Snapshot provider states expect ID to be specified in params
okotsopoulos Nov 1, 2023
174daee
Update Provider state names to reflect expected parameters
okotsopoulos Nov 3, 2023
a910819
Remove what I think is a redundant Pact plugin?
okotsopoulos Nov 9, 2023
1386281
Add instructions for verifying Pacts locally
okotsopoulos Nov 9, 2023
471cf58
Break out Pact test config into separate script
okotsopoulos Nov 13, 2023
985dbc7
Add consumerVersionSelectors to Pact provider set-up
okotsopoulos Nov 13, 2023
257250b
Rollback SnapshotService method signature changes committed by mistake
okotsopoulos Nov 13, 2023
d4b88c6
Add missing SnapshotDaoTest for retrieving nonexistent snapshot
okotsopoulos Nov 13, 2023
8f4e3e4
Rename DataRepoProviderTest -> SnapshotsApiControllerTest
okotsopoulos Nov 13, 2023
36631c8
Add unit test to verify SnapshotNotFoundException's class lineage
okotsopoulos Nov 13, 2023
98955d9
Establish Pact provider state method naming convention
okotsopoulos Nov 13, 2023
dbcb780
Enable test logging for Pact provider verification
okotsopoulos Nov 13, 2023
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
205 changes: 205 additions & 0 deletions .github/workflows/verify_consumer_pacts.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
name: Verify consumer pacts
# The purpose of this workflow is to verify ANY consumer contract(s) dependent on datarepo provider using Pact framework.
#
# The workflow meets the criteria of Pact Broker *Platinum* as described in https://docs.pact.io/pact_nirvana/step_6.
# The can-i-deploy job has been added to this workflow to gate the merge of PRs into develop branch.
#
# This workflow is triggered when
#
# 1. Consumer makes a change that results in a new pact published to Pact Broker (will verify ONLY the changed pact and publish the verification results back to the broker)
# 2. Provider makes a change (runs verification tests against ALL DEPLOYED consumer pact versions and publishes corresponding verification results)
#
#
# The workflow requires the following Pact broker credentials:
# - PACT_BROKER_USERNAME - the Pact Broker username
# - PACT_BROKER_PASSWORD - the Pact Broker password
# They are managed by Atlantis and were added to Terraform here:
# https://github.com/broadinstitute/terraform-ap-deployments/pull/1255
Comment on lines +16 to +17
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Up to you but I wouldn't leave this in, in case we change how secrets are populated in the future in GitHub. There's nothing specific to the workflow about this as all secrets in GitHub are populated the same way.

env:
TDR_LOG_APPENDER: 'Console-Standard'
on:
pull_request:
branches:
- develop
paths-ignore:
- 'README.md'
Comment on lines +24 to +25
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's nothing wrong with using paths-ignore but we don't do it elsewhere in TDR and if you wanted to really do this, there are many other .md files which could be listed here.

push:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this run on push? That means it will run after a PR is merged to develop, in addition to running before the PR is merged. Under what conditions would this detect an error that would otherwise be missed?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ultimately, the verify_cosumer_pacts.yaml workflow needs to report the bumper tag to Pact Broker as the new pact version. The only way to do this will be to publish the verification results once again using the new tag.

branches:
- develop
paths-ignore:
- 'README.md'
workflow_dispatch:
inputs:
pb-event-type:
description: 'the Pact Broker event type that triggers this workflow'
required: true
type: string
consumer-name:
description: 'the consumer name'
required: true
type: string
consumer-version-number:
description: 'the version number of the most recent consumer version associated with the pact content'
required: true
type: string
provider-version-number:
description: 'the provider version number for the verification result'
required: false
type: string
consumer-version-tags:
description: 'the list of tag names for the most recent consumer version associated with the pact content, separated by ", "'
required: true
type: string
consumer-version-branch:
description: 'the name of the branch for most recent consumer version associated with the pact content'
required: true
type: string
provider-version-branch:
description: 'the name of the branch for the provider version associated with the verification result'
required: false
type: string
consumer-labels:
description: 'the list of labels for the consumer associated with the pact content, separated by ", "'
required: false
type: string
provider-labels:
description: 'the list of labels for the provider associated with the pact content, separated by ", "'
required: false
type: string
pact-url:
description: 'the "permalink" URL to the newly published pact (the URL specifying the consumer version URL, rather than the "/latest" format'
required: true
type: string

jobs:
verify-consumer-pact:
runs-on: ubuntu-latest
permissions:
contents: 'read'
id-token: 'write'
outputs:
provider-sha: ${{ steps.verification-test.outputs.provider-sha }}

steps:
- name: Checkout current code
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Extract branch
id: extract-branch
run: |
GITHUB_EVENT_NAME=${{ github.event_name }}
if [[ "$GITHUB_EVENT_NAME" == "push" ]]; then
GITHUB_REF=${{ github.ref }}
GITHUB_SHA=${{ github.sha }}
elif [[ "$GITHUB_EVENT_NAME" == "pull_request" ]]; then
GITHUB_REF=refs/heads/${{ github.head_ref }}
GITHUB_SHA=${{ github.event.pull_request.head.sha }}
elif [[ "$GITHUB_EVENT_NAME" == "workflow_dispatch" ]]; then
GITHUB_REF=${{ github.ref }} # The Git Ref that this workflow runs on
GITHUB_SHA=${{ github.sha }} # The Git Sha that this workflow runs on
else
echo "Failed to extract branch information"
exit 1
fi
echo "CURRENT_BRANCH=${GITHUB_REF/refs\/heads\//""}" >> $GITHUB_ENV
echo "CURRENT_SHA=$GITHUB_SHA" >> $GITHUB_ENV

- name: "This step will only run when this workflow is triggered by a Pact Broker webhook event"
if: ${{ inputs.pb-event-type != '' }}
run: |
echo "pb-event-type=${{ inputs.pb-event-type }}"
echo "consumer-name=${{ inputs.consumer-name }}"
echo "consumer-version-branch/consumer-version-number=${{ inputs.consumer-version-branch }}/${{ inputs.consumer-version-number }}"
echo "provider-version-branch/provider-version-number=${{ inputs.provider-version-branch }}/${{ inputs.provider-version-number }}"
# The consumer-version-branch/consumer-version-number is practically sufficient.
# The pact-url is included here in case future pact4s client supports it.
echo "pact-url=${{ inputs.pact-url }}"
if [[ ! -z "${{ inputs.provider-version-branch }}" ]]; then
echo "PROVIDER_BRANCH=${{ inputs.provider-version-branch }}" >> $GITHUB_ENV
echo "CHECKOUT_BRANCH=${{ inputs.provider-version-branch }}" >> $GITHUB_ENV
fi
if [[ ! -z "${{ inputs.provider-version-number }}" ]]; then
echo "PROVIDER_SHA=${{ inputs.provider-version-number }}" >> $GITHUB_ENV
echo "CHECKOUT_SHA=${{ inputs.provider-version-number }}" >> $GITHUB_ENV
fi
echo "CONSUMER_NAME=${{ inputs.consumer-name }}" >> $GITHUB_ENV
echo "CONSUMER_BRANCH=${{ inputs.consumer-version-branch }}" >> $GITHUB_ENV
echo "CONSUMER_SHA=${{ inputs.consumer-version-number }}" >> $GITHUB_ENV

- name: Switch to appropriate branch
run: |
if [[ -z "${{ env.PROVIDER_BRANCH }}" ]]; then
echo "PROVIDER_BRANCH=${{ env.CURRENT_BRANCH }}" >> $GITHUB_ENV
fi
if [[ -z "${{ env.PROVIDER_SHA }}" ]]; then
echo "PROVIDER_SHA=${{ env.CURRENT_SHA }}" >> $GITHUB_ENV
fi
git fetch
if [[ ! -z "${{ env.CHECKOUT_BRANCH }}" ]] && [[ ! -z "${{ env.CHECKOUT_SHA }}" ]]; then
echo "git checkout -b ${{ env.CHECKOUT_BRANCH }} ${{ env.CHECKOUT_SHA }}"
git checkout -b ${{ env.CHECKOUT_BRANCH }} ${{ env.CHECKOUT_SHA }} || echo "already in ${{ env.CHECKOUT_BRANCH }}"
echo "git branch"
git branch
else
if [[ "${{ github.event_name }}" == "push" ]] || [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
echo "git checkout ${{ env.CURRENT_BRANCH }}"
git checkout ${{ env.CURRENT_BRANCH }}
else
echo "git checkout -b ${{ env.CURRENT_BRANCH }} ${{ env.CURRENT_SHA }}"
git checkout -b ${{ env.CURRENT_BRANCH }} ${{ env.CURRENT_SHA }}
fi
fi
echo "git rev-parse HEAD"
git rev-parse HEAD

- name: Set up JDK 17
uses: actions/setup-java@v2
with:
java-version: '17'
distribution: 'temurin'

- name: Gradle cache
uses: actions/cache@v2
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: v1-${{ runner.os }}-gradle-${{ github.ref }}-${{ github.sha }}
restore-keys: v1-${{ runner.os }}-gradle-${{ github.ref }}

- name: Verify consumer pacts and publish verification status to Pact Broker
id: verification-test
env:
PACT_PROVIDER_COMMIT: ${{ env.PROVIDER_SHA }}
PACT_PROVIDER_BRANCH: ${{ env.PROVIDER_BRANCH }}
PACT_BROKER_USERNAME: ${{ secrets.PACT_BROKER_USERNAME }}
PACT_BROKER_PASSWORD: ${{ secrets.PACT_BROKER_PASSWORD }}
run: |
echo "provider-sha=${{ env.PROVIDER_SHA }}" >> $GITHUB_OUTPUT
echo "env.CHECKOUT_BRANCH=${{ env.CHECKOUT_BRANCH }} # If not empty, this reflects the branch being checked out (generated by Pact Broker)"
echo "env.CHECKOUT_SHA=${{ env.CHECKOUT_SHA }} # If not empty, this reflects the git commit hash of the branch being checked out (generated by Pact Broker)"
echo "env.CURRENT_BRANCH=${{ env.CURRENT_BRANCH }} # This reflects the branch being checked out if CHECKOUT_BRANCH is empty"
echo "env.CURRENT_SHA=${{ env.CURRENT_SHA }} # This reflects the git commit hash of the branch being checked out if CHECKOUT_BRANCH is empty"
echo "env.PROVIDER_BRANCH=${{ env.PROVIDER_BRANCH }} # This reflects the provider branch for pact verification"
echo "env.PROVIDER_SHA=${{ env.PROVIDER_SHA }} # This reflects the provider version for pact verification"
echo "env.CONSUMER_BRANCH=${{ env.CONSUMER_BRANCH }} # This reflects the consumer branch for pact verification (generated by Pact Broker)"
echo "env.CONSUMER_SHA=${{ env.CONSUMER_SHA }} # This reflects the consumer version for pact verification (generated by Pact Broker)"
./gradlew --build-cache verifyPacts --scan

okotsopoulos marked this conversation as resolved.
Show resolved Hide resolved
can-i-deploy:
# The can-i-deploy job will run as a result of a jade-data-repo PR.
# It reports the pact verification statuses on all deployed environments.
runs-on: ubuntu-latest
if: ${{ inputs.pb-event-type == '' }}
needs: [ verify-consumer-pact ]
steps:
- name: Dispatch to terra-github-workflows
uses: broadinstitute/workflow-dispatch@v3
with:
workflow: .github/workflows/can-i-deploy.yaml
repo: broadinstitute/terra-github-workflows
ref: refs/heads/main
token: ${{ secrets.BROADBOT_TOKEN }} # github token for access to kick off a job in the private repo
inputs: '{ "pacticipant": "datarepo", "version": "${{ needs.verify-consumer-pact.outputs.provider-sha }}" }'
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,18 @@ When running locally, we are not using the proxy. Therefore, the system doesn't
If you are making code changes, run:
`./gradlew check`

### Verify Pact contracts

To verify that TDR adheres to the contracts published by its consumers, run:
```
./src/test/render-pact-configs.sh
# Reload your environment variables, e.g. src ~/.zshrc
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this assumes that the user has added export PACT_BROKER_USERNAME etc to their .zshrc? I guess overall this doc doesn't really talk about how the rendered configs are exported to the app via environment variables.

./gradlew verifyPacts # verify contracts published with TDR as the provider
```

By default, this will fetch published contracts from the live Pact broker.
Results of Pact verification are only published when running in a CI environment (not locally).

### Run jade locally

Before you run for the first time, you need to generate the credentials file by running `./render-configs.sh`
Expand Down
19 changes: 19 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,8 @@ dependencies {
exclude group: 'com.sun.jersey', module: 'jersey-server'
}

testImplementation 'au.com.dius.pact.provider:junit5:4.3.19'
testImplementation 'au.com.dius.pact.provider:junit5spring:4.3.19'

antlr "org.antlr:antlr4:4.8"
spotbugs 'com.github.spotbugs:spotbugs:4.2.3'
Expand Down Expand Up @@ -573,6 +575,23 @@ task testAll(type: Test) {
outputs.upToDateWhen { false }
}

task verifyPacts(type: Test) {
useJUnitPlatform {
includeTags 'bio.terra.common.category.Pact'
}
testLogging {
events = ["passed", "failed", "skipped", "started", "standard_out"]
}
outputs.upToDateWhen { false }
if (isCiServer) {
systemProperty 'pact.verifier.publishResults', true
}
systemProperty 'pactbroker.auth.username', System.getenv('PACT_BROKER_USERNAME')
systemProperty 'pactbroker.auth.password', System.getenv('PACT_BROKER_PASSWORD')
systemProperty 'pact.provider.version', System.getenv('PACT_PROVIDER_COMMIT')
systemProperty 'pact.provider.branch', System.getenv('PACT_PROVIDER_BRANCH')
}

compileGeneratedJava.dependsOn swaggerSources.server.code
compileGeneratedJava.dependsOn generateGrammarSource
classes.dependsOn generatedClasses
Expand Down
13 changes: 12 additions & 1 deletion docs/jade-getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,10 @@ export AZURE_SYNAPSE_SQLADMINUSER=$(cat /tmp/jade-dev-synapse-admin-user.key)
export AZURE_SYNAPSE_SQLADMINPASSWORD=$(cat /tmp/jade-dev-synapse-admin-password.key)
export AZURE_SYNAPSE_ENCRIPTIONKEY=$(cat /tmp/jade-dev-synapse-encryption-key.key)
export AZURE_SYNAPSE_INITIALIZE=false

# Pact contract test settings
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for TDR devs --

These are two new environment variables needed to verify Pacts locally.

export PACT_BROKER_USERNAME=$(cat /tmp/pact-ro-username.key)
export PACT_BROKER_PASSWORD=$(cat /tmp/pact-ro-password.key)
```

* If you're not on a **Broad-provided** computer, you may need to set the host to `localhost`
Expand All @@ -372,7 +376,14 @@ export HOST=localhost
./gradlew bootRun # build jade-data-repo with Spring Boot features
./gradlew check # linters and unit tests
./gradlew testConnected # connected tests
./gradlew testIntegration # integration tests
./gradlew testIntegration # integration tests
```

Running Pact tests can be achieved by rendering a small set of Pact-specific configurations first:
```
./src/test/render-pact-configs.sh
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the value in having this script exist separate from render-configs.sh. Is the idea that most of the time we don't run pact tests locally so it's typically not needed?

# Reload your environment variables, e.g. src ~/.zshrc
./gradlew verifyPacts # verify contracts published with TDR as the provider
```

Note that connected and integration test suites can each take 90+ minutes to run.
Expand Down
17 changes: 17 additions & 0 deletions src/test/java/bio/terra/common/category/Pact.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package bio.terra.common.category;

/** Pact contract test category. */
public interface Pact {
String TAG = "bio.terra.common.category.Pact";

/**
* The name used for Pact consumers and providers (generically known as "pacticipants") should
* match our Helm chart name in order for our deployments to be recorded. <br>
* Using the same name for both consumers and providers also allows for accurate and clear
* construction of the Pact Broker's network graph.
*/
String PACTICIPANT = "datarepo";

/** Test application property file suffix */
String PROFILE = "pacttest";
}
Loading
Loading