slides(tappaas): write the OrangeMaker podman session deck
Sixteen slides built around the live demo: two orientation diagrams (site shape with environments/zones/satellite, and the pull-based GitOps repo topology), a condensed module anatomy anchored on the real podman.json from Community/src/larsrossen/containers/podman, the three demo beats (test environment, module add, prove it), and the identity hand-out. Mermaid subgraph titles collide with the node row under flowchart TB, so both diagrams use LR; _class directives repeat 'invert' because a local _class replaces the deck-level one rather than adding to it.
This commit is contained in:
parent
91f53f1f62
commit
f1c1c79b63
1 changed files with 202 additions and 145 deletions
|
|
@ -4,229 +4,286 @@ theme: gaia
|
||||||
class: invert
|
class: invert
|
||||||
paginate: true
|
paginate: true
|
||||||
title: How to implement a TAPPaaS module
|
title: How to implement a TAPPaaS module
|
||||||
description: Walk-through of adding a new module to the TAPPaaS repository
|
description: Live build of a Podman module — OrangeMaker session
|
||||||
---
|
---
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
section { font-size: 26px; }
|
section { font-size: 26px; }
|
||||||
h1 { font-size: 1.5em; }
|
h1 { font-size: 1.5em; }
|
||||||
h2 { font-size: 1.15em; }
|
h2 { font-size: 1.15em; }
|
||||||
pre { font-size: 0.72em; line-height: 1.35; }
|
pre { font-size: 0.7em; line-height: 1.35; }
|
||||||
table { font-size: 0.8em; }
|
table { font-size: 0.78em; }
|
||||||
th, td { padding: 0.25em 0.6em; }
|
th, td { padding: 0.2em 0.55em; }
|
||||||
/* Unfinished content, loud on purpose: an unfinished deck should never be
|
/* Unfinished content, loud on purpose: an unfinished deck should never be
|
||||||
presented by accident. Written in the markdown as `TODO: ...`. */
|
presented by accident. Written in the markdown as `TODO: ...`. */
|
||||||
code.todo { color: #ff8a80; font-weight: 600; }
|
code.todo { color: #ff8a80; font-weight: 600; }
|
||||||
div.mermaid { display: flex; justify-content: center; }
|
/* Mermaid renders at its natural size, which is far too small on a projector. */
|
||||||
|
div.mermaid { display: flex; justify-content: center; width: 100%; }
|
||||||
|
div.mermaid svg { width: 100% !important; height: auto !important; max-height: 500px; }
|
||||||
|
section.diagram h2 { margin-bottom: 0.1em; }
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- _paginate: false -->
|
<!-- _paginate: false -->
|
||||||
|
|
||||||
# How to implement a TAPPaaS module
|
# Getting a module onto TAPPaaS
|
||||||
|
|
||||||
From empty directory to merged pull request
|
Live build of a **Podman** container host
|
||||||
|
|
||||||
|
OrangeMaker · `TODO: date`
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
Speaker notes go in HTML comments; press `p` in the browser for presenter view.
|
Slides are the map; the terminal is the territory. Everything here has a live
|
||||||
TODO: 30-second framing — who is in the room, what they walk out able to do.
|
counterpart — keep the deck moving and spend the time in the shell.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Who this is for
|
## The plan
|
||||||
|
|
||||||
- You want to add an **app** or a **foundation** capability to TAPPaaS
|
1. **What TAPPaaS is** — two diagrams, ten minutes, no deeper
|
||||||
- You can read shell, JSON, and a little Nix
|
2. **What a module actually is** — a json contract and three scripts
|
||||||
- You have a TAPPaaS test environment you are allowed to break
|
3. **Build one live** — a test environment, then `podman` into it
|
||||||
|
4. **Prove it works** — tests, then the web console
|
||||||
|
5. **You get a login** — your own identity on this system
|
||||||
|
|
||||||
**Prerequisites**
|
You leave with: a mental model, a module you watched get built, and an account.
|
||||||
|
|
||||||
- A clone of `TAPPaaS/TAPPaaS`, branch `TODO: ADR007 or stable`
|
|
||||||
- SSH access to a test site
|
|
||||||
- `TODO: tooling list`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Vocabulary in 60 seconds
|
<!-- _class: invert diagram -->
|
||||||
|
|
||||||
| Term | Means |
|
## The shape of a TAPPaaS site
|
||||||
| --- | --- |
|
|
||||||
| **Module** | One installable unit under `src/apps/` or `src/foundation/` |
|
|
||||||
| **Service** | A part of a module with its own lifecycle scripts |
|
|
||||||
| **Manager** | Owns desired state for a domain |
|
|
||||||
| **Controller** | Applies desired state to one concrete technology |
|
|
||||||
|
|
||||||
`TODO: tighten against ADR-007 taxonomy; drop the terms that do not matter here`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Anatomy of a module
|
|
||||||
|
|
||||||
```text
|
|
||||||
src/apps/<module>/
|
|
||||||
├── README.md # what it is, why it exists
|
|
||||||
├── DESIGN.md # how it is put together
|
|
||||||
├── INSTALL.md # operator-facing install notes
|
|
||||||
├── UPGRADE.md # operator-facing upgrade notes
|
|
||||||
├── AUTHORS.md # who to ask
|
|
||||||
├── LICENSE
|
|
||||||
├── <module>.json # declaration: the module's contract
|
|
||||||
├── <module>.nix # runtime definition
|
|
||||||
├── install.sh # create it
|
|
||||||
├── update.sh # move it forward
|
|
||||||
├── test.sh # prove it works
|
|
||||||
└── services/<svc>/ # per-service install/update/test/delete
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## The path from nothing to merged
|
|
||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart LR
|
flowchart LR
|
||||||
A[Scaffold from<br/>00-Template] --> B[Declare<br/>module.json]
|
sat["satellite VPS<br/>public IP · ingress<br/>off-site backup"]
|
||||||
B --> C[Define runtime<br/>module.nix]
|
subgraph z0["mgmt · zone mgmt"]
|
||||||
C --> D[install.sh]
|
cicd["tappaas-cicd"]
|
||||||
D --> E[services/]
|
net["network"]
|
||||||
E --> F[update.sh]
|
clu["cluster"]
|
||||||
F --> G[test.sh]
|
bak["backup"]
|
||||||
G --> H[Docs]
|
idp["identity"]
|
||||||
H --> I[PR + CI]
|
end
|
||||||
|
subgraph z1["default · zone srv"]
|
||||||
|
nc["nextcloud"]
|
||||||
|
ha["home-assistant"]
|
||||||
|
end
|
||||||
|
subgraph z2["test · zone hacklab"]
|
||||||
|
pod["podman — today"]
|
||||||
|
end
|
||||||
|
sat --> net
|
||||||
|
cicd --> nc
|
||||||
|
cicd --> pod
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 1 — Scaffold
|
## The words we will use
|
||||||
|
|
||||||
|
| Word | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| **Module** | The smallest deployable unit — its own VM, its own json contract. Foundation *and* apps are modules. |
|
||||||
|
| **Environment** | A tenant. `mgmt` and one named after the site always exist; add more freely. |
|
||||||
|
| **Zone** | A VLAN with a firewall policy. Modules land in their environment's zone. |
|
||||||
|
| **Mothership** | `tappaas-cicd` — where the managers run and where you type. |
|
||||||
|
| **Satellite** | Optional VPS, for sites with no usable public IP. |
|
||||||
|
|
||||||
|
Everything is a module. There is one model to learn, not eight.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
<!-- _class: invert diagram -->
|
||||||
|
|
||||||
|
## How code gets in: pull-based GitOps
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart RL
|
||||||
|
subgraph t["TAPPaaS repository"]
|
||||||
|
stable["stable"]
|
||||||
|
main["main"]
|
||||||
|
end
|
||||||
|
subgraph c["Community repository"]
|
||||||
|
cm["main"]
|
||||||
|
end
|
||||||
|
subgraph inst["Your TAPPaaS · tappaas-cicd"]
|
||||||
|
lt["clone of TAPPaaS"]
|
||||||
|
lc["clone of Community"]
|
||||||
|
end
|
||||||
|
lt -->|pull| stable
|
||||||
|
lc -->|pull| cm
|
||||||
|
```
|
||||||
|
|
||||||
|
Nothing pushes into your site. It pulls, on a schedule, and reconciles.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Managers: the verbs you will watch me type
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cp -r src/apps/00-Template src/apps/<module>
|
module-manager module add|test|reconcile|modify|delete
|
||||||
|
environment-manager add|modify|reconcile
|
||||||
|
network-manager zone add|merge|reconcile
|
||||||
|
people-manager user add|modify · reconcile --apply
|
||||||
```
|
```
|
||||||
|
|
||||||
- Rename `template.json` → `<module>.json`, `template.nix` → `<module>.nix`
|
Admins drive **verbs**, never hand-edited JSON. Each manager owns one slice of
|
||||||
- Read `README-template.md`, then delete it
|
desired state; controllers push it into OPNsense, Proxmox, the switch, Authentik.
|
||||||
- `TODO: is there a scaffold script? if not, should there be?`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 2 — Declare the module
|
## A module is a contract and three scripts
|
||||||
|
|
||||||
`<module>.json`
|
```text
|
||||||
|
podman/
|
||||||
|
├── podman.json # the contract — what it is, needs, provides
|
||||||
|
├── install.sh # put the software in the VM (once)
|
||||||
|
├── update.sh # keep it patched (on schedule)
|
||||||
|
├── test.sh # prove it still works (gates updates)
|
||||||
|
├── README.md # what it is
|
||||||
|
└── INSTALL.md # what automation cannot do for you
|
||||||
|
```
|
||||||
|
|
||||||
|
That is the whole surface. Everything else is the platform's job.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## The contract, for real
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"TODO": "smallest working declaration"
|
"description": "Podman container host — Debian 13 VM with rootless Podman + Cockpit",
|
||||||
|
"vmname": "podman", "vmid": 812,
|
||||||
|
"dependsOn": ["cluster:vm", "templates:debian", "backup:vm", "network:proxy"],
|
||||||
|
"config": {
|
||||||
|
"cluster:vm": { "cores": 2, "memory": "2048", "diskSize": "20G",
|
||||||
|
"image": "debian-13-generic-amd64.qcow2" },
|
||||||
|
"network:proxy": { "proxyPort": 9090, "proxyUpstreamTls": true,
|
||||||
|
"proxyUpstreamHttp1": true, "proxyAllowedZones": ["mgmt"] }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
- What the module *is*, not how it is built
|
`dependsOn` is the whole trick: four services, declared — not scripted.
|
||||||
- `TODO: required keys, optional keys, validation`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 3 — Define the runtime
|
## The three scripts, honestly
|
||||||
|
|
||||||
`<module>.nix`
|
| Script | Called | Does |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| `install.sh` | once, at add | For podman: `exec update.sh` — install *is* update here |
|
||||||
|
| `update.sh` | every schedule | apt: `podman`, `podman-compose`, `cockpit`, `cockpit-podman`; enable the rootless socket |
|
||||||
|
| `test.sh` | on demand + before every update merge | VM reachable · podman present · plugin present · console answers on `:9090` |
|
||||||
|
|
||||||
```nix
|
If `test.sh` is honest, unattended updates are safe. That is the entire deal.
|
||||||
# TODO: minimal example
|
|
||||||
```
|
|
||||||
|
|
||||||
- `TODO: what belongs in nix vs. what belongs in install.sh`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 4 — `install.sh`
|
## What you did not have to write
|
||||||
|
|
||||||
- Idempotent: safe to run twice
|
- A VM — `cluster:vm` built it from the Debian 13 cloud image
|
||||||
- Fails loudly, never half-way
|
- OS prep — `templates:debian` did apt + guest agent
|
||||||
- `pre-install.sh` when something must exist first
|
- A VLAN, an interface, DHCP, firewall rules — the zone came with the environment
|
||||||
|
- `https://podman.<domain>` with a real certificate — `network:proxy`, one dependency
|
||||||
|
- Nightly backup to PBS — `backup:vm`
|
||||||
|
- Scheduled updates and health reporting — the mothership
|
||||||
|
|
||||||
|
Six lines of json bought all of it.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 1 — an environment of our own
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# TODO: skeleton with the house conventions
|
# srvTest ships in the zone template; a fresh one keeps the demo self-contained
|
||||||
|
network-manager zone add hacklab --from-zone srv
|
||||||
|
|
||||||
|
environment-manager add test \
|
||||||
|
--display "Hackerspace test" \
|
||||||
|
--zone hacklab \
|
||||||
|
--domain test.<our-domain>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
A tenant with its own VLAN, its own firewall posture, its own DNS names —
|
||||||
|
and nothing in it can touch production.
|
||||||
|
|
||||||
|
`TODO: our domain — and do we build the zone live or pre-bake it?`
|
||||||
|
|
||||||
|
<!-- Show zones.json before/after, and the OPNsense interface appearing. -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Step 5 — Services
|
## Demo 2 — install the module
|
||||||
|
|
||||||
|
```bash
|
||||||
|
module-manager module add podman --environment test
|
||||||
|
```
|
||||||
|
|
||||||
|
Watch the order: dependencies resolve first, then the VM, then the network, then
|
||||||
|
`install.sh`. Install order is **computed** from every module's `dependsOn` — nobody
|
||||||
|
maintains a list.
|
||||||
|
|
||||||
|
<!-- Expect a few minutes. Good moment for questions. -->
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Demo 3 — prove it, then look at it
|
||||||
|
|
||||||
|
```bash
|
||||||
|
module-manager module test podman
|
||||||
|
```
|
||||||
|
|
||||||
|
Then open **`https://podman.<domain>`** — Cockpit, with the Podman page.
|
||||||
|
|
||||||
|
- Reachable from the `mgmt` zone only. Not from the internet, by declaration.
|
||||||
|
- Cockpit authenticates against **Linux accounts on the VM** — the cloud-init
|
||||||
|
`tappaas` user has no password until someone sets one.
|
||||||
|
|
||||||
|
`TODO: pre-create the demo login, or set the password live?`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Where this module lives
|
||||||
|
|
||||||
```text
|
```text
|
||||||
services/<svc>/install-service.sh
|
Community/src/larsrossen/containers/podman/
|
||||||
services/<svc>/update-service.sh
|
|
||||||
services/<svc>/test-service.sh
|
|
||||||
services/<svc>/delete-service.sh
|
|
||||||
```
|
```
|
||||||
|
|
||||||
- One service = one thing with its own lifecycle
|
Three homes for a module, all first-class:
|
||||||
- `TODO: when to split into services vs. keep it in the module`
|
|
||||||
|
|
||||||
---
|
| Home | For |
|
||||||
|
|
||||||
## Step 6 — `update.sh`
|
|
||||||
|
|
||||||
- Upgrade in place, no data loss
|
|
||||||
- Version pinning: `TODO`
|
|
||||||
- Rollback story: `TODO`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 7 — `test.sh`
|
|
||||||
|
|
||||||
- Runs against a real environment, not a mock
|
|
||||||
- Exit non-zero on failure, quiet on success
|
|
||||||
- `TODO: what CI runs, and what only a human can check`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Step 8 — Documentation
|
|
||||||
|
|
||||||
| File | Answers |
|
|
||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `README.md` | What is this and why would I install it? |
|
| **TAPPaaS** repo, by pull request | modules the whole project should carry |
|
||||||
| `DESIGN.md` | How is it built, what were the trade-offs? |
|
| **Community** repo | yours, shared, no gatekeeping — today's podman |
|
||||||
| `INSTALL.md` | How do I install it, step by step? |
|
| **Private** repo | yours, not shared |
|
||||||
| `UPGRADE.md` | What breaks between versions? |
|
|
||||||
|
|
||||||
Docs are synced to the docs site — a missing README is a missing page.
|
Adding a repository is one `site-manager repository add`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Try it on a test site
|
## Your turn — identity on this TAPPaaS
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# TODO: the real loop
|
people-manager user add <you> --email <you>@example.org \
|
||||||
|
--roles user --groups orangemaker__users
|
||||||
|
|
||||||
|
people-manager reconcile # preview
|
||||||
|
people-manager reconcile --apply # push to Authentik
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Deploy to the test environment
|
One login per person, roles via groups. `TODO: which groups/roles do participants get?`
|
||||||
2. Run `test.sh`
|
|
||||||
3. Break it on purpose, run it again
|
<!-- Do these live, one per participant, while the podman VM builds. -->
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Submit
|
## Take it further
|
||||||
|
|
||||||
- Branch: `TODO: naming convention`
|
- **Develop a module** — tappaas.org/generated/develop-a-module
|
||||||
- One module per pull request
|
- **Every json field** — tappaas.org/generated/schemas
|
||||||
- CI must be green
|
- **Zones and the access model** — tappaas.org/generated/zones
|
||||||
- Review checklist:
|
- **Repository topology** — tappaas.org/generated/design/cicd-git
|
||||||
- [ ] Idempotent install
|
- **This deck** — slides.makerfloss.eu/tappaas/how-to/new-module
|
||||||
- [ ] `test.sh` passes on a clean site
|
|
||||||
- [ ] Docs complete
|
|
||||||
- [ ] `TODO`
|
|
||||||
|
|
||||||
---
|
Pick an app you want on a sovereign box. Open an issue first — someone may already
|
||||||
|
be packaging it.
|
||||||
## Pitfalls
|
|
||||||
|
|
||||||
- `TODO: the three mistakes reviewers actually see`
|
|
||||||
- `TODO`
|
|
||||||
- `TODO`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Where to go next
|
|
||||||
|
|
||||||
- `src/apps/00-Template/` — the canonical starting point
|
|
||||||
- `docs/ADR/ADR-007 - TAPPaaS Taxonomy.md`
|
|
||||||
- `TODO: contribution guide link`
|
|
||||||
|
|
||||||
**Questions?**
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue