Compare commits
3
Commits
784fff8faf
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ef8d5645e3 | ||
|
|
b5d1ffda95 | ||
|
|
d358ce29c5 |
No files matched your search
@@ -12,4 +12,7 @@
|
|||||||
secrets.yml
|
secrets.yml
|
||||||
vault.yml
|
vault.yml
|
||||||
|
|
||||||
|
roles/*/upstream/container/
|
||||||
|
roles/*/upstream/application/
|
||||||
|
|
||||||
TODO.md
|
TODO.md
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
demo_files_do_not_delete/
|
||||||
@@ -7,8 +7,7 @@ upstream files are reference snapshots only and must not be deployed directly.
|
|||||||
|
|
||||||
Each Docker-backed role has:
|
Each Docker-backed role has:
|
||||||
|
|
||||||
- `templates/compose.yaml.j2`: base deployed Compose file
|
- `templates/compose.yaml.j2`: deployed Compose template
|
||||||
- `templates/compose.override.yaml.j2`: NBOPS-specific local override
|
|
||||||
- `templates/env.j2`: generated environment file
|
- `templates/env.j2`: generated environment file
|
||||||
- `upstream/`: copied upstream reference files
|
- `upstream/`: copied upstream reference files
|
||||||
|
|
||||||
@@ -49,19 +48,28 @@ Entries that use `readme_paths` or `compose_paths` must include both `url` and
|
|||||||
|
|
||||||
The script also looks for Compose-shaped fenced YAML blocks in README files.
|
The script also looks for Compose-shaped fenced YAML blocks in README files.
|
||||||
When exactly one candidate is found, `--apply` writes it as
|
When exactly one candidate is found, `--apply` writes it as
|
||||||
`compose.from-readme.yaml`. Compose YAML fetched from `compose_urls` is
|
`docker-compose.yml` inside that upstream purpose directory. Compose YAML
|
||||||
written as `compose.from-url.yaml`, or `compose.from-url-N.yaml` when a
|
copied from `compose_paths` or fetched from `compose_urls` is also normalized
|
||||||
repository has multiple fallback URLs. A role-level `local_compose_file` is
|
to `docker-compose.yml`. URL sources are fallback material and do not
|
||||||
copied under `roles/<role>/upstream/local/`.
|
overwrite a Compose file already staged from the repository or README parse.
|
||||||
|
A role-level `local_compose_file` is copied under `roles/<role>/upstream/local/`.
|
||||||
|
|
||||||
|
Repository snapshots are written under `roles/<role>/upstream/<purpose>/`.
|
||||||
|
On `--apply`, each `upstream/<purpose>/` directory is replaced with a fresh
|
||||||
|
snapshot for that source, so stale README and Compose files are removed.
|
||||||
|
The script displays a single-line progress bar on interactive terminals while
|
||||||
|
processing sources.
|
||||||
4. Update `roles/<role>/upstream/SOURCE.md` with source URL, ref, snapshot date,
|
4. Update `roles/<role>/upstream/SOURCE.md` with source URL, ref, snapshot date,
|
||||||
fetched paths, and any manual verification notes.
|
fetched paths, and any manual verification notes.
|
||||||
5. Commit upstream snapshot changes separately from NBOPS deployment template
|
5. Commit upstream snapshot changes separately from NBOPS deployment template
|
||||||
changes.
|
changes.
|
||||||
6. Diff upstream snapshots against current role templates.
|
6. Diff upstream snapshots against current role templates.
|
||||||
7. Apply upstream structural changes to `templates/compose.yaml.j2`.
|
7. Apply upstream structural changes to `templates/compose.yaml.j2`. If a
|
||||||
8. Keep NBOPS-local changes in `templates/compose.override.yaml.j2`, including:
|
role-level `upstream/docker-compose.override.yml` exists, treat it as the
|
||||||
Traefik labels, internal networks, host paths, ports, restart policy, logging,
|
primary source for the template; otherwise use `upstream/docker-compose.yml`.
|
||||||
resource limits, and deployment-specific environment wiring.
|
8. Keep NBOPS-local changes in the deployed template, including Traefik labels,
|
||||||
|
internal networks, host paths, ports, restart policy, logging, resource
|
||||||
|
limits, and deployment-specific environment wiring.
|
||||||
9. Replace mutable image tags with pinned versions where practical.
|
9. Replace mutable image tags with pinned versions where practical.
|
||||||
10. Document breaking changes or operator actions in the role README.
|
10. Document breaking changes or operator actions in the role README.
|
||||||
|
|
||||||
@@ -70,6 +78,27 @@ Entries that use `readme_paths` or `compose_paths` must include both `url` and
|
|||||||
- Do not edit generated Compose files on VMs.
|
- Do not edit generated Compose files on VMs.
|
||||||
- Do not deploy files from `upstream/` directly.
|
- Do not deploy files from `upstream/` directly.
|
||||||
- Do not treat `status: verify` sources as trusted until manually checked.
|
- Do not treat `status: verify` sources as trusted until manually checked.
|
||||||
|
- Treat `upstream/<purpose>/` as generated snapshot output; do not keep manual
|
||||||
|
edits there.
|
||||||
|
- To remove generated purpose snapshots and any template files explicitly marked
|
||||||
|
with `nbops: derived-from-upstream`, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/vendor-upstream-snapshots.py --clean-upstreams <role>
|
||||||
|
```
|
||||||
|
|
||||||
|
This deletes directories like `upstream/application/` and
|
||||||
|
`upstream/container/`, but leaves files such as `upstream/SOURCE.md` and
|
||||||
|
root-level override YAMLs alone. The old `--clean-purposes` flag is retained
|
||||||
|
as a compatibility alias.
|
||||||
|
- To target a specific upstream purpose, add `--upstream`:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/vendor-upstream-snapshots.py --clean-upstreams --upstream container <role>
|
||||||
|
```
|
||||||
|
|
||||||
|
`--upstream` may be repeated, or `--upstreams container,application` may be
|
||||||
|
used for comma-separated values.
|
||||||
- Prefer pinned tags or digests over `latest` and other mutable tags.
|
- Prefer pinned tags or digests over `latest` and other mutable tags.
|
||||||
- Keep HTTP application ports unpublished in Traefik mode; document internal
|
- Keep HTTP application ports unpublished in Traefik mode; document internal
|
||||||
service ports as comments in generated Compose.
|
service ports as comments in generated Compose.
|
||||||
|
|||||||
@@ -10,11 +10,67 @@ Expected VM defaults:
|
|||||||
- CPU type `host`
|
- CPU type `host`
|
||||||
- QEMU Guest Agent enabled
|
- QEMU Guest Agent enabled
|
||||||
- Cloud-Init drive attached
|
- Cloud-Init drive attached
|
||||||
|
- Serial console attached as `serial0: socket`
|
||||||
|
- Display set to `vga: serial0` for Proxmox xterm.js console access
|
||||||
- 2 vCPU, 2 GB RAM, 32 GB disk unless a service needs more
|
- 2 vCPU, 2 GB RAM, 32 GB disk unless a service needs more
|
||||||
|
|
||||||
|
Use the helper script on a Proxmox node to create the base template:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/create-proxmox-template.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The script prompts for VMID, name, storage, bridge, CPU, memory, disk size, and
|
||||||
|
image source. Press Enter to accept the displayed defaults. It downloads the
|
||||||
|
Debian 13 GenericCloud qcow2 image unless a local `--image-file` is provided,
|
||||||
|
imports it into Proxmox storage, attaches a Cloud-Init drive, configures serial
|
||||||
|
console support, and converts the VM to a template.
|
||||||
|
|
||||||
|
For automation or previews:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/create-proxmox-template.sh --dry-run --yes
|
||||||
|
scripts/create-proxmox-template.sh --non-interactive --vmid 9000 --storage local-lvm --bridge vmbr0
|
||||||
|
```
|
||||||
|
|
||||||
|
If a PBS storage/datastore ID is supplied, the template's main disk is created
|
||||||
|
with the Proxmox disk `backup=1` flag. Leaving it empty keeps the main disk at
|
||||||
|
`backup=0`.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/create-proxmox-template.sh --pbs-storage pbs-main
|
||||||
|
```
|
||||||
|
|
||||||
|
To initialise all VMs declared in `host_vars/*.yml`, run:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
scripts/init-proxmox-vms.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
The VM initialiser checks whether the template VMID exists. If it does not, it
|
||||||
|
runs `scripts/create-proxmox-template.sh` first. Each host var file with a
|
||||||
|
`proxmox_vmid` is cloned from the template and configured from its `proxmox_*`
|
||||||
|
hardware settings.
|
||||||
|
|
||||||
Cloud-Init is only responsible for Day 0 bootstrap: user creation, SSH
|
Cloud-Init is only responsible for Day 0 bootstrap: user creation, SSH
|
||||||
hardening, Docker installation, baseline packages, firewall defaults, directory
|
hardening, Docker installation, baseline packages, firewall defaults, directory
|
||||||
layout, PKI directory preparation, and `ansible-pull`.
|
layout, PKI directory preparation, enabling `serial-getty@ttyS0.service`, and
|
||||||
|
`ansible-pull`.
|
||||||
|
|
||||||
|
Proxmox hardware settings must be applied outside Cloud-Init. For a VM or
|
||||||
|
template, set:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
qm set <vmid> --serial0 socket --vga serial0
|
||||||
|
```
|
||||||
|
|
||||||
|
The Cloud-Init template enables `serial-getty@ttyS0.service` by default and sets
|
||||||
|
`TERM=xterm-256color` for that getty. Override these variables if needed:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
cloud_init_serial_console_enabled: true
|
||||||
|
cloud_init_serial_console_tty: ttyS0
|
||||||
|
cloud_init_serial_console_term: xterm-256color
|
||||||
|
```
|
||||||
|
|
||||||
Applications and durable service configuration belong in Ansible roles.
|
Applications and durable service configuration belong in Ansible roles.
|
||||||
|
|
||||||
+27
-27
@@ -65,7 +65,7 @@ sources:
|
|||||||
vendor_dir: roles/authentik/upstream
|
vendor_dir: roles/authentik/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/goauthentik/authentik.git
|
url: https://github.com/goauthentik/authentik.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -77,7 +77,7 @@ sources:
|
|||||||
vendor_dir: roles/authentik_ldap/upstream
|
vendor_dir: roles/authentik_ldap/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/goauthentik/authentik.git
|
url: https://github.com/goauthentik/authentik.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -89,7 +89,7 @@ sources:
|
|||||||
vendor_dir: roles/authentik_proxy/upstream
|
vendor_dir: roles/authentik_proxy/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/goauthentik/authentik.git
|
url: https://github.com/goauthentik/authentik.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -101,7 +101,7 @@ sources:
|
|||||||
vendor_dir: roles/authentik_rac/upstream
|
vendor_dir: roles/authentik_rac/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/goauthentik/authentik.git
|
url: https://github.com/goauthentik/authentik.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -113,7 +113,7 @@ sources:
|
|||||||
vendor_dir: roles/cleanuparr/upstream
|
vendor_dir: roles/cleanuparr/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/cleanuparr/cleanuparr.git
|
url: https://github.com/cleanuparr/cleanuparr.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -167,7 +167,7 @@ sources:
|
|||||||
vendor_dir: roles/flaresolverr/upstream
|
vendor_dir: roles/flaresolverr/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/FlareSolverr/FlareSolverr.git
|
url: https://github.com/FlareSolverr/FlareSolverr.git
|
||||||
ref: master
|
ref: master
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -179,7 +179,7 @@ sources:
|
|||||||
vendor_dir: roles/gitea/upstream
|
vendor_dir: roles/gitea/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/go-gitea/gitea.git
|
url: https://github.com/go-gitea/gitea.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -191,7 +191,7 @@ sources:
|
|||||||
vendor_dir: roles/gitea_act_runner/upstream
|
vendor_dir: roles/gitea_act_runner/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://gitea.com/gitea/act_runner.git
|
url: https://gitea.com/gitea/act_runner.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -203,7 +203,7 @@ sources:
|
|||||||
vendor_dir: roles/grafana/upstream
|
vendor_dir: roles/grafana/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/grafana/grafana.git
|
url: https://github.com/grafana/grafana.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -215,7 +215,7 @@ sources:
|
|||||||
vendor_dir: roles/grafana_renderer/upstream
|
vendor_dir: roles/grafana_renderer/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/grafana/grafana-image-renderer.git
|
url: https://github.com/grafana/grafana-image-renderer.git
|
||||||
ref: master
|
ref: master
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -227,7 +227,7 @@ sources:
|
|||||||
vendor_dir: roles/homarr/upstream
|
vendor_dir: roles/homarr/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/homarr-labs/homarr.git
|
url: https://github.com/homarr-labs/homarr.git
|
||||||
ref: dev
|
ref: dev
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -239,7 +239,7 @@ sources:
|
|||||||
vendor_dir: roles/immich/upstream
|
vendor_dir: roles/immich/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/immich-app/immich.git
|
url: https://github.com/immich-app/immich.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -281,7 +281,7 @@ sources:
|
|||||||
vendor_dir: roles/jellyseerr/upstream
|
vendor_dir: roles/jellyseerr/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/Fallenbagel/jellyseerr.git
|
url: https://github.com/Fallenbagel/jellyseerr.git
|
||||||
ref: develop
|
ref: develop
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -305,7 +305,7 @@ sources:
|
|||||||
vendor_dir: roles/kiwix_serve/upstream
|
vendor_dir: roles/kiwix_serve/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/kiwix/kiwix-tools.git
|
url: https://github.com/kiwix/kiwix-tools.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -317,7 +317,7 @@ sources:
|
|||||||
vendor_dir: roles/loki/upstream
|
vendor_dir: roles/loki/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/grafana/loki.git
|
url: https://github.com/grafana/loki.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -341,7 +341,7 @@ sources:
|
|||||||
vendor_dir: roles/ntfy/upstream
|
vendor_dir: roles/ntfy/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/binwiederhier/ntfy.git
|
url: https://github.com/binwiederhier/ntfy.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -353,7 +353,7 @@ sources:
|
|||||||
vendor_dir: roles/ocis/upstream
|
vendor_dir: roles/ocis/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/owncloud/ocis.git
|
url: https://github.com/owncloud/ocis.git
|
||||||
ref: master
|
ref: master
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -365,7 +365,7 @@ sources:
|
|||||||
vendor_dir: roles/paperless_ngx/upstream
|
vendor_dir: roles/paperless_ngx/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/paperless-ngx/paperless-ngx.git
|
url: https://github.com/paperless-ngx/paperless-ngx.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -377,7 +377,7 @@ sources:
|
|||||||
vendor_dir: roles/pelican_panel/upstream
|
vendor_dir: roles/pelican_panel/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/pelican-dev/panel.git
|
url: https://github.com/pelican-dev/panel.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -401,7 +401,7 @@ sources:
|
|||||||
vendor_dir: roles/pinchflat/upstream
|
vendor_dir: roles/pinchflat/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/kieraneglin/pinchflat.git
|
url: https://github.com/kieraneglin/pinchflat.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -437,7 +437,7 @@ sources:
|
|||||||
vendor_dir: roles/pterodactyl/upstream
|
vendor_dir: roles/pterodactyl/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/pterodactyl/panel.git
|
url: https://github.com/pterodactyl/panel.git
|
||||||
ref: 1.0-develop
|
ref: 1.0-develop
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -461,7 +461,7 @@ sources:
|
|||||||
vendor_dir: roles/requarks/upstream
|
vendor_dir: roles/requarks/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/requarks/wiki.git
|
url: https://github.com/requarks/wiki.git
|
||||||
ref: master
|
ref: master
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -485,7 +485,7 @@ sources:
|
|||||||
vendor_dir: roles/traefik/upstream
|
vendor_dir: roles/traefik/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/traefik/traefik.git
|
url: https://github.com/traefik/traefik.git
|
||||||
ref: master
|
ref: master
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -509,7 +509,7 @@ sources:
|
|||||||
vendor_dir: roles/uptime_kuma/upstream
|
vendor_dir: roles/uptime_kuma/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/louislam/uptime-kuma.git
|
url: https://github.com/louislam/uptime-kuma.git
|
||||||
ref: master
|
ref: master
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -521,7 +521,7 @@ sources:
|
|||||||
vendor_dir: roles/valkey/upstream
|
vendor_dir: roles/valkey/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/valkey-io/valkey.git
|
url: https://github.com/valkey-io/valkey.git
|
||||||
ref: unstable
|
ref: unstable
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -533,7 +533,7 @@ sources:
|
|||||||
vendor_dir: roles/vaultwarden/upstream
|
vendor_dir: roles/vaultwarden/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/dani-garcia/vaultwarden.git
|
url: https://github.com/dani-garcia/vaultwarden.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
@@ -545,7 +545,7 @@ sources:
|
|||||||
vendor_dir: roles/wizarr/upstream
|
vendor_dir: roles/wizarr/upstream
|
||||||
local_compose_file: ""
|
local_compose_file: ""
|
||||||
repositories:
|
repositories:
|
||||||
- purpose: application
|
- purpose: container
|
||||||
url: https://github.com/Wizarrrr/wizarr.git
|
url: https://github.com/Wizarrrr/wizarr.git
|
||||||
ref: main
|
ref: main
|
||||||
readme_paths: [README.md]
|
readme_paths: [README.md]
|
||||||
|
|||||||
@@ -1 +1,4 @@
|
|||||||
*.yml
|
all.yml
|
||||||
|
!all.yml.example
|
||||||
|
!README.md
|
||||||
|
!.gitignore
|
||||||
Whitespace-only changes.
@@ -5,3 +5,25 @@ Create one file per VM, for example `host_vars/media-01.yml`.
|
|||||||
Use host variables for hostnames, service enablement, storage paths, and port
|
Use host variables for hostnames, service enablement, storage paths, and port
|
||||||
assignments. Keep credentials and TLS private material outside Git.
|
assignments. Keep credentials and TLS private material outside Git.
|
||||||
|
|
||||||
|
## Proxmox VM Hardware
|
||||||
|
|
||||||
|
`scripts/init-proxmox-vms.sh` reads top-level `proxmox_*` keys from each
|
||||||
|
`host_vars/*.yml` file. Files without `proxmox_vmid` are skipped.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
proxmox_vmid: 101
|
||||||
|
proxmox_name: cloud
|
||||||
|
proxmox_template_vmid: 9000
|
||||||
|
proxmox_storage: local-lvm
|
||||||
|
proxmox_bridge: vmbr0
|
||||||
|
proxmox_memory: 4096
|
||||||
|
proxmox_cores: 2
|
||||||
|
proxmox_cpu: host
|
||||||
|
proxmox_disk_size: 64G
|
||||||
|
proxmox_backup_enabled: true
|
||||||
|
proxmox_ipconfig0: ip=dhcp
|
||||||
|
proxmox_onboot: true
|
||||||
|
proxmox_start: false
|
||||||
|
```
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `alloy` role.
|
Role: `alloy`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,15 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `arr` role.
|
Role: `arr`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- sonarr-container README `README.md`: copied
|
||||||
|
- radarr-container README `README.md`: copied
|
||||||
|
- lidarr-container README `README.md`: copied
|
||||||
|
- bazarr-container README `README.md`: copied
|
||||||
|
README Compose candidates: 4
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `authentik` role.
|
Role: `authentik`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `https://docs.goauthentik.io/compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `authentik_ldap` role.
|
Role: `authentik_ldap`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `https://docs.goauthentik.io/add-secure-apps/outposts/manual-deploy-docker-compose/`: invalid (response is not Compose-shaped YAML)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
authentik-ldap:
|
||||||
|
container_name: authentik-ldap
|
||||||
|
restart: unless-stopped
|
||||||
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/ldap}:latest
|
||||||
|
ports:
|
||||||
|
- 389:3389
|
||||||
|
- 636:6636
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_HOST: https://example.com
|
||||||
|
AUTHENTIK_INSECURE: "false"
|
||||||
|
AUTHENTIK_TOKEN:
|
||||||
|
depends_on:
|
||||||
|
- authentik-server
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `authentik_proxy` role.
|
Role: `authentik_proxy`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `https://docs.goauthentik.io/add-secure-apps/outposts/manual-deploy-docker-compose/`: invalid (response is not Compose-shaped YAML)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
services:
|
||||||
|
authentik_proxy:
|
||||||
|
container_name: authentik-proxy-outpost
|
||||||
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/server}:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
# Optionally specify the container's network, which must be able to reach the core authentik server.
|
||||||
|
# networks:
|
||||||
|
# - foo
|
||||||
|
ports:
|
||||||
|
- 9000:9000
|
||||||
|
- 9443:9443
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_HOST: https://example.com
|
||||||
|
AUTHENTIK_INSECURE: "false"
|
||||||
|
AUTHENTIK_TOKEN:
|
||||||
|
# Optional setting to be used when `authentik_host` for internal communication doesn't match the public URL.
|
||||||
|
# AUTHENTIK_HOST_BROWSER: https://external-domain.tld
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `authentik_rac` role.
|
Role: `authentik_rac`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `https://docs.goauthentik.io/add-secure-apps/outposts/manual-deploy-docker-compose/`: invalid (response is not Compose-shaped YAML)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
authentik-rac:
|
||||||
|
container_name: authentik-rac
|
||||||
|
restart: unless-stopped
|
||||||
|
image: ${AUTHENTIK_IMAGE:-ghcr.io/goauthentik/rac}:latest
|
||||||
|
environment:
|
||||||
|
AUTHENTIK_HOST: https://example.com
|
||||||
|
AUTHENTIK_INSECURE: "false"
|
||||||
|
AUTHENTIK_TOKEN: changeme
|
||||||
|
depends_on:
|
||||||
|
- authentik-server
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `cleanuparr` role.
|
Role: `cleanuparr`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `code_server` role.
|
Role: `code_server`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 1
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `filebrowser_quantum` role.
|
Role: `filebrowser_quantum`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `verify`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
- container Compose URL `https://filebrowserquantum.com/en/docs/user-guides/other/standalone/`: invalid (response is not Compose-shaped YAML)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `fiveetools` role.
|
Role: `fiveetools`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `verify`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
- container Compose `docker-compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `flaresolverr` role.
|
Role: `flaresolverr`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker-compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `gitea` role.
|
Role: `gitea`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `https://docs.gitea.com/installation/install-with-docker-rootless`: invalid (response is not Compose-shaped YAML)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `gitea_act_runner` role.
|
Role: `gitea_act_runner`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `https://docs.gitea.com/usage/actions/act-runner#start-the-runner-using-docker-compose`: invalid (response is not Compose-shaped YAML)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `grafana` role.
|
Role: `grafana`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose URL `[] # Host own, all examples are bad`: invalid (unsupported URL scheme)
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `grafana_renderer` role.
|
Role: `grafana_renderer`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `homarr` role.
|
Role: `homarr`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: missing
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `immich` role.
|
Role: `immich`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker/docker-compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `ipv6nat` role.
|
Role: `ipv6nat`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `jellyfin` role.
|
Role: `jellyfin`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 1
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `jellyseerr` role.
|
Role: `jellyseerr`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker-compose.yml`: missing
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `kavita` role.
|
Role: `kavita`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `kiwix_serve` role.
|
Role: `kiwix_serve`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `loki` role.
|
Role: `loki`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `production/docker-compose.yaml`: missing
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `nginx_content` role.
|
Role: `nginx_content`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `ntfy` role.
|
Role: `ntfy`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker-compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `ocis` role.
|
Role: `ocis`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `deployments/examples/ocis_full/docker-compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `paperless_ngx` role.
|
Role: `paperless_ngx`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker/compose/docker-compose.postgres.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `pelican_panel` role.
|
Role: `pelican_panel`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `compose-full-stack.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `php_fpm` role.
|
Role: `php_fpm`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `pinchflat` role.
|
Role: `pinchflat`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `postgres` role.
|
Role: `postgres`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `prometheus` role.
|
Role: `prometheus`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `documentation/examples/prometheus.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `pterodactyl` role.
|
Role: `pterodactyl`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `qbittorrent` role.
|
Role: `qbittorrent`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 1
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,11 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `requarks` role.
|
Role: `requarks`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `syncthing` role.
|
Role: `syncthing`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 1
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `traefik` role.
|
Role: `traefik`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `transmission` role.
|
Role: `transmission`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- container README `README.md`: copied
|
||||||
|
README Compose candidates: 1
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `uptime_kuma` role.
|
Role: `uptime_kuma`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `compose.yaml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `valkey` role.
|
Role: `valkey`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `vaultwarden` role.
|
Role: `vaultwarden`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker/docker-compose.yml`: missing
|
||||||
|
README Compose candidates: 1
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
# Upstream Snapshot
|
# Upstream Snapshot
|
||||||
|
|
||||||
This directory stores copied upstream reference files for the `wizarr` role.
|
Role: `wizarr`
|
||||||
|
Snapshot source inventory: `docs/upstream-sources.yml`
|
||||||
|
Snapshot status: `tracked`
|
||||||
|
|
||||||
See `docs/upstream-sources.yml` for repository URLs, refs, README paths, Compose paths, and verification status.
|
Fetched repositories:
|
||||||
|
- application README `README.md`: copied
|
||||||
|
- application Compose `docker-compose.yml`: copied
|
||||||
|
README Compose candidates: 0
|
||||||
|
Ambiguous README files: 0
|
||||||
|
|
||||||
Snapshot files are reference material only. Deployments are generated from this role's `templates/` directory.
|
These files are reference snapshots only. Deployments are generated from role templates.
|
||||||
Executable
+425
@@ -0,0 +1,425 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
VMID=9000
|
||||||
|
NAME=debian-13-genericcloud
|
||||||
|
IMAGE_URL=https://cloud.debian.org/images/cloud/trixie/latest/debian-13-genericcloud-amd64.qcow2
|
||||||
|
IMAGE_FILE=
|
||||||
|
CACHE_DIR=/var/lib/vz/template/qcow2
|
||||||
|
STORAGE=local-lvm
|
||||||
|
CLOUDINIT_STORAGE=
|
||||||
|
BRIDGE=vmbr0
|
||||||
|
MEMORY=2048
|
||||||
|
CORES=2
|
||||||
|
CPU=host
|
||||||
|
DISK_SIZE=32G
|
||||||
|
MACHINE=q35
|
||||||
|
BIOS=ovmf
|
||||||
|
SCSI_HW=virtio-scsi-pci
|
||||||
|
NET_MODEL=virtio
|
||||||
|
PBS_STORAGE=
|
||||||
|
DRY_RUN=0
|
||||||
|
ASSUME_YES=0
|
||||||
|
INTERACTIVE=1
|
||||||
|
VMID_SET=0
|
||||||
|
NAME_SET=0
|
||||||
|
IMAGE_URL_SET=0
|
||||||
|
IMAGE_FILE_SET=0
|
||||||
|
CACHE_DIR_SET=0
|
||||||
|
STORAGE_SET=0
|
||||||
|
CLOUDINIT_STORAGE_SET=0
|
||||||
|
BRIDGE_SET=0
|
||||||
|
MEMORY_SET=0
|
||||||
|
CORES_SET=0
|
||||||
|
CPU_SET=0
|
||||||
|
DISK_SIZE_SET=0
|
||||||
|
PBS_STORAGE_SET=0
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Create a Proxmox VM template from the Debian 13 GenericCloud image.
|
||||||
|
|
||||||
|
Run this on a Proxmox node as root.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
scripts/create-proxmox-template.sh [options]
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--vmid ID Template VMID. Default: 9000
|
||||||
|
--name NAME Template name. Default: debian-13-genericcloud
|
||||||
|
--storage STORAGE Disk storage. Default: local-lvm
|
||||||
|
--cloudinit-storage NAME Cloud-Init storage. Default: same as --storage
|
||||||
|
--bridge BRIDGE Network bridge. Default: vmbr0
|
||||||
|
--memory MB Memory in MB. Default: 2048
|
||||||
|
--cores N vCPU count. Default: 2
|
||||||
|
--cpu TYPE CPU type. Default: host
|
||||||
|
--disk-size SIZE Resize imported disk to this size. Default: 32G
|
||||||
|
--image-url URL Debian GenericCloud qcow2 URL.
|
||||||
|
--image-file PATH Use an existing local qcow2 instead of downloading.
|
||||||
|
--cache-dir PATH Download cache directory. Default: /var/lib/vz/template/qcow2
|
||||||
|
--pbs-storage NAME PBS storage/datastore ID. Enables main disk backup flag when set.
|
||||||
|
--dry-run Print commands without executing them.
|
||||||
|
--yes Accept defaults for any unanswered prompts.
|
||||||
|
--non-interactive Do not prompt; use defaults and provided flags.
|
||||||
|
-h, --help Show this help.
|
||||||
|
|
||||||
|
The template is configured with:
|
||||||
|
- Q35 machine type
|
||||||
|
- OVMF / UEFI
|
||||||
|
- VirtIO SCSI
|
||||||
|
- VirtIO network
|
||||||
|
- QEMU guest agent
|
||||||
|
- Cloud-Init drive
|
||||||
|
- serial0 socket
|
||||||
|
- vga serial0 for Proxmox xterm.js console support
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_value() {
|
||||||
|
local label=$1
|
||||||
|
local default_value=$2
|
||||||
|
local value
|
||||||
|
|
||||||
|
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||||
|
printf '%s\n' "$default_value"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s [%s]: ' "$label" "$default_value" >&2
|
||||||
|
read -r value
|
||||||
|
if [ -z "$value" ]; then
|
||||||
|
printf '%s\n' "$default_value"
|
||||||
|
else
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_optional_value() {
|
||||||
|
local label=$1
|
||||||
|
local value
|
||||||
|
|
||||||
|
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||||
|
printf '\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s [leave empty]: ' "$label" >&2
|
||||||
|
read -r value
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_confirm() {
|
||||||
|
local label=$1
|
||||||
|
local value
|
||||||
|
|
||||||
|
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s [Y/n]: ' "$label" >&2
|
||||||
|
read -r value
|
||||||
|
case "$value" in
|
||||||
|
""|y|Y|yes|YES)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf '%s\n' "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf 'ERROR: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
printf '+'
|
||||||
|
printf ' %q' "$@"
|
||||||
|
printf '\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
need_command() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
download_image() {
|
||||||
|
local url=$1
|
||||||
|
local dest=$2
|
||||||
|
|
||||||
|
if [ -s "$dest" ]; then
|
||||||
|
log "Using cached image: $dest"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
run mkdir -p "$(dirname "$dest")"
|
||||||
|
if command -v curl >/dev/null 2>&1; then
|
||||||
|
run curl -fL --retry 3 --retry-delay 2 -o "$dest" "$url"
|
||||||
|
elif command -v wget >/dev/null 2>&1; then
|
||||||
|
run wget -O "$dest" "$url"
|
||||||
|
else
|
||||||
|
die "curl or wget is required to download the image"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--vmid)
|
||||||
|
VMID=${2:?missing value for --vmid}
|
||||||
|
VMID_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--name)
|
||||||
|
NAME=${2:?missing value for --name}
|
||||||
|
NAME_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--storage)
|
||||||
|
STORAGE=${2:?missing value for --storage}
|
||||||
|
STORAGE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--cloudinit-storage)
|
||||||
|
CLOUDINIT_STORAGE=${2:?missing value for --cloudinit-storage}
|
||||||
|
CLOUDINIT_STORAGE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--bridge)
|
||||||
|
BRIDGE=${2:?missing value for --bridge}
|
||||||
|
BRIDGE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--memory)
|
||||||
|
MEMORY=${2:?missing value for --memory}
|
||||||
|
MEMORY_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--cores)
|
||||||
|
CORES=${2:?missing value for --cores}
|
||||||
|
CORES_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--cpu)
|
||||||
|
CPU=${2:?missing value for --cpu}
|
||||||
|
CPU_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--disk-size)
|
||||||
|
DISK_SIZE=${2:?missing value for --disk-size}
|
||||||
|
DISK_SIZE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--image-url)
|
||||||
|
IMAGE_URL=${2:?missing value for --image-url}
|
||||||
|
IMAGE_URL_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--image-file)
|
||||||
|
IMAGE_FILE=${2:?missing value for --image-file}
|
||||||
|
IMAGE_FILE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--cache-dir)
|
||||||
|
CACHE_DIR=${2:?missing value for --cache-dir}
|
||||||
|
CACHE_DIR_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--pbs-storage)
|
||||||
|
PBS_STORAGE=${2:?missing value for --pbs-storage}
|
||||||
|
PBS_STORAGE_SET=1
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--yes)
|
||||||
|
ASSUME_YES=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--non-interactive)
|
||||||
|
INTERACTIVE=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
die "unknown option: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_interactive_config() {
|
||||||
|
if [ "$VMID_SET" -eq 0 ]; then
|
||||||
|
VMID=$(prompt_value "Template VMID" "$VMID")
|
||||||
|
fi
|
||||||
|
if [ "$NAME_SET" -eq 0 ]; then
|
||||||
|
NAME=$(prompt_value "Template name" "$NAME")
|
||||||
|
fi
|
||||||
|
if [ "$STORAGE_SET" -eq 0 ]; then
|
||||||
|
STORAGE=$(prompt_value "Disk storage" "$STORAGE")
|
||||||
|
fi
|
||||||
|
if [ "$CLOUDINIT_STORAGE_SET" -eq 0 ]; then
|
||||||
|
CLOUDINIT_STORAGE=$(prompt_value "Cloud-Init storage" "${CLOUDINIT_STORAGE:-$STORAGE}")
|
||||||
|
fi
|
||||||
|
if [ "$BRIDGE_SET" -eq 0 ]; then
|
||||||
|
BRIDGE=$(prompt_value "Network bridge" "$BRIDGE")
|
||||||
|
fi
|
||||||
|
if [ "$MEMORY_SET" -eq 0 ]; then
|
||||||
|
MEMORY=$(prompt_value "Memory MB" "$MEMORY")
|
||||||
|
fi
|
||||||
|
if [ "$CORES_SET" -eq 0 ]; then
|
||||||
|
CORES=$(prompt_value "vCPU cores" "$CORES")
|
||||||
|
fi
|
||||||
|
if [ "$CPU_SET" -eq 0 ]; then
|
||||||
|
CPU=$(prompt_value "CPU type" "$CPU")
|
||||||
|
fi
|
||||||
|
if [ "$DISK_SIZE_SET" -eq 0 ]; then
|
||||||
|
DISK_SIZE=$(prompt_value "Disk size after resize" "$DISK_SIZE")
|
||||||
|
fi
|
||||||
|
if [ "$CACHE_DIR_SET" -eq 0 ]; then
|
||||||
|
CACHE_DIR=$(prompt_value "Image cache directory" "$CACHE_DIR")
|
||||||
|
fi
|
||||||
|
if [ "$IMAGE_FILE_SET" -eq 0 ]; then
|
||||||
|
IMAGE_FILE=$(prompt_optional_value "Local qcow2 image file")
|
||||||
|
fi
|
||||||
|
if [ -z "$IMAGE_FILE" ] && [ "$IMAGE_URL_SET" -eq 0 ]; then
|
||||||
|
IMAGE_URL=$(prompt_value "Debian GenericCloud image URL" "$IMAGE_URL")
|
||||||
|
fi
|
||||||
|
if [ "$PBS_STORAGE_SET" -eq 0 ]; then
|
||||||
|
PBS_STORAGE=$(prompt_optional_value "PBS storage/datastore ID for backup flag")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
print_summary() {
|
||||||
|
cat <<SUMMARY
|
||||||
|
|
||||||
|
Template configuration:
|
||||||
|
VMID: $VMID
|
||||||
|
Name: $NAME
|
||||||
|
Disk storage: $STORAGE
|
||||||
|
Cloud-Init storage: $CLOUDINIT_STORAGE
|
||||||
|
Bridge: $BRIDGE
|
||||||
|
Memory: $MEMORY MB
|
||||||
|
Cores: $CORES
|
||||||
|
CPU: $CPU
|
||||||
|
Disk size: $DISK_SIZE
|
||||||
|
Machine: $MACHINE
|
||||||
|
BIOS: $BIOS
|
||||||
|
Serial console: serial0 socket, vga serial0
|
||||||
|
SUMMARY
|
||||||
|
|
||||||
|
if [ -n "$IMAGE_FILE" ]; then
|
||||||
|
printf ' Image file: %s\n' "$IMAGE_FILE"
|
||||||
|
else
|
||||||
|
printf ' Image URL: %s\n' "$IMAGE_URL"
|
||||||
|
printf ' Cache directory: %s\n' "$CACHE_DIR"
|
||||||
|
fi
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
printf ' Mode: dry-run\n'
|
||||||
|
fi
|
||||||
|
if [ -n "$PBS_STORAGE" ]; then
|
||||||
|
printf ' Backup flag: enabled for main disk; PBS storage: %s\n' "$PBS_STORAGE"
|
||||||
|
else
|
||||||
|
printf ' Backup flag: disabled for main disk\n'
|
||||||
|
fi
|
||||||
|
printf '\n'
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
parse_args "$@"
|
||||||
|
|
||||||
|
collect_interactive_config
|
||||||
|
|
||||||
|
if [ -z "$CLOUDINIT_STORAGE" ]; then
|
||||||
|
CLOUDINIT_STORAGE=$STORAGE
|
||||||
|
fi
|
||||||
|
|
||||||
|
print_summary
|
||||||
|
if ! prompt_confirm "Create this Proxmox template?"; then
|
||||||
|
die "cancelled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" -eq 0 ]; then
|
||||||
|
need_command qm
|
||||||
|
need_command awk
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ] && [ "$DRY_RUN" -eq 0 ]; then
|
||||||
|
die "run this script as root on a Proxmox node"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" -eq 0 ] && qm status "$VMID" >/dev/null 2>&1; then
|
||||||
|
die "VMID $VMID already exists; choose another --vmid or remove it manually"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local image_path
|
||||||
|
if [ -n "$IMAGE_FILE" ]; then
|
||||||
|
image_path=$IMAGE_FILE
|
||||||
|
[ "$DRY_RUN" -eq 1 ] || [ -f "$image_path" ] || die "image file not found: $image_path"
|
||||||
|
else
|
||||||
|
image_path="$CACHE_DIR/$(basename "$IMAGE_URL")"
|
||||||
|
download_image "$IMAGE_URL" "$image_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run qm create "$VMID" \
|
||||||
|
--name "$NAME" \
|
||||||
|
--ostype l26 \
|
||||||
|
--machine "$MACHINE" \
|
||||||
|
--bios "$BIOS" \
|
||||||
|
--memory "$MEMORY" \
|
||||||
|
--cores "$CORES" \
|
||||||
|
--cpu "$CPU" \
|
||||||
|
--agent enabled=1 \
|
||||||
|
--scsihw "$SCSI_HW" \
|
||||||
|
--net0 "$NET_MODEL,bridge=$BRIDGE" \
|
||||||
|
--serial0 socket \
|
||||||
|
--vga serial0
|
||||||
|
|
||||||
|
if [ "$BIOS" = "ovmf" ]; then
|
||||||
|
run qm set "$VMID" --efidisk0 "$STORAGE:0,pre-enrolled-keys=0"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run qm importdisk "$VMID" "$image_path" "$STORAGE"
|
||||||
|
|
||||||
|
local imported_disk
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
imported_disk="$STORAGE:vm-$VMID-disk-0"
|
||||||
|
log "Dry-run assumption: imported disk will be $imported_disk"
|
||||||
|
else
|
||||||
|
imported_disk=$(qm config "$VMID" | awk -F': ' '/^unused[0-9]+:/{print $2; exit}')
|
||||||
|
[ -n "$imported_disk" ] || die "could not find imported unused disk in qm config for VMID $VMID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
local disk_backup=0
|
||||||
|
if [ -n "$PBS_STORAGE" ]; then
|
||||||
|
disk_backup=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
run qm set "$VMID" --scsi0 "$imported_disk,discard=on,ssd=1,backup=$disk_backup"
|
||||||
|
run qm set "$VMID" --ide2 "$CLOUDINIT_STORAGE:cloudinit"
|
||||||
|
run qm set "$VMID" --boot order=scsi0
|
||||||
|
run qm set "$VMID" --ipconfig0 ip=dhcp
|
||||||
|
|
||||||
|
if [ -n "$DISK_SIZE" ]; then
|
||||||
|
run qm resize "$VMID" scsi0 "$DISK_SIZE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
run qm template "$VMID"
|
||||||
|
|
||||||
|
log "Created Proxmox template $NAME ($VMID)"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Executable
+367
@@ -0,0 +1,367 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
HOST_VARS_DIR=host_vars
|
||||||
|
TEMPLATE_VMID=9000
|
||||||
|
TEMPLATE_STORAGE=local-lvm
|
||||||
|
TEMPLATE_BRIDGE=vmbr0
|
||||||
|
PBS_STORAGE=
|
||||||
|
DRY_RUN=0
|
||||||
|
ASSUME_YES=0
|
||||||
|
INTERACTIVE=1
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
Initialise Proxmox VMs from host_vars/*.yml.
|
||||||
|
|
||||||
|
Run this on a Proxmox node as root from the repository root.
|
||||||
|
|
||||||
|
Each host var file must set proxmox_vmid to be managed by this script.
|
||||||
|
Files without proxmox_vmid are skipped.
|
||||||
|
|
||||||
|
Supported host vars:
|
||||||
|
proxmox_vmid: 101
|
||||||
|
proxmox_name: cloud
|
||||||
|
proxmox_template_vmid: 9000
|
||||||
|
proxmox_storage: local-lvm
|
||||||
|
proxmox_bridge: vmbr0
|
||||||
|
proxmox_memory: 4096
|
||||||
|
proxmox_cores: 2
|
||||||
|
proxmox_cpu: host
|
||||||
|
proxmox_disk_size: 64G
|
||||||
|
proxmox_backup_enabled: true
|
||||||
|
proxmox_ipconfig0: ip=dhcp
|
||||||
|
proxmox_onboot: true
|
||||||
|
proxmox_start: false
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--host-vars-dir PATH Host vars directory. Default: host_vars
|
||||||
|
--template-vmid ID Default template VMID. Default: 9000
|
||||||
|
--template-storage NAME Storage used if template must be created. Default: local-lvm
|
||||||
|
--template-bridge NAME Bridge used if template must be created. Default: vmbr0
|
||||||
|
--pbs-storage NAME PBS storage/datastore ID. Passed to template script and used as default backup target note.
|
||||||
|
--dry-run Print commands without executing them.
|
||||||
|
--yes Accept defaults for prompts.
|
||||||
|
--non-interactive Do not prompt.
|
||||||
|
-h, --help Show this help.
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf '%s\n' "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
die() {
|
||||||
|
printf 'ERROR: %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
printf '+'
|
||||||
|
printf ' %q' "$@"
|
||||||
|
printf '\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
"$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
need_command() {
|
||||||
|
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_value() {
|
||||||
|
local label=$1
|
||||||
|
local default_value=$2
|
||||||
|
local value
|
||||||
|
|
||||||
|
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||||
|
printf '%s\n' "$default_value"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s [%s]: ' "$label" "$default_value" >&2
|
||||||
|
read -r value
|
||||||
|
if [ -z "$value" ]; then
|
||||||
|
printf '%s\n' "$default_value"
|
||||||
|
else
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_optional_value() {
|
||||||
|
local label=$1
|
||||||
|
local value
|
||||||
|
|
||||||
|
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||||
|
printf '\n'
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s [leave empty]: ' "$label" >&2
|
||||||
|
read -r value
|
||||||
|
printf '%s\n' "$value"
|
||||||
|
}
|
||||||
|
|
||||||
|
prompt_confirm() {
|
||||||
|
local label=$1
|
||||||
|
local value
|
||||||
|
|
||||||
|
if [ "$ASSUME_YES" -eq 1 ] || [ "$INTERACTIVE" -eq 0 ] || [ ! -t 0 ]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf '%s [Y/n]: ' "$label" >&2
|
||||||
|
read -r value
|
||||||
|
case "$value" in
|
||||||
|
""|y|Y|yes|YES)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
yaml_scalar() {
|
||||||
|
local file=$1
|
||||||
|
local key=$2
|
||||||
|
awk -v key="$key" '
|
||||||
|
$0 ~ "^[[:space:]]*" key ":" {
|
||||||
|
sub("^[[:space:]]*" key ":[[:space:]]*", "", $0)
|
||||||
|
sub("[[:space:]]+#.*$", "", $0)
|
||||||
|
gsub(/^["'\'']|["'\'']$/, "", $0)
|
||||||
|
print $0
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
' "$file"
|
||||||
|
}
|
||||||
|
|
||||||
|
bool_true() {
|
||||||
|
case "${1:-}" in
|
||||||
|
true|True|TRUE|yes|Yes|YES|1|on|On|ON)
|
||||||
|
return 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
return 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_args() {
|
||||||
|
while [ "$#" -gt 0 ]; do
|
||||||
|
case "$1" in
|
||||||
|
--host-vars-dir)
|
||||||
|
HOST_VARS_DIR=${2:?missing value for --host-vars-dir}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--template-vmid)
|
||||||
|
TEMPLATE_VMID=${2:?missing value for --template-vmid}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--template-storage)
|
||||||
|
TEMPLATE_STORAGE=${2:?missing value for --template-storage}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--template-bridge)
|
||||||
|
TEMPLATE_BRIDGE=${2:?missing value for --template-bridge}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--pbs-storage)
|
||||||
|
PBS_STORAGE=${2:?missing value for --pbs-storage}
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--dry-run)
|
||||||
|
DRY_RUN=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--yes)
|
||||||
|
ASSUME_YES=1
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--non-interactive)
|
||||||
|
INTERACTIVE=0
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
-h|--help)
|
||||||
|
usage
|
||||||
|
exit 0
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
die "unknown option: $1"
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
collect_config() {
|
||||||
|
TEMPLATE_VMID=$(prompt_value "Default template VMID" "$TEMPLATE_VMID")
|
||||||
|
TEMPLATE_STORAGE=$(prompt_value "Default template/storage name" "$TEMPLATE_STORAGE")
|
||||||
|
TEMPLATE_BRIDGE=$(prompt_value "Default bridge" "$TEMPLATE_BRIDGE")
|
||||||
|
if [ -z "$PBS_STORAGE" ]; then
|
||||||
|
PBS_STORAGE=$(prompt_optional_value "PBS storage/datastore ID")
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
template_exists() {
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
qm status "$TEMPLATE_VMID" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_exists() {
|
||||||
|
local vmid=$1
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
qm status "$vmid" >/dev/null 2>&1
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_template() {
|
||||||
|
if template_exists; then
|
||||||
|
log "Template VMID $TEMPLATE_VMID already exists."
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Template VMID $TEMPLATE_VMID not found; creating it first."
|
||||||
|
local args=(
|
||||||
|
scripts/create-proxmox-template.sh
|
||||||
|
--vmid "$TEMPLATE_VMID"
|
||||||
|
--storage "$TEMPLATE_STORAGE"
|
||||||
|
--bridge "$TEMPLATE_BRIDGE"
|
||||||
|
--yes
|
||||||
|
--non-interactive
|
||||||
|
)
|
||||||
|
if [ -n "$PBS_STORAGE" ]; then
|
||||||
|
args+=(--pbs-storage "$PBS_STORAGE")
|
||||||
|
fi
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
args+=(--dry-run)
|
||||||
|
fi
|
||||||
|
run "${args[@]}"
|
||||||
|
}
|
||||||
|
|
||||||
|
disk_volume() {
|
||||||
|
local vmid=$1
|
||||||
|
local storage=$2
|
||||||
|
if [ "$DRY_RUN" -eq 1 ]; then
|
||||||
|
printf '%s:vm-%s-disk-0\n' "$storage" "$vmid"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
qm config "$vmid" | awk -F': ' '/^scsi0:/{split($2, parts, ","); print parts[1]; exit}'
|
||||||
|
}
|
||||||
|
|
||||||
|
init_vm_from_host_vars() {
|
||||||
|
local file=$1
|
||||||
|
local stem
|
||||||
|
stem=$(basename "$file" .yml)
|
||||||
|
|
||||||
|
local vmid
|
||||||
|
vmid=$(yaml_scalar "$file" proxmox_vmid)
|
||||||
|
if [ -z "$vmid" ]; then
|
||||||
|
log "Skipping $file: proxmox_vmid not set"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local name template_vmid storage bridge memory cores cpu disk_size backup_enabled ipconfig0 onboot start_vm
|
||||||
|
name=$(yaml_scalar "$file" proxmox_name)
|
||||||
|
[ -n "$name" ] || name=$(yaml_scalar "$file" nbops_hostname)
|
||||||
|
[ -n "$name" ] || name=$stem
|
||||||
|
template_vmid=$(yaml_scalar "$file" proxmox_template_vmid)
|
||||||
|
[ -n "$template_vmid" ] || template_vmid=$TEMPLATE_VMID
|
||||||
|
storage=$(yaml_scalar "$file" proxmox_storage)
|
||||||
|
[ -n "$storage" ] || storage=$TEMPLATE_STORAGE
|
||||||
|
bridge=$(yaml_scalar "$file" proxmox_bridge)
|
||||||
|
[ -n "$bridge" ] || bridge=$TEMPLATE_BRIDGE
|
||||||
|
memory=$(yaml_scalar "$file" proxmox_memory)
|
||||||
|
[ -n "$memory" ] || memory=2048
|
||||||
|
cores=$(yaml_scalar "$file" proxmox_cores)
|
||||||
|
[ -n "$cores" ] || cores=2
|
||||||
|
cpu=$(yaml_scalar "$file" proxmox_cpu)
|
||||||
|
[ -n "$cpu" ] || cpu=host
|
||||||
|
disk_size=$(yaml_scalar "$file" proxmox_disk_size)
|
||||||
|
[ -n "$disk_size" ] || disk_size=32G
|
||||||
|
backup_enabled=$(yaml_scalar "$file" proxmox_backup_enabled)
|
||||||
|
[ -n "$backup_enabled" ] || backup_enabled=false
|
||||||
|
ipconfig0=$(yaml_scalar "$file" proxmox_ipconfig0)
|
||||||
|
[ -n "$ipconfig0" ] || ipconfig0=ip=dhcp
|
||||||
|
onboot=$(yaml_scalar "$file" proxmox_onboot)
|
||||||
|
[ -n "$onboot" ] || onboot=true
|
||||||
|
start_vm=$(yaml_scalar "$file" proxmox_start)
|
||||||
|
[ -n "$start_vm" ] || start_vm=false
|
||||||
|
|
||||||
|
if vm_exists "$vmid"; then
|
||||||
|
log "Skipping $name ($vmid): VM already exists"
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Initialising $name ($vmid) from $file"
|
||||||
|
run qm clone "$template_vmid" "$vmid" --name "$name" --full true --storage "$storage"
|
||||||
|
run qm set "$vmid" --memory "$memory" --cores "$cores" --cpu "$cpu"
|
||||||
|
run qm set "$vmid" --net0 "virtio,bridge=$bridge"
|
||||||
|
run qm set "$vmid" --ipconfig0 "$ipconfig0"
|
||||||
|
|
||||||
|
if bool_true "$onboot"; then
|
||||||
|
run qm set "$vmid" --onboot 1
|
||||||
|
else
|
||||||
|
run qm set "$vmid" --onboot 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
local volume backup_flag
|
||||||
|
volume=$(disk_volume "$vmid" "$storage")
|
||||||
|
backup_flag=0
|
||||||
|
if bool_true "$backup_enabled"; then
|
||||||
|
backup_flag=1
|
||||||
|
fi
|
||||||
|
run qm set "$vmid" --scsi0 "$volume,discard=on,ssd=1,backup=$backup_flag"
|
||||||
|
|
||||||
|
if [ -n "$disk_size" ]; then
|
||||||
|
run qm resize "$vmid" scsi0 "$disk_size"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if bool_true "$start_vm"; then
|
||||||
|
run qm start "$vmid"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
parse_args "$@"
|
||||||
|
collect_config
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" -eq 0 ]; then
|
||||||
|
need_command qm
|
||||||
|
need_command awk
|
||||||
|
[ "$(id -u)" -eq 0 ] || die "run this script as root on a Proxmox node"
|
||||||
|
fi
|
||||||
|
|
||||||
|
[ -d "$HOST_VARS_DIR" ] || die "host vars directory not found: $HOST_VARS_DIR"
|
||||||
|
|
||||||
|
cat <<SUMMARY
|
||||||
|
|
||||||
|
VM initialisation:
|
||||||
|
Host vars: $HOST_VARS_DIR/*.yml
|
||||||
|
Template VMID: $TEMPLATE_VMID
|
||||||
|
Default storage: $TEMPLATE_STORAGE
|
||||||
|
Default bridge: $TEMPLATE_BRIDGE
|
||||||
|
PBS storage: ${PBS_STORAGE:-not set}
|
||||||
|
Mode: $([ "$DRY_RUN" -eq 1 ] && printf 'dry-run' || printf 'apply')
|
||||||
|
|
||||||
|
SUMMARY
|
||||||
|
|
||||||
|
if ! prompt_confirm "Initialise VMs from host vars?"; then
|
||||||
|
die "cancelled"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_template
|
||||||
|
|
||||||
|
local found=0
|
||||||
|
for file in "$HOST_VARS_DIR"/*.yml; do
|
||||||
|
[ -e "$file" ] || continue
|
||||||
|
found=1
|
||||||
|
init_vm_from_host_vars "$file"
|
||||||
|
done
|
||||||
|
[ "$found" -eq 1 ] || die "no .yml files found in $HOST_VARS_DIR"
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
@@ -22,6 +22,9 @@ from urllib.request import Request, urlopen
|
|||||||
ROOT = Path(__file__).resolve().parents[1]
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
INVENTORY = ROOT / "docs" / "upstream-sources.yml"
|
INVENTORY = ROOT / "docs" / "upstream-sources.yml"
|
||||||
COMPOSE_BLOCK_LANGS = {"", "yaml", "yml", "docker-compose", "compose"}
|
COMPOSE_BLOCK_LANGS = {"", "yaml", "yml", "docker-compose", "compose"}
|
||||||
|
NORMALIZED_COMPOSE_NAME = "docker-compose.yml"
|
||||||
|
DERIVED_TEMPLATE_MARKER = "nbops: derived-from-upstream"
|
||||||
|
DERIVED_TEMPLATE_NAMES = ("compose.yaml.j2", "compose.override.yaml.j2")
|
||||||
|
|
||||||
|
|
||||||
def parse_inline_list(value: str) -> list[str]:
|
def parse_inline_list(value: str) -> list[str]:
|
||||||
@@ -137,6 +140,21 @@ def copy_if_present(src_root: Path, relative_path: str, dest_root: Path, apply:
|
|||||||
return "copied"
|
return "copied"
|
||||||
|
|
||||||
|
|
||||||
|
def copy_compose_if_present(src_root: Path, relative_path: str, dest_root: Path, apply: bool) -> str:
|
||||||
|
src = src_root / relative_path
|
||||||
|
if not src.is_file():
|
||||||
|
return "missing"
|
||||||
|
if not looks_like_compose(src.read_text(errors="replace")):
|
||||||
|
return "invalid"
|
||||||
|
if not apply:
|
||||||
|
return "found"
|
||||||
|
|
||||||
|
dest = dest_root / NORMALIZED_COMPOSE_NAME
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.copy2(src, dest)
|
||||||
|
return "copied"
|
||||||
|
|
||||||
|
|
||||||
def copy_local_file_if_present(relative_path: str, dest_root: Path, apply: bool) -> str:
|
def copy_local_file_if_present(relative_path: str, dest_root: Path, apply: bool) -> str:
|
||||||
src = ROOT / relative_path
|
src = ROOT / relative_path
|
||||||
if not src.is_file():
|
if not src.is_file():
|
||||||
@@ -152,6 +170,17 @@ def copy_local_file_if_present(relative_path: str, dest_root: Path, apply: bool)
|
|||||||
return "copied"
|
return "copied"
|
||||||
|
|
||||||
|
|
||||||
|
def replace_directory(src: Path, dest: Path) -> None:
|
||||||
|
if dest.exists():
|
||||||
|
shutil.rmtree(dest)
|
||||||
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
shutil.move(str(src), str(dest))
|
||||||
|
|
||||||
|
|
||||||
|
def preferred_override_exists(snapshot_dir: Path) -> bool:
|
||||||
|
return (snapshot_dir / "docker-compose.override.yml").is_file()
|
||||||
|
|
||||||
|
|
||||||
def fetch_compose_url(url: str) -> tuple[str, str, str]:
|
def fetch_compose_url(url: str) -> tuple[str, str, str]:
|
||||||
parsed = urlparse(url)
|
parsed = urlparse(url)
|
||||||
if parsed.scheme not in {"http", "https"}:
|
if parsed.scheme not in {"http", "https"}:
|
||||||
@@ -173,12 +202,6 @@ def fetch_compose_url(url: str) -> tuple[str, str, str]:
|
|||||||
return "found", body, ""
|
return "found", body, ""
|
||||||
|
|
||||||
|
|
||||||
def compose_url_filename(index: int, total: int) -> str:
|
|
||||||
if total == 1:
|
|
||||||
return "compose.from-url.yaml"
|
|
||||||
return f"compose.from-url-{index}.yaml"
|
|
||||||
|
|
||||||
|
|
||||||
def extract_fenced_blocks(markdown: str) -> list[tuple[str, str]]:
|
def extract_fenced_blocks(markdown: str) -> list[tuple[str, str]]:
|
||||||
pattern = re.compile(r"^```([^\n`]*)\n(.*?)^```", re.MULTILINE | re.DOTALL)
|
pattern = re.compile(r"^```([^\n`]*)\n(.*?)^```", re.MULTILINE | re.DOTALL)
|
||||||
blocks = []
|
blocks = []
|
||||||
@@ -226,6 +249,61 @@ def print_table(rows: list[list[str]]) -> None:
|
|||||||
print(fmt(row))
|
print(fmt(row))
|
||||||
|
|
||||||
|
|
||||||
|
def progress_enabled() -> bool:
|
||||||
|
return sys.stderr.isatty()
|
||||||
|
|
||||||
|
|
||||||
|
def render_progress(current: int, total: int, label: str) -> None:
|
||||||
|
if not progress_enabled() or total <= 0:
|
||||||
|
return
|
||||||
|
width = 28
|
||||||
|
filled = min(width, int(width * current / total))
|
||||||
|
bar = "#" * filled + "-" * (width - filled)
|
||||||
|
sys.stderr.write(f"\r\033[K[{bar}] {current}/{total} {label}")
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def clear_progress() -> None:
|
||||||
|
if progress_enabled():
|
||||||
|
sys.stderr.write("\r\033[K")
|
||||||
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|
||||||
|
def snapshot_has_content(path: Path) -> bool:
|
||||||
|
return path.exists() and any(path.iterdir())
|
||||||
|
|
||||||
|
|
||||||
|
def split_filter_values(values: list[str], csv_value: str) -> set[str]:
|
||||||
|
filters: set[str] = set()
|
||||||
|
for value in values:
|
||||||
|
filters.update(item.strip() for item in value.split(",") if item.strip())
|
||||||
|
filters.update(item.strip() for item in csv_value.split(",") if item.strip())
|
||||||
|
return filters
|
||||||
|
|
||||||
|
|
||||||
|
def filtered_repositories(config: dict, upstream_filter: set[str]) -> list[dict]:
|
||||||
|
repos = config["repositories"]
|
||||||
|
if not upstream_filter:
|
||||||
|
return repos
|
||||||
|
return [repo for repo in repos if repo["purpose"] in upstream_filter]
|
||||||
|
|
||||||
|
|
||||||
|
def clean_derived_templates(role: str) -> list[list[str]]:
|
||||||
|
rows: list[list[str]] = []
|
||||||
|
template_dir = ROOT / "roles" / role / "templates"
|
||||||
|
for name in DERIVED_TEMPLATE_NAMES:
|
||||||
|
path = template_dir / name
|
||||||
|
if not path.is_file():
|
||||||
|
continue
|
||||||
|
text = path.read_text(errors="replace")
|
||||||
|
if DERIVED_TEMPLATE_MARKER not in text:
|
||||||
|
rows.append([role, "template", str(path), "kept: unmarked"])
|
||||||
|
continue
|
||||||
|
path.unlink()
|
||||||
|
rows.append([role, "template", str(path), "removed"])
|
||||||
|
return rows
|
||||||
|
|
||||||
|
|
||||||
def snapshot_source(vendor_dir: Path, role: str, status: str, notes: list[str], apply: bool) -> None:
|
def snapshot_source(vendor_dir: Path, role: str, status: str, notes: list[str], apply: bool) -> None:
|
||||||
if not apply:
|
if not apply:
|
||||||
return
|
return
|
||||||
@@ -246,11 +324,73 @@ def snapshot_source(vendor_dir: Path, role: str, status: str, notes: list[str],
|
|||||||
(vendor_dir / "SOURCE.md").write_text("\n".join(text))
|
(vendor_dir / "SOURCE.md").write_text("\n".join(text))
|
||||||
|
|
||||||
|
|
||||||
|
def clean_upstream_directories(selected: dict[str, dict], upstream_filter: set[str]) -> int:
|
||||||
|
rows: list[list[str]] = []
|
||||||
|
removed = 0
|
||||||
|
total = sum(len(filtered_repositories(config, upstream_filter)) for config in selected.values())
|
||||||
|
current = 0
|
||||||
|
|
||||||
|
for role, config in selected.items():
|
||||||
|
vendor_dir = ROOT / config["vendor_dir"]
|
||||||
|
repos = filtered_repositories(config, upstream_filter)
|
||||||
|
for repo in repos:
|
||||||
|
current += 1
|
||||||
|
purpose_dir = vendor_dir / repo["purpose"]
|
||||||
|
render_progress(current, total, f"clean {role}/{repo['purpose']}")
|
||||||
|
if purpose_dir.is_dir():
|
||||||
|
shutil.rmtree(purpose_dir)
|
||||||
|
removed += 1
|
||||||
|
result = "removed"
|
||||||
|
else:
|
||||||
|
result = "missing"
|
||||||
|
rows.append([role, repo["purpose"], str(purpose_dir), result])
|
||||||
|
rows.extend(clean_derived_templates(role))
|
||||||
|
|
||||||
|
clear_progress()
|
||||||
|
headers = ["Role", "Purpose", "Path", "Result"]
|
||||||
|
widths = [len(header) for header in headers]
|
||||||
|
for row in rows:
|
||||||
|
for index, value in enumerate(row):
|
||||||
|
widths[index] = max(widths[index], len(value))
|
||||||
|
|
||||||
|
def fmt(row: list[str]) -> str:
|
||||||
|
return " ".join(value.ljust(widths[index]) for index, value in enumerate(row))
|
||||||
|
|
||||||
|
print(fmt(headers))
|
||||||
|
print(" ".join("-" * width for width in widths))
|
||||||
|
for row in rows:
|
||||||
|
print(fmt(row))
|
||||||
|
print(f"\nRemoved {removed} upstream purpose directories.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
def main() -> int:
|
||||||
parser = argparse.ArgumentParser(description="Test or vendor upstream README/Compose snapshots.")
|
parser = argparse.ArgumentParser(description="Test or vendor upstream README/Compose snapshots.")
|
||||||
parser.add_argument("roles", nargs="*", help="Role names to process. Defaults to all roles.")
|
parser.add_argument("roles", nargs="*", help="Role names to process. Defaults to all roles.")
|
||||||
parser.add_argument("--apply", action="store_true", help="Copy fetched files into role upstream directories.")
|
parser.add_argument("--apply", action="store_true", help="Copy fetched files into role upstream directories.")
|
||||||
parser.add_argument("--no-parse-readme", action="store_true", help="Do not extract Compose YAML from README blocks.")
|
parser.add_argument("--no-parse-readme", action="store_true", help="Do not extract Compose YAML from README blocks.")
|
||||||
|
parser.add_argument(
|
||||||
|
"--clean-upstreams",
|
||||||
|
action="store_true",
|
||||||
|
help="Delete upstream/<purpose> directories and marked derived templates for selected roles, then exit.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--clean-purposes",
|
||||||
|
dest="clean_upstreams",
|
||||||
|
action="store_true",
|
||||||
|
help=argparse.SUPPRESS,
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--upstream",
|
||||||
|
action="append",
|
||||||
|
default=[],
|
||||||
|
help="Limit processing to an upstream purpose name such as container or application. Repeatable.",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--upstreams",
|
||||||
|
default="",
|
||||||
|
help="Comma-separated upstream purpose names to process.",
|
||||||
|
)
|
||||||
parser.add_argument("--list", action="store_true", help="List roles in the upstream inventory and exit.")
|
parser.add_argument("--list", action="store_true", help="List roles in the upstream inventory and exit.")
|
||||||
parser.add_argument("--validate-inventory", action="store_true", help="Parse the inventory and exit without cloning.")
|
parser.add_argument("--validate-inventory", action="store_true", help="Parse the inventory and exit without cloning.")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
@@ -269,9 +409,22 @@ def main() -> int:
|
|||||||
if not selected:
|
if not selected:
|
||||||
print(f"no matching roles found for: {', '.join(args.roles)}", file=sys.stderr)
|
print(f"no matching roles found for: {', '.join(args.roles)}", file=sys.stderr)
|
||||||
return 2
|
return 2
|
||||||
|
upstream_filter = split_filter_values(args.upstream, args.upstreams)
|
||||||
|
if upstream_filter:
|
||||||
|
known_upstreams = {repo["purpose"] for config in selected.values() for repo in config["repositories"]}
|
||||||
|
unknown_upstreams = sorted(upstream_filter - known_upstreams)
|
||||||
|
if unknown_upstreams:
|
||||||
|
print(f"no matching upstream purpose found for: {', '.join(unknown_upstreams)}", file=sys.stderr)
|
||||||
|
return 2
|
||||||
|
if args.clean_upstreams:
|
||||||
|
return clean_upstream_directories(selected, upstream_filter)
|
||||||
|
|
||||||
rows: list[list[str]] = []
|
rows: list[list[str]] = []
|
||||||
failures: list[str] = []
|
failures: list[str] = []
|
||||||
|
total_steps = sum(len(filtered_repositories(config, upstream_filter)) for config in selected.values())
|
||||||
|
if not upstream_filter:
|
||||||
|
total_steps += sum(1 for config in selected.values() if config["local_compose_file"])
|
||||||
|
current_step = 0
|
||||||
|
|
||||||
for role, config in selected.items():
|
for role, config in selected.items():
|
||||||
vendor_dir = ROOT / config["vendor_dir"]
|
vendor_dir = ROOT / config["vendor_dir"]
|
||||||
@@ -285,20 +438,29 @@ def main() -> int:
|
|||||||
notes: list[str] = []
|
notes: list[str] = []
|
||||||
snapshot_notes: list[str] = []
|
snapshot_notes: list[str] = []
|
||||||
|
|
||||||
for repo in config["repositories"]:
|
for repo in filtered_repositories(config, upstream_filter):
|
||||||
|
current_step += 1
|
||||||
|
render_progress(current_step, total_steps, f"{role}/{repo['purpose']}")
|
||||||
repo_count += 1
|
repo_count += 1
|
||||||
with tempfile.TemporaryDirectory(prefix=f"nbops-upstream-{role}-") as tmp:
|
with tempfile.TemporaryDirectory(prefix=f"nbops-upstream-{role}-") as tmp:
|
||||||
tmpdir = Path(tmp)
|
tmpdir = Path(tmp)
|
||||||
|
clone_dir = tmpdir / "repo"
|
||||||
|
purpose_dest = vendor_dir / repo["purpose"]
|
||||||
|
purpose_snapshot_dir = purpose_dest
|
||||||
|
if args.apply:
|
||||||
|
purpose_snapshot_dir = tmpdir / "__snapshot__" / repo["purpose"]
|
||||||
|
purpose_snapshot_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
ok = False
|
ok = False
|
||||||
if "url" in repo:
|
if "url" in repo:
|
||||||
ok, output = run_capture(["git", "clone", "--depth", "1", "--branch", repo["ref"], repo["url"], str(tmpdir)])
|
ok, output = run_capture(["git", "clone", "--depth", "1", "--branch", repo["ref"], repo["url"], str(clone_dir)])
|
||||||
if not ok:
|
if not ok:
|
||||||
notes.append(f"{repo['purpose']}: clone failed")
|
notes.append(f"{repo['purpose']}: clone failed")
|
||||||
failures.append(f"{role}/{repo['purpose']}: {output.splitlines()[-1] if output.splitlines() else 'clone failed'}")
|
failures.append(f"{role}/{repo['purpose']}: {output.splitlines()[-1] if output.splitlines() else 'clone failed'}")
|
||||||
|
|
||||||
if ok:
|
if ok:
|
||||||
for path in repo["readme_paths"]:
|
for path in repo["readme_paths"]:
|
||||||
result = copy_if_present(tmpdir, path, vendor_dir / repo["purpose"], args.apply)
|
result = copy_if_present(clone_dir, path, purpose_snapshot_dir, args.apply)
|
||||||
if result == "missing":
|
if result == "missing":
|
||||||
readme_missing += 1
|
readme_missing += 1
|
||||||
else:
|
else:
|
||||||
@@ -307,30 +469,38 @@ def main() -> int:
|
|||||||
|
|
||||||
if args.no_parse_readme:
|
if args.no_parse_readme:
|
||||||
continue
|
continue
|
||||||
candidates = readme_compose_candidates(tmpdir / path)
|
candidates = readme_compose_candidates(clone_dir / path)
|
||||||
readme_candidates += len(candidates)
|
readme_candidates += len(candidates)
|
||||||
if len(candidates) > 1:
|
if len(candidates) > 1:
|
||||||
readme_ambiguous += 1
|
readme_ambiguous += 1
|
||||||
if args.apply and len(candidates) == 1:
|
if args.apply and len(candidates) == 1:
|
||||||
dest = vendor_dir / repo["purpose"] / "compose.from-readme.yaml"
|
dest = purpose_snapshot_dir / NORMALIZED_COMPOSE_NAME
|
||||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
dest.write_text(candidates[0]["body"])
|
dest.write_text(candidates[0]["body"])
|
||||||
|
|
||||||
for path in repo["compose_paths"]:
|
for path in repo["compose_paths"]:
|
||||||
result = copy_if_present(tmpdir, path, vendor_dir / repo["purpose"], args.apply)
|
result = copy_compose_if_present(clone_dir, path, purpose_snapshot_dir, args.apply)
|
||||||
if result == "missing":
|
if result == "missing":
|
||||||
compose_missing += 1
|
compose_missing += 1
|
||||||
|
elif result == "invalid":
|
||||||
|
compose_missing += 1
|
||||||
|
notes.append(f"{repo['purpose']}: Compose `{path}` invalid")
|
||||||
else:
|
else:
|
||||||
compose_found += 1
|
compose_found += 1
|
||||||
snapshot_notes.append(f"- {repo['purpose']} Compose `{path}`: {result}")
|
snapshot_notes.append(f"- {repo['purpose']} Compose `{path}`: {result}")
|
||||||
|
|
||||||
compose_urls = repo["compose_urls"]
|
compose_urls = repo["compose_urls"]
|
||||||
for index, url in enumerate(compose_urls, start=1):
|
for index, url in enumerate(compose_urls, start=1):
|
||||||
|
if args.apply and (purpose_snapshot_dir / NORMALIZED_COMPOSE_NAME).is_file():
|
||||||
|
snapshot_notes.append(
|
||||||
|
f"- {repo['purpose']} Compose URL `{url}`: skipped; `{NORMALIZED_COMPOSE_NAME}` already staged"
|
||||||
|
)
|
||||||
|
continue
|
||||||
result, body, reason = fetch_compose_url(url)
|
result, body, reason = fetch_compose_url(url)
|
||||||
if result == "found":
|
if result == "found":
|
||||||
compose_found += 1
|
compose_found += 1
|
||||||
if args.apply:
|
if args.apply:
|
||||||
dest = vendor_dir / repo["purpose"] / compose_url_filename(index, len(compose_urls))
|
dest = purpose_snapshot_dir / NORMALIZED_COMPOSE_NAME
|
||||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||||
dest.write_text(body)
|
dest.write_text(body)
|
||||||
result = "copied"
|
result = "copied"
|
||||||
@@ -340,7 +510,21 @@ def main() -> int:
|
|||||||
suffix = f" ({reason})" if reason else ""
|
suffix = f" ({reason})" if reason else ""
|
||||||
snapshot_notes.append(f"- {repo['purpose']} Compose URL `{url}`: {result}{suffix}")
|
snapshot_notes.append(f"- {repo['purpose']} Compose URL `{url}`: {result}{suffix}")
|
||||||
|
|
||||||
if config["local_compose_file"]:
|
if args.apply and snapshot_has_content(purpose_snapshot_dir):
|
||||||
|
if repo["purpose"] == "container" and preferred_override_exists(purpose_snapshot_dir):
|
||||||
|
notes.append("container override preferred")
|
||||||
|
snapshot_notes.append(
|
||||||
|
"- container `docker-compose.override.yml`: prefer this when updating `templates/compose.yaml.j2`"
|
||||||
|
)
|
||||||
|
replace_directory(purpose_snapshot_dir, purpose_dest)
|
||||||
|
elif args.apply:
|
||||||
|
snapshot_notes.append(f"- {repo['purpose']} snapshot: no files staged; existing purpose directory left unchanged")
|
||||||
|
elif repo["purpose"] == "container" and preferred_override_exists(purpose_dest):
|
||||||
|
notes.append("container override preferred")
|
||||||
|
|
||||||
|
if config["local_compose_file"] and not upstream_filter:
|
||||||
|
current_step += 1
|
||||||
|
render_progress(current_step, total_steps, f"{role}/local")
|
||||||
result = copy_local_file_if_present(config["local_compose_file"], vendor_dir, args.apply)
|
result = copy_local_file_if_present(config["local_compose_file"], vendor_dir, args.apply)
|
||||||
if result in {"missing", "invalid"}:
|
if result in {"missing", "invalid"}:
|
||||||
compose_missing += 1
|
compose_missing += 1
|
||||||
@@ -371,6 +555,7 @@ def main() -> int:
|
|||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
clear_progress()
|
||||||
print_table(rows)
|
print_table(rows)
|
||||||
|
|
||||||
if failures:
|
if failures:
|
||||||
|
|||||||
@@ -4,6 +4,9 @@ fqdn: {{ cloud_init_fqdn | default(nbops_fqdn) }}
|
|||||||
manage_etc_hosts: true
|
manage_etc_hosts: true
|
||||||
timezone: {{ cloud_init_timezone | default(nbops_timezone) }}
|
timezone: {{ cloud_init_timezone | default(nbops_timezone) }}
|
||||||
locale: {{ cloud_init_locale | default(nbops_locale) }}
|
locale: {{ cloud_init_locale | default(nbops_locale) }}
|
||||||
|
{% set serial_console_enabled = cloud_init_serial_console_enabled | default(true) %}
|
||||||
|
{% set serial_console_tty = cloud_init_serial_console_tty | default('ttyS0') %}
|
||||||
|
{% set serial_console_term = cloud_init_serial_console_term | default('xterm-256color') %}
|
||||||
|
|
||||||
users:
|
users:
|
||||||
- default
|
- default
|
||||||
@@ -72,6 +75,14 @@ write_files:
|
|||||||
net.ipv6.conf.all.forwarding = 1
|
net.ipv6.conf.all.forwarding = 1
|
||||||
fs.inotify.max_user_watches = 1048576
|
fs.inotify.max_user_watches = 1048576
|
||||||
fs.inotify.max_user_instances = 1024
|
fs.inotify.max_user_instances = 1024
|
||||||
|
{% if serial_console_enabled | bool %}
|
||||||
|
- path: /etc/systemd/system/serial-getty@{{ serial_console_tty }}.service.d/10-nbops-xterm.conf
|
||||||
|
owner: root:root
|
||||||
|
permissions: "0644"
|
||||||
|
content: |
|
||||||
|
[Service]
|
||||||
|
Environment=TERM={{ serial_console_term }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
runcmd:
|
runcmd:
|
||||||
- install -m 0755 -d /etc/apt/keyrings
|
- install -m 0755 -d /etc/apt/keyrings
|
||||||
@@ -87,9 +98,12 @@ runcmd:
|
|||||||
- ufw allow {{ cloud_init_ssh_port | default(nbops_ssh_port) }}/tcp
|
- ufw allow {{ cloud_init_ssh_port | default(nbops_ssh_port) }}/tcp
|
||||||
- ufw --force enable
|
- ufw --force enable
|
||||||
- systemctl enable --now chrony qemu-guest-agent docker unattended-upgrades
|
- systemctl enable --now chrony qemu-guest-agent docker unattended-upgrades
|
||||||
|
{% if serial_console_enabled | bool %}
|
||||||
|
- systemctl daemon-reload
|
||||||
|
- systemctl enable --now serial-getty@{{ serial_console_tty }}.service
|
||||||
|
{% endif %}
|
||||||
- sysctl --system
|
- sysctl --system
|
||||||
- ansible-galaxy collection install -r {{ cloud_init_repo_path | default('/opt/nbops-ansible') }}/requirements.yml || true
|
- ansible-galaxy collection install -r {{ cloud_init_repo_path | default('/opt/nbops-ansible') }}/requirements.yml || true
|
||||||
- ansible-pull -U {{ cloud_init_repo_url }} -i localhost, -c local {{ cloud_init_playbook | default('playbooks/pull.yml') }} --accept-host-key
|
- ansible-pull -U {{ cloud_init_repo_url }} -i localhost, -c local {{ cloud_init_playbook | default('playbooks/pull.yml') }} --accept-host-key
|
||||||
|
|
||||||
final_message: "nbops cloud-init bootstrap complete after $UPTIME seconds"
|
final_message: "nbops cloud-init bootstrap complete after $UPTIME seconds"
|
||||||
|
|
||||||
Reference in new issue
Block a user