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
- create.https create a product
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 mixin
s (which we’ll get to later in this tutorial).
And our ping.https
file specifies how to make the ping call to the service.
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).config
choices can be overridden with values. This allows
any request to be easily repurposed to a different environment
(in particular, replaying tests).
Set env=prod
to override https://stage.example.com
.
(try the other way as well).
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),
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.
Updates info for a single product.
Deletes a single product.
Lists products.
With all this set up we can finally explore how pardon works with a collection of requests.
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.
In the spirit of progressive enhancement, we can add a parameterized value in our input,
and then define it.
type
optional? The service
, action
, and endpoint
values can be used
to select (or at least dramatically narrow down) which endpoint templates Pardon
even attempts to evaluate.
First, please delete the entire input. We don’t need it!
Then select the endpoint directly.
you can try different endpoints, and/or env=stage
if you like,
for the get/update/delete endpoints, you’ll need to specify a
product=...
value as well.
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.
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.
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.).
In the introduction, we described the collection templates as specifying the “bones” of the API call, and the input request as the “meat”. Now we’re using the template system to add additional information to the request which is neither part of the determination of which API endpoint to call, nor is it part of the interesting data being sent. This type of data is more like “feathers”. We’re not going to use this analogy, I just thought it might be interesting to think about.
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.
One option is removing the mixin from service.yaml and including it selectively from every authenticated endpoint.
but that touches many files. As the collection grows, this kind of maintenance would grow as well.
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.
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.
This implements the same change in behavior with two changes instead of N+1, and provides some additional controls we explore here:
Notice the authorization header disappears as expected.
Override the default authorization
to force an authenticated ping request!
Override the default authorization
to force an unauthenticated products request.
Why would we do this? I don’t know, maybe for testing? To assert dominance over our framework?
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.