recipe-maintainer: public snapshot (secrets + deployment plans removed, single commit)

Sanitized single-commit public mirror of recipe-maintainer.
- Removed test-ssh/.testenv (live creds); added test-ssh/.testenv.example placeholders.
- Removed plans/ and planned-updates/ (deployment-planning docs) so no client/
  deployment domains appear in the public repo.
- All other secret stores were already gitignored.
- docs.coopcloud.tech retained as a submodule (public upstream).
This commit is contained in:
2026-06-16 20:18:24 +00:00
commit f283a371bb
253 changed files with 15975 additions and 0 deletions
+6
View File
@@ -0,0 +1,6 @@
*.tfstate
*.tfstate.backup
*.tfvars
.terraform/
.terraform.lock.hcl
.testenv
+93
View File
@@ -0,0 +1,93 @@
# Terraform test infrastructure
Provisions a DigitalOcean droplet for running Co-op Cloud recipe tests. The droplet gets Debian 13, Docker, Docker Swarm, a `proxy` overlay network, and UFW/fail2ban — everything needed to deploy recipes with `abra`.
A reserved IP is assigned to the droplet so DNS survives droplet destroy/recreate. DNS is managed externally (not by Terraform). The default domain is `t1cc.commoninternet.net` with wildcard DNS pointing to the reserved IP.
## Prerequisites
- [Terraform](https://developer.hashicorp.com/terraform/install) >= 1.0
- A DigitalOcean API token ([create one here](https://cloud.digitalocean.com/account/api/tokens))
- SSH key pair at `../test-ssh/test-ssh-keys/nptest` (the public key must already be uploaded to DigitalOcean as `nptest.pub`)
## Quick start (setup.sh)
The easiest way to provision is with the `setup.sh` wrapper, which handles reserved IP allocation and tfvars generation automatically:
```bash
# 1. Create .testenv with your DO token
echo 'DO_TOKEN=dop_v1_...' > terraform/.testenv
# 2. Run setup (allocates reserved IP on first run, reuses it after)
./terraform/setup.sh
```
This runs `terraform init` + `terraform apply`, then prints the reserved IP for DNS.
## Quick start (manual)
```bash
cd terraform
# 1. Create your tfvars file
cp terraform.tfvars.example terraform.tfvars
# Edit terraform.tfvars — set do_token and reserved_ip
# 2. Initialize and apply
terraform init
terraform apply
```
After apply, deploy Traefik before deploying recipes — use `abra` for that.
## Connecting
After apply, get the SSH command:
```bash
terraform output ssh_command
```
## What gets provisioned
| Resource | Description |
|---|---|
| `digitalocean_ssh_key` (data) | Looks up the `nptest.pub` key already on DigitalOcean |
| `digitalocean_droplet` | Debian 13, 2 vCPU / 8 GB, AMS3 |
| `digitalocean_reserved_ip_assignment` | Assigns a pre-allocated reserved IP to the droplet |
| `digitalocean_firewall` | Allows all inbound/outbound TCP, UDP, and ICMP |
Cloud-init installs Docker, configures UFW (allow all) and fail2ban, initializes Swarm, and creates the `proxy` overlay network.
## Variables
All variables have defaults except `do_token` and `reserved_ip`. See `terraform.tfvars.example` for the full list.
| Variable | Default | Description |
|---|---|---|
| `do_token` | *(required)* | DigitalOcean API token |
| `reserved_ip` | *(required)* | Pre-allocated reserved IP to assign to the droplet |
| `domain` | `t1cc.commoninternet.net` | Base domain for test instances |
| `droplet_name` | `coopcloud-test` | Droplet name |
| `region` | `ams3` | DO region |
| `size` | `s-2vcpu-8gb-amd` | Droplet size |
| `image` | `debian-13-x64` | OS image |
| `ssh_private_key_path` | `../test-ssh/test-ssh-keys/nptest` | SSH private key for provisioner connection |
## Outputs
| Output | Description |
|---|---|
| `droplet_ip` | Droplet's ephemeral IPv4 (use reserved_ip for DNS) |
| `reserved_ip` | Static reserved IP — point DNS here |
| `domain` | Base domain for test instances |
| `ssh_command` | SSH command to connect to the droplet |
## Tearing down
```bash
cd terraform
terraform destroy
```
This deletes the droplet, reserved IP assignment, and firewall. The reserved IP itself is kept (managed outside Terraform via `setup.sh`).
+37
View File
@@ -0,0 +1,37 @@
#cloud-config
package_update: true
package_upgrade: true
packages:
- curl
- git
- ufw
- fail2ban
write_files:
# Docker daemon config for Swarm (live-restore must be false for swarm mode)
- path: /etc/docker/daemon.json
content: |
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
runcmd:
# Install Docker
- curl -fsSL https://get.docker.com | sh
# Configure firewall
- ufw default allow incoming
- ufw default allow outgoing
- ufw --force enable
# Initialize Docker Swarm
- docker swarm init --advertise-addr $(curl -s http://169.254.169.254/metadata/v1/interfaces/public/0/ipv4/address)
# Create the proxy network used by Traefik and all recipes
- docker network create --driver overlay --attachable proxy
+72
View File
@@ -0,0 +1,72 @@
data "digitalocean_ssh_key" "test" {
name = "nptest.pub"
}
resource "digitalocean_droplet" "test" {
name = var.droplet_name
region = var.region
size = var.size
image = var.image
ssh_keys = [data.digitalocean_ssh_key.test.id]
user_data = file("${path.module}/cloud-init.yaml")
connection {
type = "ssh"
host = self.ipv4_address
user = "root"
private_key = file(var.ssh_private_key_path)
port = 22
}
# Wait for cloud-init to finish before considering the droplet ready
provisioner "remote-exec" {
inline = ["cloud-init status --wait"]
}
}
# --- Reserved IP (created outside Terraform, passed in as variable) ---
resource "digitalocean_reserved_ip_assignment" "test" {
ip_address = var.reserved_ip
droplet_id = digitalocean_droplet.test.id
}
# --- Firewall ---
resource "digitalocean_firewall" "test" {
name = "${var.droplet_name}-fw"
droplet_ids = [digitalocean_droplet.test.id]
# Allow all inbound
inbound_rule {
protocol = "tcp"
port_range = "1-65535"
source_addresses = ["0.0.0.0/0", "::/0"]
}
inbound_rule {
protocol = "udp"
port_range = "1-65535"
source_addresses = ["0.0.0.0/0", "::/0"]
}
inbound_rule {
protocol = "icmp"
source_addresses = ["0.0.0.0/0", "::/0"]
}
# Allow all outbound
outbound_rule {
protocol = "tcp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "udp"
port_range = "1-65535"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
outbound_rule {
protocol = "icmp"
destination_addresses = ["0.0.0.0/0", "::/0"]
}
}
+20
View File
@@ -0,0 +1,20 @@
output "droplet_ip" {
description = "Droplet's ephemeral IPv4 (use reserved_ip for DNS)"
value = digitalocean_droplet.test.ipv4_address
}
output "reserved_ip" {
description = "Static reserved IP — point t1cc.commoninternet.net DNS here"
value = var.reserved_ip
}
output "domain" {
description = "Base domain for test instances"
value = var.domain
}
output "ssh_command" {
description = "SSH command to connect to the droplet"
value = "ssh -i ${var.ssh_private_key_path} root@${var.domain}"
}
+14
View File
@@ -0,0 +1,14 @@
terraform {
required_version = ">= 1.0"
required_providers {
digitalocean = {
source = "digitalocean/digitalocean"
version = "~> 2.0"
}
}
}
provider "digitalocean" {
token = var.do_token
}
+99
View File
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
# Provision the terraform test droplet end-to-end.
#
# Usage:
# 1. Create terraform/.testenv with your DO token:
# DO_TOKEN=dop_v1_...
# 2. Run: ./terraform/setup.sh
#
# On first run this allocates a DigitalOcean reserved IP and saves it
# to .testenv as RESERVED_IP. On subsequent runs it reuses that IP.
# The reserved IP survives droplet destroy/recreate — set your DNS once.
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
TESTENV="$SCRIPT_DIR/.testenv"
# --- Source .testenv ---
if [[ ! -f "$TESTENV" ]]; then
echo "ERROR: $TESTENV not found."
echo ""
echo "Create it with your DigitalOcean API token:"
echo " echo 'DO_TOKEN=dop_v1_...' > $TESTENV"
exit 1
fi
# shellcheck source=/dev/null
source "$TESTENV"
if [[ -z "${DO_TOKEN:-}" ]]; then
echo "ERROR: DO_TOKEN is not set in $TESTENV"
exit 1
fi
# --- Ensure a reserved IP exists ---
if [[ -z "${RESERVED_IP:-}" ]]; then
echo "No RESERVED_IP in .testenv, allocating one..."
RESERVED_IP="$(curl -s -X POST \
-H "Authorization: Bearer $DO_TOKEN" \
-H "Content-Type: application/json" \
-d '{"region":"ams3"}' \
https://api.digitalocean.com/v2/reserved_ips \
| grep -o '"ip":"[^"]*"' | head -1 | cut -d'"' -f4)"
if [[ -z "$RESERVED_IP" ]]; then
echo "ERROR: Failed to allocate reserved IP"
exit 1
fi
echo "RESERVED_IP=$RESERVED_IP" >> "$TESTENV"
echo "Allocated and saved reserved IP: $RESERVED_IP"
else
echo "Using existing reserved IP: $RESERVED_IP"
fi
# --- Generate terraform.tfvars ---
TFVARS="$SCRIPT_DIR/terraform.tfvars"
cat > "$TFVARS" <<EOF
do_token = "$DO_TOKEN"
reserved_ip = "$RESERVED_IP"
EOF
echo "Wrote $TFVARS"
# --- Terraform init + apply ---
cd "$SCRIPT_DIR"
if [[ ! -d .terraform ]]; then
echo "Running terraform init..."
terraform init
fi
echo "Running terraform apply..."
terraform apply -auto-approve
# --- Grab outputs ---
DOMAIN="$(terraform output -raw domain)"
echo ""
echo "Reserved IP: $RESERVED_IP"
echo "Domain: $DOMAIN"
echo ""
echo "Point DNS for $DOMAIN (A + wildcard) to: $RESERVED_IP"
# --- Switch repo to terraform mode ---
cd "$REPO_DIR"
./switch-test-instance.sh t1cc
echo ""
echo "Setup complete. You can now SSH with:"
echo " cd test-ssh && ssh -F ssh-config $DOMAIN"
+15
View File
@@ -0,0 +1,15 @@
# Copy to terraform.tfvars and fill in values
#
# Get a DO API token from: https://cloud.digitalocean.com/account/api/tokens
# If using setup.sh, these are generated automatically from .testenv.
do_token = "dop_v1_..."
reserved_ip = "x.x.x.x"
# Optional overrides (defaults shown):
# domain = "t1cc.commoninternet.net"
# droplet_name = "coopcloud-test"
# region = "ams3"
# size = "s-2vcpu-4gb"
# image = "debian-13-x64"
# ssh_private_key_path = "../test-ssh/test-ssh-keys/nptest"
+46
View File
@@ -0,0 +1,46 @@
variable "do_token" {
description = "DigitalOcean API token"
type = string
sensitive = true
}
variable "domain" {
description = "Base domain for test instances (e.g. test.example.com). Each recipe gets a subdomain like <recipe>.test.example.com"
type = string
default = "t1cc.commoninternet.net"
}
variable "droplet_name" {
description = "Name of the DigitalOcean droplet"
type = string
default = "coopcloud-test"
}
variable "region" {
description = "DigitalOcean region"
type = string
default = "ams3"
}
variable "size" {
description = "Droplet size (slug)"
type = string
default = "s-2vcpu-8gb-amd"
}
variable "image" {
description = "Droplet image (OS)"
type = string
default = "debian-13-x64"
}
variable "ssh_private_key_path" {
description = "Path to the SSH private key for provisioner connection"
type = string
default = "../test-ssh/test-ssh-keys/nptest"
}
variable "reserved_ip" {
description = "Pre-allocated DigitalOcean reserved IP to assign to the droplet"
type = string
}