Hanny's legendary infrastructure as code solution.
Project description
Infrable
Hanny's legendary infrastructure as code solution.
pip install -U infrable # requires python >= 3.10
Table of contents
Preface
In an infinite void surrounded by blinking lights, Hanny woke up with her memory hollow, only her name echoing in the dark corners of her mind. She found herself floating in a realm between the tangible world of Python code and the ethereal cosmos of the space. A synthetic voice resonated around her about a crucial infrastructure migration project but it was all Greek to her. Her memory, once the key to the Python kingdom, failed to provide any answers. She was stuck in an interstellar labyrinth, entrusted with a technology mission she could not recall, her only anchors being a mechanical keyboard and lines of code whizzing past on the nearby screen.
Chapter 1 - Chaos
In the void of her discontent, Hanny started to recall fragments of a past life: back on Earth, where a looming infrastructure migration project threatened to upend everything. Humanity grappled with a chaotic infrastructure and an ill-prepared toolkit. Python developers fought with convoluted tools, while the desperate search for declarative configuration management proved futile. Amidst this disarray, Hanny was catapulted into the cosmos, a reluctant knight tasked with an unenviable quest: to find an uncomplicated yet competent solution capable of taming this looming catastrophe.
Chapter 2 - Hosts and services
In a moment of revelation, Hanny grasped the intricacies of infrastructure: hosts and the services they housed, the vital backbone of any system. She understood the criticality of maintaining a single reference source, a complete, harmonious documentation capturing these elemental relationships. Committing herself to uphold this single source of truth principle, she began the painstaking process of documentation, pouring every detail into a consolidated testament christened infra.py.
infra.py
template_prefix = "https://github.com/username/repo/blob/main"
# Hosts/ -----------------------------------------------------------------------
dev_host = Host(fqdn="dev.example.com", ip="127.0.0.1")
beta_host = Host(fqdn="beta.example.com", ip="127.0.0.1")
prod_host = Host(fqdn="prod.example.com", ip="127.0.0.1")
# /Hosts -----------------------------------------------------------------------
# Services/ --------------------------------------------------------------------
dev_web = Service(host=dev_host, port=8080)
beta_web = Service(host=beta_host, port=8080)
prod_web = Service(host=prod_host, port=8080)
dev_nginx = Service(host=dev_host, port=80)
beta_nginx = Service(host=beta_host, port=80)
prod_nginx = Service(host=prod_host, port=80)
# /Services --------------------------------------------------------------------
Chapter 3 - Templates
Gradually piecing together her fragmented memories, Hanny realized configuration files for the host deployments ought to be maintained as templates, drawing values as needed from "infra.py". Back on Earth, the challenge had been a lack of a coherent system to document the path of these files, a problem of organization that posed a significant hurdle. Then, in a moment of genius, Hanny conceived a groundbreaking solution. She'd document the files' path directly within the configuration templates themselves. And so, with renewed vigor, she started adding crucial details as header comments nestled within the configuration files, a legendary stroke promising to transform the face of infrastructure migration.
templates/nginx/web.j2
# vim: syn=nginx
# ---
# src: {{ template_prefix }}/{{ _template.src }}
# dest: {{ dev_nginx.host }}:/etc/nginx/sites-enabled/web
# chmod: 644
# chown: root:root
# ---
server {
listen 80;
listen [::]:80;
server_name {{ dev_nginx.host.fqdn }} www.{{ dev_nginx.host.fqdn }};
location / {
proxy_pass http://localhost:{{ dev_web.port }};
include proxy_params;
}
}
Chapter 4 - The Deploy or Recover Workflow
In the vast expanse of uncertainty, one thing became crystal clear to Hanny: the importance of reviewing the files made through "infra.py" and comparing these with the currently deployed configurations before pushing them. This process would intercept any live changes and ensure their inclusion in the templates. Her cautious nature also recognized the essential function of maintaining local backups of the utilized configurations, providing an insurance of sorts. To address any complications, she conceived a failsafe measure: The Deploy or Recover Workflow. It promised relief from human error while ensuring an approach to easily revert and recover the service, another triumphant stride in Hanny’s cosmic saga.
Deploy workflow
infrable files deploy
## same as
# infrable files gen
# infrable files pull
# infrable files backup
# infrable files push
flowchart TD;
A[gen: generate artifacts from templates and infra.py as .new files] --> B[pull: for each generated artifact pull the currently deployed version from server as .old files];
B --> C[backup: copy the artifacts in a local backup directory for easy recovery in case of failure];
C --> E[diff: compare the .new and .old files];
E -- push: for each result --> F{is there any difference?};
F -- yes --> H[display the diff];
F -- no --> G[skip and delete artifacts];
H --> I{confirm push?};
I -- yes --> J[push the file onto the server];
I -- no --> K[skip and delete artifacts];
Recover workflow
infrable files recover
## same as
# infrable files revert
# infrable files push
flowchart TD;
A[revert: copy the artifacts from the given or latest backup directory into artifacts directory but in reverse order] --> B[diff: compare the .new and .old files];
B --> C[push: run the steps for the push workflow]
Chapter 5 - Commands and Tasks
TODO: Document commands
# Run a command on a host by name
infrable remote dev_host "sudo systemctl reload nginx"
# Or by service name
infrable remote dev_nginx "sudo systemctl reload nginx"
# Or all affected hosts (as per files diff)
infrable remote affected-hosts "sudo systemctl reload nginx"
# Or
infrable files affected-hosts | infrable remote - "sudo systemctl reload nginx"
TODO: Document tasks
infra.py
nginx_dev.typer = typer.Typer()
@nginx_dev.typer.command()
def reload(test: bool = True):
assert nginx.host, "Service must have a host to reload"
if test:
nginx.host.remote().sudo.nginx("-t")
nginx.host.remote().sudo.systemctl.reload.nginx()
infrable nginx-dev reload --test
Or define a global reload task
infra.py
reload = typer.Typer()
@reload.command()
def nginx_dev(test: bool = True):
assert nginx.host, "Service must have a host to reload"
if test:
nginx.host.remote().sudo.nginx("-t")
nginx.host.remote().sudo.systemctl.reload.nginx()
infrable reload nginx-dev --test
Chapter 6 - Environments and switches
TODO: Document environments
infra.py
# Environments/ ----------------------------------------------------------------
dev = "dev"
beta = "beta"
prod = "prod"
environments = {dev, beta, prod}
env = Switch(environments, init=dev) # <-- defining a switch between different environments
current_env = env()
# /Environments ----------------------------------------------------------------
# Hosts/ -----------------------------------------------------------------------
dev_host = Host(fqdn="dev.example.com", ip="127.0.0.1")
beta_host = Host(fqdn="beta.example.com", ip="127.0.0.1")
prod_host = Host(fqdn="prod.example.com", ip="127.0.0.1")
managed_hosts = env( # <-- defining hosts for different environments
dev=[dev_host],
beta=[beta_host],
prod=[prod_host],
)
# /Hosts -----------------------------------------------------------------------
# Services/ --------------------------------------------------------------------
web = Service(
host=env(dev=dev_host, beta=beta_host, prod=prod_host),
port=8080,
)
nginx = Service(
port=80,
host=env(dev=dev_host, beta=beta_host, prod=prod_host),
)
# /Services --------------------------------------------------------------------
Update templates to use the environment specific values hosts:
templates/nginx/proxy_params.j2
# vim: syn=nginx
# ---
# src: {{ template_prefix }}/{{ _template.src }}
# dest:
# {% for host in managed_hosts %}
# - loc: {{ host }}:/etc/nginx/proxy_params
# {% endfor %}
# chmod: 644
# chown: root:root
# ---
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
Switch management:
# Switch current environment
infrable switch env [dev|beta|prod]
# Check all switch values
infrable switches
Chapter 6 - Meta and Secrets
TODO: Document secrets
infra.py
common_ssh_key = readfile("secrets/common/ssh_key") # <-- read a secret file
web = Service(
meta=Meta(secret_key=common_ssh_key), # <-- attach metadata to items
host=env(dev=dev_host, beta=beta_host, prod=prod_host),
port=8080,
)
Managing secrets:
vim secrets/web/secret_key
Chapter 7 - The Python Shell
Import in a Python shell
from infrable import files
import infra
files.deploy()
infra.nginx.host.remote().sudo.systemctl.reload.nginx()
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
File details
Details for the file infrable-0.0.1.tar.gz
.
File metadata
- Download URL: infrable-0.0.1.tar.gz
- Upload date:
- Size: 20.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.10.14
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 63b86c415e5847787886f6e8aefaf16073843bbb3236dc7e6481029045b6a1c8 |
|
MD5 | 568cdb816dc0eee11b89fe977782e5be |
|
BLAKE2b-256 | 9f8302c0ebd5014cd4caa0e7a21af4705e40f262b5070efc6f813336453cd9ca |
File details
Details for the file infrable-0.0.1-py3-none-any.whl
.
File metadata
- Download URL: infrable-0.0.1-py3-none-any.whl
- Upload date:
- Size: 28.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.0.0 CPython/3.10.14
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 7ed9b503f804db0c4d7f66e3f0605a6a72e052dfec651d8e83b50d5315fc9e66 |
|
MD5 | 08beb52b8b9c63bc644701d7933c927f |
|
BLAKE2b-256 | 92b00b0fd07cda4ea699b2a4db3b416498aa6b4bc42ab8deba50d0be00aba97b |