Skip to content

Pardon Collections

This tutorial builds on the concepts explained in Pardon Templates,

Services and Endpoints

A Pardon collection is generally one or more services anchored by a service.yaml file and with each endpoint represented by an https (HTTP Schema) file.

Schema files are a sequence of http request and response templates, and optionally some additional inline configuration.

A basic collection defining a service and the actions for a products REST API might look like this.

  • Directorycollection
    • Directoryexample the example service
      service=“example”
      • service.yaml common configuration for all the endpoints
      • ping.https a basic endpoint
        endpoint=example/ping
        action=ping
      • Directoryproducts we can group related endpoints
        • create.https create a product
          action=“create”
          endpoint=“example/products/create”
        • get.https get a product
          action=“get”
          endpoint=“example/products/get”
        • update.https update a product
        • delete.https delete a product
        • list.https list products

When developing a collection, one might start with a service.yaml and confirm the setup with something like a ping.https endpoint.

The service.yaml file contains common specifications like config, defaults, and mixins (which we’ll get to later in this tutorial).

config:
origin:
env:
prod: https://example.com
stage: https://stage.example.com

So far, so good: we should be able to build requests for both stage and production origins.

Input values

The input request is the “most direct” mechanism for getting data into Pardon, but not everything can be solved without another layer of indirection!

Values can supply data to templates, and can even be used to rewrite templates where config alternatives are involved.

In these exercises a new input field for values is available to explore this mechanism (the input).

Exercises
Try setting a value in the values input.

config choices can be overridden with values. This allows any request to be easily repurposed to a different environment (in particular, replaying tests).

env=stage
Loading Pardon Playground...

A RESTful resource

The products lifecycle would also defined with https files:

For product creation, this template only defines the name field (which makes it mandatory),

create.https
>>>
POST https://example.com/products
Content-Type: application/json
{
"name": "{{name}}"
}

Defining the response template instructs pardon to extract the product value from the response, assuming it matches. Equally importantly, it provides a little documentation about the shape of the response for humans. We will cover dataflow in a later section, as it’s more of an advanced topic for scripting.

The REST of the product resources endpoints are specified minimally here, just enough to distinguish them from each other.

Requests info for a single product.

example/products/get.https
>>>
GET https://example.com/products/{{product}}

With all this set up we can finally explore how pardon works with a collection of requests.

Exercises
Set a value via values.

Notice how we can provide the {{name}} value here without having to specify a request body at all!

Many simple requests can be parameterized to take advantage of this abbreviated input mechanism.

env=stage name=thneed
Loading Pardon Playground...

Mixins

Oh no. In our rush to add features, we forgot to include authorization headers to call these endpoints: the security team took care of this in the gateway on stage, and now all of our endpoints need to be updated, and tested.

We want to tell Pardon that this service needs authentication, rather than updating every endpoint. We just need to specify a mixin at the service level.

example/service.yaml
config:
origin:
env:
prod: https://example.com
stage: https://stage.example.com
mixin:
- ./auth.mix.https

We’re putting it next to our service in the file tree. But any relative path inside the service directory is fine.

  • Directorycollection
    • Directoryexample
      • service.yaml
      • auth.mix.https
      • ping.https
      • Directoryproducts/
        • create.https

The contents look (kind of) like any other https endpoint.

example/auth.mix.https
>>>
ANY //
Authorization: {{ @auth = `${env}-auth-token` }}

The special request method ANY matches all request methods, and the special value // matches any URL. (A “mixin” is mixed-in only if it is “compatible” with the request.)

This adds an authorization header to all requests.

Let’s try it out in stage and confirm all our endpoints are working and have authorization applied… (The @ in @auth marks it a secret. You can see the value using the lock button in this exercise.).

Loading Pardon Playground...

Mixin-Match

TL;DR: mixins progressively enhance the input request, but only if they match.

They do not affect whether the endpoint template matches the input request.

Awesome! We’ve got a security measure applied to our requests. And we provided confirmation of all our requests working on stage, so ops rolled these changes out to production.

Unfortunately, now alerts are firing because the (oops, only in production!) liveness probe hitting /ping requires an auth token and is not provisioned to use one. We missed this case in staging because our updated collection sent auth tokens to every endpoint.

Well, it’s not actually an outage, and the fix isn’t even on our end (ops is in control of removing the Authorization header check from the /ping endpoint), but we do want to adjust our collection to align with the deployment.

It is cleaner (in this case, at least) to specify an opt-out for the few exceptional endpoints that don’t need auth, rather than opting-in. To have the mixin supply an opt-out behavior, we can add a config section.

example/auth.mix.https
config:
authorization: token
>>>
ANY //
Authorization: {{ @auth = `${env}-auth-token` }}

Just like origin in the main request, this authorization value needs to match (or be not specified) in the input for this mixin to be applicable.

We can then default authorization as none in our ping endpoint (choosing any value other than token), which disables the mixin selectively.

example/ping.https
defaults:
authorization: none
>>>
GET https://example.com/ping

This implements the same change in behavior with two changes instead of N+1, and provides some additional controls we explore here:

Exercises
Set the endpoint to ping.

Notice the authorization header disappears as expected.

GET https://example.com/ping
Loading Pardon Playground...

Collection Layering

To match team structures, collections are defined in layers:

A base layer might define the endpoints, but credentials can be defined in another. Different teams have different access to credentials to the same services, so Pardon supports separation of concerns and access requirements by allowing layers of functionality to be applied.

A root pardon config defines the layers of collections to use. We’ll need to revisit this in the section on scripting.

Next Steps

Next we will cover more dataflow mechanisms.

Afterwards we will revisit layering a bit in scripting.