Giter VIP home page Giter VIP logo

pact-plugins's Introduction

Pact Plugins

Architecture to support plugins with Pact

Pact Plugin Build (Gradle) Pact Plugin Build (Rust)

Plugin architecture

The plugins are enabled via a message passing mechanism over gRPC. Each language implements a driver which provides the mechanism to locate and load plugins, as well as a catalogue of features (like mock servers, matchers and provider verifiers) and a central message bus to enable communication between the language implementation and the plugins.

Plugin driver

The plugin driver is responsible for providing access to the plugins from the language implementation (which is where the pact tests are being executed).

Main responsibilities:

  • The ability to find plugins.
  • Load plugins and extract the plugin manifests that describe what the plugin provides.
  • Provide a catalogue of features provided by the plugins.
  • Provide a messaging bus to facilitate communication between the language implementation and the plugins.
  • Manage the plugin lifecycles.

See Plugin driver design docs.

There are two implementations of plugin drivers: JVM and Rust.

Plugins

Plugins are required to start up a gRPC server when loaded, and respond to messages from the plugin driver. They provide a manifest that describes the features they provide and the mechanism needed to load them.

Main responsibilities:

  • Have a plugin manifest that describes the plugin and how to load it.
  • Start a gRPC server on load and provide the port to the driver that loaded it.
  • Provide a catalogue of features the plugin provides when the driver requests it.
  • Respond to messages from the driver.

See the guide to writing a Pact plugin.

Plugins that provide protocol transport implementations

Plugins can provide support for new protocols. The main features that the plugin would provide is to be able to create the protocol transport payloads and create a mock server that can deal with them.

See Protocol design docs.

For an example, see the gRPC plugin, it supports gRPC over HTTP/2.

Plugins that provide support for different types of content

These plugins provide the ability to match and generate different types of contents which are used with existing protocol implementations.

See Content matcher design docs.

There are two example prototype plugins that support matching different types of content: Protobuf and CSV.

See PactFlow Protobuf/gRPC plugin for a PactFlow supported plugin.

Plugins that provide matchers/generators (WIP)

TODO 🚧

Background

Pact was created initially to support the rise of RESTful microservices and has grown to be the de-facto API contract testing tool.

One of the strengths of Pact is its specification, allowing anybody to create a new language binding in an interoperable way. Whilst this has been great at unifying compatibility, the sprawl of languages makes it hard to add significant new features/behaviour into the framework quickly (e.g. GraphQL or Protobuf support).

The "shared core"

We have attempted to combat this time-to-market problem, by focussing on a shared implementation (the "shared core") in many of the languages. We initially bundled Ruby, because it was convenient, but have been slowly moving to our Rust core which solves many of the challenges that bundling Ruby presented.

It is worth noting that the "shared core" approach has largely been a successful exercise in this regard. There are many data points, but the implementation of WIP/Pending pacts was released (elapsed, not effort) in just a few weeks for the libraries that wrapped Ruby. In most cases, an update of the Ruby "binaries", mapping flags from the language specific API to dispatch to the underlying Ruby process, a README update and a release was all that was required. In many cases, new functionality is still published with an update to the Ruby binary, which has been automated through a script.

Moving beyond HTTP

But, the industry has continued to innovate since Pact was created in 2013, and RESTful microservices are only one of the key use cases these days - protocols such as Protobufs and Graphql, transports such as TCP, UDP and HTTP/2 and interaction modes (e.g. streaming or server initiated) are starting to become the norm. Standards such as AsyncAPI and CloudEvent are also starting to emerge.

For example, Pact is still a rather HTTP centric library, and the mixed success in retrofitting "message support" into all languages shows that extensions outside of this boundary aren't trivial, and in some respects are a second class citizen.

The reason is simple: HTTP doesn't change very often, so once a language has implemented a sensible DSL for it and integrated to the core, it's more a matter of fine tuning things. Adding message pact is a paradigm shift relative to HTTP, and requires a whole new developer experience of authoring tests, integrating to the core and so on, for the language author to consider.

Being able to mix and match protocol, transport and interaction mode would be helpful in expanding the use cases.

Further, being able to add custom contract testing behaviour for bespoke use cases would be helpful in situations where we can't justify the effort to build into the framework itself (custom protocols in banking such as AS2805 come to mind).

To give some sense of magnitude to the challenge, this table shows some of the Pact deficiencies across popular microservice deployments.

83211994-ced39200-a1a1-11ea-8804-19b633cbb1d6

The "shared core" approach can only take us so far, and we need another mechanism for extending behaviour outside of the responsibilities of this core. This is where I see a plugin approach working with our "shared core" model.

pact-plugins's People

Contributors

austek avatar mefellows avatar rholshausen avatar theswiftfox avatar tienvx avatar timbeemster avatar uglyog avatar you54f avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pact-plugins's Issues

CSV Plugin > Allow setting order of columns when using headers

Currently we can't set order of columns if we use headers:

$response
     ->setStatus(200)
    ->setBody([
        'csvHeaders' => true,
        'column:id' => "matching(regex, '^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$','{$id}')",
        'column:name' => "matching(type,'{$name}')",
        'column:gender' => "matching(regex, 'male|female|other','{$gender}')",
    ])
    ->setContentType('text/csv')
 ;

Then mock server will response with columns that are sorted by column's name in ascending order:

gender,id,name
$gender,$id,$name

I suggest adding 'csvColumns' => 'id,name,gender' so:

  • Mock server will return columns in this order
  • Provider verifier will verify csv's columns in this order

Strange hanging provider verification

I've been running into a strange verification issue, where my build just hangs. Locally, it seems to reliably run just fine (M1 Mac and also via intel emulation in Docker).

The setup:

2022/12/22 10:07:12 lumberjack logging initialised
2022/12/22 10:07:12 [DEBUG] initialised logging
2022/12/22 10:07:12 [INFO] server on port 43957
2022/12/22 10:07:12 [INFO] InitPlugin request: plugin-driver-rust 0.2.2
2022/12/22 10:07:12 [INFO] UpdateCatalogue request: [type:TRANSPORT  key:"matt" key:"matt"  values:{key:"content-types"  value:"text/matt;application/matt"}]

2022/12/22 10:08:00 lumberjack logging initialised
2022/12/22 10:08:00 [DEBUG] initialised logging
2022/12/22 10:08:00 [INFO] server on port 43251
2022/12/22 10:08:00 [INFO] InitPlugin request: plugin-driver-rust 0.2.2

