Using a generic webhook to trigger builds

In contrast to OpenShift 2 where one would use git to push up the code for your application into OpenShift, with that triggering a new build and deployment, OpenShift 3 will instead pull your code from your source code repository. This difference means that under OpenShift 3, pushing changes up to your code repository will not by default automatically trigger a new build and deployment.

To enable automatic builds when code changes are pushed up to your code repository, it is necessary to link your source code repository and OpenShift using a webhook integration.

If you were for example using GitHub to host the code repository for your application, you would configure GitHub to trigger a GitHub webhook on a push, where the target URL is a special URL exposed by the OpenShift cluster where your application is hosted. That way when a push is performed, OpenShift will be notified by GitHub and a new build triggered automatically.

Veer Muchandi has demonstrated how to do this previously in a video on this blog and further information can be found in our documentation about using the GitHub webhook mechanism.

What though if you didn’t want to trigger a new build immediately after code was pushed to GitHub and instead you wished to have the code passed through a service such as Travis-CI which would run code tests first.

For this case you would need to use a Generic webhook. In this blog post I am going to walk you through setting up this scenario using a webhook proxy service to mediate between Travis-CI and OpenShift.

The problem of interoperability

A webhook in web development is a method of augmenting or altering the behaviour of a web application, through the use of custom callbacks. The idea behind a webhook is that when you update some resource on a web site or via a web service, it can trigger some further action by initiating a HTTP request against a special URL of yet another web service.

Great in concept, but one downside of webhooks are that their uses are so varied that it is hard to develop standards as to what information may be carried by the URL for the request, in request headers, or the payload of the request itself.

What tends to happen is that the producers of the webhook callback, define their own specification as to the data they will send. If you as a consumer want to allow for maximum interoperability, you have two choices. You either try and support all the different formats for webhook producers you may want to interoperate with, or you define your own format for what you expect and then rely on others to use that format or create a webhook proxy which translates callbacks from one format to another.

In the case of OpenShift it takes the middle ground. It provides builtin support for the most popular webhook producers it would need to deal with, such as GitHub, accepting the GitHub webhook format, but also defines its own format for a Generic webhook.

Linking OpenShift with Travis-CI

If you are not familiar with Travis-CI, it is a continuous integration service used to build and test software projects hosted at GitHub. It is especially popular due to its free tier offering for projects which are released as Open Source.

Whether you are running a web site related to an Open Source project, or are a commercial organisation using its paid service, you may wish to use it to run tests on your application code and only deploy the updated code if it passes all your tests.

For OpenShift 2, the Travis-CI system had a special provider plugin for OpenShift. As explained above, the way that source code is deployed is different between OpenShift 2 and 3 and so that existing plugin will not work with OpenShift 3.

The alternative for OpenShift 3 is to make use of the webhook notification support in Travis-CI. This is where we hit the interoperability problem raised above as Travis-CI defines its own format for the webhook callback which is generated and that doesn’t match what OpenShift is expecting to receive.

Comparing webhook callback formats

The format of the webhook callback that Travis-CI generates is documented on the Travis-CI site. You can check out the full format there, but a couple of key points about the format are:

  • The authorization secret it generates so that a consumer can validate the source of a webhook callback is sent in the HTTP Authorization request header.
  • The content of the request is sent as application/x-www-form-urlencoded, but where the information of interest is actually sent as URL encoded JSON data stored in the payload field of the format data.

The Generic webhook that OpenShift expects uses its own different format. The notable differences compared to what Travis-CI generates are:

  • The authorization secret needs to be sent as part of the URL, the Authorization header would therefore be ignored.
  • The content of the request is expected to be application/json and not as form data.
  • The layout of the data within the JSON part of the payload is different.

For the Generic webhook in OpenShift, the payload sent in the request content is actually optional and a build can be deployed purely based on a request sent to the correct URL. We could therefore technically still point the Travis-CI webhook notification at OpenShift and get it to work. Unfortunately though, the URL that Travis-CI triggers the webhook request against, is defined in the .travis.yml file.

The problem with this if you haven’t worked it out, is that this means the authorization secret would need to be included in the URL listed in the .travis.yml file. For an Open Source project this would mean it is publicly visible to everyone, which isn’t what you would want.

In addition to this issue, when a payload isn’t provided to OpenShift with the details of the Git repository branch and commit corresponding to the test run, then OpenShift can only assume that it should run a build against the last commit on the branch it is linked to. This may not correspond to what the test was run against.

It is therefore important to be able to properly pass across some of the details generated in the Travis-CI webhook callback into the OpenShift Generic webhook format to ensure everything works correctly.

