openshift-update-proxy 1.0.2__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (24) hide show
  1. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/PKG-INFO +128 -21
  2. openshift_update_proxy-1.2.0/README.md +309 -0
  3. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/pyproject.toml +1 -1
  4. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy/__init__.py +1 -1
  5. openshift_update_proxy-1.2.0/src/openshift_update_proxy/app.py +299 -0
  6. openshift_update_proxy-1.2.0/src/openshift_update_proxy/cache.py +18 -0
  7. openshift_update_proxy-1.2.0/src/openshift_update_proxy/catalog.py +147 -0
  8. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy/config.py +9 -0
  9. openshift_update_proxy-1.2.0/src/openshift_update_proxy/graph.py +39 -0
  10. openshift_update_proxy-1.2.0/src/openshift_update_proxy/lifecycle.py +71 -0
  11. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy.egg-info/PKG-INFO +128 -21
  12. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy.egg-info/SOURCES.txt +7 -1
  13. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/tests/test_app.py +62 -0
  14. openshift_update_proxy-1.2.0/tests/test_catalog.py +195 -0
  15. openshift_update_proxy-1.2.0/tests/test_lifecycle.py +145 -0
  16. openshift_update_proxy-1.0.2/README.md +0 -202
  17. openshift_update_proxy-1.0.2/src/openshift_update_proxy/app.py +0 -138
  18. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/LICENSE +0 -0
  19. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/setup.cfg +0 -0
  20. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy/__main__.py +0 -0
  21. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy.egg-info/dependency_links.txt +0 -0
  22. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy.egg-info/entry_points.txt +0 -0
  23. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy.egg-info/requires.txt +0 -0
  24. {openshift_update_proxy-1.0.2 → openshift_update_proxy-1.2.0}/src/openshift_update_proxy.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: openshift-update-proxy
3
- Version: 1.0.2
3
+ Version: 1.2.0
4
4
  Summary: Forwarding proxy for OpenShift update resources (Cincinnati API, mirror, release signatures)
5
5
  Author-email: Simon Lauger <simon@lauger.de>
6
6
  License-Expression: Apache-2.0
