Skip to main content

Automate Catalyst region upgrades

Diagrid works out which release a self-managed region should install next from the region's release channel and policy. You can read that decision in two places:

  • The Control Plane API tells you whether an upgrade is waiting and which version it is. Call it with an API key from anywhere.
  • The catalyst-release-availability ConfigMap lives in your cluster, and the Catalyst agent keeps it up to date. It also names the exact chart to install, pinned by digest.
important

Diagrid never applies an upgrade to a self-managed region. It does not run Helm, and it does not edit your Argo CD Application or anything else in your cluster. Your tooling reads the decision, and you choose when and how to apply it.

Read the decision from the API​

Every region read includes the decision in status.release:

diagrid region get my-region --output json

The release policy endpoint returns the same decision together with the region's channel and mode. Authenticate with an API key:

curl -s https://api.r1.diagrid.io/apis/cra.diagrid.io/v1beta2/regions/my-region/releasesubscription \
-H "x-diagrid-api-key: ${DIAGRID_API_KEY}"
{
"region": "my-region",
"component": "catalyst-dataplane",
"channel": "stable",
"mode": "follow",
"decisionRequired": false,
"status": {
"upgradeAvailable": true,
"reason": "NEXT_HOP_SELECTED",
"currentVersion": "1.133.0",
"nextVersion": "1.134.0",
"destinationVersion": "1.135.0",
"channelHeadVersion": "1.135.0"
}
}
FieldMeaning
upgradeAvailabletrue when there is a release to move to now.
reasonWhy the region can or cannot move. See Reason codes.
currentVersionThe release Diagrid observed the region running. Empty when the region is older than 1.115.0.
nextVersionThe one release to install now. It can be an intermediate step on the way to destinationVersion. Empty when the region must not move.
destinationVersionThe release the region is heading for, which can be several upgrades away. Install nextVersion instead.
channelHeadVersionThe newest release on the region's channel.
frozenUntilWhen a freeze window lifts. Set only with FREEZE_WINDOW_ACTIVE.

The same endpoint accepts a PUT to change the channel or pause upgrades, which is mode hold. The request sets channel and mode together, so read the policy first and send the channel back unchanged when you only want to pause upgrades. See Get a region's release policy and Set a region's release policy.

Reason codes​

NEXT_HOP_SELECTED means a release is waiting. Every other code means "do nothing now":

CodeMeaning
ALREADY_AT_DESTINATIONThe region runs the newest release it can move to.
FREEZE_WINDOW_ACTIVEA freeze window applies. frozenUntil says when it lifts.
SUBSCRIPTION_HELD, SUBSCRIPTION_PINNEDPaused upgrades or a pin keep the region where it is.
RELEASE_BLOCKEDDiagrid has stopped rollouts of that release.
CHANNEL_HEAD_UNKNOWNDiagrid has not recorded a release for the channel yet.
CURRENT_RELEASE_UNKNOWNDiagrid cannot tell what the region runs. Upgrade the region to 1.115.0 or later.
NO_SUPPORTED_UPGRADE_PATH, AGENT_VERSION_TOO_OLD, NOT_ENTITLED, RELEASE_RETIRED, PINNED_RELEASE_UNRESOLVED, DOWNGRADE_NOT_ALLOWEDThese do not clear on their own. Contact Diagrid support.

Read the decision in your cluster​

The API does not say where to install a release from. For that, turn on the catalyst-release-availability ConfigMap. The Catalyst agent asks Diagrid for the decision every five minutes and writes it into the cra-agent namespace, where your tooling reads it with ordinary Kubernetes access.

Turn on the ConfigMap​

The ConfigMap requires a Catalyst region version 1.117.0 or later. Add these values to your catalyst-values.yaml:

agent:
config:
release_availability:
enabled: true
endpoint: https://catalyst-releases.r1.diagrid.io
# Optional. Seconds between refreshes. The default is 300.
interval_in_sec: 300
# Optional. Grants read access to the ConfigMap.
readers:
- name: catalyst-upgrade # the ServiceAccount your upgrade job runs as
namespace: ci

Set both enabled and endpoint. With enabled alone the agent logs a warning and writes no ConfigMap. The agent calls the endpoint over mutual TLS, so allow outbound HTTPS to catalyst-releases.r1.diagrid.io without TLS inspection. See Outbound network connections.

Each entry in readers gets a Role that grants get on this one ConfigMap, and a RoleBinding to that ServiceAccount. To manage RBAC yourself, leave readers unset. Grant get with resourceNames: ["catalyst-release-availability"], and do not grant list or watch. Kubernetes cannot limit those to one name, so they would expose every ConfigMap in cra-agent.

Upgrade the region as usual. The ConfigMap appears within a minute of the agent restarting.

What the ConfigMap contains​

apiVersion: v1
kind: ConfigMap
metadata:
name: catalyst-release-availability
namespace: cra-agent
data:
state: current
reason: NEXT_HOP_SELECTED
component: catalyst-dataplane
channel: stable
mode: follow
nextVersion: 1.134.0
nextReleaseDigest: sha256:65d4…
nextChartReference: oci://public.ecr.aws/diagrid/catalyst@sha256:b048…
nextChartRepository: oci://public.ecr.aws/diagrid/catalyst
nextChartDigest: sha256:b048…
currentVersion: 1.133.0
currentReleaseDigest: sha256:2c80…
issuedAt: "2026-10-07T09:00:00Z"
expiresAt: "2026-10-07T09:15:00Z"
lastRefreshAt: "2026-10-07T09:01:00Z"
refreshError: ""
answer.json: |
{ … }

