-
-
Notifications
You must be signed in to change notification settings - Fork 187
Description
Background
There are two apis for publishing contracts
OLD: http://{broker}/pacts/provider/{provider}/consumer/{consumer}/version/{version]
and
NEW: http://{broker}/contracts/publish
The Pact Broker Client supports a feature flag
PACT_BROKER_FEATURES=publish_pacts_using_old_api
which allows the end user to revert to using the former endpoint
Problem
Since the Rack 3 upgrade, the old api for publishing pacts returns an error.
This came to light when I was upgrading pact_broker-client & pact_broker to use the rust core
- ✅ Verifications of the endpoint with the Ruby core pass
- ❌ Verifications of the endpoint with the Rust core fail
- ❌ Publications via API or using the pact_broker-client fail when using the old endpoint
Pact-Ruby seems to be masking the issue, so it hasn't come to light
Stack Trace
Failed to publish Foo/Bar pact due to error: PactBroker::Client::Error - {"error":{"message":"unexpected end of input at line 1 column 1",
"reference":"JPaSjsCmMm",
"backtrace":["/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/json-2.12.2/lib/json/common.rb:338:in `parse'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/json-2.12.2/lib/json/common.rb:338:in `parse'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/pacts/parse.rb:8:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/pacts/content.rb:20:in `from_json'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/pacts/generate_sha.rb:24:in `extract_verifiable_content_for_sha'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/pacts/generate_sha.rb:16:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/pacts/service.rb:26:in `generate_sha'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/api/resources/pact.rb:131:in `pact_version_sha'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/api/resources/pact.rb:117:in `disallowed_modification?'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/api/resources/pact.rb:57:in `is_conflict?'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/decision/flow.rb:500:in `p3'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/decision/fsm.rb:31:in `block (2 levels) in run'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/decision/fsm.rb:51:in `handle_exceptions'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/decision/fsm.rb:31:in `block in run'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/decision/fsm.rb:29:in `loop'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/decision/fsm.rb:29:in `run'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/dispatcher.rb:46:in `block in dispatch'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/as-notifications-1.0.2/lib/as/notifications.rb:161:in `instrument'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/events.rb:75:in `instrument'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webmachine-2.0.1/lib/webmachine/dispatcher.rb:45:in `dispatch'",
"/home/runner/work/pact_broker/pact_broker/lib/webmachine/adapters/rack3_adapter.rb:44:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/cascade.rb:46:in `block in call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/cascade.rb:37:in `each'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/cascade.rb:37:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/database_transaction.rb:46:in `block in call_with_transaction'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/sequel-5.96.0/lib/sequel/database/transactions.rb:264:in `_transaction'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/sequel-5.96.0/lib/sequel/database/transactions.rb:239:in `block in transaction'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/sequel-5.96.0/lib/sequel/connection_pool/timed_queue.rb:90:in `hold'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/sequel-5.96.0/lib/sequel/database/connecting.rb:283:in `synchronize'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/sequel-5.96.0/lib/sequel/database/transactions.rb:197:in `transaction'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/database_transaction.rb:45:in `call_with_transaction'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/database_transaction.rb:24:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/no_auth.rb:9:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/builder.rb:283:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/cascade.rb:46:in `block in call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/cascade.rb:37:in `each'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/cascade.rb:37:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/urlmap.rb:76:in `block in call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/urlmap.rb:60:in `each'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/urlmap.rb:60:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/static.rb:162:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/hal_browser/redirect.rb:20:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/set_base_url.rb:15:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/convert_file_extension_to_accept_header.rb:28:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/add_cache_header.rb:9:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/static.rb:162:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/static.rb:162:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/add_vary_header.rb:34:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/add_pact_broker_version_header.rb:14:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/reset_thread_data.rb:13:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/invalid_uri_protection.rb:26:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/application_context.rb:12:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/use_when.rb:31:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-protection-4.1.1/lib/rack/protection/content_security_policy.rb:73:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/rack/pact_broker/use_when.rb:29:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-protection-4.1.1/lib/rack/protection/xss_header.rb:20:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-protection-4.1.1/lib/rack/protection/json_csrf.rb:28:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-protection-4.1.1/lib/rack/protection/base.rb:53:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-protection-4.1.1/lib/rack/protection/frame_options.rb:33:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/builder.rb:283:in `call'",
"/home/runner/work/pact_broker/pact_broker/lib/pact_broker/app.rb:90:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/tempfile_reaper.rb:20:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/lint.rb:93:in `response'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/lint.rb:16:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/show_exceptions.rb:31:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/common_logger.rb:43:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/sinatra-4.1.1/lib/sinatra/base.rb:269:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rack-3.2.1/lib/rack/content_length.rb:20:in `call'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/rackup-2.2.1/lib/rackup/handler/webrick.rb:111:in `service'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webrick-1.9.1/lib/webrick/httpserver.rb:140:in `service'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webrick-1.9.1/lib/webrick/httpserver.rb:96:in `run'",
"/opt/hostedtoolcache/Ruby/3.2.9/x64/lib/ruby/gems/3.2.0/gems/webrick-1.9.1/lib/webrick/server.rb:310:in `block in start_thread'"]}}
Testing
I've created a simple script to
- checkout a specific version of tthe pact_broker codebase
- bundle install
- install pact_broker-client
- download a pact file
- start the pact broker and wait for it to start
- publish the pact
I have tested against
- ❌ Locally via pact_broker repo (rackup)
- ✅ Docker - Latest Pact Broker Image
- ✅ Docker - Building latest pact broker image locally
- ❌ Docker - Running Pact Broker locally, in
./pact_brokerto use Gemfile.lock - ✅ PactFlow
I would have expected the Docker images to have all failed, but oddly running it on my mac with the same bundle fails, but passes with a freshly built local image 🤯
Edit: Mystery solved as to why it doesn't work across projects.
Running with
✅ bundle exec puma
❌ bundle exec rackup -s puma
❌ bundle exec rackup -s webrick
Test Script
name: publish_pacts api test
on:
push:
branches:
- issue/publish_pacts_using_old_api
workflow_dispatch:
inputs:
git_commit:
description: 'Git commit hash to checkout'
required: false
type: string
jobs:
publish_pact_old_api:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.git_commit || github.sha }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- run: gem install pact_broker-client
- run: bundle install
- run: bundle exec rackup &
- run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.3/wait-for | sh -s -- localhost:9292 -t 120 -- echo broker is up
- run: |
curl -L https://raw.githubusercontent.com/pact-foundation/pact_broker/refs/heads/master/script/foo-bar.json -o foo-bar.json
pact-broker publish foo-bar.json --consumer-app-version 1.2.26
name: Publish pacts using old API
shell: bash
env:
PACT_BROKER_FEATURES: publish_pacts_using_old_api
PACT_BROKER_BASE_URL: http://localhost:9292
publish_pact_new_api:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ ubuntu-latest ]
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.git_commit || github.sha }}
- name: Setup Ruby
uses: ruby/setup-ruby@v1
with:
ruby-version: '3.2'
- run: gem install pact_broker-client
- run: bundle install
- run: bundle exec rackup &
- run: wget -qO- https://raw.githubusercontent.com/eficode/wait-for/v2.2.3/wait-for | sh -s -- localhost:9292 -t 120 -- echo broker is up
- run: |
curl -L https://raw.githubusercontent.com/pact-foundation/pact_broker/refs/heads/master/script/foo-bar.json -o foo-bar.json
pact-broker publish foo-bar.json --consumer-app-version 1.2.26
name: Publish pacts using new API
shell: bash
env:
PACT_BROKER_BASE_URL: http://localhost:9292Root Cause
Looks to have been introduced post the upgrade to Rack 3 - PR
- ✅ Last commit on rack 2 - b3da850
- ❌ First commit for rack 3 - ed1e8b2
- ❌ First commit for rack 3 that shows issue - 3ac959d
It should be noted the first commit for Rack 3 upgrade failed due to headers not being downcased, and therefore the third commit shown, highlights the issue we can see today.
Rack 3 Upgrade guide for reference - https://github.com/rack/rack/blob/main/UPGRADE-GUIDE.md
✅ bundle exec puma
❌ bundle exec rackup -s puma
❌ bundle exec rackup -s webrick