À retenir
- provider-ovh 2.19.1 abandonne complètement la CLI Terraform. Les 168 ressources managées par scope d’API se réconcilient in-process, dans le contrôleur, sous forme d’appels de fonctions Go.
- L’image ne contient plus le binaire
terraform, ni le binaire natif du provider OVHcloud, nibash, ni une arborescence/terraformaccessible en écriture. Plus deterraform init, plus de workspace sur disque, plus d’egress vers un registry à l’exécution.- C’est un numéro de patch qui porte des ruptures de CRD. Lisez la section migration avant de mettre à jour : l’une d’elles rejettera des objets existants.
La version 2.19.1 de provider-ovh, notre provider Crossplane pour OVHcloud, est sortie le 3 septembre 2026. Elle suit le même provider Terraform OVHcloud 2.19.0 que la version précédente et porte le provider à 168 ressources managées par scope d’API et 341 CRD.
La surface amont n’a pas bougé. Ce qui a bougé, c’est la manière même dont le provider parle à OVHcloud — et c’est le plus gros changement d’architecture qu’ait connu le projet.
Ce que faisait réellement un provider Crossplane bâti sur upjet
upjet génère un provider Crossplane à partir d’un provider Terraform. Historiquement, il le faisait en exécutant Terraform. Pour chaque objet managé, à chaque réconciliation, le contrôleur :
- créait un répertoire de workspace sur disque et y écrivait un fichier
.tfet un fichier d’état ; - lançait
terraform initen sous-processus, en résolvant le provider OVHcloud depuis un miroir de système de fichiers embarqué dans l’image ; - lançait
terraform planen sous-processus, puis parsait la sortie JSON pour décider si l’objet était à jour ; - lançait
terraform applyen sous-processus quand il ne l’était pas.
Ça fonctionne. Ça veut aussi dire un contrôleur Kubernetes dont le vrai moteur d’exécution est un sous-processus, dont le vrai état est une arborescence de fichiers, et dont l’image doit embarquer deux binaires supplémentaires pour fonctionner.
Voici ce dont l’image 2.19.0 avait besoin rien que pour démarrer :
FROM alpine:3.17.1
RUN apk --no-cache add ca-certificates bash
ENV PLUGIN_DIR=/terraform/provider-mirror/registry.terraform.io/${TERRAFORM_PROVIDER_SOURCE}/...
ENV TF_CLI_CONFIG_FILE=/terraform/.terraformrc
ADD https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_..._${TARGETOS}_${TARGETARCH}.zip /tmp
ADD ${TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX}/..._${TARGETOS}_${TARGETARCH}.zip /tmp
RUN unzip ... -d /usr/local/bin \
&& chmod +x /usr/local/bin/terraform \
&& unzip ... -d ${PLUGIN_DIR} \
&& chown -R ${USER_ID}:${USER_ID} /terraform
Un shell, une distribution Terraform, un plugin provider vendorisé, et une arborescence en écriture appartenant à l’utilisateur d’exécution.
Ce que fait 2.19.1 à la place
upjet supporte depuis un moment deux modes de réconciliation in-process : l’un qui appelle directement les fonctions CRUD d’un provider Terraform Plugin SDK v2, l’autre qui dialogue avec les interfaces du Plugin Framework dans le même processus. La 2.19.1 bascule toutes les ressources dessus.
106 types de ressources passent par le chemin SDKv2. 70 par le chemin Plugin Framework. Aucun par la CLI.
Il n’y a plus de sous-processus. Observe, Create, Update et Delete sont des appels Go dans le code du provider OVHcloud, lié au binaire du contrôleur. Le Dockerfile complet tient maintenant en ceci :
FROM alpine:3.24.1
RUN apk --no-cache add ca-certificates
ADD "bin/${TARGETOS}_${TARGETARCH}/provider" /usr/local/bin/provider
## Métadonnées du provider Terraform consommées par le contrôleur au démarrage.
## Toutes les ressources sont réconciliées in-process (Terraform Plugin SDK /
## Framework) ; aucune CLI Terraform ni binaire natif n'est embarqué ici.
ENV TERRAFORM_VERSION=${TERRAFORM_VERSION}
ENV TERRAFORM_PROVIDER_SOURCE=${TERRAFORM_PROVIDER_SOURCE}
ENV TERRAFORM_PROVIDER_VERSION=${TERRAFORM_PROVIDER_VERSION}
USER ${USER_ID}
EXPOSE 8080
ENTRYPOINT ["provider"]
Un binaire Go et un bundle de CA. Même bash a disparu.
Ce que ça vous apporte en production
Plus de répertoire de travail en écriture. Ce qui justifiait ce répertoire, c’était l’arborescence de workspace. Elle n’existe plus, ce qui lève l’obstacle à faire tourner le contrôleur avec un système de fichiers racine en lecture seule, l’un des durcissements de base que nous appliquons dans nos missions de platform engineering.
Plus d’egress vers un registry à l’exécution. Le chemin CLI résolvait un plugin provider à chaque init, depuis un miroir qui devait exister dans l’image. Il n’y a plus rien à résoudre. Sur un cluster avec une politique d’egress sur le namespace du control plane, c’est une exception de moins à justifier.
Moins de surface à scanner. Deux binaires tiers ont quitté l’image. Chaque CVE que l’un ou l’autre accumulait devenait votre CVE, que le chemin de code soit atteignable ou non.
Plus de fork/exec par réconciliation. Un terraform init suivi d’un plan pour chaque objet à chaque boucle, c’est de la création de processus, des E/S disque et du parsing JSON payés en plus de l’appel d’API que vous vouliez réellement faire. Nous ne publions pas de benchmark ici — un benchmark honnête demande une flotte réelle et les vrais quotas de l’API OVHcloud — mais le travail supprimé n’a rien d’anecdotique.
Le bug que ça a fait sortir, et pourquoi aucun test ne l’a vu
Le câblage du chemin SDKv2 a mis au jour quelque chose qui mérite d’être écrit, parce que c’est un piège pour quiconque mène la même migration sur son propre provider upjet.
Sur le chemin Plugin Framework, upjet configure lui-même le provider à partir de terraform.Setup.Configuration. Sur le chemin SDKv2, non. Il transmet tel quel le champ Meta de terraform.Setup comme meta du provider à Diff, RefreshWithoutUpgrade et Apply, et ne le renseigne jamais. Or chaque ressource SDKv2 d’OVHcloud commence par une assertion de type meta.(*Config).
Laissez Meta à nil, et les 106 ressources adossées au SDK paniquent dès leur premier Observe :
interface conversion: interface {} is nil, not *ovh.Config
Pas un crash loop — controller-runtime rattrape la panique — simplement toutes les ressources SDK en échec permanent de réconciliation, en silence, avec une stack trace dans les logs. Aucun job de CI du dépôt ne l’aurait attrapé : le job de diff de CRD compare des schémas, le job de build compile, et le job de déploiement installe le provider sans credentials, donc ne réconcilie jamais rien.
La 2.19.1 configure le provider et renseigne ps.Meta, en mettant le résultat en cache par ProviderConfig et par configuration effective. Ce cache n’est pas de l’optimisation prématurée : configurer le provider OVHcloud exécute loadAndValidate et appelle GET /auth/details, et Connect s’exécute à chaque réconciliation de chaque objet. Un access token OAuth renouvelé produit une clé de cache différente : un client mis en cache n’est jamais réutilisé avec des credentials qui ont changé depuis.
Migrer : le numéro de patch vous ment
La 2.19.1 est une version patch qui embarque des ruptures de CRD. C’est un arbitrage assumé pour ne pas laisser les utilisateurs bloqués sur une image vulnérable, et cela signifie que le numéro de version ne vous préviendra pas. Voici les trois changements qui peuvent mordre.
1. Les champs entiers sont désormais integer, plus number (506 propriétés)
terraform providers schema -json rend le TypeInt de Terraform en number. C’est ce que portaient les CRD générées, sur 506 propriétés. Aucun de ces champs n’est jamais fractionnaire : les déclarer en integer est une correction, pas un changement de comportement.
Les manifestes et les objets stockés ne sont pas concernés : 3 était valide en number et l’est en integer.
La vraie rupture est dans les types Go générés, où les champs correspondants passent de *float64 à *int64. Cela ne vous concerne que si vous importez apis/{cluster,namespaced}/... directement — dans une composition function, un opérateur ou un harnais de test. Si vous écrivez du YAML, il n’y a rien à faire.
2. Deux maps de chaînes deviennent atomiques pour le server-side apply
x-kubernetes-map-type: granular n’est plus positionné sur User.openstackRc (groupe cloud) ni sur PrivateNetwork.regionsOpenstackIds (groupe network) — dans forProvider, initProvider et atProvider, dans les deux groupes d’API, soit huit chemins au total.
Ces maps sont désormais atomiques. Un client qui applique la map en prend la propriété entière. Deux clients ne peuvent plus posséder chacun des clés individuelles, et un apply partiel remplace la map au lieu d’y fusionner. Si un contrôleur et un humain écrivent tous deux des clés dans l’une de ces maps, ce montage cesse de se comporter comme vous l’attendez.
3. CloudProject (vrack) exige maintenant projectId — et rejettera des objets existants
C’est celui qui réveillera quelqu’un. CloudProject dans le groupe vrack gagne une règle de validation CEL qui rend spec.forProvider.projectId obligatoire dès que managementPolicies contient Create, Update ou *.
Les objets qui ne le renseignent pas et portent l’une de ces policies sont rejetés à leur prochaine mise à jour. Ils continuent de tourner jusqu’à ce que quelque chose y touche — exactement le genre de latence qui transforme une montée de version en incident trois semaines plus tard.
Repérez-les avant de mettre à jour :
# scope cluster
kubectl get cloudprojects.vrack.ovh.edixos.io -o json \
| jq -r '.items[]
| select((.spec.forProvider.projectId // .spec.initProvider.projectId) == null)
| .metadata.name'
# scope namespace
kubectl get cloudprojects.vrack.ovh.m.edixos.io -A -o json \
| jq -r '.items[]
| select((.spec.forProvider.projectId // .spec.initProvider.projectId) == null)
| "\(.metadata.namespace)/\(.metadata.name)"'
Tout ce qui s’affiche a besoin d’un projectId avant la mise à jour, sous peine d’échouer à sa prochaine écriture.
Sécurité
La 2.19.1 corrige aussi tout ce que Trivy remontait sur l’image 2.19.0. Go passe de 1.25.6 à 1.25.13, et la base Alpine de 3.17.1 — en fin de vie — à 3.24.1, avec les dépendances Go concernées.
Un scan Trivy de l’image obtenue ne remonte aucune vulnérabilité HIGH ni CRITICAL. govulncheck ne rapporte aucune vulnérabilité affectant le code du provider.
Un avis reste visible et mérite d’être nommé plutôt que masqué : golang.org/x/crypto/openpgp est signalé au niveau du module sous GO-2026-5932. Il n’existe pas de version corrigée, et le paquet concerné n’est ni importé ni appelé par ce provider.
Le pin Terraform de build passe de 1.8.1 à 1.15.9. Il ne sert qu’à générer config/schema.json et n’est pas embarqué ; régénérer avec 1.15.9 produit une sortie octet pour octet identique, donc aucune modification de CRD n’en découle.
À savoir aussi, côté 2.19.0
Si vous venez de la 2.18.0, la 2.19.0 a ajouté deux ressources dans les deux groupes d’API :
Instance encapsule le nouveau ovh_cloud_instance amont : flavorId (redimensionne en place), imageId, powerState, networks, securityGroupIds, volumeIds et shares. Deux arêtes vives : changer imageId reconstruit l’instance et efface le disque système, et omettre securityGroupIds applique le groupe default du projet alors qu’une liste vide explicite n’en applique aucun — soit une instance qui n’accepte aucun trafic entrant.
InstanceGroup encapsule ovh_cloud_instance_group : un groupe de placement avec une policy AFFINITY ou ANTI_AFFINITY. Il est immuable — changer name, region ou policy le remplace — et l’appartenance se définit uniquement via le groupId d’une Instance.
C’est la première des deux API d’instances OVHcloud exposée comme CRD distincte. La ProjectInstance préexistante n’est pas touchée et conserve son propre schéma.
Mettre à jour
cat <<EOF | kubectl apply -f -
apiVersion: pkg.crossplane.io/v1
kind: Provider
metadata:
name: provider-ovh
spec:
package: xpkg.upbound.io/edixos/provider-ovh:v2.19.1
EOF
Une séquence sans mauvaise surprise :
- Lancer les deux commandes
kubectl/jqci-dessus et corriger chaqueCloudProjectaffiché. - Vérifier si quelque chose écrit des clés dans
User.openstackRcouPrivateNetwork.regionsOpenstackIdsen parallèle d’un autre écrivain. - Si vous importez les types Go générés, recompiler contre la 2.19.1 : le compilateur vous montrera chaque
*float64devenu*int64. - Mettre à jour le provider.
- Vérifier que vos objets managés reviennent en
SYNCEDetREADY. Comme la réconciliation a changé de chemin, c’est l’étape qui valide le nouveau mode avec vos vrais credentials.
Si votre politique de cluster réservait un répertoire de travail au provider ou autorisait un egress spécifiquement vers le registry Terraform, les deux peuvent maintenant disparaître.
Changelog complet : v2.19.0…v2.19.1
Remerciements
Tous les changements de cette version viennent de la communauté, aucun de moi.
@ekarlso a signé la #65 — le passage à la réconciliation in-process, le retrait de la CLI Terraform de l’image, puis le correctif du meta provider SDKv2 et son cache. C’est un gros changement, mené avec soin, sur la partie la plus porteuse du provider.
@VeSeWe a signé sa première contribution avec la #63 — les montées de Go, d’Alpine et des dépendances qui amènent l’image à zéro vulnérabilité HIGH et CRITICAL. Bienvenue, et merci.
Si vous utilisez provider-ovh, les issues et pull requests sont le moyen le plus direct d’orienter la suite.
Pour aller plus loin
Une version plus récente est sortie : la provider-ovh 2.23.0 ajoute 16 ressources, dont la nouvelle API load balancer, sans rupture pour les manifestes.
Si vous découvrez le provider, le guide pas à pas provider-ovh couvre l’installation, les credentials et un premier cluster managé. Vous montez depuis la 2.17.0 ou une version antérieure ? Lisez d’abord 2.18.0 et 2.17.0 : toutes deux retirent des champs, une migration plus lourde que celle-ci.
Nous maintenons provider-ovh et construisons des control planes OVHcloud dessus : conseil Crossplane · platform engineering · nous écrire.
Réponses directes
Questions fréquentes
Qu'est-ce qui change dans provider-ovh 2.19.1 ?
Toutes les ressources OVHcloud se réconcilient désormais in-process, via le Terraform Plugin SDK et le Plugin Framework, au lieu d'appeler la CLI Terraform. Le binaire terraform et le binaire natif du provider OVHcloud ne sont plus embarqués dans l'image.
provider-ovh a-t-il encore besoin de la CLI Terraform à l'exécution ?
Non. La réconciliation n'écrit plus de workspace sur disque et ne lance plus terraform init : le contrôleur n'a besoin ni d'un répertoire de travail accessible en écriture, ni d'un accès réseau à un registry Terraform. L'image ne contient qu'un binaire Go sur Alpine.
provider-ovh 2.19.1 est-elle une version cassante ?
Oui, malgré le numéro de patch. 506 propriétés de CRD passent du type number au type integer, x-kubernetes-map-type granular disparaît sur deux maps de chaînes, et CloudProject dans le groupe vrack gagne une règle CEL exigeant projectId. Les manifestes ne bougent pas ; le code Go et certains objets existants, si.
Pourquoi les champs entiers changent-ils de type dans les CRD ?
terraform providers schema -json rend le TypeInt de Terraform en number, et c'est ce que portaient les schémas générés. Aucun des champs concernés n'est jamais fractionnaire : les déclarer en integer est une correction. Côté Go, ces champs passent de *float64 à *int64.
Quels objets CloudProject cassent après la mise à jour vers 2.19.1 ?
Ceux du groupe d'API vrack sans spec.forProvider.projectId ni spec.initProvider.projectId dont les managementPolicies contiennent Create, Update ou *. Une nouvelle règle de validation CEL les rejette à leur prochaine mise à jour tant que le champ n'est pas renseigné.