From 87ef197b18be70a135d33ed5af55be9e6aac576c Mon Sep 17 00:00:00 2001 From: Stephane Robert Date: Thu, 29 Sep 2022 16:32:11 +0200 Subject: [PATCH] initial commit --- .gitignore | 213 +++++++++++++++++++++++++ .yamllint | 33 ++++ README.md | 101 ++++++++++++ defaults/main.yml | 2 + handlers/main.yml | 2 + meta/argument_specs.yml | 12 ++ meta/main.yml | 22 +++ molecule/default/Dockerfile.j2 | 22 +++ molecule/default/converge.yml | 7 + molecule/default/molecule.yml | 27 ++++ molecule/default/tests/conftest.py | 22 +++ molecule/default/tests/test_default.py | 18 +++ tasks/gather_facts.yml | 29 ++++ tasks/main.yml | 35 ++++ tests/inventory | 2 + tests/test.yml | 5 + vars/main.yml | 104 ++++++++++++ 17 files changed, 656 insertions(+) create mode 100644 .gitignore create mode 100644 .yamllint create mode 100644 README.md create mode 100644 defaults/main.yml create mode 100644 handlers/main.yml create mode 100644 meta/argument_specs.yml create mode 100644 meta/main.yml create mode 100644 molecule/default/Dockerfile.j2 create mode 100644 molecule/default/converge.yml create mode 100644 molecule/default/molecule.yml create mode 100644 molecule/default/tests/conftest.py create mode 100644 molecule/default/tests/test_default.py create mode 100644 tasks/gather_facts.yml create mode 100644 tasks/main.yml create mode 100644 tests/inventory create mode 100644 tests/test.yml create mode 100644 vars/main.yml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f41a57d --- /dev/null +++ b/.gitignore @@ -0,0 +1,213 @@ +# File created using '.gitignore Generator' for Visual Studio Code: https://bit.ly/vscode-gig +# Created by https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,ansible,python +# Edit at https://www.toptal.com/developers/gitignore?templates=visualstudiocode,linux,ansible,python + +### Ansible ### +*.retry + +### Linux ### +*~ + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +### Python ### +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +### VisualStudioCode ### +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +!.vscode/*.code-snippets + +# Local History for Visual Studio Code +.history/ + +# Built Visual Studio Code Extensions +*.vsix + +### VisualStudioCode Patch ### +# Ignore all local history of files +.history +.ionide + +# Support for Project snippet scope +.vscode/*.code-snippets + +# Ignore code-workspaces +*.code-workspace + +# End of https://www.toptal.com/developers/gitignore/api/visualstudiocode,linux,ansible,python + +# Custom rules (everything added below won't be overriden by 'Generate .gitignore File' if you use 'Update' option) + diff --git a/.yamllint b/.yamllint new file mode 100644 index 0000000..8827676 --- /dev/null +++ b/.yamllint @@ -0,0 +1,33 @@ +--- +# Based on ansible-lint config +extends: default + +rules: + braces: + max-spaces-inside: 1 + level: error + brackets: + max-spaces-inside: 1 + level: error + colons: + max-spaces-after: -1 + level: error + commas: + max-spaces-after: -1 + level: error + comments: disable + comments-indentation: disable + document-start: disable + empty-lines: + max: 3 + level: error + hyphens: + level: error + indentation: disable + key-duplicates: enable + line-length: disable + new-line-at-end-of-file: disable + new-lines: + type: unix + trailing-spaces: disable + truthy: disable diff --git a/README.md b/README.md new file mode 100644 index 0000000..fbfc0c5 --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +# bootstrap + +[![Ansible version](https://img.shields.io/badge/ansible-%3E%3D2.10-black.svg?style=flat-square&logo=ansible)](https://github.com/ansible/ansible) + +⭐ Star us on GitHub — it motivates us a lot! + +your role description + +**Platforms Supported**: + +None. + +## ⚠️ Requirements + +Ansible >= 2.1. + +### Ansible role dependencies + +None. + +## ⚡ Installation + +### Install with Ansible Galaxy + +```shell +ansible-galaxy install bootstrap +``` + +### Install with git + +If you do not want a global installation, clone it into your `roles_path`. + +```bash +git clone bootstrap +``` + +But I often add it as a submodule in a given `playbook_dir` repository. + +```bash +git submodule add roles/bootstrap +``` + +As the role is not managed by Ansible Galaxy, you do not have to specify the +github user account. + +### ✏️ Example Playbook + +Basic usage is: + +```yaml +- hosts: all + roles: + - role: bootstrap + vars: + bootstrap_timeout: 10 + +``` + +## ⚙️ Role Variables + +Variables are divided in three types. + +The **default vars** section shows you which variables you may +override in your ansible inventory. As a matter of fact, all variables should +be defined there for explicitness, ease of documentation as well as overall +role manageability. + +The **context variables** are shown in section below hint you +on how runtime context may affects role execution. + +### Default variables + +#### main + +Bootsrap a machine for Ansible. + +| Variable Name | Required | Type | Default | Elements | Description | +|---------------|----------|------|---------|----------|-------------| +| bootstrap_timeout | False | int | 10 | | | + +### Context variables + +Those variables from `vars/*.{yml,json}` are loaded dynamically during task +runtime using the `include_vars` module. + +Variables loaded from `vars/main.yml`. + +| Variable Name | Value | +|---------------|-------| +| bootstrap_os_family_map | Alpine:
- Alpine
Archlinux:
- Archlinux
- Antergos
- Manjaro
Debian:
- Debian
- Ubuntu
- Raspbian
- Neon
- KDE neon
- Linux Mint
- SteamOS
- Devuan
- Kali
- Cumulus Linux
- Pop!_OS
- Parrot
- Pardus GNU/Linux
Gentoo:
- Gentoo
- Funtoo
RedHat:
- RedHat
- Fedora
- CentOS
- Scientific
- SLC
- Ascendos
- CloudLinux
- PSBM
- Rocky
- OracleLinux
- OVS
- OEL
- Amazon
- Virtuozzo
- XenServer
- Alibaba
- EulerOS
- openEuler
- AlmaLinux
Suse:
- SLED
- openSUSE Tumbleweed
- openSUSE Leap
- SLES_SAP
- SUSE_LINUX
- SLES
- openSUSE
- SuSE
| +| bootstrap_install | {{ _bootstrap_install[bootstrap_distribution ~'_'~ bootstrap_distribution_major_version] \|default( _bootstrap_install[bootstrap_distribution] ) \|default( _bootstrap_install[bootstrap_os_family] ) }} | +| _bootstrap_install | Alpine:
raw: LANG=C apk update ; apk add {{ bootstrap_packages }}
stdout_regex: Installing
Archlinux:
raw: LANG=C pacman -Sy --noconfirm {{ bootstrap_packages }}
stdout_regex: ' installing python'
Debian:
raw: LANG=C apt-get update && apt-get install -y {{ bootstrap_packages }}
stdout_regex: ' 0 newly installed'
Gentoo:
raw: LANG=C equery l {{ bootstrap_packages }} \| \| (emaint -a sync ; emerge -qkv {{
bootstrap_packages }} ; echo 'changed')
stdout_regex: changed
RedHat:
raw: LANG=C yum -y install {{ bootstrap_packages }}
stdout_regex: Nothing
Suse:
raw: LANG=C zypper -n install {{ bootstrap_packages }}
stdout_regex: Nothing
| +| _bootstrap_packages | Alpine: python3 sudo
Amazon: python sudo
Archlinux: python sudo
CentOS_7: python sudo
Debian: python3 sudo gnupg python3-apt
Debian_8: python sudo gnupg
Debian_9: python sudo gnupg
Gentoo: python sudo gentoolkit
RedHat: python3 sudo
RedHat_7: python sudo
Suse: python3 python3-xml sudo
| +| bootstrap_packages | {{ _bootstrap_packages[bootstrap_distribution ~'_'~ bootstrap_distribution_major_version] \|default( _bootstrap_packages[bootstrap_distribution] ) \|default( _bootstrap_packages[bootstrap_os_family] ) }} | +| bootstrap_facts_packages | {{ _bootstrap_packages[ansible_distribution ~'_'~ ansible_distribution_major_version] \|default( _bootstrap_packages[ansible_distribution] ) \|default( _bootstrap_packages[ansible_os_family] ) }} | + + + +## Author Information + +your company (optional) \ No newline at end of file diff --git a/defaults/main.yml b/defaults/main.yml new file mode 100644 index 0000000..5ebc023 --- /dev/null +++ b/defaults/main.yml @@ -0,0 +1,2 @@ +--- +bootstrap_timeout: 10 diff --git a/handlers/main.yml b/handlers/main.yml new file mode 100644 index 0000000..b6ef2ba --- /dev/null +++ b/handlers/main.yml @@ -0,0 +1,2 @@ +--- +# handlers file for stephrobert.bootstrap diff --git a/meta/argument_specs.yml b/meta/argument_specs.yml new file mode 100644 index 0000000..588a9bc --- /dev/null +++ b/meta/argument_specs.yml @@ -0,0 +1,12 @@ +--- +argument_specs: + main: + short_description: "Bootsrap a machine for Ansible." + description: > + Install minimal package to allow Ansible to manage Hosts. + author: Stephane ROBERT + options: + bootstrap_timeout: + type: int + default: 10 + description: "Time (in seconds) to wait for connection." diff --git a/meta/main.yml b/meta/main.yml new file mode 100644 index 0000000..fee167e --- /dev/null +++ b/meta/main.yml @@ -0,0 +1,22 @@ +galaxy_info: + author: Stéphane ROBERT + description: Prepare your system to be managed by Ansible. + company: none + license: MPL-2.0 + min_ansible_version: "2.12" + platforms: + - name: Fedora + versions: + - "35" + - "36" + - name: Ubuntu + versions: + - "jammy" + - name: Debian + versions: + - "bullseye" + galaxy_tags: + - python + - ansible + - sudo +dependencies: [] diff --git a/molecule/default/Dockerfile.j2 b/molecule/default/Dockerfile.j2 new file mode 100644 index 0000000..ad7a6af --- /dev/null +++ b/molecule/default/Dockerfile.j2 @@ -0,0 +1,22 @@ +# Molecule managed + +{% if item.registry is defined %} +FROM {{ item.registry.url }}/{{ item.image }} +{% else %} +FROM {{ item.image }} +{% endif %} + +{% if item.env is defined %} +{% for var, value in item.env.items() %} +{% if value %} +ENV {{ var }} {{ value }} +{% endif %} +{% endfor %} +{% endif %} + +RUN if [ $(command -v apt-get) ]; then apt-get update && apt-get install -y python3 sudo bash ca-certificates iproute2 systemd systemd-sysv wget gpg && apt-get clean; \ + elif [ $(command -v yum) ]; then yum install -y python3 sudo bash iproute systemd firewalld initscripts wget; \ + fi + +RUN wget -O /usr/bin/systemctl https://raw.githubusercontent.com/gdraheim/docker-systemctl-replacement/master/files/docker/systemctl3.py && \ + chmod +x /usr/bin/systemctl diff --git a/molecule/default/converge.yml b/molecule/default/converge.yml new file mode 100644 index 0000000..2b820d3 --- /dev/null +++ b/molecule/default/converge.yml @@ -0,0 +1,7 @@ +--- +- name: Converge + hosts: all + tasks: + - name: Include stephrobert.init.bootstrap + include_role: + name: stephrobert.init.bootstrap diff --git a/molecule/default/molecule.yml b/molecule/default/molecule.yml new file mode 100644 index 0000000..d7fc089 --- /dev/null +++ b/molecule/default/molecule.yml @@ -0,0 +1,27 @@ +--- +dependency: + name: galaxy +driver: + name: docker +platforms: + - name: stephrobert_${image:-debian}-${tag:-latest} + image: ${image:-debian}:${tag:-latest} + dockerfile: Dockerfile.j2 + tty: True + privileged: True + pre_build_image: False + volumes: + - "/sys/fs/cgroup:/sys/fs/cgroup:rw" + - "/var/run/docker.sock:/var/run/docker.sock:ro" + stop_signal: "SIGRTMIN+3" + capabilities: + - SYS_ADMIN + - SYS_TIME + - LINUX_IMMUTABLE + command: "/lib/systemd/systemd" +provisioner: + name: ansible +verifier: + name: testinfra + options: + s: true \ No newline at end of file diff --git a/molecule/default/tests/conftest.py b/molecule/default/tests/conftest.py new file mode 100644 index 0000000..f7ddb3f --- /dev/null +++ b/molecule/default/tests/conftest.py @@ -0,0 +1,22 @@ +"""PyTest Fixtures.""" +from __future__ import absolute_import + +import os + +import pytest + + +def pytest_runtest_setup(item): + """Run tests only when under molecule with testinfra installed.""" + try: + import testinfra + except ImportError: + pytest.skip("Test requires testinfra", allow_module_level=True) + if "MOLECULE_INVENTORY_FILE" in os.environ: + pytest.testinfra_hosts = testinfra.utils.ansible_runner.AnsibleRunner( + os.environ["MOLECULE_INVENTORY_FILE"] + ).get_hosts("all") + else: + pytest.skip( + "Test should run only from inside molecule.", allow_module_level=True + ) diff --git a/molecule/default/tests/test_default.py b/molecule/default/tests/test_default.py new file mode 100644 index 0000000..d670cbc --- /dev/null +++ b/molecule/default/tests/test_default.py @@ -0,0 +1,18 @@ +"""Role testing files using testinfra.""" + + +def test_hosts_file(host): + """Validate /etc/hosts file.""" + f = host.file("/etc/hosts") + assert f.exists + assert f.user == "root" + assert f.group == "root" + +def test_python_is_installed(host): + distribution=host.system_info.distribution + if distribution in ['debian','fedora','ubuntu']: + python = host.package("python3") + else: + python = host.package("python") + + assert python.is_installed diff --git a/tasks/gather_facts.yml b/tasks/gather_facts.yml new file mode 100644 index 0000000..d35c370 --- /dev/null +++ b/tasks/gather_facts.yml @@ -0,0 +1,29 @@ +--- +- name: Get os release + ansible.builtin.raw: "cat /etc/os-release" + become: false + check_mode: false + register: bootstrap_facts + changed_when: false + +- name: set bootstrap facts (I) + ansible.builtin.set_fact: + bootstrap_distribution: "{{ item }}" + bootstrap_distribution_major_version: "{{ bootstrap_facts.stdout_lines | join(',') | regex_replace( + '^.*VERSION_ID=\"(\\d{1,2})(\\.\\d{1,4})*?\".*$','\\1') | default('NA') }}" + loop: "{{ bootstrap_os_family_map | dict2items | map(attribute='value') | flatten }}" + when: + - bootstrap_facts.rc == 0 + - bootstrap_distribution is not defined + - bootstrap_facts.stdout is regex('PRETTY_NAME=.'~ bootstrap_search[item] | default(item) ~'.*') + become: no + +- name: set bootstrap facts (II) + ansible.builtin.set_fact: + bootstrap_os_family: "{{ item.key }}" + loop: "{{ bootstrap_os_family_map | dict2items }}" + loop_control: + label: "{{ item.key }}" + when: + - bootstrap_distribution in item.value + become: no diff --git a/tasks/main.yml b/tasks/main.yml new file mode 100644 index 0000000..04e3812 --- /dev/null +++ b/tasks/main.yml @@ -0,0 +1,35 @@ +--- +# tasks file for +- name: Wait for port to be available + become: no + block: + - name: Constitute minimal facts + ansible.builtin.wait_for_connection: + timeout: "{{ bootstrap_timeout }}" + vars: + ansible_connection: ssh + rescue: + - name: Constitute minimal facts + ansible.builtin.include_tasks: + file: gather_facts.yml + + - name: Install bootstrap packages (raw) + ansible.builtin.raw: "{{ bootstrap_install.raw }}" + register: bootstrap_install_packages + changed_when: + - (bootstrap_install.stdout_regex in bootstrap_install_packages.stdout and + bootstrap_os_family in [ "Alpine", "Archlinux", "Gentoo" ]) or + (bootstrap_install.stdout_regex not in bootstrap_install_packages.stdout and + bootstrap_os_family in [ "Debian", "RedHat", "Rocky", "Suse" ]) + +- name: Ensure system is prepared + become: no + block: + - name: Gather ansible facts + ansible.builtin.setup: + + - name: Install bootstrap packages (package) + ansible.builtin.package: + name: "{{ item }}" + state: present + loop: "{{ bootstrap_facts_packages.split() }}" diff --git a/tests/inventory b/tests/inventory new file mode 100644 index 0000000..878877b --- /dev/null +++ b/tests/inventory @@ -0,0 +1,2 @@ +localhost + diff --git a/tests/test.yml b/tests/test.yml new file mode 100644 index 0000000..724b22f --- /dev/null +++ b/tests/test.yml @@ -0,0 +1,5 @@ +--- +- hosts: localhost + remote_user: root + roles: + - stephrobert.bootstrap diff --git a/vars/main.yml b/vars/main.yml new file mode 100644 index 0000000..ca566bc --- /dev/null +++ b/vars/main.yml @@ -0,0 +1,104 @@ +--- +bootstrap_os_family_map: + Alpine: + - Alpine + Archlinux: + - Archlinux + - Antergos + - Manjaro + Debian: + - Debian + - Ubuntu + - Raspbian + - Neon + - KDE neon + - Linux Mint + - SteamOS + - Devuan + - Kali + - Cumulus Linux + - "Pop!_OS" + - Parrot + - Pardus GNU/Linux + Gentoo: + - Gentoo + - Funtoo + RedHat: + - RedHat + - Fedora + - CentOS + - Scientific + - SLC + - Ascendos + - CloudLinux + - PSBM + - Rocky + - OracleLinux + - OVS + - OEL + - Amazon + - Virtuozzo + - XenServer + - Alibaba + - EulerOS + - openEuler + - AlmaLinux + Suse: + - SLED + - openSUSE Tumbleweed + - openSUSE Leap + - SLES_SAP + - SUSE_LINUX + - SLES + - openSUSE + - SuSE + +# Map the right install command, based on gathered bootstrap facts. +bootstrap_install: + "{{ _bootstrap_install[bootstrap_distribution ~'_'~ bootstrap_distribution_major_version]|default( + _bootstrap_install[bootstrap_distribution] )|default( + _bootstrap_install[bootstrap_os_family] ) }}" + +_bootstrap_install: + Alpine: + raw: "LANG=C apk update ; apk add {{ bootstrap_packages }}" + stdout_regex: "Installing" + Archlinux: + raw: "LANG=C pacman -Sy --noconfirm {{ bootstrap_packages }}" + stdout_regex: " installing python" + Debian: + raw: "LANG=C apt-get update && apt-get install -y {{ bootstrap_packages }}" + stdout_regex: " 0 newly installed" + Gentoo: + raw: "LANG=C equery l {{ bootstrap_packages }} || + (emaint -a sync ; emerge -qkv {{ bootstrap_packages }} ; echo 'changed')" + stdout_regex: "changed" + RedHat: + raw: "LANG=C yum -y install {{ bootstrap_packages }}" + stdout_regex: "Nothing" + Suse: + raw: "LANG=C zypper -n install {{ bootstrap_packages }}" + stdout_regex: "Nothing" + +_bootstrap_packages: + Alpine: python3 sudo + Archlinux: python sudo + Debian: python3 sudo gnupg python3-apt + Gentoo: python sudo gentoolkit + RedHat: &redhat_packages python3 sudo + Suse: python3 python3-xml sudo + Amazon: python sudo + CentOS_7: python sudo + Debian_8: python sudo gnupg + Debian_9: python sudo gnupg + RedHat_7: python sudo + +bootstrap_packages: + "{{ _bootstrap_packages[bootstrap_distribution ~'_'~ bootstrap_distribution_major_version]|default( + _bootstrap_packages[bootstrap_distribution] )|default( + _bootstrap_packages[bootstrap_os_family] ) }}" + +bootstrap_facts_packages: + "{{ _bootstrap_packages[ansible_distribution ~'_'~ ansible_distribution_major_version]|default( + _bootstrap_packages[ansible_distribution] )|default( + _bootstrap_packages[ansible_os_family] ) }}"