2022/12/22 10:08:00 lumberjack logging initialised
2022/12/22 10:08:00 [DEBUG] initialised logging
2022/12/22 10:08:00 [INFO] server on port 38435
2022/12/22 10:08:00 [INFO] InitPlugin request: plugin-driver-rust 0.2.2

2022/12/22 10:08:00 lumberjack logging initialised
2022/12/22 10:08:00 [DEBUG] initialised logging
2022/12/22 10:08:00 [INFO] server on port 34817

The test times out after 30s (default Golang test timeout). If you notice, the plugin is actually started 4 times (resulting in 4 separate processes on different ports). 3 of them received an InitPlugin request and the first also received the UpdateCatalogue request.

Running locally, I can see that 3/4 of the matt processes were started and continue to run after the tests complete:

  UID   PID  PPID   C STIME   TTY           TIME CMD
  502 28591     1   0  9:21pm ttys001    0:00.01 /Users/matthew.fellows/.pact/plugins/matt-0.0.7/matt
  502 28653     1   0  9:21pm ttys001    0:00.01 /Users/matthew.fellows/.pact/plugins/matt-0.0.7/matt
  502 28715     1   0  9:21pm ttys001    0:00.02 /Users/matthew.fellows/.pact/plugins/matt-0.0.7/matt

This doesn't seem to happen with the protobuf plugin, so I do have my suspicions of this being a matt plugin specific problem.

Update

