roues : pip par le meme chemin que les collections — le controleur telecharge, la cible n installe rien du dehors
LA DERNIERE DEPENDANCE QUI SORTAIT PAR ELLE-MEME. Les collections Ansible suivent
depuis longtemps l idiome de ce depot : le CONTROLEUR telecharge une fois puis
pousse par SSH, aucun flux nouveau depuis la cible, et l artefact devient
deployable hors ligne. pip sortait encore vers PyPI, en HTTPS et par nom.
Un tenant n a NI DNS SORTANT NI 443 vers l Internet, et c est voulu. Mesure sur
ops-01, premiere machine de la reconstruction de Chezlepro :
ERROR: Could not find a version that satisfies the requirement ansible-core
Echec temporaire dans la resolution du nom
PAS LE PAQUET DEBIAN : trixie propose ansible-core 2.19.4, hors de la plage
epinglee. Y aller demanderait de deplacer l epinglage vers une version majeure aux
changements de gabarits connus, trois jours apres l avoir pose pour cause de
portabilite.
--no-index EST LE POINT, PAS UNE OPTIMISATION. Sans lui, pip resterait capable de
sortir vers PyPI le jour ou le depot local serait incomplet : la reussite
dependrait d un flux que ce tenant n a pas le droit d avoir, et une lignee
autonome deviendrait une lignee qui a l air autonome.
LE CONTROLEUR ET LA CIBLE DOIVENT PARTAGER LEUR PYTHON, et ca ne se devine pas.
pyyaml porte du C compile ; une roue cp313 posee sur un python 3.12 ne s installe
pas, et l echec accuserait le depot local au lieu de l ecart entre deux machines.
Mesure et dit ici, ou on peut encore le nommer.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019f91zs9SDdwSzL2CVei6on
This commit is contained in:
parent
ceb832eeac
commit
aca63011ea
2 changed files with 116 additions and 2 deletions
|
|
@ -153,3 +153,32 @@ serveur_ops_cache_collections: >-
|
|||
{{ (setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops')) }}/collections-{{
|
||||
lookup('file', serveur_ops_requirements_controleur) | hash('sha1') | truncate(12, true, '') }}
|
||||
serveur_ops_requirements_controleur: "{{ playbook_dir }}/../../requirements.yml"
|
||||
|
||||
# --- LA ROUE HORS-LIGNE : PYTHON PAR LE MEME CHEMIN QUE LES COLLECTIONS -------
|
||||
#
|
||||
# LA DERNIERE DEPENDANCE QUI SORTAIT PAR ELLE-MEME (2026-08-28). Les collections
|
||||
# Ansible suivent depuis longtemps l'idiome de ce depot — « le CONTROLEUR telecharge une
|
||||
# fois, puis pousse par SSH ; aucun flux nouveau depuis la cible, et l'artefact devient
|
||||
# deployable hors ligne, ce qui est le sens meme d'une lignee autonome ». `pip`, lui,
|
||||
# sortait encore vers PyPI, en HTTPS et par nom de domaine.
|
||||
#
|
||||
# Un tenant n'a NI DNS SORTANT NI 443 vers l'Internet, et c'est voulu. Mesure sur ops-01,
|
||||
# premiere machine de la reconstruction de Chezlepro :
|
||||
#
|
||||
# ERROR: Could not find a version that satisfies the requirement ansible-core<2.19
|
||||
# Echec temporaire dans la resolution du nom
|
||||
#
|
||||
# POURQUOI PAS LE PAQUET DEBIAN : trixie propose `ansible-core 2.19.4`, hors de la plage
|
||||
# epinglee. Y aller demanderait de deplacer l'epinglage vers une version majeure aux
|
||||
# changements de gabarits connus, trois jours apres l'avoir pose pour cause de
|
||||
# portabilite. Ce n'est pas le moment, et ce n'est pas ce probleme-ci.
|
||||
#
|
||||
# L'EMPREINTE DES SOURCES ENTRE DANS LE CHEMIN, comme pour les collections : changer une
|
||||
# ligne de `requirements-python.txt` ou l'epinglage d'ansible-core cree un cache neuf,
|
||||
# donc un telechargement. Les anciens restent et documentent ce qu'exigeaient les
|
||||
# versions precedentes.
|
||||
serveur_ops_roues_source: "{{ playbook_dir }}/../../requirements-python.txt"
|
||||
serveur_ops_cache_roues: >-
|
||||
{{ (setops_cache_artefacts | default(lookup('env', 'HOME') + '/.cache/setops')) }}/roues-{{
|
||||
(lookup('file', serveur_ops_roues_source) ~ serveur_ops_ansible) | hash('sha1') | truncate(12, true, '') }}
|
||||
serveur_ops_roues_depot: "{{ serveur_ops_racine }}/.roues-hors-ligne"
|
||||
|
|
|
|||
|
|
@ -93,13 +93,97 @@
|
|||
group: "{{ serveur_ops_utilisateur }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Installer Ansible dans l'environnement isolé
|
||||
# --- LA ROUE HORS-LIGNE ------------------------------------------------------
|
||||
#
|
||||
# MEME IDIOME QUE LES COLLECTIONS, quelques lignes plus bas : le CONTROLEUR telecharge
|
||||
# une fois dans son cache, puis pousse par SSH. La cible n'ouvre aucun flux, et
|
||||
# l'artefact devient deployable hors ligne.
|
||||
|
||||
- name: Preparer le cache des roues Python sur le controleur
|
||||
ansible.builtin.file:
|
||||
path: "{{ serveur_ops_cache_roues }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
run_once: true
|
||||
|
||||
# LE CONTROLEUR ET LA CIBLE DOIVENT PARTAGER LEUR PYTHON, et ca ne se devine pas.
|
||||
#
|
||||
# `pip download` rend les roues du systeme qui telecharge. `ansible-core`, `proxmoxer` et
|
||||
# `requests` sont universelles (`py3-none-any`), mais `pyyaml` porte du C compile : une
|
||||
# roue `cp313` posee sur un python 3.12 ne s'installe pas. L'echec serait « No matching
|
||||
# distribution found » a l'installation — un message qui accuse le depot local alors que
|
||||
# le coupable est l'ecart entre deux machines. On le mesure ici, ou on peut encore le dire.
|
||||
- name: Lire le Python du controleur
|
||||
ansible.builtin.command:
|
||||
argv: ["python3", "-c", "import sys; print('%d.%d' % sys.version_info[:2])"]
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
run_once: true
|
||||
changed_when: false
|
||||
register: serveur_ops_python_controleur
|
||||
|
||||
- name: Exiger que le controleur et la cible partagent leur version de Python
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- serveur_ops_python_controleur.stdout | trim
|
||||
== ansible_python_version.split('.')[0:2] | join('.')
|
||||
fail_msg: >-
|
||||
Le controleur tourne Python {{ serveur_ops_python_controleur.stdout | trim }} et
|
||||
{{ inventory_hostname }} tourne Python
|
||||
{{ ansible_python_version.split('.')[0:2] | join('.') }}. Les roues telechargees ne
|
||||
s'installeraient pas sur la cible, et l'echec accuserait le depot local au lieu de
|
||||
cet ecart. Materialiser depuis un controleur de meme famille — pendant une
|
||||
insemination, c'est le runner du SITE, qui porte le meme Debian que la machine
|
||||
qu'il amorce.
|
||||
|
||||
# `argv` ET NON `cmd` : le chemin du controleur peut contenir une ESPACE (« Espace
|
||||
# Chezlepro/… » chez le mainteneur). Meme piege que pour ansible-galaxy le 2026-08-23.
|
||||
- name: Telecharger les roues sur le controleur (une seule fois par empreinte)
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- "python3"
|
||||
- "-m"
|
||||
- "pip"
|
||||
- "download"
|
||||
- "--dest"
|
||||
- "{{ serveur_ops_cache_roues }}"
|
||||
- "--requirement"
|
||||
- "{{ serveur_ops_roues_source }}"
|
||||
- "{{ serveur_ops_ansible }}"
|
||||
- "pyyaml"
|
||||
delegate_to: localhost
|
||||
become: false
|
||||
run_once: true
|
||||
register: serveur_ops_roues_telechargees
|
||||
changed_when: "'Saved' in serveur_ops_roues_telechargees.stdout"
|
||||
# LE GARDE-FOU SUIT LE RESULTAT, PAS LE GESTE (lecon du 2026-08-27) : on saute quand le
|
||||
# cache PORTE deja des roues, pas quand une commande a deja tourne un jour.
|
||||
when: not ansible_check_mode
|
||||
|
||||
- name: Deposer les roues sur le runner
|
||||
ansible.builtin.copy:
|
||||
src: "{{ serveur_ops_cache_roues }}/"
|
||||
dest: "{{ serveur_ops_roues_depot }}/"
|
||||
owner: "{{ serveur_ops_utilisateur }}"
|
||||
group: "{{ serveur_ops_utilisateur }}"
|
||||
mode: "0644"
|
||||
directory_mode: "0755"
|
||||
when: not ansible_check_mode
|
||||
|
||||
# `--no-index` EST LE POINT, PAS UNE OPTIMISATION. Sans lui, pip resterait capable de
|
||||
# sortir vers PyPI le jour ou le depot local serait incomplet — et la reussite dependrait
|
||||
# alors d'un flux que ce tenant n'a pas le droit d'avoir. Un repli silencieux vers
|
||||
# l'Internet transformerait une lignee autonome en lignee qui a l'air autonome.
|
||||
- name: Installer Ansible dans l'environnement isolé (hors ligne)
|
||||
ansible.builtin.pip:
|
||||
name:
|
||||
- "{{ serveur_ops_ansible }}"
|
||||
- "pyyaml"
|
||||
virtualenv: "{{ serveur_ops_venv }}"
|
||||
state: present
|
||||
extra_args: "--no-index --find-links {{ serveur_ops_roues_depot }}"
|
||||
become: true
|
||||
become_user: "{{ serveur_ops_utilisateur }}"
|
||||
when: not ansible_check_mode
|
||||
|
|
@ -111,11 +195,12 @@
|
|||
# TACHE SEPAREE : dans `ansible.builtin.pip`, `name` et `requirements` sont MUTUELLEMENT
|
||||
# EXCLUSIFS. Les fusionner ne produit pas une erreur parlante — le module ignore l'un des
|
||||
# deux, et la bibliotheque manquante ne se revele qu'au premier module qui en depend.
|
||||
- name: Installer les bibliotheques Python du controleur (source declaree)
|
||||
- name: Installer les bibliotheques Python du controleur (source declaree, hors ligne)
|
||||
ansible.builtin.pip:
|
||||
requirements: "{{ serveur_ops_racine }}/requirements-python.txt"
|
||||
virtualenv: "{{ serveur_ops_venv }}"
|
||||
state: present
|
||||
extra_args: "--no-index --find-links {{ serveur_ops_roues_depot }}"
|
||||
become: true
|
||||
become_user: "{{ serveur_ops_utilisateur }}"
|
||||
when: not ansible_check_mode
|
||||
|
|
|
|||
Loading…
Reference in a new issue