GitOps-driven Managed Infrastructure Framework
Project description
infranaut
GitOps-driven Managed Infrastructure Framework — a Python CLI that scaffolds and operates multi-client cloud infrastructure using OpenTofu/Terragrunt + Ansible.
Overview
infranaut generates and manages the directory structure, Terragrunt HCL files, and Ansible inventories for any number of projects deployed on Hetzner Cloud. Each project contains environments; each environment contains workloads.
workspace/
├── .env # GIT_USER, GIT_TOKEN, etc.
├── core/ # Shared Terraform modules & Ansible roles
│ ├── opentofu/
│ └── ansible/
└── projects/
└── acme/
├── opentofu/
│ ├── _env.hcl
│ ├── _root.hcl
│ ├── dev/
│ │ ├── env.hcl
│ │ ├── network/
│ │ └── gitlab/
│ └── shared/
│ └── ssh-keys/
└── ansible/
├── ansible.cfg
└── inventories/
└── dev/
└── gitlab/
Supported workload types
| Type | Description |
|---|---|
gitlab |
GitLab CE/EE server |
kubernetes_cluster |
Multi-node k8s cluster (controlplanes + workers) |
single_node |
Generic standalone server (Mattermost, Nextcloud, …) |
multi_node |
Generic primary/secondary cluster |
mail_server |
Postfix + Dovecot mail stack |
Installation
From PyPI (production)
pip install infranaut
From source (development)
git clone https://gitlab.kopsengineering.com/ksoft/infrastructur/infranaut_ctl.git
cd infranaut_ctl
pip install -e ".[dev]"
Quick start
# 1 — Create a workspace and pull the shared core
infranaut init /srv/infra --core-provider https://gitlab.example.com/ksoft/infra-core.git
# 2 — Create a project
infranaut project create --name acme
# 3 — Add an environment
infranaut env create --project acme --name dev
# 4 — Add workloads
infranaut workload create --project acme --env dev --type gitlab
infranaut workload create --project acme --env dev --type kubernetes_cluster --name k8s
infranaut workload create --project acme --env dev --type single_node --name mattermost
# 5 — Fill in generated inputs.hcl and env.hcl, then deploy
infranaut deploy --project acme --env dev --workload gitlab
# 6 — Regenerate dynamic inventory after a Terraform apply
infranaut inventory generate --project acme --env dev --workload gitlab
Core providers
--core-provider controls what goes into core/ at infranaut init time.
| Provider | Effect |
|---|---|
empty (default) |
Creates an empty core/ skeleton — add your own modules. |
https://…/repo.git |
Clones the repo with --depth 1 into core/. Credentials are read from the workspace .env file: GIT_USER and GIT_TOKEN. |
Git credentials are injected into the HTTPS URL automatically and are never printed to the console.
.env format for git provider
# workspace/.env
GIT_USER=my-ci-user
GIT_TOKEN=glpat-xxxxxxxxxxxxxxxxxxxx
Commands reference
infranaut init [PATH] [--core-provider PROVIDER]
infranaut project create --name NAME
infranaut project list
infranaut env create --project PROJECT --name ENV
infranaut env list --project PROJECT
infranaut workload create --project PROJECT --env ENV --type TYPE [--name NAME]
infranaut workload list --project PROJECT --env ENV
infranaut inventory generate --project PROJECT --env ENV --workload WORKLOAD
infranaut deploy --project PROJECT --env ENV [--workload WORKLOAD]
infranaut version
Development workflow
Prerequisites
- Python 3.11+
pip install -e ".[dev]"
Run tests
pytest tests/ -v
Lint
ruff check infranaut/
ruff format infranaut/
Smoke test (full end-to-end, no cloud)
mkdir -p /tmp/smoke && cd /tmp/smoke
infranaut init .
infranaut project create --name acme
infranaut env create --project acme --name dev
infranaut workload create --project acme --env dev --type gitlab
infranaut workload create --project acme --env dev --type kubernetes_cluster --name k8s
infranaut workload create --project acme --env dev --type single_node --name mattermost
infranaut workload create --project acme --env dev --type multi_node --name app
infranaut workload create --project acme --env dev --type mail_server
infranaut workload list --project acme --env dev
Release workflow
infranaut uses semantic versioning. Releases are triggered by pushing a vX.Y.Z tag; GitLab CI builds and publishes to PyPI automatically.
Steps
1. Bump the version in two files:
# infranaut/__init__.py
__version__ = "0.4.0"
# pyproject.toml
version = "0.4.0"
2. Commit and tag:
git add infranaut/__init__.py pyproject.toml
git commit -m "chore: bump version to 0.4.0"
git tag v0.4.0
git push origin dev
git push origin v0.4.0
The CI pipeline runs automatically:
lint— ruff style check (non-blocking)test— pytest unit testssmoke-test— full CLI end-to-end without cloud accesspublish-pypi— builds sdist + wheel, publishes to PyPI viaPYPI_TOKEN
Required CI/CD variable
In GitLab → Settings → CI/CD → Variables:
| Variable | Type | Description |
|---|---|---|
PYPI_TOKEN |
Masked | PyPI API token with publish scope |
Test procedure — fresh server
Run this after each release to validate the published package on a clean machine.
# 1. Install from PyPI (no local checkout)
pip install infranaut==0.4.0
# 2. Verify the CLI is reachable
infranaut version
infranaut --help
# 3. Initialise a workspace with the empty core
mkdir -p /tmp/infra-test && cd /tmp/infra-test
infranaut init .
# Expected: workspace marker created, core/ skeleton present
# 4. Create a project and environment
infranaut project create --name acme
infranaut env create --project acme --name dev
infranaut env create --project acme --name prod
# 5. Create all workload types
infranaut workload create --project acme --env dev --type gitlab
infranaut workload create --project acme --env dev --type kubernetes_cluster --name k8s
infranaut workload create --project acme --env dev --type single_node --name mattermost
infranaut workload create --project acme --env dev --type multi_node --name backend
infranaut workload create --project acme --env dev --type mail_server
# 6. Check the generated tree
find /tmp/infra-test/projects/acme -type f | sort
# 7. Verify key generated files exist and are non-empty
test -f projects/acme/opentofu/_env.hcl && echo "OK _env.hcl"
test -f projects/acme/opentofu/dev/env.hcl && echo "OK env.hcl"
test -f projects/acme/opentofu/dev/gitlab/terragrunt.hcl && echo "OK gitlab terragrunt"
test -f projects/acme/opentofu/dev/k8s/terragrunt.hcl && echo "OK k8s terragrunt"
test -f projects/acme/opentofu/dev/k8s/inputs.hcl && echo "OK k8s inputs"
test -f projects/acme/ansible/inventories/dev/gitlab/inventory.yml && echo "OK gitlab inventory"
test -f projects/acme/ansible/inventories/dev/k8s/inventory.yml && echo "OK k8s inventory"
test -f projects/acme/ansible/inventories/dev/gitlab/site.yml && echo "OK gitlab site.yml"
# 8. List projects and workloads (CLI consistency check)
infranaut project list
infranaut workload list --project acme --env dev
# 9. Test init with git core provider (requires git + valid token)
mkdir -p /tmp/infra-git && cd /tmp/infra-git
echo "GIT_USER=your-user" > .env
echo "GIT_TOKEN=your-token" >> .env
infranaut init . --core-provider https://gitlab.example.com/your-org/infra-core.git
# Expected: core/ contains the cloned repo content
# 10. Cleanup
rm -rf /tmp/infra-test /tmp/infra-git
What to check after each step:
- No Python tracebacks
- All
OKlines printed in step 7 - Generated HCL and YAML files have the correct project/env/workload names substituted
ansible.cfg,inventories/,group_vars/directories created underansible/
Project structure (infranaut_ctl)
infranaut_ctl/
├── infranaut/
│ ├── __init__.py
│ ├── main.py # CLI entry point (Typer app)
│ ├── config.py # Constants (workload types, etc.)
│ ├── core_provider.py # Core init: empty or git clone
│ ├── renderer.py # Jinja2 template renderer
│ ├── output_bridge.py # Terraform → Ansible variable bridge
│ ├── commands/
│ │ ├── init.py
│ │ ├── project.py
│ │ ├── env_workload.py
│ │ ├── inventory_cmd.py
│ │ └── deploy.py
│ └── templates/
│ ├── project/
│ │ ├── opentofu/ # _env.hcl, _root.hcl, env.hcl, envrc templates
│ │ └── ansible/ # ansible.cfg template
│ └── workloads/
│ ├── gitlab/
│ ├── kubernetes_cluster/
│ ├── single_node/
│ ├── multi_node/
│ ├── mail_server/
│ ├── network/
│ └── ssh_keys/
├── tests/
├── pyproject.toml
└── .gitlab-ci.yml
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file infranaut-0.3.0.tar.gz.
File metadata
- Download URL: infranaut-0.3.0.tar.gz
- Upload date:
- Size: 43.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
207841659f43061d0d5cef2ad8358791c86faa74024a91e059081fb14103738d
|
|
| MD5 |
ac3208492cf7d5139c6c780164479eaa
|
|
| BLAKE2b-256 |
de6e468759628af3ff0dfef0cad5fdab2bec99ecdcc71214c5edc52f1d1866a4
|
File details
Details for the file infranaut-0.3.0-py3-none-any.whl.
File metadata
- Download URL: infranaut-0.3.0-py3-none-any.whl
- Upload date:
- Size: 69.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.11.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc68a06d6c6b35bca289829929c677a207088f84b2256547959bee80a1e6c463
|
|
| MD5 |
2e74db02968f6eff664c859e0e801452
|
|
| BLAKE2b-256 |
1673f906f948e7ea71dd4e190ecd1acd4ea4eba82860f229a3b626d3ffa211d2
|