Skip to content

Commit 6580860

Browse files
committed
WIP: Document how to change provider registries
Signed-off-by: Nic Cope <[email protected]>
1 parent 3555176 commit 6580860

File tree

2 files changed

+404
-0
lines changed

2 files changed

+404
-0
lines changed
Lines changed: 396 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,396 @@
1+
---
2+
title: Change Provider Registries
3+
weight: 415
4+
description: "Safely change from one provider to a compatible provider in a different registry"
5+
---
6+
7+
When changing a provider's OCI reference to a different registry, the provider
8+
itself updates normally with a new revision. However, any **dependencies that
9+
Crossplane automatically installs** (like family providers) are treated as
10+
entirely separate packages, requiring careful coordination.
11+
12+
This problem affects:
13+
14+
* **Providers with family dependencies**: Most cloud providers depend on a
15+
family provider (like `provider-gcp-compute` depending on `provider-family-gcp`)
16+
* **Providers installed as Configuration dependencies**: When a Configuration
17+
package lists providers as dependencies
18+
19+
**Single providers without dependencies change automatically** - no manual
20+
intervention needed.
21+
22+
The most common conflict occurs with **family provider dependencies**. For
23+
example, when changing `provider-gcp-compute` from Upbound to Crossplane
24+
Contrib, both versions depend on different family providers:
25+
26+
* Old: `xpkg.upbound.io/upbound/provider-gcp-compute` → depends on `upbound/provider-family-gcp`
27+
* New: `xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute` → depends on `crossplane-contrib/provider-family-gcp`
28+
29+
This creates two active family providers competing for the same resources,
30+
causing errors like:
31+
32+
```console
33+
cannot establish control of object: providerconfigusages.gcp.upbound.io is already controlled by ProviderRevision upbound-provider-family-gcp-f0aa3640a6a9
34+
```
35+
36+
## Prerequisites
37+
38+
Before changing providers, ensure:
39+
40+
* Crossplane v2.0 or later
41+
* `kubectl` access to your cluster
42+
* Understanding of your current provider setup
43+
* Time for testing and validation
44+
* Backup of critical resources
45+
46+
{{<hint "tip">}}
47+
Test this change in a staging environment first. The process is safe but
48+
requires careful verification at each step.
49+
{{</hint>}}
50+
51+
## Why manual coordination is recommended
52+
53+
Crossplane names package dependencies using OCI repository paths. It uses the
54+
pattern `<org>-<repo>`. When you change from `upbound/provider-gcp-compute` to
55+
`crossplane-contrib/provider-gcp-compute`, Crossplane sees these as completely
56+
different providers and creates both:
57+
58+
* `upbound-provider-family-gcp` (old, still active)
59+
* `crossplane-contrib-provider-family-gcp` (new, requires ownership coordination)
60+
61+
Both family providers manage the same CRDs and MRDs, requiring careful
62+
coordination of ownership transfer.
63+
64+
Crossplane takes this conservative approach because determining package
65+
equivalence is complex. For example, an older Upbound provider version might
66+
differ significantly from a newer Crossplane Contrib version - with additional
67+
CRDs, different controller behavior, or updated APIs. Automatically replacing
68+
dependencies could break existing resources or introduce unexpected behavior.
69+
70+
Manual coordination gives you explicit control to safely transition ownership
71+
from old to new providers with full visibility into each step.
72+
73+
## Process overview
74+
75+
The manual process follows this safe sequence:
76+
77+
1. **Update provider package** - Change the provider's OCI reference
78+
2. **Identify conflicts** - Find which family provider is conflicted
79+
3. **Set manual activation** - Prevent the old family provider from staying active
80+
4. **Deactivate old revision** - Stop the old family provider from managing resources
81+
5. **Verify transition** - Ensure the new family provider manages all resources
82+
6. **Clean up** - Remove the old family provider
83+
84+
This deliberate approach gives you full control over the timing and
85+
validation, ensures no resources are orphaned, and provides clear rollback
86+
points.
87+
88+
## Step-by-step process
89+
90+
This example changes from Upbound's GCP Compute provider to the Crossplane
91+
community version, which demonstrates the family provider dependency issue:
92+
93+
* **From**: `xpkg.upbound.io/upbound/provider-gcp-compute:v1.14.1`
94+
* **To**: `xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v2.0.0`
95+
96+
This change requires coordinating multiple package dependencies because each
97+
version depends on a different family provider.
98+
99+
### Step 1: Inventory current providers
100+
101+
Check your current provider setup:
102+
103+
```shell
104+
kubectl get providers
105+
```
106+
107+
```console
108+
NAME INSTALLED HEALTHY PACKAGE AGE
109+
upbound-provider-family-gcp True True xpkg.upbound.io/upbound/provider-family-gcp:v1.14.1 30d
110+
provider-gcp-compute True True xpkg.upbound.io/upbound/provider-gcp-compute:v1.14.1 30d
111+
```
112+
113+
List managed resources to understand what the provider manages:
114+
115+
```shell
116+
kubectl get managed
117+
```
118+
119+
```console
120+
NAME READY SYNCED EXTERNAL-NAME AGE
121+
address.compute.gcp.upbound.io/my-address True True my-address-abc123 5d
122+
```
123+
124+
### Step 2: Update the provider package
125+
126+
Edit the existing provider to change its package reference:
127+
128+
```shell
129+
kubectl patch provider provider-gcp-compute --type=merge \
130+
-p='{"spec":{"package":"xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v2.0.0"}}'
131+
```
132+
133+
This creates a new family provider dependency. Check what providers exist:
134+
135+
```shell
136+
kubectl get providers
137+
```
138+
139+
```console
140+
NAME INSTALLED HEALTHY PACKAGE AGE
141+
crossplane-contrib-provider-family-gcp True False xpkg.crossplane.io/crossplane-contrib/provider-family-gcp:v2.0.0 2m
142+
upbound-provider-family-gcp True True xpkg.upbound.io/upbound/provider-family-gcp:v1.14.1 30d
143+
provider-gcp-compute True True xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v2.0.0 2m
144+
```
145+
146+
Notice the new family provider is `HEALTHY=False` due to the ownership conflict.
147+
148+
### Step 3: Set old family provider to manual activation
149+
150+
Prevent the old family provider from automatically activating its revisions:
151+
152+
```shell
153+
kubectl patch provider upbound-provider-family-gcp --type=merge \
154+
-p='{"spec":{"revisionActivationPolicy":"Manual"}}'
155+
```
156+
157+
Verify the change:
158+
159+
```shell
160+
kubectl get provider upbound-provider-family-gcp -o yaml | grep revisionActivationPolicy
161+
```
162+
163+
```console
164+
revisionActivationPolicy: Manual
165+
```
166+
167+
### Step 4: Deactivate old family provider revision
168+
169+
Find the current revision of the old family provider:
170+
171+
```shell
172+
kubectl get providerrevisions | grep upbound-provider-family-gcp
173+
```
174+
175+
```console
176+
NAME HEALTHY REVISION IMAGE STATE DEP-FOUND DEP-INSTALLED AGE
177+
upbound-provider-family-gcp-f0aa3640a6a9 True 1 xpkg.upbound.io/upbound/provider-family-gcp:v1.14.1 Active 0 0 30d
178+
```
179+
180+
Set the old family provider revision to inactive:
181+
182+
```shell
183+
kubectl patch providerrevision upbound-provider-family-gcp-f0aa3640a6a9 --type=merge \
184+
-p='{"spec":{"desiredState":"Inactive"}}'
185+
```
186+
187+
### Step 5: Verify the new family provider becomes healthy
188+
189+
Check that the new family provider can now establish control:
190+
191+
```shell
192+
kubectl get providers
193+
```
194+
195+
```console
196+
NAME INSTALLED HEALTHY PACKAGE AGE
197+
crossplane-contrib-provider-family-gcp True True xpkg.crossplane.io/crossplane-contrib/provider-family-gcp:v2.0.0 5m
198+
upbound-provider-family-gcp True True xpkg.upbound.io/upbound/provider-family-gcp:v1.14.1 30d
199+
provider-gcp-compute True True xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v2.0.0 5m
200+
```
201+
202+
Verify the old family provider revision is inactive:
203+
204+
```shell
205+
kubectl get providerrevisions | grep upbound-provider-family-gcp
206+
```
207+
208+
```console
209+
NAME HEALTHY REVISION IMAGE STATE DEP-FOUND DEP-INSTALLED AGE
210+
upbound-provider-family-gcp-f0aa3640a6a9 True 1 xpkg.upbound.io/upbound/provider-family-gcp:v1.14.1 Inactive 0 0 30d
211+
```
212+
213+
Check that managed resources are still healthy:
214+
215+
```shell
216+
kubectl get managed
217+
```
218+
219+
```console
220+
NAME READY SYNCED EXTERNAL-NAME AGE
221+
address.compute.gcp.upbound.io/my-address True True my-address-abc123 5d
222+
```
223+
224+
### Step 6: Delete the old family provider
225+
226+
After verifying everything works correctly, remove the old family provider:
227+
228+
```shell
229+
kubectl delete provider upbound-provider-family-gcp
230+
```
231+
232+
### Step 7: Final verification
233+
234+
Confirm only the new providers remain:
235+
236+
```shell
237+
kubectl get providers
238+
```
239+
240+
```console
241+
NAME INSTALLED HEALTHY PACKAGE AGE
242+
crossplane-contrib-provider-family-gcp True True xpkg.crossplane.io/crossplane-contrib/provider-family-gcp:v2.0.0 10m
243+
provider-gcp-compute True True xpkg.crossplane.io/crossplane-contrib/provider-gcp-compute:v2.0.0 10m
244+
```
245+
246+
Verify managed resources are still healthy:
247+
248+
```shell
249+
kubectl get managed
250+
```
251+
252+
```console
253+
NAME READY SYNCED EXTERNAL-NAME AGE
254+
address.compute.gcp.upbound.io/my-address True True my-address-abc123 5d
255+
```
256+
257+
## Other common scenarios
258+
259+
### Providers without dependencies
260+
261+
For providers that don't depend on other packages (like `provider-helm`,
262+
`provider-kubernetes`, or standalone providers), the change happens automatically:
263+
264+
1. Update the provider's `spec.package`
265+
2. Crossplane creates a new revision with the new package
266+
3. The old revision automatically becomes inactive
267+
4. No conflicts occur because there are no dependencies
268+
269+
Examples of providers that typically change automatically:
270+
* `provider-helm`
271+
* `provider-kubernetes`
272+
* `provider-sql`
273+
* Single-resource providers without family dependencies
274+
275+
{{<hint "tip">}}
276+
You can check which providers have dependencies by inspecting the package lock:
277+
278+
```shell
279+
kubectl get lock lock -o yaml
280+
```
281+
282+
Look for providers with non-empty `dependencies` arrays. For example:
283+
```yaml
284+
- name: provider-gcp-compute-a41e4ba551fc
285+
dependencies:
286+
- constraints: '>= 0.0.0'
287+
package: xpkg.crossplane.io/crossplane-contrib/provider-family-gcp
288+
type: Provider
289+
```
290+
291+
Providers with empty `dependencies: []` change automatically.
292+
{{</hint>}}
293+
294+
### Configuration package dependencies
295+
296+
When changing providers that are dependencies of Configuration packages, the
297+
same conflicts can occur. The Configuration creates provider dependencies based
298+
on OCI references, so changing registries creates duplicate providers.
299+
300+
To change providers in this scenario:
301+
1. Update the Configuration's package OCI reference
302+
2. Follow the same manual coordination steps for any conflicted providers
303+
3. Verify all providers the Configuration depends on are healthy
304+
305+
### Rollback procedure
306+
307+
If you need to rollback during the process:
308+
309+
1. **Before deleting the old provider (step 6)**: Reactivate the old family
310+
provider revision:
311+
```shell
312+
kubectl patch providerrevision upbound-provider-family-gcp-f0aa3640a6a9 --type=merge \
313+
-p='{"spec":{"desiredState":"Active"}}'
314+
```
315+
316+
Then deactivate the new family provider revision and delete the new provider.
317+
318+
2. **After deleting the old provider**: You must recreate the old provider
319+
because Crossplane automatically deletes all revisions when you delete a
320+
provider. Create this manifest:
321+
322+
```yaml
323+
apiVersion: pkg.crossplane.io/v1
324+
kind: Provider
325+
metadata:
326+
name: upbound-provider-family-gcp
327+
spec:
328+
package: xpkg.upbound.io/upbound/provider-family-gcp:v1.14.1
329+
```
330+
331+
Save as `rollback-provider.yaml` and apply:
332+
```shell
333+
kubectl apply -f rollback-provider.yaml
334+
```
335+
336+
Then follow the process in reverse to switch back.
337+
338+
## Troubleshooting
339+
340+
### New family provider stays unhealthy
341+
342+
If the new family provider remains `HEALTHY=False` after deactivating the old one:
343+
344+
1. Check the provider revision status:
345+
```shell
346+
kubectl get providerrevisions | grep crossplane-contrib-provider-family
347+
```
348+
349+
2. Look for ownership conflict errors in the provider logs:
350+
```shell
351+
kubectl logs -n crossplane-system -l pkg.crossplane.io/provider=crossplane-contrib-provider-family-gcp
352+
```
353+
354+
3. Verify the old revision is truly inactive:
355+
```shell
356+
kubectl describe providerrevision upbound-provider-family-gcp-f0aa3640a6a9
357+
```
358+
359+
### CRD ownership conflicts persist
360+
361+
If you see persistent ownership conflicts:
362+
363+
```console
364+
cannot establish control of object: providerconfigusages.gcp.upbound.io is already controlled by ProviderRevision upbound-provider-family-gcp-f0aa3640a6a9
365+
```
366+
367+
This usually means the old revision is still active. Double-check:
368+
369+
1. The old provider has `revisionActivationPolicy: Manual`
370+
2. The old revision has `desiredState: Inactive`
371+
3. Wait a few minutes for the change to propagate
372+
373+
### Provider won't deactivate
374+
375+
If the old provider revision won't become inactive:
376+
377+
1. Check for active managed resources preventing deactivation
378+
2. Verify the patch command succeeded
379+
3. Check provider logs for errors:
380+
```shell
381+
kubectl logs -n crossplane-system -l pkg.crossplane.io/provider=upbound-provider-family-gcp
382+
```
383+
384+
## Next steps
385+
386+
After successfully changing providers:
387+
388+
* Update any documentation referencing the old provider
389+
* Consider changing to v2 namespaced resources if using Crossplane v2
390+
* Review other providers for potential registry changes
391+
* Share your experience with the Crossplane community
392+
393+
For more information:
394+
* [Provider documentation]({{<ref "../packages/providers">}})
395+
* [Troubleshooting guide]({{<ref "troubleshoot-crossplane">}})
396+
* [Crossplane community](https://crossplane.io/community/)

0 commit comments

Comments
 (0)