Merge pull request #96 from corvus-migratorius/role-miniforge
Migrate the role
This commit is contained in:
46
roles/miniforge/README.md
Normal file
46
roles/miniforge/README.md
Normal file
@@ -0,0 +1,46 @@
|
||||
Role Name
|
||||
=========
|
||||
|
||||
Install the Miniforge distribution for each supplied user and, optionally, deploy Mamba environment from provided YAML files.
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Role Variables
|
||||
--------------
|
||||
|
||||
`miniforge_release`: Miniforge release version
|
||||
`miniforge_script_path`: where to download the installation script
|
||||
`miniforge_release_base_url`: base URL
|
||||
`miniforge_create_envs`: whethere to create environments for each user or not
|
||||
|
||||
`miniforge_users`: a list of dictionaries representing user accounts that need Miniforge
|
||||
`manifest_dir`: path to folder on the Ansible controller host where environment files are kept
|
||||
`miniforge_condarc`: a dictionary that will be YAMLified into the condarc file
|
||||
|
||||
NOTE: `miniforge_users` items must have `name` string attribute and, optionally, a `mamba_files` list attribute (filenames must be available under the `manifest_dir`).
|
||||
|
||||
Dependencies
|
||||
------------
|
||||
|
||||
None
|
||||
|
||||
Example Playbook
|
||||
----------------
|
||||
|
||||
See `molecule` > `default` > `converge.yml`
|
||||
|
||||
----------------
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
BSD
|
||||
|
||||
Author Information
|
||||
------------------
|
||||
|
||||
corvus-migratorius@proton.me
|
||||
masayganova@gmail.com
|
||||
5
roles/miniforge/defaults/main.yml
Normal file
5
roles/miniforge/defaults/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
miniforge_release: "26.3.2-2"
|
||||
miniforge_script_path: "/tmp/{{ miniforge_install_script_name }}.sh"
|
||||
miniforge_release_base_url: "https://github.com/conda-forge/miniforge/releases/download/"
|
||||
miniforge_create_envs: false
|
||||
1
roles/miniforge/handlers/main.yml
Normal file
1
roles/miniforge/handlers/main.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
||||
17
roles/miniforge/meta/main.yml
Normal file
17
roles/miniforge/meta/main.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
galaxy_info:
|
||||
role_name: miniforge
|
||||
namespace: genlab
|
||||
author: Alexander Gorelyshev
|
||||
company: Genlab, LLC
|
||||
description: Deploy the Miniforge distribution of the Mamba package manager
|
||||
license: MIT
|
||||
min_ansible_version: "2.1"
|
||||
|
||||
platforms:
|
||||
- name: "Ubuntu"
|
||||
versions: ["jammy", "noble"]
|
||||
|
||||
galaxy_tags: []
|
||||
|
||||
dependencies: []
|
||||
29
roles/miniforge/molecule/default/converge.yml
Normal file
29
roles/miniforge/molecule/default/converge.yml
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
# you can use {{all_users | selectattr('name', 'in', mamba_users)}} to narrow down a larger list
|
||||
- name: Converge
|
||||
hosts: all
|
||||
vars:
|
||||
mamba_users:
|
||||
- name: alice
|
||||
mamba_files: [project-alpha.yml, project-beta.yml]
|
||||
- name: bob
|
||||
mamba_files: [project-alpha.yml, project-beta.yml]
|
||||
- name: charlie
|
||||
|
||||
|
||||
pre_tasks:
|
||||
- name: "Create test users"
|
||||
loop: [alice, bob, charlie]
|
||||
ansible.builtin.user:
|
||||
name: "{{ item }}"
|
||||
state: present
|
||||
create_home: true
|
||||
shell: /bin/bash
|
||||
|
||||
roles:
|
||||
- role: genlab.common.miniforge
|
||||
miniforge_users: "{{ mamba_users }}"
|
||||
manifest_dir: "manifests"
|
||||
miniforge_create_envs: true
|
||||
miniforge_condarc:
|
||||
channels: [conda-forge]
|
||||
10
roles/miniforge/molecule/default/manifests/project-alpha.yml
Normal file
10
roles/miniforge/molecule/default/manifests/project-alpha.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: env_alpha
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python~=3.12
|
||||
- pip>=22.2
|
||||
- stress-ng==0.17.03
|
||||
- pip:
|
||||
- humanfriendly
|
||||
@@ -0,0 +1,9 @@
|
||||
---
|
||||
name: cucubeta
|
||||
channels:
|
||||
- conda-forge
|
||||
dependencies:
|
||||
- python~=3.10
|
||||
- pip>=22.2
|
||||
- pip:
|
||||
- arrow
|
||||
27
roles/miniforge/molecule/default/molecule.yml
Normal file
27
roles/miniforge/molecule/default/molecule.yml
Normal file
@@ -0,0 +1,27 @@
|
||||
---
|
||||
dependency:
|
||||
name: galaxy
|
||||
|
||||
driver:
|
||||
name: docker
|
||||
|
||||
platforms:
|
||||
- name: ubuntu
|
||||
image: geerlingguy/docker-${MOLECULE_DISTRO:-ubuntu2204}-ansible:latest
|
||||
pre_build_image: true
|
||||
command: ${MOLECULE_DOCKER_COMMAND:-""}
|
||||
volumes:
|
||||
- /sys/fs/cgroup:/sys/fs/cgroup:rw
|
||||
cgroupns_mode: host
|
||||
privileged: true
|
||||
|
||||
provisioner:
|
||||
name: ansible
|
||||
|
||||
verifier:
|
||||
name: ansible
|
||||
|
||||
lint: |
|
||||
set -e
|
||||
yamllint .
|
||||
ansible-lint .
|
||||
70
roles/miniforge/molecule/default/verify.yml
Normal file
70
roles/miniforge/molecule/default/verify.yml
Normal file
@@ -0,0 +1,70 @@
|
||||
---
|
||||
- name: Verify
|
||||
hosts: all
|
||||
gather_facts: false
|
||||
any_errors_fatal: true
|
||||
|
||||
pre_tasks:
|
||||
- name: "Verify | include default vars"
|
||||
ansible.builtin.include_vars:
|
||||
dir: "{{ lookup('env', 'MOLECULE_PROJECT_DIRECTORY') }}/defaults/"
|
||||
extensions: ['yml']
|
||||
|
||||
tasks:
|
||||
- name: "Verify | get the version of Miniforge installed"
|
||||
become: true
|
||||
become_user: alice
|
||||
changed_when: false
|
||||
register: miniforge_installed_version
|
||||
ansible.builtin.command:
|
||||
cmd: ~/miniforge3/bin/conda --version
|
||||
|
||||
- name: "Verify | check the installed version of Miniforge"
|
||||
ansible.builtin.assert:
|
||||
that: "'{{ miniforge_release | split('-') | first }}' in miniforge_installed_version.stdout"
|
||||
success_msg: "OK, expected version installed: '{{ miniforge_installed_version.stdout }}'"
|
||||
fail_msg: "FAIL: unexpected version installed: '{{ miniforge_installed_version.stdout }}'"
|
||||
|
||||
- name: "Verify | check Mamba installing correct tools from channels"
|
||||
become: true
|
||||
become_user: alice
|
||||
changed_when: false
|
||||
vars:
|
||||
env: env_alpha
|
||||
register: miniforge_tool_version
|
||||
ansible.builtin.command:
|
||||
cmd: ~/miniforge3/envs/{{ env }}/bin/stress-ng --version
|
||||
|
||||
- name: "Verify | check the version of the installed tool"
|
||||
ansible.builtin.assert:
|
||||
that: "'stress-ng, version 0.17.03' in miniforge_tool_version.stdout"
|
||||
success_msg: "OK, expected version installed: '{{ miniforge_tool_version.stdout }}'"
|
||||
fail_msg: "FAIL: unexpected version installed: '{{ miniforge_tool_version.stdout }}'"
|
||||
|
||||
- name: "Verify | check the deployed environments for Alice (pip)"
|
||||
become: true
|
||||
become_user: alice
|
||||
loop:
|
||||
- { env: 'env_alpha', module: 'humanfriendly' }
|
||||
- { env: 'cucubeta', module: 'arrow' }
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: ~/miniforge3/envs/{{ item.env }}/bin/python -c 'import {{ item.module }}'
|
||||
|
||||
- name: "Verify | check the deployed environments for Bob"
|
||||
become: true
|
||||
become_user: bob
|
||||
loop:
|
||||
- { env: 'cucubeta', module: 'arrow' }
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: ~/miniforge3/envs/{{ item.env }}/bin/python -c 'import {{ item.module }}'
|
||||
|
||||
- name: "Verify | check that no environments were deployed for Charlie"
|
||||
become: true
|
||||
become_user: charlie
|
||||
register: miniforge_charlie_envs
|
||||
failed_when: miniforge_charlie_envs.stdout_lines | length > 0
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "ls ~/miniforge3/envs"
|
||||
15
roles/miniforge/tasks/main.yml
Normal file
15
roles/miniforge/tasks/main.yml
Normal file
@@ -0,0 +1,15 @@
|
||||
---
|
||||
- name: "Download miniforge from '{{ miniforge_release_base_url }}'"
|
||||
retries: 3
|
||||
delay: 1
|
||||
ansible.builtin.get_url:
|
||||
timeout: 10
|
||||
url: "{{ miniforge_release_base_url }}/{{ miniforge_release }}/{{ miniforge_install_script_name }}.sh"
|
||||
dest: "{{ miniforge_script_path }}"
|
||||
mode: "0664"
|
||||
|
||||
- name: "Deploy Mamba in a user loop"
|
||||
loop: "{{ miniforge_users }}"
|
||||
loop_control:
|
||||
loop_var: user
|
||||
ansible.builtin.include_tasks: "miniforge.yml"
|
||||
24
roles/miniforge/tasks/manifests.yml
Normal file
24
roles/miniforge/tasks/manifests.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
- name: "Manifests | get contents of the manifest file"
|
||||
register: miniforge_manifest_content
|
||||
ansible.builtin.slurp:
|
||||
src: "{{ miniforge_homedir }}/{{ file }}"
|
||||
|
||||
# Since the file can be named whatever, we need to extract the environment name
|
||||
# as that is what the actual environment folder name will be
|
||||
- name: "Manifests | extract environment name from manifest file"
|
||||
ansible.builtin.set_fact:
|
||||
miniforge_env_name: '{{ (miniforge_manifest_content.content | b64decode | from_yaml).name }}'
|
||||
|
||||
- name: "Manifests | display the extracted environment name"
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ miniforge_env_name }}"
|
||||
|
||||
- name: "Manifests | create environment: {{ miniforge_env_name }}"
|
||||
become: true
|
||||
become_user: "{{ user.name }}"
|
||||
ansible.builtin.shell:
|
||||
cmd: "{{ miniforge_homedir }}/miniforge3/bin/mamba env create -f {{ miniforge_homedir }}/{{ file }}"
|
||||
executable: /bin/bash
|
||||
# ensures that the task runs only when the environment doesn't exist
|
||||
creates: "{{ miniforge_homedir }}/miniforge3/envs/{{ miniforge_env_name }}"
|
||||
101
roles/miniforge/tasks/miniforge.yml
Normal file
101
roles/miniforge/tasks/miniforge.yml
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
# These variables are registered as host vars, for whatever deep reason
|
||||
- name: "Miniforge | get current user entity info ({{ user.name }})"
|
||||
ansible.builtin.getent:
|
||||
database: passwd
|
||||
key: "{{ user.name }}"
|
||||
split: ":"
|
||||
|
||||
- name: "Miniforge | get current user home directory ({{ user.name }})"
|
||||
ansible.builtin.set_fact:
|
||||
miniforge_homedir: "{{ getent_passwd[user.name][4] }}"
|
||||
|
||||
- name: "Miniforge | set miniforge dir ({{ user.name }})"
|
||||
ansible.builtin.set_fact:
|
||||
miniforge_dir: "{{ miniforge_homedir }}/miniforge3"
|
||||
|
||||
- name: "Miniforge | build miniforge bin path ({{ user.name }})"
|
||||
ansible.builtin.set_fact:
|
||||
miniforge_mamba_bin_path: "{{ miniforge_dir }}/bin/mamba"
|
||||
|
||||
- name: "Miniforge | display miniforge script path ({{ user.name }})"
|
||||
ansible.builtin.debug:
|
||||
msg: "{{ miniforge_script_path }}"
|
||||
|
||||
- name: "Miniforge | install miniforge ({{ user.name }})"
|
||||
become: true
|
||||
become_user: "{{ user.name }}"
|
||||
vars:
|
||||
allow_world_readable_tmpfiles: true
|
||||
block:
|
||||
- name: "Miniforge | check Mamba availability ({{ user.name }})"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ miniforge_mamba_bin_path }} info"
|
||||
rescue:
|
||||
- name: "Miniforge | install miniforge in a non-interactive mode ({{ user.name }})"
|
||||
ansible.builtin.command:
|
||||
cmd: "/bin/bash {{ miniforge_script_path }} -b"
|
||||
creates: "{{ miniforge_homedir }}/miniforge3"
|
||||
|
||||
- name: "Miniforge | initialize Mamba/Conda ({{ user.name }})"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ miniforge_mamba_bin_path }} shell init"
|
||||
|
||||
- name: "Miniforge | check if Mamba has become available ({{ user.name }})"
|
||||
changed_when: false
|
||||
ansible.builtin.command:
|
||||
cmd: "{{ miniforge_mamba_bin_path }} info"
|
||||
|
||||
|
||||
- name: "Miniforge | deploy default .condarc if it doesn't exist already ({{ user.name }})"
|
||||
become: true
|
||||
become_user: "{{ user.name }}"
|
||||
block:
|
||||
- name: "Miniforge | check if .condarc already exists ({{ user.name }})"
|
||||
register: miniforge_condarc_stat
|
||||
failed_when: miniforge_condarc_stat.stat.exists is false
|
||||
ansible.builtin.stat:
|
||||
path: "{{ miniforge_homedir }}/.condarc"
|
||||
|
||||
rescue:
|
||||
- name: "Miniforge | set default .condarc from external file if provided ({{ user.name }})"
|
||||
when: condarc_file is defined and condarc_file != ""
|
||||
ansible.builtin.copy:
|
||||
src: "{{ condarc_file }}"
|
||||
dest: "{{ miniforge_homedir }}/.condarc"
|
||||
mode: "0600"
|
||||
|
||||
- name: "Miniforge | set default content via .condarc ({{ user.name }})"
|
||||
when: condarc_file is not defined or condarc_file == ""
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ miniforge_homedir }}/.condarc"
|
||||
content: "{{ miniforge_condarc | to_nice_yaml }}"
|
||||
mode: "0600"
|
||||
|
||||
|
||||
- name: "Miniforge | deploy manifest files for user: {{ user.name }}"
|
||||
when:
|
||||
- "manifest_dir is defined"
|
||||
- "user.mamba_files is defined"
|
||||
loop: "{{ user.mamba_files }}"
|
||||
become: true
|
||||
become_user: "{{ user.name }}"
|
||||
ansible.builtin.copy:
|
||||
src: "{{ manifest_dir }}/{{ item }}"
|
||||
dest: "{{ miniforge_homedir }}/{{ item }}"
|
||||
owner: "{{ user.name }}"
|
||||
group: "{{ user.name }}"
|
||||
mode: "0660"
|
||||
|
||||
|
||||
# Variables from the outer scope (like 'user') will get passed down to the included tasks
|
||||
- name: "Miniforge | create environments from deployed manifests for user: {{ user.name }}"
|
||||
when:
|
||||
- "miniforge_create_envs is true"
|
||||
- "user.mamba_files is defined"
|
||||
loop: "{{ user.mamba_files }}"
|
||||
loop_control:
|
||||
loop_var: file
|
||||
ansible.builtin.include_tasks: "manifests.yml"
|
||||
2
roles/miniforge/vars/main.yml
Normal file
2
roles/miniforge/vars/main.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
miniforge_install_script_name: "Miniforge3-{{ ansible_system }}-{{ ansible_architecture }}"
|
||||
Reference in New Issue
Block a user