Every key except answer.json is a single-line string, so kubectl with jsonpath reads any of them:

kubectl get configmap catalyst-release-availability -n cra-agent \
-o jsonpath='{.data.nextVersion}'
KeyMeaning
stateRead this first. See the table below.
reasonThe reason code, as in the API.
nextVersionThe one release to install now. Empty when the region must not move.
nextChartReferenceThe chart to install, pinned by digest. nextChartRepository and nextChartDigest give the same thing in parts.
currentVersionThe release Diagrid observed the region running.
issuedAt, expiresAtWhen Diagrid made the decision, and when it stops being valid. A decision is valid for about 15 minutes.
lastRefreshAt, refreshErrorWhen the agent last asked, and why that failed. refreshError is empty after a success.
answer.jsonThe full decision as Diagrid sent it, including frozenUntil.
stateMeaningWhat to do
currentThe decision had not expired when the agent wrote it.Act on it.
expiredThe decision is past expiresAt, because the agent could not reach Diagrid for a while.Nothing. Check refreshError.
unavailableThe agent has never received a decision.Nothing. Check refreshError.

When a refresh fails, the agent keeps the last decision and records the failure in refreshError. It never deletes the ConfigMap or writes half a decision. Setting enabled back to false stops the refreshes but leaves the ConfigMap in place, so delete it if nothing should read it.

Rules for your tooling​

  • Read it on a schedule. nextVersion is one step. After you install it, the next refresh can offer the next one.
  • Check expiresAt yourself. state is only as fresh as the agent's last refresh. If the agent stops, nothing marks the decision as expired.
  • Install by digest. A version tag can be moved to another chart, but a digest cannot.
  • Do nothing when there is no nextVersion. Never fall back to the newest chart in the registry. It skips every block, freeze window and tested upgrade path.

Example: upgrade with Argo CD​

This example keeps the Catalyst version in Git. A scheduled job reads the ConfigMap and opens a pull request. When you merge it, Argo CD rolls out the new release.

Upgrading a self-managed region with Argo CDYour clusterDiagrid CloudControl planeCatalyst agentConfigMapcra-agentArgo CDGit repositoryUpgrade jobyour schedulemTLSreadsopens PRsync
Upgrading a self-managed region with Argo CDDiagrid publishes the decision into your cluster. Your job turns it into a pull request, and Argo CD applies it once you merge.
  1. Register the Diagrid chart registry with Argo CD:

    apiVersion: v1
    kind: Secret
    metadata:
    name: diagrid-charts
    namespace: argocd
    labels:
    argocd.argoproj.io/secret-type: repository
    stringData:
    type: helm
    name: diagrid
    url: public.ecr.aws/diagrid
    enableOCI: "true"
  2. Create an Application that pins the region to an exact version, and commit it to the Git repository Argo CD watches, for example as apps/catalyst.yaml:

    apiVersion: argoproj.io/v1alpha1
    kind: Application
    metadata:
    name: catalyst
    namespace: argocd
    spec:
    project: default
    source:
    repoURL: public.ecr.aws/diagrid
    chart: catalyst
    # Written by the upgrade job. Always an exact version, never a range.
    targetRevision: 1.133.0
    helm:
    releaseName: catalyst
    values: |
    # the contents of your catalyst-values.yaml
    destination:
    server: https://kubernetes.default.svc
    namespace: cra-agent
    syncPolicy:
    automated:
    prune: false
    selfHeal: true
  3. Run an upgrade job on a schedule, such as every 15 minutes. It reads the ConfigMap and opens a pull request when a release is waiting. The job needs get on the ConfigMap (see readers in Turn on the ConfigMap), push access to your repository, and crane, yq and gh:

    #!/usr/bin/env bash
    set -euo pipefail

    read_key() {
    kubectl get configmap catalyst-release-availability -n cra-agent \
    -o "jsonpath={.data.$1}"
    }

    STATE=$(read_key state)
    if [ "${STATE}" != "current" ]; then
    echo "no usable decision (state: ${STATE}): $(read_key refreshError)" >&2
    exit 1
    fi

    # The timestamps are RFC 3339 in UTC, so a string comparison is enough.
    if [[ ! "$(date -u +%Y-%m-%dT%H:%M:%SZ)" < "$(read_key expiresAt)" ]]; then
    echo "the published decision has expired" >&2
    exit 1
    fi

    VERSION=$(read_key nextVersion)
    if [ -z "${VERSION}" ]; then
    echo "no upgrade waiting: $(read_key reason)"
    exit 0
    fi

    # Argo CD installs by version, so check the tag still points at the chart Diagrid named.
    if [ "$(crane digest "public.ecr.aws/diagrid/catalyst:${VERSION}")" != "$(read_key nextChartDigest)" ]; then
    echo "the ${VERSION} tag no longer matches the published digest" >&2
    exit 1
    fi

    yq -i ".spec.source.targetRevision = \"${VERSION}\"" apps/catalyst.yaml
    git checkout -b "catalyst-${VERSION}"
    git commit -am "Upgrade Catalyst to ${VERSION}"
    git push origin "catalyst-${VERSION}"
    gh pr create --fill

    A run with nothing to do exits without touching the repository.

  4. Review and merge the pull request. Argo CD syncs the Application and upgrades the region to the new version.

A region several releases behind gets one pull request at a time. After Argo CD applies one, the agent reports the new version and the job's next run opens a pull request for the next step.

If your job cannot reach the cluster, read status.nextVersion from the API instead. The API does not return the chart digest, so you lose the digest check.

Next steps​