Writing a Custom Concourse Resource — Overview

Alexander Jansing
DevOps Dudes
Published in
5 min readApr 6, 2020

--

In this article I am going to give a brief overview about resources and link to my GitHub repository with a working Concourse resource written in Python. I will also write subsequent articles going more in-depth about each stage of resource. I will update this article with links to the check, in, and out articles.

Photo by Silvia Fang on Unsplash

Writing a Concourse resource is not terribly difficult, but instructions on how to do so are generally incomplete. I have managed to stitch together a couple of resource using these tutorials and GitHub repository as guidance:

The resource I wrote reads and writes to/from MongoDB. I was not too concerned with giving the resource too many features since this is just a tutorial, but if you want to improve upon my work feel free to fork my repository. I would love to see what you can do!

Note: I wrote my resource in Python and my example code snippets will reflect that.

Getting Familiar with Concourse Resources

Concourse resources can be simply described as a Docker image with three executables: check, in, and out in the /opt/resource/ directory. How you decide to define these executables is up to you as long as you also create a Docker image that can run your code. My scripts are written in Python because it is a very simple language and I find the language very enjoyable to write in.

I like to think of Concourse resource types in terms of Abstract types from Object Oriented Programming. An abstract type is blueprint for how you should be writing a type of object. There are methods with some default logic that you can override, but it is not always necessary. This does not hold 100% for Concourse resources, but when you write your Dockerfile you will need to COPY in something for check, in, and out and make them executable.

Snippet including scripts to resource

Say you wanted to write a resource that only fetched data from a site but did not write anywhere. You still need to provide an out, but it could be something as simple as:

No-op resource script

Overview of the Resource

Before diving into the specifics about the MongoDB resource I need explain the context it is being used in.

Example pipeline

Expected Output of a Resource Stage

Concourse looks for an output in standard out. In Python, this is done by using print with its default value for the file parameter. This is often done with a statement like

Example Concourse Expected Output

If you want to have your resource print something upon execution, you will need to print it to a different stream, like sys.stderr. This is something that I gathered from cf-platform-eng/concourse-pypi-resource’s common.py and expanded upon:

Modified Print to Standard Error Function

Even with this knowledge, you may soon realize that check is not very verbose. Even if you print something to standard out, you will not see it unless the resource fails. I managed to induce a failure by providing the wrong credentials to the database for you see:

Induced Failure for Example

You can see that the first line of the standard error is something I included in my code, but would not normally be visible without that script failing.

About the check

The check is performed before anything can use the resource. This can be seen if you click on a resource block (smaller blocks on the left-hand side of a task).

check is used to detect new versions of something that the resource is designed to interact with. For example the git resource type monitors for new git hashes. And my mongo-resource monitors for new ObjectIDs.

Resource Check

About the in

in is performed in two situations:

  1. When a job is triggered, the resource is an input for the job, and the check has returned something. You do not need to use the output of the check in the action you use, but the typical resource will use what check found. Even the time resource type writes will provide you with a timestamp you could use in a task within a job it is an input for.
  2. After an out. I will explain this in the section About the out.

The example pipeline is set up such that each time the check finds a new ObjectID in MongoDB, it triggers a new task in my do the thing job. Once do the thing starts, is pulls in common-image, this-repo, and the resource mongo-database into the job and the task grabs its own inputs (note: Concourse will prevent a pipeline from flying if the resource is not used.) In the example pipeline we use all the inputs to the job.

The in uses the ObjectID (version) passed from the check to look up the corresponding entry in the database and save the json result to a file named <ObjectID>.json and prints the version back out to standard out.

Resource In

About the out

Regardless of what the example pipeline is doing in its task the out takes whatever is in the path (directory or file) (in this case the mongo-resource resource directory) and tries to either update or insert anything it finds into the database.

The out declares it is done by outputting a version just like check and in. This is because the put calls upon in again once it finishes out to ensure that the output arrived at its intended destination.

Where I leave you for now

I have written separate articles on how to write the check, in, and out and linked them below:

Please feel free to reach out to me with any questions. These articles are my first few and I would love it if I could get some constructive criticism about my writing or if I should elaborate more. I wrote these articles because I had to find resources from several sites just to start writing something and wants to lessen that burden for someone who could write a better resource than me!

--

--

Alexander Jansing
DevOps Dudes

Data Scientist / Software Engineer Engineer with Five Years of Experience. I love getting lost in a good problem.