@@ -36,10 +36,10 @@ Dynamic: license-file
36
36
  [![PyPI](https://img.shields.io/pypi/v/openshift-update-proxy)](https://pypi.org/project/openshift-update-proxy/)
37
37
  [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
38
38
 
39
- A small Flask based service which forwards HTTP requests to `api.openshift.com` and
40
- `mirror.openshift.com`. Built for restricted networks where OpenShift clusters have no
41
- direct internet access, but a central egress proxy (or a single host with internet
42
- access) exists.
39
+ A small Flask based service which forwards HTTP requests to `api.openshift.com`,
40
+ `mirror.openshift.com`, `catalog.redhat.com` and `access.redhat.com`. Built for
41
+ restricted networks where OpenShift clusters have no direct internet access, but a
42
+ central egress proxy (or a single host with internet access) exists.
43
43
 
44
44
  ## Features
45
45
 
@@ -51,6 +51,11 @@ access) exists.
51
51
  `ClusterVersion.spec.signatureStores` (OpenShift 4.14+)
52
52
  - 🗺️ **ConfigMap Generator** - renders ready-to-apply signature ConfigMaps for the
53
53
  classic disconnected verification workflow
54
+ - 🎛️ **Operator Catalog API** - serves operator channels and versions from the
55
+ Red Hat Pyxis API (`catalog.redhat.com`), ready to use as a
56
+ [Renovate custom datasource](https://docs.renovatebot.com/modules/datasource/custom/)
57
+ - 📅 **Supported Versions API** - combines the Red Hat product lifecycle API with the
58
+ update graph to list supported OpenShift minor versions and their latest release
54
59
  - 🚦 **Egress Proxy Aware** - honors `HTTPS_PROXY` / `NO_PROXY` for all upstream requests
55
60
  - 🐳 **Hardened Container** - UBI9 based, rootless (UID 1001), digest-pinned base image,
56
61
  Cosign signed
@@ -64,22 +69,28 @@ flowchart LR
64
69
  subgraph restricted["Restricted network"]
65
70
  CVO["Cluster Version Operator"]
66
71
  ADMIN["Admin (oc / curl)"]
72
+ RENOVATE["Renovate"]
67
73
  PROXY["openshift-update-proxy"]
68
74
  end
69
75
 
70
76
  subgraph internet["Internet"]
71
77
  API["api.openshift.com"]
72
78
  MIRROR["mirror.openshift.com"]
79
+ PYXIS["catalog.redhat.com"]
80
+ LIFECYCLE["access.redhat.com"]
73
81
  end
74
82
 
75
83
  CVO -- "/api/upgrades_info/v1/graph" --> PROXY
76
84
  CVO -- "/signatures/sha256=…" --> PROXY
77
85
  ADMIN -- "/configmaps/sha256=…" --> PROXY
78
86
  ADMIN -- "/pub/…" --> PROXY
87
+ RENOVATE -- "/operators/v1/…" --> PROXY
79
88
 
80
89
  PROXY -- "optional egress proxy (HTTPS_PROXY)" --> EGRESS["Egress Proxy"]
81
90
  EGRESS --> API
82
91
  EGRESS --> MIRROR
92
+ EGRESS --> PYXIS
93
+ EGRESS --> LIFECYCLE
83
94
  ```
84
95
 
85
96
  ## Endpoints
@@ -89,7 +100,12 @@ flowchart LR
89
100
  | `/api/<path>` | `https://api.openshift.com/api/` | Cincinnati update graph (`/api/upgrades_info/v1/graph`) |
90
101
  | `/pub/<path>` | `https://mirror.openshift.com/pub/` | OpenShift mirror (clients, release artifacts) |
91
102
  | `/signatures/<path>` | `https://mirror.openshift.com/pub/openshift-v4/signatures/openshift/release/` | Release image signature store |
92
- | `/configmaps/sha256=<digest>` | derived from signature store | Ready-to-apply signature ConfigMap (YAML) |
103
+ | `/configmaps/<version or sha256=digest>` | derived from signature store | Ready-to-apply signature ConfigMap (YAML) |
104
+ | `/catalog/<path>` | `https://catalog.redhat.com/api/containers/v1/` | Red Hat Pyxis API (operator catalog metadata) |
105
+ | `/operators/v1/<catalog>/<package>/channels` | derived from Pyxis | Channels, default channel and latest CSV per channel |
106
+ | `/operators/v1/<catalog>/<package>/<channel>/releases` | derived from Pyxis | Version feed in Renovate custom datasource format |
107
+ | `/lifecycle/<path>` | `https://access.redhat.com/product-life-cycles/api/v1/` | Red Hat product lifecycle API |
108
+ | `/versions/v1/supported` | derived from lifecycle API + update graph | Supported OpenShift minors with latest release per channel |
93
109
  | `/healthz` | - | Health check for liveness/readiness probes |
94
110
 
95
111
  ## Configuration
@@ -103,6 +119,10 @@ All configuration is done via environment variables:
103
119
  | `API_UPSTREAM` | `https://api.openshift.com/api/` | Cincinnati API base URL |
104
120
  | `MIRROR_UPSTREAM` | `https://mirror.openshift.com/pub/` | Mirror base URL |
105
121
  | `SIGNATURE_UPSTREAM` | `https://mirror.openshift.com/pub/openshift-v4/signatures/openshift/release/` | Signature store base URL |
122
+ | `CATALOG_UPSTREAM` | `https://catalog.redhat.com/api/containers/v1/` | Red Hat Pyxis API base URL |
123
+ | `CATALOG_CACHE_TTL` | `600` | Cache TTL in seconds for operator catalog lookups (`0` disables caching) |
124
+ | `LIFECYCLE_UPSTREAM` | `https://access.redhat.com/product-life-cycles/api/v1/` | Red Hat product lifecycle API base URL |
125
+ | `LIFECYCLE_CACHE_TTL` | `3600` | Cache TTL in seconds for lifecycle and latest-release lookups (`0` disables caching) |
106
126
  | `REQUEST_TIMEOUT` | `30` | Upstream request timeout in seconds |
107
127
  | `LISTEN_HOST` | `0.0.0.0` | Listen address |
108
128
  | `LISTEN_PORT` | `5000` | Listen port |
@@ -157,26 +177,21 @@ spec:
157
177
  ### Release signatures
158
178
 
159
179
  For updates by digest (`oc adm upgrade --to-image ...@sha256:...`) the CVO must verify the
160
- release image signature. There are two ways to get signatures into a restricted cluster:
180
+ release image signature.
161
181
 
162
- **Option 1: Signature store (OpenShift 4.14+)**
182
+ The `/configmaps/` endpoint fetches all signatures for a release and renders a
183
+ ready-to-apply ConfigMap (same format as `oc adm release mirror` / oc-mirror produces).
184
+ It accepts a release version directly - the digest is resolved via the update graph:
163
185
 
164
- Point the cluster at the `/signatures/` endpoint of the proxy:
165
-
166
- ```yaml
167
- apiVersion: config.openshift.io/v1
168
- kind: ClusterVersion
169
- metadata:
170
- name: version
171
- spec:
172
- signatureStores:
173
- - url: http://update-proxy.example.com:5000/signatures
186
+ ```bash
187
+ curl -s "http://update-proxy.example.com:5000/configmaps/4.16.8" | oc apply -f -
174
188
  ```
175
189
 
176
- **Option 2: Signature ConfigMap**
190
+ The optional `arch` (default `amd64`) and `channel_prefix` (default `stable`) query
191
+ parameters select the architecture and the update channel used for the lookup, e.g.
192
+ `/configmaps/4.16.8?arch=arm64`.
177
193
 
178
- The `/configmaps/` endpoint fetches all signatures for a release digest and renders a
179
- ready-to-apply ConfigMap (same format as `oc adm release mirror` / oc-mirror produces):
194
+ Alternatively, a release digest can be passed directly:
180
195
 
181
196
  ```bash
182
197
  DIGEST=$(oc adm release info quay.io/openshift-release-dev/ocp-release:4.16.8-x86_64 -o jsonpath='{.digest}')
@@ -186,6 +201,98 @@ curl -s "http://update-proxy.example.com:5000/configmaps/${DIGEST/:/=}" | oc app
186
201
  The ConfigMap is created in `openshift-config-managed` with the
187
202
  `release.openshift.io/verification-signatures` label, where the CVO picks it up.
188
203
 
204
+ > **Note:** The ClusterVersion API also has a `spec.signatureStores` field, but it is
205
+ > gated behind the TechPreview-only `SignatureStores` feature gate and will not be
206
+ > promoted to GA ([OTA-1118](https://issues.redhat.com/browse/OTA-1118)). The ConfigMap
207
+ > above is the supported way to provide signatures.
208
+
209
+ ## Operator catalog and Renovate
210
+
211
+ The `/operators/v1/` endpoints answer "which operator versions exist in which
212
+ channel?" without pulling the multi-hundred-MB catalog index images and without any
213
+ registry credentials. The data comes from the public
214
+ [Red Hat Pyxis API](https://catalog.redhat.com/api/containers/docs/) and covers all
215
+ four default catalogs: `redhat-operators`, `certified-operators`,
216
+ `community-operators` and `redhat-marketplace`.
217
+
218
+ List channels, default channel and the latest CSV per channel:
219
+
220
+ ```bash
221
+ curl -s "http://update-proxy.example.com:5000/operators/v1/redhat-operators/openshift-gitops-operator/channels?ocp_version=4.16"
222
+ ```
223
+
224
+ ```json
225
+ {
226
+ "package": "openshift-gitops-operator",
227
+ "organization": "redhat-operators",
228
+ "default_channel": "latest",
229
+ "channels": [
230
+ {"name": "gitops-1.21", "latest_version": "1.21.4", "latest_csv": "openshift-gitops-operator.v1.21.4"}
231
+ ]
232
+ }
233
+ ```
234
+
235
+ List all versions of a channel in the format Renovate expects from a
236
+ [custom datasource](https://docs.renovatebot.com/modules/datasource/custom/):
237
+
238
+ ```bash
239
+ curl -s "http://update-proxy.example.com:5000/operators/v1/redhat-operators/openshift-gitops-operator/gitops-1.21/releases?ocp_version=4.16"
240
+ ```
241
+
242
+ ```json
243
+ {
244
+ "releases": [
245
+ {"version": "1.21.3", "releaseTimestamp": "2026-08-14T23:45:28.177000+00:00"},
246
+ {"version": "1.21.4", "releaseTimestamp": "2026-09-03T12:13:37.391000+00:00"}
247
+ ]
248
+ }
249
+ ```
250
+
251
+ The optional `ocp_version` query parameter limits results to bundles shipped in the
252
+ catalog for that OpenShift minor version. Responses are cached in memory for
253
+ `CATALOG_CACHE_TTL` seconds. The raw Pyxis API is available under `/catalog/`, e.g.
254
+ `/catalog/operators/indices?filter=organization==redhat-operators` lists all index
255
+ image tags with their end-of-life dates.
256
+
257
+ With this feed, Renovate can bump pinned operator versions (`startingCSV` in OLM
258
+ `Subscription` manifests, whether managed directly via Argo CD or embedded in ACM
259
+ policies) just like any other dependency - merging the PR rolls out the operator
260
+ update. See [examples/renovate/](examples/renovate/) for a complete working setup:
261
+ a `renovate.json` with the custom datasource and regex manager, plus matching
262
+ Subscription and ACM Policy manifests.
263
+
264
+ ## Supported OpenShift versions
265
+
266
+ `/versions/v1/supported` combines the
267
+ [Red Hat product lifecycle API](https://access.redhat.com/product-life-cycles) with
268
+ the Cincinnati update graph: all OpenShift minor versions that are not end-of-life,
269
+ together with the latest release in the corresponding update channel.
270
+
271
+ ```bash
272
+ curl -s "http://update-proxy.example.com:5000/versions/v1/supported"
273
+ ```
274
+
275
+ ```json
276
+ {
277
+ "product": "OpenShift Container Platform",
278
+ "architecture": "amd64",
279
+ "versions": [
280
+ {"version": "4.22", "support_phase": "Full Support", "channel": "stable-4.22", "latest_release": "4.22.11"},
281
+ {"version": "4.20", "support_phase": "Maintenance Support", "channel": "stable-4.20", "latest_release": "4.20.35"}
282
+ ]
283
+ }
284
+ ```
285
+
286
+ The optional `channel_prefix` (default `stable`, e.g. `eus`, `fast`, `candidate`) and
287
+ `arch` (default `amd64`) query parameters select the channel and architecture;
288
+ `latest_release` is `null` when the channel has no published releases yet. The raw
289
+ lifecycle API is available under `/lifecycle/`, e.g.
290
+ `/lifecycle/products?name=OpenShift Container Platform`.
291
+
292
+ [examples/create-configmaps.sh](examples/create-configmaps.sh) combines this with
293
+ the `/configmaps/` endpoint: it fetches the release signatures for a set of update
294
+ channels through the proxy and applies them as ConfigMaps.
295
+
189
296
  ## Local Development
190
297
 
191
298
  ```bash
@@ -0,0 +1,309 @@
1
+ # 🔄 openshift-update-proxy
2
+
3
+ [![CI](https://github.com/slauger/openshift-update-proxy/actions/workflows/ci.yml/badge.svg)](https://github.com/slauger/openshift-update-proxy/actions/workflows/ci.yml)
4
+ [![Release](https://github.com/slauger/openshift-update-proxy/actions/workflows/release.yml/badge.svg)](https://github.com/slauger/openshift-update-proxy/actions/workflows/release.yml)
5
+ [![PyPI](https://img.shields.io/pypi/v/openshift-update-proxy)](https://pypi.org/project/openshift-update-proxy/)
6
+ [![License](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
7
+
8
+ A small Flask based service which forwards HTTP requests to `api.openshift.com`,
9
+ `mirror.openshift.com`, `catalog.redhat.com` and `access.redhat.com`. Built for
10
+ restricted networks where OpenShift clusters have no direct internet access, but a
11
+ central egress proxy (or a single host with internet access) exists.
12
+
13
+ ## Features
14
+
15
+ - 🔀 **Update Graph Proxy** - forwards Cincinnati update graph requests
16
+ (`/api/upgrades_info/v1/graph`) to `api.openshift.com`
17
+ - 📦 **Mirror Proxy** - forwards requests for clients and release artifacts to
18
+ `mirror.openshift.com/pub`
19
+ - 🔏 **Signature Store** - serves release image signatures for
20
+ `ClusterVersion.spec.signatureStores` (OpenShift 4.14+)
21
+ - 🗺️ **ConfigMap Generator** - renders ready-to-apply signature ConfigMaps for the
22
+ classic disconnected verification workflow
23
+ - 🎛️ **Operator Catalog API** - serves operator channels and versions from the
24
+ Red Hat Pyxis API (`catalog.redhat.com`), ready to use as a
25
+ [Renovate custom datasource](https://docs.renovatebot.com/modules/datasource/custom/)
26
+ - 📅 **Supported Versions API** - combines the Red Hat product lifecycle API with the
27
+ update graph to list supported OpenShift minor versions and their latest release
28
+ - 🚦 **Egress Proxy Aware** - honors `HTTPS_PROXY` / `NO_PROXY` for all upstream requests
29
+ - 🐳 **Hardened Container** - UBI9 based, rootless (UID 1001), digest-pinned base image,
30
+ Cosign signed
31
+ - ⛵ **Helm Chart** - deploy to Kubernetes/OpenShift with probes and sane security defaults
32
+ - 🩺 **Health Endpoint** - `/healthz` for liveness and readiness probes
33
+
34
+ ## How it works
35
+
36
+ ```mermaid
37
+ flowchart LR
38
+ subgraph restricted["Restricted network"]
39
+ CVO["Cluster Version Operator"]
40
+ ADMIN["Admin (oc / curl)"]
41
+ RENOVATE["Renovate"]
42
+ PROXY["openshift-update-proxy"]
43
+ end
44
+
45
+ subgraph internet["Internet"]
46
+ API["api.openshift.com"]
47
+ MIRROR["mirror.openshift.com"]
48
+ PYXIS["catalog.redhat.com"]
49
+ LIFECYCLE["access.redhat.com"]
50
+ end
51
+
52
+ CVO -- "/api/upgrades_info/v1/graph" --> PROXY
53
+ CVO -- "/signatures/sha256=…" --> PROXY
54
+ ADMIN -- "/configmaps/sha256=…" --> PROXY
55
+ ADMIN -- "/pub/…" --> PROXY
56
+ RENOVATE -- "/operators/v1/…" --> PROXY
57
+
58
+ PROXY -- "optional egress proxy (HTTPS_PROXY)" --> EGRESS["Egress Proxy"]
59
+ EGRESS --> API
60
+ EGRESS --> MIRROR
61
+ EGRESS --> PYXIS
62
+ EGRESS --> LIFECYCLE
63
+ ```
64
+
65
+ ## Endpoints
66
+
67
+ | Endpoint | Upstream | Purpose |
68
+ |----------|----------|---------|
69
+ | `/api/<path>` | `https://api.openshift.com/api/` | Cincinnati update graph (`/api/upgrades_info/v1/graph`) |
70
+ | `/pub/<path>` | `https://mirror.openshift.com/pub/` | OpenShift mirror (clients, release artifacts) |
71
+ | `/signatures/<path>` | `https://mirror.openshift.com/pub/openshift-v4/signatures/openshift/release/` | Release image signature store |
72
+ | `/configmaps/<version or sha256=digest>` | derived from signature store | Ready-to-apply signature ConfigMap (YAML) |
73
+ | `/catalog/<path>` | `https://catalog.redhat.com/api/containers/v1/` | Red Hat Pyxis API (operator catalog metadata) |
74
+ | `/operators/v1/<catalog>/<package>/channels` | derived from Pyxis | Channels, default channel and latest CSV per channel |
75
+ | `/operators/v1/<catalog>/<package>/<channel>/releases` | derived from Pyxis | Version feed in Renovate custom datasource format |
76
+ | `/lifecycle/<path>` | `https://access.redhat.com/product-life-cycles/api/v1/` | Red Hat product lifecycle API |
77
+ | `/versions/v1/supported` | derived from lifecycle API + update graph | Supported OpenShift minors with latest release per channel |
78
+ | `/healthz` | - | Health check for liveness/readiness probes |
79
+
80
+ ## Configuration
81
+
82
+ All configuration is done via environment variables:
83
+
84
+ | Variable | Default | Description |
85
+ |----------|---------|-------------|
86
+ | `HTTPS_PROXY` | - | Egress proxy for upstream requests (standard `requests` behaviour, `NO_PROXY` is honored) |
87
+ | `INSECURE_SKIP_TLS_VERIFY` | `false` | Skip TLS certificate verification for upstream requests (`true`/`1`/`yes`) |
88
+ | `API_UPSTREAM` | `https://api.openshift.com/api/` | Cincinnati API base URL |
89
+ | `MIRROR_UPSTREAM` | `https://mirror.openshift.com/pub/` | Mirror base URL |
90
+ | `SIGNATURE_UPSTREAM` | `https://mirror.openshift.com/pub/openshift-v4/signatures/openshift/release/` | Signature store base URL |
91
+ | `CATALOG_UPSTREAM` | `https://catalog.redhat.com/api/containers/v1/` | Red Hat Pyxis API base URL |
92
+ | `CATALOG_CACHE_TTL` | `600` | Cache TTL in seconds for operator catalog lookups (`0` disables caching) |
93
+ | `LIFECYCLE_UPSTREAM` | `https://access.redhat.com/product-life-cycles/api/v1/` | Red Hat product lifecycle API base URL |
94
+ | `LIFECYCLE_CACHE_TTL` | `3600` | Cache TTL in seconds for lifecycle and latest-release lookups (`0` disables caching) |
95
+ | `REQUEST_TIMEOUT` | `30` | Upstream request timeout in seconds |
96
+ | `LISTEN_HOST` | `0.0.0.0` | Listen address |
97
+ | `LISTEN_PORT` | `5000` | Listen port |
98
+
99
+ ## Quick Start
100
+
101
+ ### Container
102
+
103
+ ```bash
104
+ docker run --rm -p 5000:5000 \
105
+ -e HTTPS_PROXY=http://proxy.example.com:3128 \
106
+ ghcr.io/slauger/openshift-update-proxy:latest
107
+ ```
108
+
109
+ The image is based on `registry.access.redhat.com/ubi9/python-314`, runs as UID `1001`
110
+ and is built from the `Containerfile` in this repository.
111
+
112
+ ### Helm
113
+
114
+ The chart is published as an OCI artifact to ghcr.io on every release:
115
+
116
+ ```bash
117
+ helm install update-proxy oci://ghcr.io/slauger/charts/openshift-update-proxy \
118
+ --set env[0].name=HTTPS_PROXY,env[0].value=http://proxy.example.com:3128
119
+ ```
120
+
121
+ Or from a git checkout: `helm install update-proxy ./chart`
122
+
123
+ ### PyPI
124
+
125
+ ```bash
126
+ python3 -m venv .venv && source .venv/bin/activate
127
+ pip install openshift-update-proxy
128
+ openshift-update-proxy
129
+ ```
130
+
131
+ ## Cluster integration
132
+
133
+ ### Update graph
134
+
135
+ Point the ClusterVersion `upstream` at the proxy:
136
+
137
+ ```yaml
138
+ apiVersion: config.openshift.io/v1
139
+ kind: ClusterVersion
140
+ metadata:
141
+ name: version
142
+ spec:
143
+ upstream: http://update-proxy.example.com:5000/api/upgrades_info/v1/graph
144
+ ```
145
+
146
+ ### Release signatures
147
+
148
+ For updates by digest (`oc adm upgrade --to-image ...@sha256:...`) the CVO must verify the
149
+ release image signature.
150
+
151
+ The `/configmaps/` endpoint fetches all signatures for a release and renders a
152
+ ready-to-apply ConfigMap (same format as `oc adm release mirror` / oc-mirror produces).
153
+ It accepts a release version directly - the digest is resolved via the update graph:
154
+
155
+ ```bash
156
+ curl -s "http://update-proxy.example.com:5000/configmaps/4.16.8" | oc apply -f -
157
+ ```
158
+
159
+ The optional `arch` (default `amd64`) and `channel_prefix` (default `stable`) query
160
+ parameters select the architecture and the update channel used for the lookup, e.g.
161
+ `/configmaps/4.16.8?arch=arm64`.
162
+
163
+ Alternatively, a release digest can be passed directly:
164
+
165
+ ```bash
166
+ DIGEST=$(oc adm release info quay.io/openshift-release-dev/ocp-release:4.16.8-x86_64 -o jsonpath='{.digest}')
167
+ curl -s "http://update-proxy.example.com:5000/configmaps/${DIGEST/:/=}" | oc apply -f -
168
+ ```
169
+
170
+ The ConfigMap is created in `openshift-config-managed` with the
171
+ `release.openshift.io/verification-signatures` label, where the CVO picks it up.
172
+
173
+ > **Note:** The ClusterVersion API also has a `spec.signatureStores` field, but it is
174
+ > gated behind the TechPreview-only `SignatureStores` feature gate and will not be
175
+ > promoted to GA ([OTA-1118](https://issues.redhat.com/browse/OTA-1118)). The ConfigMap
176
+ > above is the supported way to provide signatures.
177
+
178
+ ## Operator catalog and Renovate
179
+
180
+ The `/operators/v1/` endpoints answer "which operator versions exist in which
181
+ channel?" without pulling the multi-hundred-MB catalog index images and without any
182
+ registry credentials. The data comes from the public
183
+ [Red Hat Pyxis API](https://catalog.redhat.com/api/containers/docs/) and covers all
184
+ four default catalogs: `redhat-operators`, `certified-operators`,
185
+ `community-operators` and `redhat-marketplace`.
186
+
187
+ List channels, default channel and the latest CSV per channel:
188
+
189
+ ```bash
190
+ curl -s "http://update-proxy.example.com:5000/operators/v1/redhat-operators/openshift-gitops-operator/channels?ocp_version=4.16"
191
+ ```
192
+
193
+ ```json
194
+ {
195
+ "package": "openshift-gitops-operator",
196
+ "organization": "redhat-operators",
197
+ "default_channel": "latest",
198
+ "channels": [
199
+ {"name": "gitops-1.21", "latest_version": "1.21.4", "latest_csv": "openshift-gitops-operator.v1.21.4"}
200
+ ]
201
+ }
202
+ ```
203
+
204
+ List all versions of a channel in the format Renovate expects from a
205
+ [custom datasource](https://docs.renovatebot.com/modules/datasource/custom/):
206
+
207
+ ```bash
208
+ curl -s "http://update-proxy.example.com:5000/operators/v1/redhat-operators/openshift-gitops-operator/gitops-1.21/releases?ocp_version=4.16"
209
+ ```
210
+
211
+ ```json
212
+ {
213
+ "releases": [
214
+ {"version": "1.21.3", "releaseTimestamp": "2026-08-14T23:45:28.177000+00:00"},
215
+ {"version": "1.21.4", "releaseTimestamp": "2026-09-03T12:13:37.391000+00:00"}
216
+ ]
217
+ }
218
+ ```
219
+
220
+ The optional `ocp_version` query parameter limits results to bundles shipped in the
221
+ catalog for that OpenShift minor version. Responses are cached in memory for
222
+ `CATALOG_CACHE_TTL` seconds. The raw Pyxis API is available under `/catalog/`, e.g.
223
+ `/catalog/operators/indices?filter=organization==redhat-operators` lists all index
224
+ image tags with their end-of-life dates.
225
+
226
+ With this feed, Renovate can bump pinned operator versions (`startingCSV` in OLM
227
+ `Subscription` manifests, whether managed directly via Argo CD or embedded in ACM
228
+ policies) just like any other dependency - merging the PR rolls out the operator
229
+ update. See [examples/renovate/](examples/renovate/) for a complete working setup:
230
+ a `renovate.json` with the custom datasource and regex manager, plus matching
231
+ Subscription and ACM Policy manifests.
232
+
233
+ ## Supported OpenShift versions
234
+
235
+ `/versions/v1/supported` combines the
236
+ [Red Hat product lifecycle API](https://access.redhat.com/product-life-cycles) with
237
+ the Cincinnati update graph: all OpenShift minor versions that are not end-of-life,
238
+ together with the latest release in the corresponding update channel.
239
+
240
+ ```bash
241
+ curl -s "http://update-proxy.example.com:5000/versions/v1/supported"
242
+ ```
243
+
244
+ ```json
245
+ {
246
+ "product": "OpenShift Container Platform",
247
+ "architecture": "amd64",
248
+ "versions": [
249
+ {"version": "4.22", "support_phase": "Full Support", "channel": "stable-4.22", "latest_release": "4.22.11"},
250
+ {"version": "4.20", "support_phase": "Maintenance Support", "channel": "stable-4.20", "latest_release": "4.20.35"}
251
+ ]
252
+ }
253
+ ```
254
+
255
+ The optional `channel_prefix` (default `stable`, e.g. `eus`, `fast`, `candidate`) and
256
+ `arch` (default `amd64`) query parameters select the channel and architecture;
257
+ `latest_release` is `null` when the channel has no published releases yet. The raw
258
+ lifecycle API is available under `/lifecycle/`, e.g.
259
+ `/lifecycle/products?name=OpenShift Container Platform`.
260
+
261
+ [examples/create-configmaps.sh](examples/create-configmaps.sh) combines this with
262
+ the `/configmaps/` endpoint: it fetches the release signatures for a set of update
263
+ channels through the proxy and applies them as ConfigMaps.
264
+
265
+ ## Local Development
266
+
267
+ ```bash
268
+ python3 -m venv .venv
269
+ source .venv/bin/activate
270
+ pip install -e ".[dev]"
271
+ openshift-update-proxy
272
+ ```
273
+
274
+ Run tests and linting:
275
+
276
+ ```bash
277
+ make test
278
+ make lint
279
+ ```
280
+
281
+ Build the container image:
282
+
283
+ ```bash
284
+ make build
285
+ ```
286
+
287
+ ## Supply Chain Security
288
+
289
+ - The UBI9 base image is pinned by digest and kept up to date by
290
+ [Renovate](https://docs.renovatebot.com/); remaining CVEs are patched at build time via
291
+ `dnf upgrade`.
292
+ - Python and GitHub Actions dependencies are also managed by Renovate (with automerge for
293
+ non-major updates).
294
+ - Releases are fully automated with
295
+ [python-semantic-release](https://python-semantic-release.readthedocs.io/) based on
296
+ Conventional Commits and published to PyPI.
297
+ - Container images are signed with [Cosign](https://github.com/sigstore/cosign) (keyless,
298
+ GitHub Actions OIDC). Verify with:
299
+
300
+ ```bash
301
+ cosign verify \
302
+ --certificate-identity-regexp 'https://github.com/slauger/openshift-update-proxy/.*' \
303
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com \
304
+ ghcr.io/slauger/openshift-update-proxy:latest
305
+ ```
306
+
307
+ ## License
308
+
309
+ [Apache License 2.0](LICENSE)
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "openshift-update-proxy"
7
- version = "1.0.2"
7
+ version = "1.2.0"
8
8
  description = "Forwarding proxy for OpenShift update resources (Cincinnati API, mirror, release signatures)"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """OpenShift update proxy for disconnected environments."""
2
2
 
3
- __version__ = "1.0.2"
3
+ __version__ = "1.2.0"