The only way to connect two different webhook request formats together when differences exist, is to use a webhook proxy service.

Although there are webhook proxy services out there which offer a general format translation and proxying service, I decided to create my own webhook proxy service and run it in OpenShift to act as the required bridge, so you can see what is involved and how you could also do it yourself for any third party service you may need to work with.

Using a webhook proxy service

The webhook proxy service I have implemented to bridge between Travis-CI and OpenShift, can be found at:

It consists of a small Python web application implemented using the Flask web framework. You can see the actual code in the app.py file on the repository on GitHub. I have also included a Python pip requirements.txt file with the list of Python packages which are required by the application.

This is all that is required to host the webhook proxy service on OpenShift using the default S2I builder for Python supplied with OpenShift. All you would need to do is add a new python:2.7 application to your project using the OpenShift web console and give it the appropriate URL for the Git repository.

An easier way is to add the application using the oc command line tool by running:

oc create -f https://raw.githubusercontent.com/GrahamDumpleton/openshift-webhook-proxy/master/openshift.json

This will add the webhook proxy service, including exposing it via both HTTP and HTTPS. The name of the application will be webhook-proxy.

With the webhook proxy service running, the next steps are to configure the build configuration for the application you want to link with Travis-CI, with the authorization secret that Travis-CI will use, and update your Travis-CI configuration to generate the webhook notification when a test succeeds.

Setting the authorization secret

As mentioned above, when Travis-CI runs the webhook callback, it will send an authorization secret in the HTTP Authorization header. The webhook proxy service when translating the request, will move that into the URL as required by OpenShift. As it is Travis-CI which dictates what the authorization secret is, we need to add it to the build configuration for the target application.

To update the build configuration you can either edit it though the OpenShift web console or using oc edit on the command line. For example, if your web application is called myapp, you would run:

oc edit bc/myapp

You then need to find the triggers section and the subsection for the generic webhook configuration.

  triggers:
  - generic:
      secret: replace-this-with-authorization-secret
    type: Generic

The details of how to generate the authorization secret are described on the Travis-CI site. The snippet of Python code they provide for doing this is:

from hashlib import sha256
sha256('username/repository' + TRAVIS_TOKEN).hexdigest()

The TRAVIS_TOKEN would be found, as explained in their documentation, in your account profile on the Travis-CI site.

Enabling notifications in Travis-CI

The prior step sets up the OpenShift side, next is to configure Travis-CI through the .travis.yml file which is a part of the source code repository for the application you are using for your deployment in OpenShift and in Travis-CI for testing.

For this you need to add a notifications section containing a webhooks sub section. Details of setting up this type of notification can be found in the Travis-CI documentation.

The important part of this is what URL to use. Because we need to proxy this webhook via the proxy service we need to use the URL of the webhook proxy service we just deployed.

language: python
python:
  - "2.7"
install:
  - pip install -r requirements.txt
script:
  - python manage.py test
notifications:
  webhooks:
    urls:
      - https://webhook-proxy-notifications.a123.apps.example.com/travis-ci/api.example.com/myproject/myapp
    on_success: always
    on_failure: never
    on_start: never

The format of the URL is:

https://<webhook-proxy-host>/travis-ci/<openshift-api-host>/<project>/<application>

To determine the external host name allocated to the webhook proxy service, you can use the oc describe route command.

$ oc describe route/webhook-proxy | grep Host
Requested Host:         webhook-proxy-notifications.a123.apps.example.com

The OpenShift API host should be the host name of the OpenShift cluster displayed when you originally logged in using the oc login command.

Once the .travis.yml file has been updated, commit the change and push your change to your code repository. When Travis-CI picks up that a push has occurred, once the tests are run the webhook should be triggered. This will go to the webhook proxy service, which will translate the webhook format to that expected by OpenShift and pass it through to OpenShift. OpenShift will then start a new build and deployment of your application based on the code that passed testing under Travis-CI.

Other webhook notification types

The webhook proxy service I implemented only supports Travis-CI. It should be relatively straight forward to adapt to other third party services used in continuous integration pipelines and which use webhook notifications. If you do use it and add support for other commonly used services, do let me know via the project issues tracker on GitHub and we can see about adding in the additional support.

As to Travis-CI in particular, they do provide default integrations for many services for triggering deployments. As noted above they have provided an integration in the past for OpenShift 2. In time hopefully we will get a builtin integration for OpenShift 3, but in the interim something like this webhook proxy service does the job.

Categories
News, OpenShift Origin, Python
Tags
,
Comments are closed.