So I increased the test timeout by another 100s to get past the Golang timeout (in case that was all it was. It wasn't). The build failed with a verification error code of 2 (https://github.com/pact-foundation/pact-go/actions/runs/3758088926/jobs/6386014379#step:5:2542) which indicates the verification process wasn't able to run.

This leads me further down the "matt plugin" problem, but there are no obvious logs to indicate the process was killed/died or panic'd (I've also wrapped the plugin's main process in a panic recover(), so it would catch and log any of those). What's also confusing, is that this plugin works with the Pact JS builds on linux. But it does seem like the driver is trying to start the plugin and run the verification, but something goes wrong and it has to continually try and start a new plugin process.

The logs from the plugin are interesting, with an extra plugin process starting this time:

2022/12/22 13:02:45 lumberjack logging initialised
2022/12/22 13:02:45 [DEBUG] initialised logging
2022/12/22 13:02:45 [INFO] server on port 35233
2022/12/22 13:02:45 [INFO] InitPlugin request: plugin-driver-rust 0.2.2
2022/12/22 13:02:45 [INFO] UpdateCatalogue request: [key:"matt"  values:{key:"content-types"  value:"text/matt;application/matt"} type:TRANSPORT  key:"matt"]
2022/12/22 13:03:47 lumberjack logging initialised
2022/12/22 13:03:47 [DEBUG] initialised logging
2022/12/22 13:03:47 [INFO] server on port 36493
2022/12/22 13:03:47 [INFO] InitPlugin request: plugin-driver-rust 0.2.2
2022/12/22 13:03:47 lumberjack logging initialised
2022/12/22 13:03:47 [DEBUG] initialised logging
2022/12/22 13:03:47 [INFO] server on port 37547
2022/12/22 13:03:47 [INFO] InitPlugin request: plugin-driver-rust 0.2.2
2022/12/22 13:03:47 [INFO] UpdateCatalogue request: [type:MATCHER  key:"v3-includes" type:MATCHER  key:"v2-max-type" key:"json"  values:{key:"content-types"  value:"application/.*json,application/json-rpc,application/jsonrequest"} type:MATCHER  key:"v2-min-type" type:MATCHER  key:"v3-number-type" type:TRANSPORT  key:"matt" type:MATCHER  key:"v4-not-empty" key:"matt"  values:{key:"content-types"  value:"text/matt;application/matt"} type:CONTENT_GENERATOR  key:"json"  values:{key:"content-types"  value:"application/.*json,application/json-rpc,application/jsonrequest"} type:MATCHER  key:"v3-null" type:MATCHER  key:"v2-minmax-type" type:MATCHER  key:"v3-date" key:"text"  values:{key:"content-types"  value:"text/plain"} key:"xml"  values:{key:"content-types"  value:"application/.*xml,text/xml"} type:MATCHER  key:"v3-integer-type" type:MATCHER  key:"v4-max-equals-ignore-order" key:"multipart-form-data"  values:{key:"content-types"  value:"multipart/form-data,multipart/mixed"} type:MATCHER  key:"v4-array-contains" type:MATCHER  key:"v1-equality" type:MATCHER  key:"v4-minmax-equals-ignore-order" type:CONTENT_GENERATOR  key:"binary"  values:{key:"content-types"  value:"application/octet-stream"} type:MATCHER  key:"v3-decimal-type" type:MATCHER  key:"v4-semver" type:MATCHER  key:"v3-time" type:MATCHER  key:"v2-regex" type:MATCHER  key:"v4-min-equals-ignore-order" type:MATCHER  key:"v2-type" type:MATCHER  key:"v4-equals-ignore-order" type:MATCHER  key:"v3-datetime" type:MATCHER  key:"v3-content-type"]
2022/12/22 13:03:47 lumberjack logging initialised
2022/12/22 13:03:47 [DEBUG] initialised logging
2022/12/22 13:03:47 [INFO] server on port 41019
2022/12/22 13:03:47 [INFO] InitPlugin request: plugin-driver-rust 0.2.2
2022/12/22 13:03:47 lumberjack logging initialised
2022/12/22 13:03:47 [DEBUG] initialised logging
2022/12/22 13:03:47 [INFO] server on port 41107

What I found really interesting, is that the github agent detected the orphaned processes:

2022-12-22T13:18:57.6873264Z Cleaning up orphan processes
2022-12-22T13:18:57.7135586Z Terminate orphan process: pid (6006) (matt)
2022-12-22T13:18:57.7156236Z Terminate orphan process: pid (6014) (matt)
2022-12-22T13:18:57.7173464Z Terminate orphan process: pid (6023) (matt)

These were the first three plugins started by the framework, my assumption is that the last one the driver started (PID 6031) was shutdown by the framework (albeit I can't see any logs to validate this). This indicates the processes didn't die, but somehow the driver lost track of them or deliberately orphaned them?

find_message_type_by_name not really search in all descriptors

In method find_message_type_by_name, it try to search message by name in all descriptors, but it looks like just return error if it can't find message in the first descriptor, and won't continue.

For example, I have descriptors = [Some("google/protobuf/timestamp.proto"), Some("google/protobuf/duration.proto"), Some("feast/types/Value.proto"), Some("feast/types/Field.proto"), Some("feast/types/FeatureRow.proto"), Some("ServingService.proto")].
And set pact:proto to ServingService.proto.
When running consumer testing, I got error:
Failed to set the interaction: Failed to process protobuf: Did not find a message type 'Value' in the file descriptor 'Some("ServingService.proto")'
But actually Value is defined in Value.proto

Plugin framework cannot parse semver versioned plugins

In putting a minor temporary fix in place for pact-foundation/pact-reference#278 in the Pact Go project (for the Matt plugin to temporarily just use the first interaction in the pact as the contents), I released a version with a pre-release and build metadata (specifically 0.0.8-fix+pact.reference.278, however even 0.0.8-fix is sufficient to reproduce it).

The behaviour I noticed:

Given the plugin is installed into ~/.pact/plugins/matt-0.0.8-fix+pact.reference.278 and the pact-plugins.json had the same version:

  1. The consumer test passed
  2. The provider test would fail with the following error:
2023-06-05T13:04:03.957598Z  INFO ThreadId(02) verify_provider_async: pact_verifier: Pact file requires plugins, will load those now
2023-06-05T13:04:03.957688Z DEBUG ThreadId(02) verify_provider_async: pact_plugin_driver::plugin_manager: Loading plugin PluginDependency { name: "matt", version: Some("0.0"), dependency_type: Plugin }
...
2023-06-05T13:15:12.770707Z ERROR ThreadId(01) pact_ffi::verifier::handle: Verification execution failed: Plugin matt:0.0 was not found (in $HOME/.pact/plugins or $PACT_PLUGIN_DIR)

Given the plugin is installed into ~/.pact/plugins/matt-0.0.8 and the pact-plugins.json had the same version:

  1. The consumer test passed
  2. The provider test passed

So it seems to be related to loading in the plugins for discovery and use with the verification.

Given semver is so prevalent, I think it's reasonable to adopt it as a versioning mechanism for plugins.

Related but different to: #21

`pact-plugin-cli install` fails with a non-zero exit code if plugin is already installed

I've found that I've had to put guards in place when installing plugins via the CLI, to prevent downloading the plugins every time a local task is run.

e.g. I might have a task to install plugins that is a dependency of running a set of tests. Without the guard, I need to add the -y flag to force install the plugins (and wait for the install to happen):

if [ ! -d ~/.pact/plugins/matt-0.0.5 ]; then
    echo "--- 🐿  Installing MATT plugin"
  ~/.pact/bin/pact-plugin-cli install https://github.com/mefellows/pact-matt-plugin/releases/tag/v0.0.5
fi

Should we consider changing the exit code to 0 in the case a plugin already exists or perhaps adding a flag to skip if it's already installed?

Readme error in GRPC go examples

In provider-go:

pact_verifier_cli -f ../consumer-go/pacts/grpc-consumer-go-area-calculator-provider.json --transport grpc -p 39821

The json doesn't exist

In consumer-go:

The test method test_proto_client first sets

The link doesn't exist

How can i resolve proto import

My Main Proto file import other proto files so How can I resolve those import proto's?

I'm using java/kotlin and my Proto A -----import--> proto B-----import--->proto C and all protos are in diff package structure when I'm compiling Proto A, it's not able to resolve imports. I tried to take help from pact-plugin example but couldn't able to resolve complex or domain specific proto.

Filename `plugin.proto` is too generic, leading to conflict

I just bumped into this error, using pact-go together with the Go pulumi sdk, both registering plugin.proto:

panic: proto: file "plugin.proto" is already registered
        previously from: "github.com/pact-foundation/pact-go/v2/internal/native"
        currently from:  "github.com/pulumi/pulumi/sdk/v3/proto/go"
See https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict

Although both files have different go_package directives, as you can see here:

this doesn't seem to be enough. The filenames need to be unique as well, according this comment:

golang/protobuf#1122 (comment)

I’m from Pulumi and will submit a PR to change our filenames, but I strongly suggest to that you also rename your file to e.g. pact-plugin.proto or pact/plugin.proto to prevent future filename collisions. Since protobuf-go v1.26, such a filename collision generates a panic.

Rust CSV consumer test is failing

The CSV consumer test in rust is failing to load the plugin. It seems it cannot communicate to the plugin server, or perhaps just read the stdout:

➜  csv-consumer-rust git:(main) βœ— LOG_LEVEL=debug RUST_LOG=debug cargo test -- --nocapture
warning: struct is never constructed: `CsvClient`
 --> src/lib.rs:5:8
  |
5 | struct CsvClient {
  |        ^^^^^^^^^
  |
  = note: `#[warn(dead_code)]` on by default

warning: associated function is never used: `new`
  --> src/lib.rs:10:10
   |
10 |   pub fn new<S>(url: S) -> CsvClient where S: Into<Url> {
   |          ^^^

warning: associated function is never used: `fetch`
  --> src/lib.rs:16:16
   |
16 |   pub async fn fetch(&self, report: &str) -> anyhow::Result<String> {
   |                ^^^^^

warning: associated function is never used: `save`
  --> src/lib.rs:22:16
   |
22 |   pub async fn save(&self, report: &str, data: &Vec<Vec<String>>) -> anyhow::Result<bool> {
   |                ^^^^

warning: `csv-consumer-rust` (lib) generated 4 warnings
    Finished test [unoptimized + debuginfo] target(s) in 2.25s
     Running unittests (target/debug/deps/csv_consumer_rust-89d8d8ee55776822)

running 2 tests
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::catalogue_manager] Updated catalogue entries:
    core/content-generator/binary
    core/content-generator/json
    core/content-matcher/json
    core/content-matcher/multipart-form-data
    core/content-matcher/text
    core/content-matcher/xml
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::catalogue_manager] Updated catalogue entries:
    core/matcher/v1-equality
    core/matcher/v2-max-type
    core/matcher/v2-min-type
    core/matcher/v2-minmax-type
    core/matcher/v2-regex
    core/matcher/v2-type
    core/matcher/v3-content-type
    core/matcher/v3-date
    core/matcher/v3-datetime
    core/matcher/v3-decimal-type
    core/matcher/v3-includes
    core/matcher/v3-integer-type
    core/matcher/v3-null
    core/matcher/v3-number-type
    core/matcher/v3-time
    core/matcher/v4-array-contains
    core/matcher/v4-equals-ignore-order
    core/matcher/v4-max-equals-ignore-order
    core/matcher/v4-min-equals-ignore-order
    core/matcher/v4-minmax-equals-ignore-order
    core/matcher/v4-not-empty
    core/matcher/v4-semver
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::catalogue_manager] Updated catalogue entries:
    core/interaction/http
    core/interaction/https
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Loading plugin PluginDependency { name: "csv", version: None, dependency_type: Plugin }
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Loading plugin PluginDependency { name: "csv", version: None, dependency_type: Plugin }
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Did not find plugin, will start it
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Loading plugin manifest for plugin PluginDependency { name: "csv", version: None, dependency_type: Plugin }
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Looking for plugin in "/Users/matthewfellows/.pact/plugins"
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Found plugin manifest: "/Users/matthewfellows/.pact/plugins/csv-0.0.0/pact-plugin.json"
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Starting plugin with manifest PactPluginManifest { plugin_dir: "/Users/matthewfellows/.pact/plugins/csv-0.0.0", plugin_interface_version: 1, name: "csv", version: "0.0.0", executable_type: "exec", minimum_required_version: None, entry_point: "pact-plugin-csv", entry_points: {}, dependencies: None }
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Detected OS: Mac OS 10.15.7 [64-bit]
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Starting plugin using "/Users/matthewfellows/.pact/plugins/csv-0.0.0/pact-plugin-csv"
[2021-12-06T03:21:44Z DEBUG pact_plugin_driver::plugin_manager] Plugin csv started with PID 97516
test tests::test_csv_client has been running for over 60 seconds
test tests::test_post_csv has been running for over 60 seconds
[2021-12-06T03:22:44Z ERROR pact_plugin_driver::child_process] Timeout waiting to get plugin startup info: timed out waiting on channel
thread 'tests::test_post_csv' panicked at 'Could not load plugin - Plugin process did not output the correct startup message in 60 seconds: timed out waiting on channel', /Users/matthewfellows/.cargo/registry/src/github.com-1ecc6299db9ec823/pact_consumer-0.8.1/src/builders/pact_builder.rs:119:21
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[2021-12-06T03:22:44Z DEBUG pact_plugin_driver::plugin_manager] Did not find plugin, will start it
[2021-12-06T03:22:44Z DEBUG pact_plugin_driver::plugin_manager] Loading plugin manifest for plugin PluginDependency { name: "csv", version: None, dependency_type: Plugin }
[2021-12-06T03:22:44Z DEBUG pact_plugin_driver::plugin_manager] Starting plugin with manifest PactPluginManifest { plugin_dir: "/Users/matthewfellows/.pact/plugins/csv-0.0.0", plugin_interface_version: 1, name: "csv", version: "0.0.0", executable_type: "exec", minimum_required_version: None, entry_point: "pact-plugin-csv", entry_points: {}, dependencies: None }
test tests::test_post_csv ... FAILED

I am able to start the plugin manually and then telnet to it:

➜  csv-consumer-rust git:(main) βœ— ~/.pact/plugins/csv-0.0.0/pact-plugin-csv
{"port":51891, "serverKey":"febb62a5-ce58-41c6-9edd-18cbe36fdc25"}

The Java test works as expected.

I'm wondering if

Verifying provider's plugin pacts hang on Windows

When I verify provider's pacts that required plugins (csv, protobuf), on Linux and Mac, it work fine, but on Windows it hang.

As you can see here https://github.com/tienvx/pact-php/actions/runs/3099040342 , all tests passed on Linux and Mac. On Windows it hang for hours. I have to cancel it manually.

This is how it look like on a Windows machine:
task-manager

Some information to help debugging:

No example about how to matching message in README

I am trying to use this plugin recently, but I am not sure how to match a ENUM or message (like google.protobuf.Timestamp) in synchronous-message, I tried many times, but still can't match it correctly, and I can't find any info in the README file, could you please teach me?

MockServerMismatchedRequests is not returning mismatches with grpc

Hello,

When i am running the example with a dummy client like
func GetRectangleAndSquareArea(address string) ([2]float32, error) { var a [2]float32 a[0] = 12 a[1] = 9 return a, nil }

Consumer test are still green even if no interactions with the mock server has been done.
Is it an expected behaviour ?

Thanks

Olivier

docs: pact plugin manifest file can take args

Hey just looking at these docs - https://github.com/pact-foundation/pact-plugins/blob/main/docs/plugin-driver-design.md#plugin-manifest-file

Think this should have args in the docs.

This example for me wouldn't work

{
  "manifestVersion": 1,
  "pluginInterfaceVersion": 1,
  "name": "pact-csv",
  "version": "0.0.0",
  "executableType": "exec",
  "minimumRequiredVersion": "2.7.2",
  "entryPoint": "bundle exec main.rb"
}

I needed

{
  "manifestVersion": 1,
  "pluginInterfaceVersion": 1,
  "name": "pact-csv",
  "version": "0.0.0",
  "executableType": "exec",
  "minimumRequiredVersion": "2.7.2",
  "entryPoint": "bin/bundle",
  "args": ["exec","ruby", "main.rb"]
}

Support a better interface to matching rules

Not having to create our own content type to add metadata to contracts. We have had to make the "application/json" content matcher again so that we can add metadata fields into the contract. This means that we have had to find the core implementation of application/json and copy bits of code to keep the same behaviour and then try to add the metadata on top.
Easy to use / documented core library and utility functions for matching. For example a function we can call with a JSON message payload and a pact and find which contract is the best match. Currently we have pieced together bits of pact_matching with our own transformations from a JSON message and some metadata to match websocket messages as they come in. We also reimplemented the loop that goes over the contract and calls the comparison for each one. This process feels like it could be a bit easier for plugin developers if they want a consistent and out of the box feel for the plugin behaviour.

Currently, plugin authors either have to write their own matching rules, or use an existing implementation from one of the Pact implementations. The Pact implementations have not been designed to export this functionality for external use, so it requires knowing the internals to the Pact implementations and the capability will be different between different language implementations.

gRPC channel reported as not shut down properly

For the projects where we've implemented use of the gRPC plugin, we're seeing big red "SEVERE" warning messages that look like the following, whenever we run pact tests:

Dec 05, 2022 5:42:15 PM io.grpc.internal.ManagedChannelOrphanWrapper$ManagedChannelReference cleanQueue
SEVERE: *~*~*~ Channel ManagedChannelImpl{logId=1, target=[::1]:60118} was not shutdown properly!!! ~*~*~*
    Make sure to call shutdown()/shutdownNow() and wait until awaitTermination() returns true.
java.lang.RuntimeException: ManagedChannel allocation site
        at io.grpc.internal.ManagedChannelOrphanWrapper$ManagedChannelReference.<init>(ManagedChannelOrphanWrapper.java:93)
        at io.grpc.internal.ManagedChannelOrphanWrapper.<init>(ManagedChannelOrphanWrapper.java:53)
        at io.grpc.internal.ManagedChannelOrphanWrapper.<init>(ManagedChannelOrphanWrapper.java:44)
        at io.grpc.internal.ManagedChannelImplBuilder.build(ManagedChannelImplBuilder.java:631)
        at io.grpc.internal.AbstractManagedChannelImplBuilder.build(AbstractManagedChannelImplBuilder.java:297)
        at io.pact.plugins.jvm.core.DefaultPluginManager$tryInitPlugin$1.invoke(PluginManager.kt:776)
        at io.pact.plugins.jvm.core.Utils.handleWith(Util.kt:42)
        at io.pact.plugins.jvm.core.DefaultPluginManager.tryInitPlugin(PluginManager.kt:772)
        at io.pact.plugins.jvm.core.DefaultPluginManager.initialisePlugin(PluginManager.kt:755)
        at io.pact.plugins.jvm.core.DefaultPluginManager.loadPlugin(PluginManager.kt:378)
        at au.com.dius.pact.consumer.dsl.PactBuilder.usingPlugin(PactBuilder.kt:97)
        at au.com.dius.pact.consumer.dsl.PactBuilder.usingPlugin$default(PactBuilder.kt:94)
        at au.com.dius.pact.consumer.dsl.PactBuilder.usingPlugin(PactBuilder.kt)
        at io.pact.example.grpc.maven.PactConsumerTest.calculateRectangleArea(PactConsumerTest.java:46)
...

This does not, as far as I can tell, seem to impact the actual functioning of the plugin framework.

This can be reproduced by:

$ cd pact-plugins/examples/gRPC/area_calculator/consumer-maven
$ mvn clean verify

I spent a little time trying to track down the cause, but didn't come up with anything definitive in the time allotted. Here's what I did figure out:

  • The error is printed in void calculateRectangleArea when the ManagedChannel is built
    • This is not an issue with that channel, but one allocated prior. See quote from a maintainer of grpc-java:

      If using a debugger, you may notice you get that message when creating a channel. We check for orphaned channels when new channels are created. So you'll see we display the message within the connect(), but the message is talking about a previous channel construction.

  • The error is talking about the channel created in initPlugin (which I would've realized sooner if I had actually read the stack trace ;p)
  • shutdown is actually called, but the error prints before the channel is shut down (which makes sense - I think the channel does want to stay alive until the plugin manager is done talking to the plugins.)

Thank you for your hard work!

Plugins within Junit4 consumers

Hey folks,
Is it possible to use the protobuf plugin with a Junit4 consumer? I see an example with the calculator app using Junit5, but I don't see any examples of using a plugin in Pact with Junit4 to generate a contract.

Specifically, I am trying to generate a consumer contract within an Android project (which is limited to Junit4) and the docs for pact-jvm-consumer-junit doesn't indicate we can use plugins, and the source for ConsumerPactBuilder makes no reference to plugins.

Error when trying to invoke protoc binary

Hello,

I'm getting an error trying to use the Protobuf Plugin with Pact. The error message is:

14:02:34.224 [Test worker] DEBUG io.pact.plugins.jvm.core.DefaultPluginManager - Got response: error: "Failed to process protobuf: Failed to invoke protoc binary: exit code exit status: 1"

I am using the following versions:

consumer % ./pact-plugin-cli --version
pact-plugin-cli 0.1.1
ehaigh@WM-F77547CXHH consumer % ./pact-plugin-cli list installed
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Name     ┆ Version ┆ Interface Version ┆ Directory                                  ┆ Status  β”‚
β•žβ•β•β•β•β•β•β•β•β•β•β•ͺ═════════β•ͺ═══════════════════β•ͺ════════════════════════════════════════════β•ͺ═════════║
β”‚ protobuf ┆ 0.3.9   ┆ 1                 ┆ /Users/ehaigh/.pact/plugins/protobuf-0.3.9 ┆ enabled β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

and have a local version of protoc installed:

ehaigh@WM-F77547CXHH consumer % protoc --version
libprotoc 25.2

My pact-plugin.json looks like this:

{"pluginInterfaceVersion":1,"name":"protobuf","version":"0.3.9","executableType":"exec","minimumRequiredVersion":null,"entryPoint":"pact-protobuf-plugin","entryPoints":{},"args":null,"dependencies":null,"pluginConfig":{"hostToBindTo":"127.0.0.1","downloadUrl":"https://github.com/protocolbuffers/protobuf/releases","protocVersion":"3.11.3"}}

Can you suggest anything I could do to try and get this to work? I'm happy to use a local protoc install rather than download one if possible.

Plugin logs are hard to use

Currently, plugin logs to standard output are captured and logged via the calling frameworks logging. This can make it hard to separate from normal logs and pact logs, esp at trace level.

For me the biggest issue when start using plugin is I can't see its logs, so I really don't know what is going on.
For now my understanding about plugins is a bit better so I don't have to read its logs, but I am always afraid of it.

Here is the related thread https://pact-foundation.slack.com/archives/C047TCR7B6W/p1679760157876359

Also:

The logging point is a good one. It used to be possible to use RUST_LOG when using the plugin for consumer tests and we would see all of the child_process logs but this seems to have stopped. Being about to log to a file would be nice, capturing any stdout from the plugin process. This would also make the logs easier to read/parse as using the tracing crate in both the plugin and the driver means the child_process lines are quite noisy.

Support descriptor_set_in to protoc

For passing down the protobuf files to protoc, it looks like we only take -I here, but not --descriptor_set_in so I assume it’s not supported today to take a self-inclusive descriptor set binary file instead of raw .proto files, if that’s the case is there a plan to support it?

csv plugin - create linux aarch64 release

trying to setup pact-go tests on arm64 hardward and they have a test for the csv plugin, but there is no linux aarch64 binary.

just to track adding one, will add a PR shortly

Support tgz compression for bundled plugins

Could the support for tgz compression be added for bundled plugins.

Avro plugin is a bundled plugin and I've packaged with tgz so that users don't have to do anything after downloading it. i.e make the entry file executable.

pact-plugin-cli 0.1.1 missing linux x86_64 artifact

https://github.com/pact-foundation/pact-plugins/releases/tag/pact-plugin-cli-v0.1.1

Most peculiar -

gzip -c target/release/pact-plugin-cli > target/artifacts/pact-plugin-cli-linux-x86_64.gz

--- 🐿  Installing plugins CLI version '0.1.1' (from tag )
        Downloading from: https://github.com/pact-foundation/pact-plugins/releases/download/pact-plugin-cli-v0.1.1/pact-plugin-cli-linux-x86_64.gz
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed

  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100     9  100     9    0     0     87      0 --:--:-- --:--:-- --:--:--    88
        Downloaded /home/runner/.pact/bin/pact-plugin-cli-linux-x86_64.gz: ASCII text, with no line terminators

gzip: /home/runner/.pact/bin/pact-plugin-cli-linux-x86_64.gz: not in gzip format
make: *** [Makefile:[44](https://github.com/pact-foundation/pact-go/actions/runs/7576611778/job/20635808082#step:5:45): download_plugins] Error 1

Semver version not honoured when multiple versions of plugins installed

Consider a case where a plugin with multiple versions are installed.
Screenshot 2023-03-17 at 13 58 48

Here we have

  • 0.2.0
  • 0.2.6

Expected behaviour:-

A user can specify the version of plugin, to be used.

  • ❓ If the plugin name and version requested does not exist, it should error
    • 0.2.0 & 0.2.6 installed, 0.2.5 requested
  • ❓ If the plugin name and multiple versions exist, if the user requests a lower semver than the latest, it should be used.
    • 0.2.0 & 0.2.6 installed, 0.2.0 requested
  • βœ… If the plugin name and multiple versions exist, if the user doesn't specify a version, then the latest should be used.
    • 0.2.0 & 0.2.6 installed, none specified as requested

Actual behaviour:-

  • ⚠️ If the plugin name and version requested does not exist, it does not error and uses the latest
    • 0.2.0 & 0.2.6 installed, 0.2.5 requested
  • ⚠️If the plugin name and multiple versions exist, if the user requests a lower semver than the latest, the latest is used, ignoring the users specified version`
    • 0.2.0 & 0.2.6 installed, 0.2.0 requested
  • βœ… If the plugin name and multiple versions exist, if the user doesn't specify a version, then the latest is used
    • 0.2.0 & 0.2.6 installed, none specified as requested

Consumer side

Testing with

.using_plugin("protobuf", None).await

cd examples/gRPC/area_calculator/consumer-rust && cargo test

  • .using_plugin("protobuf", None).await will use the latest plugin
  • .using_plugin("protobuf", Some("0.2.0".to_string())).await will use the latest plugin
  • .using_plugin("protobuf", Some("0.2.5".to_string())).await will not complain that the plugin version doesn't exist and will use the latest version

Provider side

to be added

Plugin support via gradle

Hello,
I'm looking for a possibility to download protobuf plugin using gradle, so then usePlugin("protobuf") won't search on the local machine for the plugin. Is there is a solution for this?
Best regards

startMockServer doesn't set hostInterface, port, and tls in request

Hi, I started to use this plugin framework recently.
However, I got an issue and it can't work properly in my environment.
I tried to run UT and generate pact file in docker with pact-protobuf-plugin.
But I always get this exception:

io.pact.plugins.jvm.core.PactPluginMockServerErrorException: Plugin protobuf failed to start a mock server: Failed to start gRPC mock server: Address not available (os error 99)

I tried to troubleshoot a bit, and it's because the pact-protobuf-plugin want to start a local mock server, but the target "[::1]:0" is not allowed in my docker container.
And I set hotInterface and port by MockServerConfig annotation, but it's not working.
Because io.pact.plugins.jvm.core.DefaultPluginManager#startMockServer doesn't really pass the config to plugin.
I am this sure this behavior is on purpose or a bug, could you please help to take a look?
Thank you.

Issue with matching a list of enums

In the matcher do we support list of enums?

  "request": {
    "states": [
      "matching(equalTo, 'ENUM_VAL_1')"
    ]
  },

I’m getting this error --- is it because enum is not added to should_be_packed_type?

Failed to match the request message - BodyMismatches({"$.states": [BodyMismatch { path: "$.states[0]", expected: Some(b"ENUM_VAL_1"), actual: Some(b"0102"), mismatch: "Expected and actual field have different types: 1:(states, Varint, Enum) = ENUM_VAL_1 and 1:(states, LengthDelimited, Unknown) = 0102" }]})

Provider verification > auto-downloading plugins feature doesn't work as expected

Run provider verification alone did trigger auto-downloading required plugin, but it didn't work as expected.

How to reproduce:

  1. git clone https://github.com/tienvx/pact-php-csv.git
  2. composer install
  3. rm example/consumer/tests/Contract/pacts/*.json
  4. PACT_LOGLEVEL=debug phpunit --testsuite="Example Consumer" (to generate pact files. csv plugin is downloaded to ~/.pact if not downloaded)
  5. rm -rf ~/.pact
  6. PACT_LOGLEVEL=debug phpunit --testsuite="Example Provider"
  7. Expected: csv plugin is downloaded to ~/.pact, test is passed
  8. Actual: csv plugin is not downloaded, test is not passed
Log
2023-08-06T16:00:04.902489Z  INFO ThreadId(01) pact_verifier: Pact file requires plugins, will load those now
2023-08-06T16:00:04.902498Z DEBUG ThreadId(01) pact_plugin_driver::plugin_manager: Loading plugin PluginDependency { name: "csv", version: Some("0.0"), dependency_type: Plugin }
2023-08-06T16:00:04.902508Z DEBUG ThreadId(01) pact_plugin_driver::plugin_manager: Did not find plugin, will attempt to start it
2023-08-06T16:00:04.902510Z DEBUG ThreadId(01) pact_plugin_driver::plugin_manager: Loading plugin manifest for plugin PluginDependency { name: "csv", version: Some("0.0"), dependency_type: Plugin }
2023-08-06T16:00:04.902515Z DEBUG ThreadId(01) pact_plugin_driver::plugin_manager: Looking for plugin in "/home/user/.pact/plugins"
2023-08-06T16:00:04.902536Z  WARN ThreadId(01) pact_plugin_driver::plugin_manager: Could not load plugin manifest from disk, will try auto install it: Plugin csv:0.0 was not found (in $HOME/.pact/plugins or $PACT_PLUGIN_DIR)
2023-08-06T16:00:04.902859Z  INFO ThreadId(01) pact_plugin_driver::repository: Fetching index from github
2023-08-06T16:00:04.902932Z DEBUG tokio-runtime-worker hyper::client::connect::dns: resolving host="raw.githubusercontent.com"
2023-08-06T16:00:04.903238Z DEBUG ThreadId(01) hyper::client::connect::http: connecting to [2606:50c0:8003::154]:443
2023-08-06T16:00:04.950168Z DEBUG ThreadId(01) hyper::client::connect::http: connected to [2606:50c0:8003::154]:443
2023-08-06T16:00:05.001068Z DEBUG ThreadId(01) h2::client: binding client connection
2023-08-06T16:00:05.001077Z DEBUG ThreadId(01) h2::client: client connection bound
2023-08-06T16:00:05.001089Z DEBUG ThreadId(01) h2::codec::framed_write: send frame=Settings { flags: (0x0), enable_push: 0, initial_window_size: 2097152, max_frame_size: 16384 }
2023-08-06T16:00:05.001148Z DEBUG ThreadId(01) hyper::client::pool: pooling idle connection for ("https", raw.githubusercontent.com)
2023-08-06T16:00:05.001150Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_write: send frame=WindowUpdate { stream_id: StreamId(0), size_increment: 5177345 }
2023-08-06T16:00:05.001205Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_write: send frame=Headers { stream_id: StreamId(1), flags: (0x5: END_HEADERS | END_STREAM) }
2023-08-06T16:00:05.088810Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=Settings { flags: (0x0), max_concurrent_streams: 100 }
2023-08-06T16:00:05.088823Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_write: send frame=Settings { flags: (0x1: ACK) }
2023-08-06T16:00:05.088829Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=WindowUpdate { stream_id: StreamId(0), size_increment: 16711681 }
2023-08-06T16:00:05.088834Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=Settings { flags: (0x1: ACK) }
2023-08-06T16:00:05.088838Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::proto::settings: received settings ACK; applying Settings { flags: (0x0), enable_push: 0, initial_window_size: 2097152, max_frame_size: 16384 }
2023-08-06T16:00:05.089577Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=Headers { stream_id: StreamId(1), flags: (0x4: END_HEADERS) }
2023-08-06T16:00:05.089588Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=Data { stream_id: StreamId(1), flags: (0x1: END_STREAM) }
2023-08-06T16:00:05.089708Z DEBUG ThreadId(01) hyper::client::pool: reuse idle connection for ("https", raw.githubusercontent.com)
2023-08-06T16:00:05.089739Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_write: send frame=Headers { stream_id: StreamId(3), flags: (0x5: END_HEADERS | END_STREAM) }
2023-08-06T16:00:05.174052Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=Headers { stream_id: StreamId(3), flags: (0x4: END_HEADERS) }
2023-08-06T16:00:05.174068Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_read: received frame=Data { stream_id: StreamId(3), flags: (0x1: END_STREAM) }
2023-08-06T16:00:05.174955Z DEBUG ThreadId(01) pact_plugin_driver::repository: Installing plugin csv/0.0 from index
2023-08-06T16:00:05.175018Z ERROR ThreadId(01) pact_ffi::verifier::handle: Verification execution failed: Plugin csv:0.0 was not found (in $HOME/.pact/plugins or $PACT_PLUGIN_DIR)
2023-08-06T16:00:05.175018Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::codec::framed_write: send frame=GoAway { error_code: NO_ERROR, last_stream_id: StreamId(0) }
2023-08-06T16:00:05.175028Z DEBUG tokio-runtime-worker Connection{peer=Client}: h2::proto::connection: Connection::poll; connection error error=GoAway(b"", NO_ERROR, Library)
2023-08-06T16:00:05.175043Z DEBUG ThreadId(01) pact_ffi::verifier: pact_ffi::verifier::pactffi_verifier_shutdown FFI function invoked
Output of ls ~/.pact/plugins/
repository.index  repository.index.sha256
Pact file
{
"consumer": {
  "name": "csvConsumer"
},
"interactions": [
  {
    "description": "request for a report.csv",
    "interactionMarkup": {
      "markup": "# Data\n\n|Name|100|2000-01-01|\n",
      "markupType": "COMMON_MARK"
    },
    "pending": false,
    "pluginConfiguration": {
      "csv": {
        "csvHeaders": false
      }
    },
    "providerStates": [
      {
        "name": "report.csv file exist"
      }
    ],
    "request": {
      "headers": {
        "Accept": [
          "text/csv"
        ]
      },
      "method": "GET",
      "path": "/report.csv"
    },
    "response": {
      "body": {
        "content": "Name,100,2000-01-01\n",
        "contentType": "text/csv;charset=utf-8",
        "contentTypeHint": "DEFAULT",
        "encoded": false
      },
      "generators": {
        "body": {
          "column:3": {
            "format": "yyyy-MM-dd",
            "type": "DateTime"
          }
        }
      },
      "headers": {
        "content-type": [
          "text/csv"
        ]
      },
      "matchingRules": {
        "body": {
          "column:1": {
            "combine": "AND",
            "matchers": [
              {
                "match": "type"
              }
            ]
          },
          "column:2": {
            "combine": "AND",
            "matchers": [
              {
                "match": "number"
              }
            ]
          },
          "column:3": {
            "combine": "AND",
            "matchers": [
              {
                "format": "yyyy-MM-dd",
                "match": "datetime"
              }
            ]
          }
        }
      },
      "status": 200
    },
    "transport": "http",
    "type": "Synchronous/HTTP"
  }
],
"metadata": {
  "pactRust": {
    "ffi": "0.4.7",
    "mockserver": "1.2.3",
    "models": "1.1.9"
  },
  "pactSpecification": {
    "version": "4.0"
  },
  "plugins": [
    {
      "configuration": {},
      "name": "csv",
      "version": "0.0.3"
    }
  ]
},
"provider": {
  "name": "csvProvider"
}
}
  • FFI Library: 0.4.7
  • CSV Plugin: 0.0.3
  • Specification: 4.0

CSV plugin can not run on Ubuntu LTS

Pact plugin driver does not give reason why it can't start CSV plugin:

2022-01-06T01:30:14Z ERROR pact_plugin_driver::child_process] Timeout waiting to get plugin startup info: timed out waiting on channel
[2022-01-06T01:30:15Z ERROR pact_ffi::plugins] Could not load plugin - Plugin process did not output the correct startup message in 60 seconds: timed out waiting on channel

If I run CSV plugin manually I can see this error:

./pact/plugins/csv-0.0.0/pact-plugin-csv                                                                                                                                                                                                                                                08:30:45
./pact/plugins/csv-0.0.0/pact-plugin-csv: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./pact/plugins/csv-0.0.0/pact-plugin-csv)
./pact/plugins/csv-0.0.0/pact-plugin-csv: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./pact/plugins/csv-0.0.0/pact-plugin-csv)
./pact/plugins/csv-0.0.0/pact-plugin-csv: /lib/x86_64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./pact/plugins/csv-0.0.0/pact-plugin-csv)

GLIBC version:

ldd --version
ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31

OS: Linux Mint 20.2 (Based on Ubuntu 20.04)

If I build CSV plugin myself cargo build --release and copy the binary pact-plugin-csv to ./pact/plugins/csv-0.0.0/ and replace the old one, then it works fine.

Link to discussion on Slack: https://pact-foundation.slack.com/archives/C02BXLDJ7JR/p1641432975000900

Pact Plugin CLI Fails on amazonlinux2: /lib64/libc.so.6: version `GLIBC_2.29' not found

Steps to reproduce:

docker run --platform linux/amd64 -it amazonlinux:2.0.20221004.0

# From inside container:
yum install -y gzip
curl -L https://github.com/pact-foundation/pact-plugins/releases/download/pact-plugin-cli-v0.0.0/pact-plugin-cli-linux-x86_64.gz | gunzip --stdout > /usr/local/bin/pact-plugin-cli
chmod u+x /usr/local/bin/pact-plugin-cli
pact-plugin-cli --version

Output:

bash-4.2# pact-plugin-cli --version
pact-plugin-cli: /lib64/libc.so.6: version `GLIBC_2.29' not found (required by pact-plugin-cli)
pact-plugin-cli: /lib64/libc.so.6: version `GLIBC_2.28' not found (required by pact-plugin-cli)

Glibc version:

bash-4.2# ldd --version
ldd (GNU libc) 2.26
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.

Confusing error message when plugin can't be loaded

When the plugin process fails to start, the following error occurrs (in JVM version)

kotlin.UninitializedPropertyAccessException: lateinit property process has not been initialized
	at io.pact.plugins.jvm.core.ChildProcess.destroy(ChildProcess.kt:81)

eachValue matcher not working as expected

eachValue matcher for checking list of items not working as expected.

"item": ['somevalue1', 'somevalue2']
"item": "eachValue(matching(type, '00000000000000000000000000000000'))"

Getting below error from pact-plugin:

pact_protobuf_plugin::mock_service: Failed to match the request message - BodyMismatches({"$.item": [BodyMismatch { path: "$.item[0]", expected: Some(b""), actual: Some(b"00000000000000000000000000000000"), mismatch: "Unable to match '' using EachValue(MatchingRuleDefinition { value: \"00000000000000000000000000000000\", value_type: Unknown, rules: [Left(Type)], generator: None })" }]})

How can a plugin author lookup an Interaction in the Pact struct during `PrepareInteractionForVerification`?

This request was spawned from pact-foundation/pact-reference#278

Background
I'm trying to understand how a Plugin can look up the interaction in the pact file during verification.

In PrepareInteractionForVerification and VerifyInteraction the plugin will receive an InteractionKey that has a unique reference for the plugin to (presumably) label and find an interaction.

In the case of PrepareInteractionForVerification if no key is set on the interaction in the consumer DSL, the Pact struct if parsed, will not have any interactions with a key on them, and thus the interaction can't be found this way. There is no other identifying information at this step that can be used to look up the correct interaction from the pact.

This is why had the previous query about the keys above. I'm confused as to how the gRPC plugin is able to lookup by the interaction's key, despite not setting keys explicitly from the client. The Pact struct serialised over the protobuf definition does not have any keys in it:

For example, here is a Golang log message for the incoming message for VerifyInteraction:

2023/06/05 22:03:42 [INFO] received VerifyInteraction request: interactionData:{body:{contentType:"application/matt"  content:{value:"MATTMATT"}}}  config:{fields:{key:"host"  value:{string_value:"localhost"}}  fields:{key:"port"  value:{number_value:50160}}}  pact:"{\"consumer\":{\"name\":\"matttcpconsumer\"},\"interactions\":[{\"description\":\"Matt message\",\"pending\":false,\"providerStates\":[{\"name\":\"the world exists\"}],\"request\":{\"contents\":{\"content\":\"MATThellotcpMATT\",\"contentType\":\"application/matt\",\"contentTypeHint\":\"DEFAULT\",\"encoded\":false}},\"response\":[{\"contents\":{\"content\":\"MATTtcpworldMATT\",\"contentType\":\"application/matt\",\"contentTypeHint\":\"DEFAULT\",\"encoded\":false}}],\"transport\":\"matt\",\"type\":\"Synchronous/Messages\"}],\"metadata\":{\"pactRust\":{\"ffi\":\"0.4.5\",\"mockserver\":\"1.1.1\",\"models\":\"1.1.2\"},\"pactSpecification\":{\"version\":\"4.0\"},\"plugins\":[{\"configuration\":{},\"name\":\"matt\",\"version\":\"0.0.7\"}]},\"provider\":{\"name\":\"matttcpprovider\"}}"  interactionKey:"928c6a4275c7cafd"

Note how the interactionKey is present on the struct, but there is no matching key in the Pact struct.

This leads me to think that unique_key is an internal method only available to the Rust library, and not something that can be relied upon over the wire, and is why I thought removing the key generation could be a breaking change as far as behaviour is concerned.

(UPDATE: I thought perhaps I could work around it by setting a key on the interaction but I don't think there is an FFI for it)

Response from Ron:

Unfortunately, we can't remove InteractionKey from PrepareInteractionForVerification, because that will change the interface to existing plugins. We will have to create a V2 of the plugin interface.
At the moment it works by magic.

Ask

How can a plugin author lookup an Interaction in the Pact struct during PrepareInteractionForVerification?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.