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
+
+[](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] ) }}"