Skip to content

Commit 1f0c8b2

Browse files
docs: Add how-to for migrating fallback resolvers to path matcher resolvers (#748)
#### What this PR does / why we need it Adds a new how-to guide that walks users through migrating their `.ocmconfig` from the deprecated `ocm.config.ocm.software` fallback resolvers to the new `resolvers.config.ocm.software/v1alpha1` path matcher resolvers. The guide covers: - Step-by-step config migration (`prefix` → `componentNamePattern`, removing `priority`, changing the config type) - A "When You Cannot Migrate Yet" section with mermaid diagrams showing the behavioral difference (probe-and-retry vs first-match-wins) - A concrete example of a registry migration scenario where fallback resolvers are still needed - A key differences comparison table #### Which issue(s) this PR is related to Related to open-component-model/ocm-project#927 #### Type of content - [ ] Tutorial (`getting-started/` or `tutorials/`) - [x] How-to Guide (`how-to/`) - [ ] Explanation / Concept (`concepts/`) - [ ] Reference (`reference/`) - [ ] Other (infrastructure, config, fixes) #### Checklist - [x] I have read and followed the [Contributing Guide](https://github.com/open-component-model/ocm-website/blob/main/CONTRIBUTING.md) - [x] All commands/code snippets are tested and can be copy-pasted #### Testing Fully tested with ##### .ocmconfig-legacy ```yaml type: generic.config.ocm.software/v1 configurations: - type: credentials.config.ocm.software repositories: - repository: type: DockerConfig/v1 dockerConfigFile: "~/.docker/config.json" - type: ocm.config.ocm.software resolvers: - repository: type: OCIRepository/v1 baseUrl: ghcr.io subPath: matthiasbruns/ocm-tutorial-migrate-a prefix: ocm.software/tutorials/backend priority: 10 - repository: type: OCIRepository/v1 baseUrl: ghcr.io subPath: matthiasbruns/ocm-tutorial-migrate-b prefix: ocm.software/tutorials/frontend priority: 10 ``` ##### .ocmconfig-migrated ```yaml type: generic.config.ocm.software/v1 configurations: - type: credentials.config.ocm.software repositories: - repository: type: DockerConfig/v1 dockerConfigFile: "~/.docker/config.json" - type: resolvers.config.ocm.software/v1alpha1 resolvers: - repository: type: OCIRepository/v1 baseUrl: ghcr.io subPath: matthiasbruns/ocm-tutorial-migrate-a componentNamePattern: "ocm.software/tutorials/backend" - repository: type: OCIRepository/v1 baseUrl: ghcr.io subPath: matthiasbruns/ocm-tutorial-migrate-b componentNamePattern: "ocm.software/tutorials/frontend" ``` ##### app-constructor.yam ```yaml components: - name: ocm.software/tutorials/app version: "1.0.0" provider: name: ocm.software componentReferences: - name: backend-service componentName: ocm.software/tutorials/backend version: "1.0.0" - name: frontend-service componentName: ocm.software/tutorials/frontend version: "1.0.0" resources: - name: config version: "1.0.0" type: plainText input: type: utf8 text: "app deployment configuration" ``` ##### backend-constructor.yaml ```yaml components: - name: ocm.software/tutorials/backend version: "1.0.0" provider: name: ocm.software resources: - name: config version: "1.0.0" type: plainText input: type: utf8 text: "backend service configuration" ``` ##### frontend-constructor.yaml ```yaml components: - name: ocm.software/tutorials/frontend version: "1.0.0" provider: name: ocm.software resources: - name: config version: "1.0.0" type: plainText input: type: utf8 text: "frontend service configuration" ``` ##### test-migrate-resolvers.sh ```bash #!/bin/bash set -x echo $GITHUB_TOKEN | docker login ghcr.io -u ${GITHUB_USERNAME} --password-stdin # Step 1: Push backend and frontend components (no resolvers needed for leaf components) ./ocm add cv --repository ghcr.io/matthiasbruns/ocm-tutorial-migrate-a \ --constructor backend-constructor.yaml \ --component-version-conflict-policy replace \ --config ./ocm add cv --repository ghcr.io/matthiasbruns/ocm-tutorial-migrate-b \ --constructor frontend-constructor.yaml \ --component-version-conflict-policy replace \ --config # Step 2: Push app component using the LEGACY fallback config # This should work but emit a deprecation warning about fallback resolvers ./ocm add cv --repository ghcr.io/matthiasbruns/ocm-tutorial-migrate-app \ --constructor app-constructor.yaml \ --component-version-conflict-policy replace \ --config .ocmconfig-legacy # Step 3: Verify recursive resolution works with the legacy config ./ocm get cv ghcr.io/matthiasbruns/ocm-tutorial-migrate-app//ocm.software/tutorials/app:1.0.0 \ --config .ocmconfig-legacy --recursive=-1 # Step 4: Verify recursive resolution works with the MIGRATED config ./ocm get cv ghcr.io/matthiasbruns/ocm-tutorial-migrate-app//ocm.software/tutorials/app:1.0.0 \ --config .ocmconfig-migrated --recursive=-1 # Step 5: Push app component again using the migrated config (no deprecation warning) ./ocm add cv --repository ghcr.io/matthiasbruns/ocm-tutorial-migrate-app \ --constructor app-constructor.yaml \ --component-version-conflict-policy replace \ --config .ocmconfig-migrated # Step 6: Final verification with migrated config ./ocm get cv ghcr.io/matthiasbruns/ocm-tutorial-migrate-app//ocm.software/tutorials/app:1.0.0 \ --config .ocmconfig-migrated --recursive=-1 ``` <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Documentation** * Added a new migration guide with step‑by‑step instructions, examples, verification steps, troubleshooting notes, coexistence guidance, and a “when you cannot migrate yet” section. * Updated resolver-related docs, tutorials, and tips with links and a callout directing readers to the new migration guide. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Signed-off-by: Matthias Bruns <git@matthiasbruns.com> Signed-off-by: Matthias Bruns <github@matthiasbruns.com> Co-authored-by: Jakob Möller <contact@jakob-moeller.com>
1 parent e99ab07 commit 1f0c8b2

File tree

5 files changed

+274
-0
lines changed

5 files changed

+274
-0
lines changed

content/docs/concepts/resolvers.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,5 @@ For more information about OCM transfer, see the [Transfer and Transport]({{< re
7676
- [Understand Credential Resolution]({{< relref "docs/tutorials/credential-resolution.md" >}}) — Configure credentials for OCI registries
7777
- [How to Transfer Components Across an Air Gap]({{< relref "docs/how-to/air-gap-transfer.md" >}}) — Use OCM Transfer to move components between
7878
air-gapped environments
79+
- [Migrate from Deprecated Resolvers]({{< relref "docs/how-to/migrate-from-deprecated-resolvers.md" >}}) — Replace deprecated fallback
80+
resolvers with glob-based resolvers
Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
---
2+
title: "Migrate from Fallback to Deterministic Repository Resolvers"
3+
description: "Replace deprecated fallback resolvers with glob-based resolvers for deterministic and efficient component resolution."
4+
weight: 100
5+
toc: true
6+
---
7+
8+
## Goal
9+
10+
Replace the deprecated `ocm.config.ocm.software` fallback resolver configuration with the new `resolvers.config.ocm.software/v1alpha1` glob-based
11+
resolvers.
12+
13+
{{< callout context="caution" >}}
14+
The fallback resolver (`ocm.config.ocm.software`) is deprecated. It uses priority-based ordering with prefix matching and probes multiple
15+
repositories until one succeeds. The glob-based resolver (`resolvers.config.ocm.software/v1alpha1`) replaces it with first-match glob-based matching
16+
against component names, which is simpler and more efficient.
17+
{{< /callout >}}
18+
19+
## Why Migrate?
20+
21+
- The fallback resolver (`ocm.config.ocm.software`) is **deprecated** and will be removed in a future release.
22+
- The fallback resolver probes multiple repositories on every lookup, which adds latency and makes it harder to reason about which repository is used.
23+
- Glob-based resolvers use first-match semantics — the outcome is determined by list order alone, making configurations simpler to understand and debug.
24+
25+
## Prerequisites
26+
27+
- [OCM CLI]({{< relref "/docs/getting-started/ocm-cli-installation.md" >}}) installed
28+
- An existing `.ocmconfig` file that uses `ocm.config.ocm.software` resolver entries
29+
30+
## Steps
31+
32+
Suppose you have the following legacy resolver config in `~/.ocmconfig`:
33+
34+
```yaml
35+
type: generic.config.ocm.software/v1
36+
configurations:
37+
- type: ocm.config.ocm.software
38+
resolvers:
39+
- repository:
40+
type: OCIRepository/v1
41+
baseUrl: ghcr.io
42+
subPath: my-org/team-a
43+
prefix: my-org.example/services
44+
priority: 10
45+
- repository:
46+
type: OCIRepository/v1
47+
baseUrl: ghcr.io
48+
subPath: my-org/team-b
49+
prefix: my-org.example/libraries
50+
priority: 10
51+
- repository:
52+
type: CommonTransportFormat/v1
53+
filePath: ./local-archive
54+
priority: 1
55+
```
56+
57+
The following steps walk you through each change needed to migrate to glob-based resolvers.
58+
59+
{{< steps >}}
60+
61+
{{< step >}}
62+
**Change the config type from `ocm.config.ocm.software` to `resolvers.config.ocm.software/v1alpha1`**
63+
64+
Replace the configuration type:
65+
66+
```yaml
67+
configurations:
68+
- type: resolvers.config.ocm.software/v1alpha1 # was: ocm.config.ocm.software
69+
resolvers:
70+
...
71+
```
72+
73+
{{< /step >}}
74+
75+
{{< step >}}
76+
**Replace `prefix` with `componentNamePattern`**
77+
78+
The fallback resolver uses `prefix` to match component names by string prefix. The glob-based resolver uses `componentNamePattern`,
79+
which supports [glob patterns]({{< relref "docs/reference/resolver-configuration.md#component-name-patterns" >}}).
80+
In most cases, appending `/*` to the old prefix is the closest equivalent. Note that the old `prefix` also matched the bare prefix itself as an exact
81+
component name (e.g. `prefix: my-org.example/services` matched both `my-org.example/services` and `my-org.example/services/foo`). If you have
82+
components that match the bare prefix, use `{,/*}` instead:
83+
84+
```yaml
85+
resolvers:
86+
- repository:
87+
type: OCIRepository/v1
88+
baseUrl: ghcr.io
89+
subPath: my-org/team-a
90+
# matches my-org.example/services and my-org.example/services/*
91+
componentNamePattern: "my-org.example/services{,/*}" # was: prefix: my-org.example/services
92+
```
93+
94+
If no component uses the bare prefix as its name (which is the common case), `/*` is sufficient:
95+
96+
```yaml
97+
componentNamePattern: "my-org.example/services/*" # was: prefix: my-org.example/services
98+
```
99+
100+
If a resolver had an empty prefix (matching all components), use `*` as the pattern:
101+
102+
```yaml
103+
componentNamePattern: "*" # was: prefix: "" (or no prefix)
104+
```
105+
106+
{{< /step >}}
107+
108+
{{< step >}}
109+
**Remove the `priority` field**
110+
111+
Glob-based resolvers do not use priorities. Instead, resolvers are evaluated in the order they appear in the list, and the **first match wins**.
112+
That's one of the key differences from the fallback resolver, which tries all matching resolvers in priority order until one succeeds.
113+
If your legacy resolvers had equal `priority` values, keep their original list order to preserve the same resolution behaviour (the fallback resolver uses a stable sort, so equal-priority entries were tried in insertion order).
114+
115+
For new configs, place more specific patterns before broader ones:
116+
117+
```yaml
118+
resolvers:
119+
# specific patterns first
120+
- repository:
121+
type: OCIRepository/v1
122+
baseUrl: ghcr.io
123+
subPath: my-org/team-a
124+
componentNamePattern: "my-org.example/services/*"
125+
# broader patterns last
126+
- repository:
127+
type: CommonTransportFormat/v1
128+
filePath: ./local-archive
129+
componentNamePattern: "*"
130+
```
131+
132+
{{< /step >}}
133+
134+
{{< step >}}
135+
**Review the final config**
136+
137+
Your migrated config should now look like this:
138+
139+
```yaml
140+
type: generic.config.ocm.software/v1
141+
configurations:
142+
- type: resolvers.config.ocm.software/v1alpha1
143+
resolvers:
144+
- repository:
145+
type: OCIRepository/v1
146+
baseUrl: ghcr.io
147+
subPath: my-org/team-a
148+
componentNamePattern: "my-org.example/services/*"
149+
- repository:
150+
type: OCIRepository/v1
151+
baseUrl: ghcr.io
152+
subPath: my-org/team-b
153+
componentNamePattern: "my-org.example/libraries/*"
154+
- repository:
155+
type: CommonTransportFormat/v1
156+
filePath: ./local-archive
157+
componentNamePattern: "*"
158+
```
159+
160+
{{< /step >}}
161+
162+
{{< step >}}
163+
**Verify**
164+
165+
Run any OCM command that resolves components:
166+
167+
```bash
168+
ocm get cv ghcr.io/my-org/team-a//my-org.example/services/my-service:1.0.0 \
169+
--recursive=-1 --config .ocmconfig
170+
```
171+
172+
If you still see the warning `using deprecated fallback resolvers, consider switching to glob-based resolvers`, check that you removed all
173+
`ocm.config.ocm.software` configuration blocks.
174+
Both resolver types can coexist in the same config file during migration — the fallback resolvers will still work but will emit the deprecation
175+
warning.
176+
177+
{{< /step >}}
178+
179+
{{< /steps >}}
180+
181+
## Key Differences
182+
183+
| | Fallback (`ocm.config.ocm.software`) | Glob-based (`resolvers.config.ocm.software/v1alpha1`) |
184+
|----------------------|-------------------------------------------------------------------|-------------------------------------------------------|
185+
| **Matching** | String prefix on component name | Glob pattern (`*`, `?`, `[...]`) on component name |
186+
| **Resolution order** | Priority-based (highest first), then fallback through all matches | First match wins (list order) |
187+
| **Get behaviour** | Tries all matching repos until one succeeds | Returns the first matching repo deterministically |
188+
| **Add behaviour** | Adds to the first matching repo by priority | Adds to the first matching repo by list order |
189+
| **Status** | Deprecated | Active |
190+
191+
## When You Cannot Migrate Yet
192+
193+
The fallback resolver has a **probe-and-retry** behaviour that the glob-based resolver does not replicate.
194+
195+
Consider a registry migration where the same component has versions spread across multiple repositories:
196+
197+
| Version | Repository |
198+
|-------------------------------------|--------------------------------|
199+
| `my-org.example/my-component:1.0.0` | `old-registry.example/legacy` |
200+
| `my-org.example/my-component:1.5.0` | `old-registry.example/legacy` |
201+
| `my-org.example/my-component:2.0.0` | `new-registry.example/current` |
202+
203+
{{< tabs >}}
204+
{{< tab "Fallback (works)" >}}
205+
206+
The fallback resolver matches repositories by **prefix** and tries them in **priority order**. If the requested version is not found in the
207+
highest-priority repository, it falls back to the next one until it finds a match or exhausts the list.
208+
209+
In this example, both resolvers share the prefix `my-org.example`. A request for `my-component:2.0.0` finds it in the new registry (priority 10). A
210+
request for `my-component:1.0.0` misses in the new registry, falls back to the old one (priority 1), and succeeds.
211+
212+
```yaml
213+
- type: ocm.config.ocm.software
214+
resolvers:
215+
- repository:
216+
type: OCIRepository/v1
217+
baseUrl: new-registry.example
218+
subPath: current
219+
prefix: my-org.example
220+
priority: 10
221+
- repository:
222+
type: OCIRepository/v1
223+
baseUrl: old-registry.example
224+
subPath: legacy
225+
prefix: my-org.example
226+
priority: 1
227+
```
228+
229+
{{< /tab >}}
230+
{{< tab "Glob-based (breaks)" >}}
231+
232+
The glob-based resolver matches the component name against patterns in list order and returns the **first match** — it does not probe the repository
233+
or retry with the next resolver if the version is missing.
234+
235+
In this example, `componentNamePattern: "my-org.example/*"` points to the new registry only. Requesting `my-component:2.0.0` succeeds, but requesting
236+
`my-component:1.0.0` fails because the old registry is not configured and there is no fallback.
237+
238+
```yaml
239+
- type: resolvers.config.ocm.software/v1alpha1
240+
resolvers:
241+
# Only new-registry is queried — old versions are unreachable
242+
- repository:
243+
type: OCIRepository/v1
244+
baseUrl: new-registry.example
245+
subPath: current
246+
componentNamePattern: "my-org.example/*"
247+
```
248+
249+
{{< /tab >}}
250+
{{< /tabs >}}
251+
252+
The same applies to **listing component versions**: the fallback resolver accumulates versions from all matching repositories, while the glob-based
253+
resolver only queries the first match.
254+
255+
If either case applies, consolidate all versions of the affected components into a single repository before migrating your resolver config. Version-based matching is being tracked as a future feature in [ocm-project#941](https://github.com/open-component-model/ocm-project/issues/941).
256+
257+
## What's Next?
258+
259+
- [How-To: Resolving Components Across Multiple Registries]({{< relref "resolve-components-from-multiple-repositories.md" >}}) — Configure resolver
260+
entries for multi-registry setups
261+
- [Resolver Configuration Reference]({{< relref "docs/reference/resolver-configuration.md" >}}) — Full configuration schema and pattern syntax
262+
- [Resolvers]({{< relref "docs/concepts/resolvers.md" >}}) — High-level introduction to resolvers

content/docs/how-to/resolve-components-from-multiple-repositories.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,10 @@ my-org.example/component-b │ 1.0.0 │
115115

116116
{{< /steps >}}
117117

118+
{{< callout context="tip" >}}
119+
If you are migrating from the deprecated `ocm.config.ocm.software` fallback resolvers, see [Migrate from Deprecated Resolvers]({{< relref "migrate-from-deprecated-resolvers.md" >}}) for a step-by-step guide.
120+
{{< /callout >}}
121+
118122
## Tips
119123

120124
- **If multiple components share a registry path**, use a glob pattern (e.g. `example.com/services/*`) instead of
@@ -133,3 +137,5 @@ my-org.example/component-b │ 1.0.0 │
133137
- [Working with Resolvers Tutorial]({{< relref "docs/tutorials/configure-resolvers.md" >}}) — Hands-on tutorial for
134138
building and pushing components with resolvers
135139
- [Understand Credential Resolution]({{< relref "docs/tutorials/credential-resolution.md" >}}) — Configure registry credentials
140+
- [Migrate from Deprecated Resolvers]({{< relref "migrate-from-deprecated-resolvers.md" >}}) — Replace deprecated fallback
141+
resolvers with glob-based resolvers

content/docs/reference/resolver-configuration.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,3 +124,5 @@ The first matching resolver wins. Place more specific patterns before broader on
124124
- [Working with Resolvers Tutorial]({{< relref "docs/tutorials/configure-resolvers.md" >}}) — Hands-on walkthrough for setting up resolvers
125125
- [How to Resolve Components Across Multiple Registries]({{< relref "docs/how-to/resolve-components-from-multiple-repositories.md" >}}) — Recipe for
126126
multi-registry resolution
127+
- [Migrate from Deprecated Resolvers]({{< relref "docs/how-to/migrate-from-deprecated-resolvers.md" >}}) — Replace deprecated fallback
128+
resolvers with glob-based resolvers

content/docs/tutorials/configure-resolvers.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,3 +336,5 @@ Now that you know how to configure resolvers, you can:
336336
and access types (by reference)
337337
- [Signing and Verification]({{< relref "signing-and-verification.md" >}}) — Sign and verify component versions with
338338
cryptographic keys
339+
- [Migrate from Deprecated Resolvers]({{< relref "docs/how-to/migrate-from-deprecated-resolvers.md" >}}) — Replace deprecated fallback
340+
resolvers with glob-based resolvers

0 commit comments

Comments
 (0)