Skip to main content

Renovating the OpenShift Client

·505 words·3 mins·

The OpenShift Client oc is not released using GitHub releases. Instead, versions are published via the OpenShift release graph (Cincinnati format). That makes it hard to auto-update the OpenShift Client using tools like Renovate, which rely on GitHub releases or other well-known sources to fetch the latest versions.

Using a custom datasource, we can query the Cincinnati graph directly to get the latest OpenShift Client version and update our dependencies accordingly.

Cincinnati
#

Cincinnati is a graph-based format used by OpenShift to publish release information and upgrade-paths.

To list available versions, we can query the graph with curl and jq:

curl -s https://amd64.ocp.releases.ci.openshift.org/graph \
  | jq -r '.nodes[].version' \
  | sort -V

Custom Datasource
#

Using Renovate’s custom datasource, we can create a datasource that fetches the latest OpenShift Client version from the Cincinnati graph. To do so, we need JSONata rules to transform the Cincinnati graph into a format that Renovate can understand.

The needed format is:

{
  "releases": [
    {
      "version": "string"
    }
  ]
}

Fortunately, that is almost what the Cincinnati graph already looks like, so we just need to extract the version from each node:

{ "releases": $.nodes.{ "version": version } }

The resulting renovate.json looks something like this:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "customDatasources": {
    "openshift": {
      "defaultRegistryUrlTemplate": "https://amd64.ocp.releases.ci.openshift.org/graph",
      "transformTemplates": [
        "{ \"releases\": $.nodes.{ \"version\": version } }"
      ]
    }
  }
}

Lag between build and publish
#

One issue is that Red Hat sometimes takes a while to push the latest OpenShift client to mirror.openshift.com. A simple way to check this is to have a pipeline that checks if the client is already available on the mirror. Another way would be to let renovate check the mirror directly:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "packageRules": [
    {
      "matchDatasources": ["custom.openshift"],
      "postUpgradeTasks": {
        "commands": [
          "curl --silent --head --fail -o /dev/null \"https://mirror.openshift.com/pub/openshift-v4/clients/ocp/{{ newVersion }}/openshift-client-linux.tar.gz\""
        ]
      }
    }
  ]
}

This, however, requires a bit of extra configuration to make sure that the command is allowed to be executed:

module.exports = {
    allowedCommands: [
        "^curl --silent --head --fail -o /dev/null \\S+$"
    ],
};

tl;dr
#

The full renovate.json (excluding the postUpgradeTask, since that’s optional) looks like this:

{
  "$schema": "https://docs.renovatebot.com/renovate-schema.json",
  "customManagers": [
    {
      "customType": "regex",
      "managerFilePatterns": ["/^Dockerfile$/"],
      "matchStrings": [
        "# renovate: datasource=(?<datasource>[^\\s]+) depName=(?<depName>[^\\s]+)( versioning=(?<versioning>[^\\s]+))?\\sENV [^\\s]+_VERSION=\"?(?<currentValue>[^\\s]+)\"?\\s"
      ],
      "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver-coerced{{/if}}"
    }
  ],
  "customDatasources": {
    "openshift": {
      "defaultRegistryUrlTemplate": "https://amd64.ocp.releases.ci.openshift.org/graph",
      "transformTemplates": [
        "{ \"releases\": $.nodes.{ \"version\": version } }"
      ]
    }
  }
}

Our Dockerfile looks like this:

FROM registry.access.redhat.com/ubi9/ubi-minimal:latest

# renovate: datasource=custom.openshift depName=openshift
ENV OPENSHIFT_VERSION=4.0.0

RUN --mount=type=tmpfs,target=/var/cache \
  microdnf install -y --nodocs --setopt=install_weak_deps=0 gzip tar \
  && curl -sL https://mirror.openshift.com/pub/openshift-v4/clients/ocp/${OPENSHIFT_VERSION}/openshift-client-linux.tar.gz \
     | tar -C /usr/bin -xz oc kubectl

Running renovate, we should see the following:

$ docker run --rm -v $(pwd):/data -w /data -e LOG_LEVEL=debug renovate/renovate renovate --platform=local
[...]
DEBUG: packageFiles with updates (repository=local)
       "config": {
         "regex": [
           {
             "deps": [
               {
                 "depName": "openshift",
                 "currentValue": "4.0.0",
                 "datasource": "custom.openshift",
                 "updates": [
                   {
                     "bucket": "non-major",
                     "newVersion": "4.21.14",
                     "newValue": "4.21.14",
                     "newMajor": 4,
                     "newMinor": 21,
                     "newPatch": 14,
                     "updateType": "minor",
                     "isBreaking": false,
                     "branchName": "renovate/openshift-4.x"
                   }
                 ],
                 [...]
               }
             ],
             [...]
           }
         ]
       }
[...]