re-implement previous functionalities with cloud deployment and usb drive creation
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
WORKSHOP_WIFI_SSID=
|
WORKSHOP_DOMAIN=codecrispi.es
|
||||||
WORKSHOP_WIFI_PASSWORD=
|
HCLOUD_TOKEN=
|
||||||
WORKSHOP_DOMAIN=
|
HETZNER_DNS_TOKEN=
|
||||||
|
DNS_ZONE_ID=
|
||||||
|
USB_DEVICE=/dev/sdb
|
||||||
|
|||||||
102
Makefile
102
Makefile
@@ -1,20 +1,100 @@
|
|||||||
.PHONY: help vm-run clean
|
-include .env
|
||||||
|
export
|
||||||
|
|
||||||
|
.PHONY: help deploy-cloud build-usb flash-usb local-vm-run clean status destroy-cloud
|
||||||
|
|
||||||
|
DOMAIN := $(or $(WORKSHOP_DOMAIN),codecrispi.es)
|
||||||
|
USB_DEVICE := $(or $(USB_DEVICE),/dev/sdX)
|
||||||
|
|
||||||
help:
|
help:
|
||||||
@echo "🍪 Workshop VM with Containers"
|
@echo "CODE CRISPIES Workshop"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Commands:"
|
@echo "Cloud Infrastructure (Hetzner):"
|
||||||
@echo " make vm-run - Start VM with participant containers"
|
@echo " make deploy-cloud - Deploy 15 VMs to Hetzner Cloud"
|
||||||
|
@echo " make status-cloud - Check server health"
|
||||||
|
@echo " make destroy-cloud - Destroy cloud infrastructure"
|
||||||
|
@echo ""
|
||||||
|
@echo "USB Boot Drive:"
|
||||||
|
@echo " make build-usb - Build NixOS workshop ISO"
|
||||||
|
@echo " make flash-usb - Flash ISO to USB drive"
|
||||||
|
@echo ""
|
||||||
|
@echo "Local Development:"
|
||||||
|
@echo " make local-vm-run - Start local VM with containers"
|
||||||
@echo " make clean - Clean build artifacts"
|
@echo " make clean - Clean build artifacts"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Inside the VM:"
|
@echo "Config: Domain=$(DOMAIN), USB=$(USB_DEVICE)"
|
||||||
@echo " ssh root@192.168.100.11 # Connect to hopper container"
|
@echo "Required: HCLOUD_TOKEN, SSH key at ~/.ssh/id_rsa.pub"
|
||||||
@echo " ssh root@192.168.100.12 # Connect to curie container"
|
|
||||||
|
|
||||||
vm-run:
|
build-usb:
|
||||||
@echo "🚀 Starting workshop VM with containers..."
|
@echo "Building NixOS workshop ISO for $(DOMAIN)..."
|
||||||
@echo "VM will open with desktop. Terminal shows SSH commands."
|
@if [ ! -f ~/.ssh/id_ed25519.pub ]; then \
|
||||||
|
echo "SSH key not found at ~/.ssh/id_ed25519.pub"; \
|
||||||
|
echo "Generate with: ssh-keygen -t rsa -b 4096"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
nix build .#live-iso --show-trace
|
||||||
|
@echo "ISO built: result/iso/nixos.iso"
|
||||||
|
@echo "Size: $$(du -h result/iso/nixos.iso | cut -f1)"
|
||||||
|
|
||||||
|
flash-usb: build-usb
|
||||||
|
@if [ "$(USB_DEVICE)" = "/dev/sdX" ]; then \
|
||||||
|
echo "Set USB_DEVICE=/dev/sdX (find with 'lsblk')"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "About to flash $(USB_DEVICE) - THIS WILL ERASE ALL DATA!"
|
||||||
|
@echo "Verify device: $$(lsblk $(USB_DEVICE) 2>/dev/null || echo 'DEVICE NOT FOUND')"
|
||||||
|
@read -p "Continue? [y/N]: " confirm && [ "$$confirm" = "y" ]
|
||||||
|
sudo dd if=result/iso/nixos.iso of=$(USB_DEVICE) bs=4M status=progress oflag=sync
|
||||||
|
sync
|
||||||
|
@echo "USB drive ready for workshop!"
|
||||||
|
|
||||||
|
deploy-cloud:
|
||||||
|
@if [ -z "$(HCLOUD_TOKEN)" ]; then \
|
||||||
|
echo "HCLOUD_TOKEN not set"; \
|
||||||
|
echo "Get token from: https://console.hetzner.cloud/"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@if [ ! -f ~/.ssh/id_rsa.pub ]; then \
|
||||||
|
echo "SSH key not found at ~/.ssh/id_rsa.pub"; \
|
||||||
|
exit 1; \
|
||||||
|
fi
|
||||||
|
@echo "Deploying 15 workshop servers to Hetzner Cloud..."
|
||||||
|
@echo "Domain: $(DOMAIN)"
|
||||||
|
cd terraform && terraform init
|
||||||
|
cd terraform && terraform apply -auto-approve \
|
||||||
|
-var="hcloud_token=$(HCLOUD_TOKEN)" \
|
||||||
|
-var="hetzner_dns_token=$(HETZNER_DNS_TOKEN)" \
|
||||||
|
-var="dns_zone_id=$(DNS_ZONE_ID)" \
|
||||||
|
-var="domain=$(DOMAIN)" \
|
||||||
|
-var="ssh_public_key=$$(cat ~/.ssh/id_rsa.pub)"
|
||||||
|
@echo "Running health checks..."
|
||||||
|
@sleep 60
|
||||||
|
$(MAKE) status-cloud
|
||||||
|
@echo "Cloud deployment complete!"
|
||||||
|
|
||||||
|
status-cloud:
|
||||||
|
@echo "Checking server health..."
|
||||||
|
@for name in hopper curie lovelace noether hamilton franklin johnson clarke goldberg liskov wing rosen shaw karp rich; do \
|
||||||
|
printf "%-10s " "$$name:"; \
|
||||||
|
if timeout 10 curl -s -f https://traefik.$$name.$(DOMAIN)/ping >/dev/null 2>&1; then \
|
||||||
|
echo "Ready"; \
|
||||||
|
elif timeout 5 ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no workshop@$$name.$(DOMAIN) "echo ok" >/dev/null 2>&1; then \
|
||||||
|
echo "SSH OK, Traefik starting..."; \
|
||||||
|
else \
|
||||||
|
echo "Not ready"; \
|
||||||
|
fi; \
|
||||||
|
done
|
||||||
|
|
||||||
|
destroy-cloud:
|
||||||
|
@echo "This will destroy ALL workshop servers!"
|
||||||
|
@read -p "Continue? [y/N]: " confirm && [ "$$confirm" = "y" ]
|
||||||
|
cd terraform && terraform destroy -auto-approve
|
||||||
|
|
||||||
|
local-vm-run:
|
||||||
|
@echo "Starting local workshop VM..."
|
||||||
|
@echo "VM will open with desktop showing 2 participant containers"
|
||||||
nix run --impure .#local-vm
|
nix run --impure .#local-vm
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf result .direnv
|
rm -rf result .direnv terraform/.terraform terraform/terraform.tfstate*
|
||||||
|
@echo "Cleaned up build artifacts"
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
#cloud-config
|
|
||||||
users:
|
|
||||||
- name: workshop
|
|
||||||
groups: [adm, docker]
|
|
||||||
sudo: ['ALL=(ALL) NOPASSWD:ALL']
|
|
||||||
shell: /bin/bash
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- ${ssh_public_key}
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- docker.io
|
|
||||||
- curl
|
|
||||||
- git
|
|
||||||
- jq
|
|
||||||
|
|
||||||
runcmd:
|
|
||||||
# Setup Docker Swarm
|
|
||||||
- systemctl enable docker
|
|
||||||
- systemctl start docker
|
|
||||||
- usermod -aG docker workshop
|
|
||||||
- docker swarm init
|
|
||||||
- docker network create -d overlay proxy
|
|
||||||
|
|
||||||
# Install abra
|
|
||||||
- curl -fsSL https://install.abra.coopcloud.tech | bash
|
|
||||||
- mv /root/.local/bin/abra /usr/local/bin/
|
|
||||||
- chmod +x /usr/local/bin/abra
|
|
||||||
|
|
||||||
# Pre-configure abra server for this participant
|
|
||||||
- sudo -u workshop mkdir -p /home/workshop/.abra/servers
|
|
||||||
- sudo -u workshop abra server add ${participant_name}.${domain}
|
|
||||||
|
|
||||||
# Pre-setup Traefik
|
|
||||||
- sudo -u workshop abra app new traefik --domain=traefik.${participant_name}.${domain} --server=${participant_name}.${domain}#cloud-config
|
|
||||||
users:
|
|
||||||
- name: workshop
|
|
||||||
groups: [adm, docker]
|
|
||||||
sudo: ['ALL=(ALL) NOPASSWD:ALL']
|
|
||||||
shell: /bin/bash
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- ${ssh_public_key}
|
|
||||||
|
|
||||||
packages:
|
|
||||||
- docker.io
|
|
||||||
- curl
|
|
||||||
- git
|
|
||||||
- jq
|
|
||||||
|
|
||||||
runcmd:
|
|
||||||
# Setup Docker Swarm
|
|
||||||
- systemctl enable docker
|
|
||||||
- systemctl start docker
|
|
||||||
- usermod -aG docker workshop
|
|
||||||
- docker swarm init
|
|
||||||
- docker network create -d overlay proxy
|
|
||||||
|
|
||||||
# Install abra
|
|
||||||
- curl -fsSL https://install.abra.coopcloud.tech | bash
|
|
||||||
- mv /root/.local/bin/abra /usr/local/bin/
|
|
||||||
- chmod +x /usr/local/bin/abra
|
|
||||||
|
|
||||||
# Pre-configure abra server for this participant
|
|
||||||
- sudo -u workshop mkdir -p /home/workshop/.abra/servers
|
|
||||||
- sudo -u workshop abra server add ${participant_name}.${domain}
|
|
||||||
|
|
||||||
# Pre-setup Traefik
|
|
||||||
- sudo -u workshop abra app new traefik --domain=traefik.${participant_name}.${domain} --server=${participant_name}.${domain}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
locals {
|
|
||||||
domain = "codecrispi.es"
|
|
||||||
participants = [
|
|
||||||
"hopper", "curie", "lovelace", "noether", "hamilton",
|
|
||||||
"franklin", "johnson", "clarke", "goldberg", "liskov",
|
|
||||||
"wing", "rosen", "shaw", "karp", "rich"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "hcloud_server" "participant" {
|
|
||||||
for_each = toset(local.participants)
|
|
||||||
|
|
||||||
name = each.key
|
|
||||||
image = "ubuntu-22.04"
|
|
||||||
server_type = "cx21"
|
|
||||||
location = "nbg1"
|
|
||||||
|
|
||||||
ssh_keys = [hcloud_ssh_key.workshop.id]
|
|
||||||
|
|
||||||
user_data = templatefile("${path.module}/cloud-init.yml", {
|
|
||||||
participant_name = each.key
|
|
||||||
domain = local.domain
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "hcloud_dns_record" "participant_main" {
|
|
||||||
for_each = toset(local.participants)
|
|
||||||
|
|
||||||
zone_id = var.dns_zone_id
|
|
||||||
name = each.key
|
|
||||||
type = "A"
|
|
||||||
value = hcloud_server.participant[each.key].ipv4_address
|
|
||||||
ttl = 60
|
|
||||||
}
|
|
||||||
|
|
||||||
resource "hcloud_dns_record" "participant_wildcard" {
|
|
||||||
for_each = toset(local.participants)
|
|
||||||
|
|
||||||
zone_id = var.dns_zone_id
|
|
||||||
name = "*.${each.key}"
|
|
||||||
type = "A"
|
|
||||||
value = hcloud_server.participant[each.key].ipv4_address
|
|
||||||
ttl = 60
|
|
||||||
}
|
|
||||||
@@ -1,14 +1,13 @@
|
|||||||
# 🍪 CODE CRISPIES Workshop
|
|
||||||
## USB Boot Instructions
|
## USB Boot Instructions
|
||||||
|
|
||||||
### 📋 Quick Reference Card
|
### Quick Reference Card
|
||||||
|
|
||||||
**Your assigned server:** `__________.codecrispi.es`
|
**Your assigned server:** `__________.codecrispi.es`
|
||||||
**Workshop WiFi:** `CODE_CRISPIES_GUEST` / Password: `workshop2024`
|
**Workshop WiFi:** `CODE_CRISPIES_GUEST` / Password: `workshop2024`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 💻 How to Boot from USB Drive
|
## How to Boot from USB Drive
|
||||||
|
|
||||||
### Step 1: Insert USB Drive
|
### Step 1: Insert USB Drive
|
||||||
- Insert the CODE CRISPIES workshop USB drive
|
- Insert the CODE CRISPIES workshop USB drive
|
||||||
@@ -16,7 +15,7 @@
|
|||||||
|
|
||||||
### Step 2: Boot from USB
|
### Step 2: Boot from USB
|
||||||
|
|
||||||
#### 🖥️ **Desktop PC / Most Laptops**
|
#### Desktop PC / Most Laptops
|
||||||
1. **Restart** your computer
|
1. **Restart** your computer
|
||||||
2. **Press and HOLD** one of these keys immediately as it starts:
|
2. **Press and HOLD** one of these keys immediately as it starts:
|
||||||
- `F12` (most common)
|
- `F12` (most common)
|
||||||
@@ -26,19 +25,19 @@
|
|||||||
3. Select **USB Drive** or **UEFI: USB Drive** from boot menu
|
3. Select **USB Drive** or **UEFI: USB Drive** from boot menu
|
||||||
4. Press `Enter`
|
4. Press `Enter`
|
||||||
|
|
||||||
#### 🍎 **Mac (Intel)**
|
#### Mac (Intel)
|
||||||
1. **Restart** your Mac
|
1. **Restart** your Mac
|
||||||
2. **Press and HOLD** `Option` (⌥) key immediately
|
2. **Press and HOLD** `Option` (⌥) key immediately
|
||||||
3. Select the **USB drive** (may show as "EFI Boot")
|
3. Select the **USB drive** (may show as "EFI Boot")
|
||||||
4. Press `Enter`
|
4. Press `Enter`
|
||||||
|
|
||||||
#### 🍎 **Mac (Apple Silicon M1/M2)**
|
#### Mac (Apple Silicon M1/M2)
|
||||||
1. **Shut down** completely
|
1. **Shut down** completely
|
||||||
2. **Press and HOLD** the power button until you see startup options
|
2. **Press and HOLD** the power button until you see startup options
|
||||||
3. Select the **USB drive** option
|
3. Select the **USB drive** option
|
||||||
4. Click **Continue**
|
4. Click **Continue**
|
||||||
|
|
||||||
#### 🔧 **If Boot Menu Doesn't Appear**
|
#### If Boot Menu Doesn't Appear
|
||||||
|
|
||||||
**Windows PC:**
|
**Windows PC:**
|
||||||
1. Boot into Windows
|
1. Boot into Windows
|
||||||
@@ -60,7 +59,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## ✅ What You Should See
|
## What You Should See
|
||||||
|
|
||||||
1. **NixOS Boot Screen** appears
|
1. **NixOS Boot Screen** appears
|
||||||
2. System loads (takes 30-60 seconds)
|
2. System loads (takes 30-60 seconds)
|
||||||
@@ -70,7 +69,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Getting Started Commands
|
## Getting Started Commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Connect to your assigned server
|
# Connect to your assigned server
|
||||||
@@ -85,7 +84,7 @@ help
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 📱 Mobile Hotspot Instructions
|
## Mobile Hotspot Instructions
|
||||||
**If WiFi isn't working:**
|
**If WiFi isn't working:**
|
||||||
|
|
||||||
**iPhone:**
|
**iPhone:**
|
||||||
@@ -98,553 +97,24 @@ Name: Android, Password: (ask facilitator)
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🆘 Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
❌ **"No bootable device"**
|
**"No bootable device"**
|
||||||
→ Try different F-key (F11, F9, ESC)
|
→ Try different F-key (F11, F9, ESC)
|
||||||
→ USB drive may not be properly inserted
|
→ USB drive may not be properly inserted
|
||||||
|
|
||||||
❌ **Mac won't boot USB**
|
**Mac won't boot USB**
|
||||||
→ USB drive might need to be reformatted for Mac
|
→ USB drive might need to be reformatted for Mac
|
||||||
→ Ask facilitator for Mac-compatible USB
|
→ Ask facilitator for Mac-compatible USB
|
||||||
|
|
||||||
❌ **Boots to Windows/Mac instead**
|
**Boots to Windows/Mac instead**
|
||||||
→ You didn't press the boot key fast enough
|
→ You didn't press the boot key fast enough
|
||||||
→ Restart and try again immediately
|
→ Restart and try again immediately
|
||||||
|
|
||||||
❌ **Terminal doesn't open**
|
**Terminal doesn't open**
|
||||||
→ Click terminal icon in taskbar
|
→ Click terminal icon in taskbar
|
||||||
→ Or press `Ctrl + Alt + T`
|
→ Or press `Ctrl + Alt + T`
|
||||||
|
|
||||||
❌ **Can't connect to internet**
|
**Can't connect to internet**
|
||||||
→ Try different WiFi network
|
→ Try different WiFi network
|
||||||
→ Use mobile hotspot as backup
|
→ Use mobile hotspot as backup
|
||||||
```
|
|
||||||
|
|
||||||
## flake.nix
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
description = "CODE CRISPIES Workshop Live Environment";
|
|
||||||
|
|
||||||
inputs = {
|
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
nixos-generators.url = "github:nix-community/nixos-generators";
|
|
||||||
};
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs, nixos-generators }:
|
|
||||||
let
|
|
||||||
system = "x86_64-linux";
|
|
||||||
participantNames = [
|
|
||||||
"hopper" "curie" "lovelace" "noether" "hamilton"
|
|
||||||
"franklin" "johnson" "clarke" "goldberg" "liskov"
|
|
||||||
"wing" "rosen" "shaw" "karp" "rich"
|
|
||||||
];
|
|
||||||
in {
|
|
||||||
packages.${system}.live-iso = nixos-generators.nixosGenerate {
|
|
||||||
inherit system;
|
|
||||||
format = "iso";
|
|
||||||
|
|
||||||
modules = [
|
|
||||||
({ pkgs, ... }: {
|
|
||||||
# WiFi support
|
|
||||||
networking.wireless.enable = true;
|
|
||||||
networking.networkmanager.enable = true;
|
|
||||||
networking.wireless.networks = {
|
|
||||||
"CODE_CRISPIES_GUEST" = {
|
|
||||||
psk = "workshop2024";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Auto-connect to workshop WiFi
|
|
||||||
systemd.services.workshop-wifi = {
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "network.target" ];
|
|
||||||
script = ''
|
|
||||||
${pkgs.networkmanager}/bin/nmcli dev wifi connect "CODE_CRISPIES_GUEST" password "workshop2024" || true
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# Auto-login workshop user
|
|
||||||
services.getty.autologinUser = "workshop";
|
|
||||||
users.users.workshop = {
|
|
||||||
isNormalUser = true;
|
|
||||||
shell = pkgs.zsh;
|
|
||||||
};
|
|
||||||
|
|
||||||
# Workshop shell environment
|
|
||||||
programs.zsh = {
|
|
||||||
enable = true;
|
|
||||||
interactiveShellInit = ''
|
|
||||||
echo "🍪 CODE CRISPIES Workshop Environment"
|
|
||||||
echo "📶 WiFi: CODE_CRISPIES_GUEST (auto-connecting...)"
|
|
||||||
echo "📡 Available servers:"
|
|
||||||
${builtins.concatStringsSep "\n" (map (name:
|
|
||||||
"echo \" - ${name}.codecrispi.es\""
|
|
||||||
) participantNames)}
|
|
||||||
echo ""
|
|
||||||
echo "💡 Commands: connect <name> | recipes | help"
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
[ -z "$1" ] && { echo "Usage: connect <name>"; return 1; }
|
|
||||||
echo "🔗 Connecting to $1.codecrispi.es..."
|
|
||||||
ssh -o StrictHostKeyChecking=no workshop@$1.codecrispi.es
|
|
||||||
}
|
|
||||||
|
|
||||||
recipes() {
|
|
||||||
echo "🍪 Featured Co-op Cloud Recipes:"
|
|
||||||
echo ""
|
|
||||||
echo "📝 Content Management:"
|
|
||||||
echo " wordpress ghost hedgedoc dokuwiki mediawiki"
|
|
||||||
echo ""
|
|
||||||
echo "☁️ File & Collaboration:"
|
|
||||||
echo " nextcloud seafile collabora onlyoffice"
|
|
||||||
echo ""
|
|
||||||
echo "💬 Communication:"
|
|
||||||
echo " jitsi-meet matrix-synapse rocketchat mattermost"
|
|
||||||
echo ""
|
|
||||||
echo "🛒 E-commerce & Business:"
|
|
||||||
echo " prestashop invoiceninja kimai pretix"
|
|
||||||
echo ""
|
|
||||||
echo "🔧 Development & Tools:"
|
|
||||||
echo " gitea drone n8n gitlab jupyter-lab"
|
|
||||||
echo ""
|
|
||||||
echo "📊 Analytics & Monitoring:"
|
|
||||||
echo " plausible matomo uptime-kuma grafana"
|
|
||||||
echo ""
|
|
||||||
echo "🎵 Media & Social:"
|
|
||||||
echo " peertube funkwhale mastodon pixelfed jellyfin"
|
|
||||||
echo ""
|
|
||||||
echo "Deploy: abra app new <recipe> -S --domain=myapp.<name>.codecrispi.es"
|
|
||||||
echo "Browse all 100+ recipes: https://recipes.coopcloud.tech"
|
|
||||||
}
|
|
||||||
|
|
||||||
help() {
|
|
||||||
echo "🍪 CODE CRISPIES Workshop Commands:"
|
|
||||||
echo ""
|
|
||||||
echo "connect <name> - SSH to your assigned server"
|
|
||||||
echo "recipes - Show available app recipes"
|
|
||||||
echo "abra app new <recipe> -S --domain=<name>.<server>.codecrispi.es"
|
|
||||||
echo "abra app deploy <domain>"
|
|
||||||
echo "abra app ls - List your apps"
|
|
||||||
echo ""
|
|
||||||
echo "Examples:"
|
|
||||||
echo " connect hopper"
|
|
||||||
echo " abra app new wordpress -S --domain=blog.hopper.codecrispi.es"
|
|
||||||
echo " abra app deploy blog.hopper.codecrispi.es"
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [ openssh curl git networkmanager ];
|
|
||||||
|
|
||||||
# Auto-start terminal
|
|
||||||
services.xserver = {
|
|
||||||
enable = true;
|
|
||||||
displayManager = {
|
|
||||||
autoLogin.enable = true;
|
|
||||||
autoLogin.user = "workshop";
|
|
||||||
sessionCommands = "${pkgs.xterm}/bin/xterm &";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
})
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# Dev shell for local testing
|
|
||||||
devShells.${system}.default = nixpkgs.legacyPackages.${system}.mkShell {
|
|
||||||
buildInputs = with nixpkgs.legacyPackages.${system}; [
|
|
||||||
terraform
|
|
||||||
nixos-rebuild
|
|
||||||
docker
|
|
||||||
openssh
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## local/flake.nix
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
description = "Local Co-op Cloud Testing";
|
|
||||||
|
|
||||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
|
||||||
|
|
||||||
outputs = { self, nixpkgs }: {
|
|
||||||
nixosConfigurations.workshop-local = nixpkgs.lib.nixosSystem {
|
|
||||||
system = "x86_64-linux";
|
|
||||||
modules = [
|
|
||||||
{
|
|
||||||
containers = builtins.listToAttrs (map (i:
|
|
||||||
let participant = builtins.elemAt [
|
|
||||||
"hopper" "curie" "lovelace" "noether" "hamilton"
|
|
||||||
"franklin" "johnson" "clarke" "goldberg" "liskov"
|
|
||||||
"wing" "rosen" "shaw" "karp" "rich"
|
|
||||||
] (i - 1);
|
|
||||||
in {
|
|
||||||
name = "participant${toString i}";
|
|
||||||
value = {
|
|
||||||
autoStart = true;
|
|
||||||
privateNetwork = true;
|
|
||||||
hostAddress = "192.168.100.1";
|
|
||||||
localAddress = "192.168.100.${toString (10 + i)}";
|
|
||||||
|
|
||||||
config = { pkgs, ... }: {
|
|
||||||
virtualisation.docker = {
|
|
||||||
enable = true;
|
|
||||||
extraOptions = "--experimental";
|
|
||||||
};
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
docker git curl wget tar jq
|
|
||||||
];
|
|
||||||
|
|
||||||
# Helper script for workshop commands
|
|
||||||
environment.etc."workshop-helpers.sh" = {
|
|
||||||
text = ''
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
connect() {
|
|
||||||
case "$1" in
|
|
||||||
hopper|curie|lovelace|noether|hamilton|franklin|johnson|clarke|goldberg|liskov|wing|rosen|shaw|karp|rich)
|
|
||||||
echo "🔗 Connecting to $1.codecrispi.es..."
|
|
||||||
ssh -o StrictHostKeyChecking=no workshop@$1.codecrispi.es
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
echo "Available servers:"
|
|
||||||
echo " hopper curie lovelace noether hamilton franklin johnson"
|
|
||||||
echo " clarke goldberg liskov wing rosen shaw karp rich"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
recipes() {
|
|
||||||
echo "🍪 Available Co-op Cloud Recipes:"
|
|
||||||
echo ""
|
|
||||||
echo "📝 Content Management:"
|
|
||||||
echo " wordpress - Blog/CMS platform"
|
|
||||||
echo " ghost - Publishing platform"
|
|
||||||
echo " hedgedoc - Collaborative markdown editor"
|
|
||||||
echo " dokuwiki - Simple textfile based wiki"
|
|
||||||
echo " mediawiki - The wiki software that runs Wikipedia"
|
|
||||||
echo ""
|
|
||||||
echo "☁️ File & Collaboration:"
|
|
||||||
echo " nextcloud - File sync & collaboration"
|
|
||||||
echo " seafile - File hosting platform"
|
|
||||||
echo " collabora - Online Office suite"
|
|
||||||
echo " onlyoffice - Online office suite"
|
|
||||||
echo ""
|
|
||||||
echo "💬 Communication:"
|
|
||||||
echo " jitsi-meet - Video conferencing"
|
|
||||||
echo " matrix-synapse - Chat server"
|
|
||||||
echo " rocketchat - Team communication"
|
|
||||||
echo " mattermost - Team collaboration platform"
|
|
||||||
echo ""
|
|
||||||
echo "🛒 E-commerce & Business:"
|
|
||||||
echo " prestashop - E-commerce platform"
|
|
||||||
echo " invoiceninja - Invoice & billing"
|
|
||||||
echo " kimai - Time tracking"
|
|
||||||
echo " pretix - Event ticketing"
|
|
||||||
echo ""
|
|
||||||
echo "🔧 Development & Tools:"
|
|
||||||
echo " gitea - Git repository hosting"
|
|
||||||
echo " drone - CI/CD platform"
|
|
||||||
echo " n8n - Workflow automation"
|
|
||||||
echo " gitlab - DevOps platform"
|
|
||||||
echo " jupyter-lab - Interactive computing"
|
|
||||||
echo ""
|
|
||||||
echo "📊 Analytics & Monitoring:"
|
|
||||||
echo " plausible - Privacy-friendly analytics"
|
|
||||||
echo " matomo - Web analytics"
|
|
||||||
echo " uptime-kuma - Status monitoring"
|
|
||||||
echo " grafana - Observability platform"
|
|
||||||
echo ""
|
|
||||||
echo "🎵 Media & Social:"
|
|
||||||
echo " peertube - Video platform"
|
|
||||||
echo " funkwhale - Music platform"
|
|
||||||
echo " mastodon - Social networking"
|
|
||||||
echo " pixelfed - Photo sharing"
|
|
||||||
echo " jellyfin - Media system"
|
|
||||||
echo ""
|
|
||||||
echo "Usage: abra app new <recipe> -S --domain=myapp.${participant}.local"
|
|
||||||
echo "Browse all 100+ recipes: https://recipes.coopcloud.tech"
|
|
||||||
}
|
|
||||||
|
|
||||||
help() {
|
|
||||||
echo "🍪 CODE CRISPIES Workshop Commands:"
|
|
||||||
echo ""
|
|
||||||
echo "connect <name> - SSH to cloud server"
|
|
||||||
echo "recipes - Show available app recipes"
|
|
||||||
echo "abra app new <recipe> -S --domain=<name>.${participant}.local"
|
|
||||||
echo "abra app deploy <domain>"
|
|
||||||
echo "abra app ls - List your apps"
|
|
||||||
echo ""
|
|
||||||
echo "Examples:"
|
|
||||||
echo " connect hopper"
|
|
||||||
echo " abra app new wordpress -S --domain=blog.${participant}.local"
|
|
||||||
echo " abra app deploy blog.${participant}.local"
|
|
||||||
echo ""
|
|
||||||
echo "Server: ${participant}.local"
|
|
||||||
echo "Your apps will be available at: https://<name>.${participant}.local"
|
|
||||||
}
|
|
||||||
|
|
||||||
export -f connect recipes help
|
|
||||||
'';
|
|
||||||
mode = "0755";
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.workshop-setup = {
|
|
||||||
wantedBy = [ "multi-user.target" ];
|
|
||||||
after = [ "docker.service" "network-online.target" ];
|
|
||||||
wants = [ "network-online.target" ];
|
|
||||||
script = ''
|
|
||||||
# Wait for network interface
|
|
||||||
until ip addr show | grep -q "192.168.100.${toString (10 + i)}"; do
|
|
||||||
sleep 1
|
|
||||||
done
|
|
||||||
|
|
||||||
# Install abra
|
|
||||||
export HOME=/root
|
|
||||||
${pkgs.curl}/bin/curl -fsSL https://install.abra.coopcloud.tech | ${pkgs.bash}/bin/bash
|
|
||||||
|
|
||||||
# Docker swarm setup
|
|
||||||
${pkgs.docker}/bin/docker swarm init --advertise-addr 192.168.100.${toString (10 + i)} || true
|
|
||||||
${pkgs.docker}/bin/docker network create -d overlay proxy || true
|
|
||||||
|
|
||||||
# Abra server setup
|
|
||||||
mkdir -p /root/.abra/servers
|
|
||||||
/root/.local/bin/abra server add ${participant}.local
|
|
||||||
|
|
||||||
# Setup helper commands in bash profile
|
|
||||||
echo "source /etc/workshop-helpers.sh" >> /root/.bashrc
|
|
||||||
'';
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.openssh.enable = true;
|
|
||||||
networking = {
|
|
||||||
firewall.allowedTCPPorts = [ 22 80 443 ];
|
|
||||||
hostName = "${participant}.local";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
) (nixpkgs.lib.range 1 15));
|
|
||||||
|
|
||||||
# Wildcard DNS for all participant subdomains
|
|
||||||
services.dnsmasq = {
|
|
||||||
enable = true;
|
|
||||||
settings.address = builtins.concatMap (i:
|
|
||||||
let participant = builtins.elemAt [
|
|
||||||
"hopper" "curie" "lovelace" "noether" "hamilton"
|
|
||||||
"franklin" "johnson" "clarke" "goldberg" "liskov"
|
|
||||||
"wing" "rosen" "shaw" "karp" "rich"
|
|
||||||
] (i - 1);
|
|
||||||
in [
|
|
||||||
"/${participant}.local/192.168.100.${toString (10 + i)}"
|
|
||||||
"/.${participant}.local/192.168.100.${toString (10 + i)}"
|
|
||||||
]
|
|
||||||
) (nixpkgs.lib.range 1 15);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Makefile
|
|
||||||
```make
|
|
||||||
# Load .env file if it exists
|
|
||||||
-include .env
|
|
||||||
export
|
|
||||||
|
|
||||||
.PHONY: help deploy-cloud build-usb flash-usb local-shell local-deploy local-ssh clean status
|
|
||||||
|
|
||||||
DOMAIN := $(or $(WORKSHOP_DOMAIN),codecrispi.es)
|
|
||||||
USB_DEVICE := /dev/sdX
|
|
||||||
|
|
||||||
help:
|
|
||||||
@echo "🍪 CODE CRISPIES Workshop"
|
|
||||||
@echo ""
|
|
||||||
@echo "Config: WiFi=$(or $(WORKSHOP_WIFI_SSID),CODE_CRISPIES_GUEST), Domain=$(DOMAIN)"
|
|
||||||
@echo ""
|
|
||||||
@echo "Cloud Infrastructure:"
|
|
||||||
@echo " make deploy-cloud - Deploy VMs to Hetzner (with health checks)"
|
|
||||||
@echo " make status-cloud - Check cloud server status"
|
|
||||||
@echo " make destroy-cloud - Destroy cloud infrastructure"
|
|
||||||
@echo ""
|
|
||||||
@echo "USB Boot Drive:"
|
|
||||||
@echo " make build-usb - Build NixOS ISO"
|
|
||||||
@echo " make flash-usb - Flash ISO to USB (set USB_DEVICE=/dev/sdX)"
|
|
||||||
@echo ""
|
|
||||||
@echo "Local Development:"
|
|
||||||
@echo " make local-shell - Enter dev shell"
|
|
||||||
@echo " make local-deploy - Deploy local NixOS containers"
|
|
||||||
@echo " make local-ssh - SSH into local participant container"
|
|
||||||
@echo " make local-clean - Stop local containers"
|
|
||||||
|
|
||||||
build-usb:
|
|
||||||
@echo "🔨 Building NixOS workshop ISO..."
|
|
||||||
@echo "📝 Config: WiFi=$(or $(WORKSHOP_WIFI_SSID),CODE_CRISPIES_GUEST), Domain=$(DOMAIN)"
|
|
||||||
nix build .#live-iso
|
|
||||||
@echo "✅ ISO built: result/iso/nixos.iso"
|
|
||||||
|
|
||||||
flash-usb: build-usb
|
|
||||||
@echo "⚠️ Flashing to ${USB_DEVICE} - THIS WILL ERASE THE DEVICE!"
|
|
||||||
@read -p "Continue? [y/N]: " confirm && [ "$$confirm" = "y" ]
|
|
||||||
sudo dd if=result/iso/nixos.iso of=${USB_DEVICE} bs=4M status=progress oflag=sync
|
|
||||||
@echo "✅ USB drive ready!"
|
|
||||||
|
|
||||||
deploy-cloud:
|
|
||||||
@echo "🚀 Deploying to Hetzner Cloud..."
|
|
||||||
cd terraform && terraform init
|
|
||||||
cd terraform && terraform apply -auto-approve \
|
|
||||||
-var="hcloud_token=${HCLOUD_TOKEN}" \
|
|
||||||
-var="domain=${DOMAIN}" \
|
|
||||||
-var="ssh_public_key=$$(cat ~/.ssh/id_rsa.pub)"
|
|
||||||
@echo "🔍 Running health checks..."
|
|
||||||
./scripts/deploy.sh
|
|
||||||
@echo "✅ Cloud deployment complete and verified!"
|
|
||||||
|
|
||||||
status-cloud:
|
|
||||||
@echo "📊 Checking server status..."
|
|
||||||
@for name in hopper curie lovelace noether hamilton franklin johnson clarke goldberg liskov wing rosen shaw karp rich; do \
|
|
||||||
echo -n "$$name.${DOMAIN}: "; \
|
|
||||||
if curl -s -f https://traefik.$$name.${DOMAIN}/ping >/dev/null 2>&1; then \
|
|
||||||
echo "✅ Ready"; \
|
|
||||||
else \
|
|
||||||
echo "❌ Not ready"; \
|
|
||||||
fi; \
|
|
||||||
done
|
|
||||||
|
|
||||||
destroy-cloud:
|
|
||||||
cd terraform && terraform destroy -auto-approve
|
|
||||||
|
|
||||||
local-shell:
|
|
||||||
nix develop
|
|
||||||
|
|
||||||
local-deploy:
|
|
||||||
@echo "🏠 Deploying local workshop environment..."
|
|
||||||
sudo nixos-rebuild switch --flake ./local#workshop-local
|
|
||||||
@echo "✅ Local containers running!"
|
|
||||||
@echo "Available: participant1.local through participant15.local"
|
|
||||||
|
|
||||||
local-ssh:
|
|
||||||
@echo "Available local participants:"
|
|
||||||
@echo " participant1.local (192.168.100.11)"
|
|
||||||
@echo " participant2.local (192.168.100.12)"
|
|
||||||
@echo " ... through participant15.local"
|
|
||||||
@read -p "Connect to participant number [1-15]: " num; \
|
|
||||||
ssh root@192.168.100.$$((10 + $$num))
|
|
||||||
|
|
||||||
local-clean:
|
|
||||||
sudo nixos-container stop participant{1..15} || true
|
|
||||||
sudo nixos-container destroy participant{1..15} || true
|
|
||||||
|
|
||||||
clean:
|
|
||||||
rm -rf result .direnv
|
|
||||||
```
|
|
||||||
|
|
||||||
## README.md
|
|
||||||
```markdown
|
|
||||||
# 🍪 CODE CRISPIES Workshop Infrastructure
|
|
||||||
|
|
||||||
Three deployment environments for Co-op Cloud workshop:
|
|
||||||
|
|
||||||
## 🚀 Quick Start
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# 1. Build & flash USB drives
|
|
||||||
make build-usb
|
|
||||||
make flash-usb USB_DEVICE=/dev/sdX
|
|
||||||
|
|
||||||
# 2. Deploy cloud infrastructure
|
|
||||||
export HCLOUD_TOKEN=your_token
|
|
||||||
make deploy-cloud
|
|
||||||
|
|
||||||
# 3. Local development
|
|
||||||
make local-shell
|
|
||||||
make local-deploy
|
|
||||||
make local-ssh
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📁 Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
├── flake.nix # USB boot environment
|
|
||||||
├── local/flake.nix # Local NixOS containers
|
|
||||||
├── terraform/ # Hetzner Cloud infrastructure
|
|
||||||
├── scripts/deploy.sh # Cloud setup automation
|
|
||||||
├── docs/USB_BOOT_INSTRUCTIONS.md
|
|
||||||
└── Makefile # Build & deploy commands
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🌍 Three Environments
|
|
||||||
|
|
||||||
### 1. Cloud (Production)
|
|
||||||
- Hetzner VMs: `hopper.codecrispi.es`, `curie.codecrispi.es`, etc.
|
|
||||||
- Pre-configured with Docker Swarm + abra
|
|
||||||
- SSL certificates via Let's Encrypt
|
|
||||||
|
|
||||||
### 2. USB Boot (Workshop)
|
|
||||||
- NixOS live environment
|
|
||||||
- Auto-connects to workshop WiFi
|
|
||||||
- Helper functions: `connect hopper`, `recipes`, `help`
|
|
||||||
- SSH into cloud VMs
|
|
||||||
|
|
||||||
### 3. Local (Development)
|
|
||||||
- NixOS containers: `participant1.local` through `participant15.local`
|
|
||||||
- Test abra deployments locally
|
|
||||||
- Isolated Docker Swarm per container
|
|
||||||
|
|
||||||
## 🔧 Development Workflow
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Enter development environment
|
|
||||||
make local-shell
|
|
||||||
|
|
||||||
# Deploy local testing environment
|
|
||||||
make local-deploy
|
|
||||||
|
|
||||||
# SSH into local participant container
|
|
||||||
make local-ssh # Select participant 1-15
|
|
||||||
|
|
||||||
# Test app deployment inside container
|
|
||||||
abra app new wordpress -S --domain=test.participant1.local
|
|
||||||
abra app deploy test.participant1.local
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📦 Workshop Flow
|
|
||||||
|
|
||||||
1. **Participant boots USB** → NixOS live environment
|
|
||||||
2. **Connects to WiFi** → `CODE_CRISPIES_GUEST`
|
|
||||||
3. **SSH to cloud VM** → `connect hopper`
|
|
||||||
4. **Deploy apps** → `abra app new wordpress -S --domain=mysite.hopper.codecrispi.es`
|
|
||||||
5. **Access via browser** → `https://mysite.hopper.codecrispi.es`
|
|
||||||
|
|
||||||
## 🎯 Available Apps
|
|
||||||
|
|
||||||
### Featured Recipes
|
|
||||||
- **WordPress** - Blog/CMS platform
|
|
||||||
- **Nextcloud** - File sync & collaboration
|
|
||||||
- **HedgeDoc** - Collaborative markdown editor
|
|
||||||
- **Jitsi** - Video conferencing
|
|
||||||
- **PrestaShop** - E-commerce platform
|
|
||||||
- **Gitea** - Git repository hosting
|
|
||||||
- **Matrix Synapse** - Chat server
|
|
||||||
- **Plausible** - Privacy-friendly analytics
|
|
||||||
- **PeerTube** - Video platform
|
|
||||||
- **Mastodon** - Social networking
|
|
||||||
|
|
||||||
Browse all 100+ recipes at: https://recipes.coopcloud.tech
|
|
||||||
|
|
||||||
## 🧹 Cleanup
|
|
||||||
|
|
||||||
```bash
|
|
||||||
make clean # Clean local artifacts
|
|
||||||
make destroy-cloud # Destroy Hetzner infrastructure
|
|
||||||
```
|
|
||||||
|
|||||||
37
flake.lock
generated
37
flake.lock
generated
@@ -1,5 +1,41 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"nixlib": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1736643958,
|
||||||
|
"narHash": "sha256-tmpqTSWVRJVhpvfSN9KXBvKEXplrwKnSZNAoNPf/S/s=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"rev": "1418bc28a52126761c02dd3d89b2d8ca0f521181",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixpkgs.lib",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixos-generators": {
|
||||||
|
"inputs": {
|
||||||
|
"nixlib": "nixlib",
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1751903740,
|
||||||
|
"narHash": "sha256-PeSkNMvkpEvts+9DjFiop1iT2JuBpyknmBUs0Un0a4I=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixos-generators",
|
||||||
|
"rev": "032decf9db65efed428afd2fa39d80f7089085eb",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "nixos-generators",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1754937576,
|
"lastModified": 1754937576,
|
||||||
@@ -18,6 +54,7 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"nixos-generators": "nixos-generators",
|
||||||
"nixpkgs": "nixpkgs"
|
"nixpkgs": "nixpkgs"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
158
flake.nix
158
flake.nix
@@ -1,19 +1,138 @@
|
|||||||
{
|
{
|
||||||
description = "Workshop VM with Participant Containers";
|
description = "Workshop VM with Participant Containers + USB ISO";
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
|
||||||
|
nixos-generators = {
|
||||||
|
url = "github:nix-community/nixos-generators";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs }:
|
outputs = { self, nixpkgs, nixos-generators }:
|
||||||
let
|
let
|
||||||
system = "x86_64-linux";
|
system = "x86_64-linux";
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
participantNames = [ "hopper" "curie" ];
|
participantNames = [ "hopper" "curie" ];
|
||||||
|
fullParticipantNames = [
|
||||||
|
"hopper" "curie" "lovelace" "noether" "hamilton"
|
||||||
|
"franklin" "johnson" "clarke" "goldberg" "liskov"
|
||||||
|
"wing" "rosen" "shaw" "karp" "rich"
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
packages.${system} = {
|
packages.${system} = {
|
||||||
local-vm = self.nixosConfigurations.workshop-vm.config.system.build.vm;
|
local-vm = self.nixosConfigurations.workshop-vm.config.system.build.vm;
|
||||||
|
|
||||||
|
live-iso = nixos-generators.nixosGenerate {
|
||||||
|
inherit system;
|
||||||
|
format = "iso";
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
({ pkgs, ... }: {
|
||||||
|
system.stateVersion = "25.05";
|
||||||
|
|
||||||
|
isoImage.makeEfiBootable = true;
|
||||||
|
isoImage.makeUsbBootable = true;
|
||||||
|
|
||||||
|
networking.wireless.enable = true;
|
||||||
|
networking.networkmanager.enable = true;
|
||||||
|
networking.hostName = "workshop-live";
|
||||||
|
|
||||||
|
services.getty.autologinUser = "workshop";
|
||||||
|
users.users.workshop = {
|
||||||
|
isNormalUser = true;
|
||||||
|
shell = pkgs.zsh;
|
||||||
|
extraGroups = [ "networkmanager" "wheel" ];
|
||||||
|
password = "";
|
||||||
|
};
|
||||||
|
|
||||||
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
openssh curl git networkmanager firefox xterm
|
||||||
|
];
|
||||||
|
|
||||||
|
programs.zsh = {
|
||||||
|
enable = true;
|
||||||
|
interactiveShellInit = ''
|
||||||
|
echo "CODE CRISPIES Workshop Environment"
|
||||||
|
echo "Available servers:"
|
||||||
|
${builtins.concatStringsSep "\n" (map (name:
|
||||||
|
"echo \" - ${name}.codecrispi.es\""
|
||||||
|
) fullParticipantNames)}
|
||||||
|
echo ""
|
||||||
|
echo "Commands: connect <name> | recipes | help"
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
[ -z "$1" ] && { echo "Usage: connect <name>"; return 1; }
|
||||||
|
echo "Connecting to $1.codecrispi.es..."
|
||||||
|
ssh -o StrictHostKeyChecking=no workshop@$1.codecrispi.es
|
||||||
|
}
|
||||||
|
|
||||||
|
recipes() {
|
||||||
|
echo "Available Co-op Cloud Recipes:"
|
||||||
|
echo ""
|
||||||
|
echo "Content Management:"
|
||||||
|
echo " wordpress ghost hedgedoc dokuwiki mediawiki"
|
||||||
|
echo ""
|
||||||
|
echo "File & Collaboration:"
|
||||||
|
echo " nextcloud seafile collabora onlyoffice"
|
||||||
|
echo ""
|
||||||
|
echo "Communication:"
|
||||||
|
echo " jitsi-meet matrix-synapse rocketchat mattermost"
|
||||||
|
echo ""
|
||||||
|
echo "E-commerce & Business:"
|
||||||
|
echo " prestashop invoiceninja kimai pretix"
|
||||||
|
echo ""
|
||||||
|
echo "Development & Tools:"
|
||||||
|
echo " gitea drone n8n gitlab jupyter-lab"
|
||||||
|
echo ""
|
||||||
|
echo "Analytics & Monitoring:"
|
||||||
|
echo " plausible matomo uptime-kuma grafana"
|
||||||
|
echo ""
|
||||||
|
echo "Media & Social:"
|
||||||
|
echo " peertube funkwhale mastodon pixelfed jellyfin"
|
||||||
|
echo ""
|
||||||
|
echo "Deploy: abra app new <recipe> -S --domain=myapp.<name>.codecrispi.es"
|
||||||
|
echo "Browse all: https://recipes.coopcloud.tech"
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo "CODE CRISPIES Workshop Commands:"
|
||||||
|
echo ""
|
||||||
|
echo "connect <name> - SSH to your assigned server"
|
||||||
|
echo "recipes - Show available app recipes"
|
||||||
|
echo "sudo nmcli dev wifi connect SSID password PASSWORD"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " connect hopper"
|
||||||
|
echo " sudo nmcli dev wifi connect CODE_CRISPIES_GUEST password workshop2024"
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f connect recipes help
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.xserver = {
|
||||||
|
enable = true;
|
||||||
|
desktopManager.xfce.enable = true;
|
||||||
|
displayManager = {
|
||||||
|
lightdm.enable = true;
|
||||||
|
autoLogin.enable = true;
|
||||||
|
autoLogin.user = "workshop";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.services.workshop-welcome = {
|
||||||
|
wantedBy = [ "graphical-session.target" ];
|
||||||
|
after = [ "graphical-session.target" ];
|
||||||
|
script = "${pkgs.xterm}/bin/xterm -title 'CODE CRISPIES Workshop' -e 'zsh' &";
|
||||||
|
serviceConfig.Type = "forking";
|
||||||
|
};
|
||||||
|
})
|
||||||
|
];
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
nixosConfigurations.workshop-vm = nixpkgs.lib.nixosSystem {
|
nixosConfigurations.workshop-vm = nixpkgs.lib.nixosSystem {
|
||||||
@@ -36,7 +155,6 @@
|
|||||||
security.pam.services.login.allowNullPassword = true;
|
security.pam.services.login.allowNullPassword = true;
|
||||||
security.sudo.wheelNeedsPassword = false;
|
security.sudo.wheelNeedsPassword = false;
|
||||||
|
|
||||||
# GUI setup
|
|
||||||
services.xserver = {
|
services.xserver = {
|
||||||
enable = true;
|
enable = true;
|
||||||
desktopManager.xfce.enable = true;
|
desktopManager.xfce.enable = true;
|
||||||
@@ -48,32 +166,29 @@
|
|||||||
autoLogin.user = "workshop";
|
autoLogin.user = "workshop";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Auto-open terminal with helper commands
|
|
||||||
services.xserver.displayManager.sessionCommands = ''
|
services.xserver.displayManager.sessionCommands = ''
|
||||||
${pkgs.xfce.xfce4-terminal}/bin/xfce4-terminal --title="🍪 Workshop Terminal" \
|
${pkgs.xfce.xfce4-terminal}/bin/xfce4-terminal --title="Workshop Terminal" \
|
||||||
--command="bash -c '
|
--command="bash -c '
|
||||||
echo \"🍪 Workshop VM Ready!\";
|
echo \"Workshop VM Ready!\";
|
||||||
echo \"\";
|
echo \"\";
|
||||||
echo \"🔌 SSH into containers:\";
|
echo \"SSH into containers:\";
|
||||||
echo \" sudo connect hopper # Container login\";
|
echo \" sudo connect hopper # Container login\";
|
||||||
echo \" sudo connect curie # Container login\";
|
echo \" sudo connect curie # Container login\";
|
||||||
echo \" ssh root@192.168.100.11 # Direct SSH to hopper\";
|
echo \" ssh root@192.168.100.11 # Direct SSH to hopper\";
|
||||||
echo \" ssh root@192.168.100.12 # Direct SSH to curie\";
|
echo \" ssh root@192.168.100.12 # Direct SSH to curie\";
|
||||||
echo \"\";
|
echo \"\";
|
||||||
echo \"📦 Container management:\";
|
echo \"Container management:\";
|
||||||
echo \" sudo containers # List all containers\";
|
echo \" sudo containers # List all containers\";
|
||||||
echo \" sudo logs # Show setup logs\";
|
echo \" sudo logs # Show setup logs\";
|
||||||
echo \"\";
|
echo \"\";
|
||||||
echo \"✨ Abra is pre-installed in containers!\";
|
echo \"Abra is pre-installed in containers!\";
|
||||||
echo \"\";
|
echo \"\";
|
||||||
bash
|
bash
|
||||||
'" &
|
'" &
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# System packages including helper scripts
|
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
firefox curl git jq nano tree nixos-container
|
firefox curl git jq nano tree nixos-container
|
||||||
# Custom helper scripts that work with sudo
|
|
||||||
(pkgs.writeScriptBin "connect" ''
|
(pkgs.writeScriptBin "connect" ''
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
if [ -z "$1" ]; then
|
if [ -z "$1" ]; then
|
||||||
@@ -103,7 +218,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Container configurations with automated abra installation
|
|
||||||
containers = builtins.listToAttrs (builtins.genList (i:
|
containers = builtins.listToAttrs (builtins.genList (i:
|
||||||
let
|
let
|
||||||
name = builtins.elemAt participantNames i;
|
name = builtins.elemAt participantNames i;
|
||||||
@@ -147,52 +261,45 @@
|
|||||||
docker curl git wget jq bash
|
docker curl git wget jq bash
|
||||||
];
|
];
|
||||||
|
|
||||||
# Automated abra installation service
|
|
||||||
systemd.services.workshop-setup = {
|
systemd.services.workshop-setup = {
|
||||||
wantedBy = [ "multi-user.target" ];
|
wantedBy = [ "multi-user.target" ];
|
||||||
after = [ "network-online.target" "docker.service" ];
|
after = [ "network-online.target" "docker.service" ];
|
||||||
wants = [ "network-online.target" ];
|
wants = [ "network-online.target" ];
|
||||||
script = ''
|
script = ''
|
||||||
echo "🍪 Setting up ${name} container..."
|
echo "Setting up ${name} container..."
|
||||||
|
|
||||||
# Wait for network
|
|
||||||
for i in {1..10}; do
|
for i in {1..10}; do
|
||||||
if ${pkgs.curl}/bin/curl -s --max-time 5 google.com >/dev/null 2>&1; then
|
if ${pkgs.curl}/bin/curl -s --max-time 5 google.com >/dev/null 2>&1; then
|
||||||
echo "✅ Network ready"
|
echo "Network ready"
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
echo "⏳ Waiting for network... ($i/10)"
|
echo "Waiting for network... ($i/10)"
|
||||||
sleep 2
|
sleep 2
|
||||||
done
|
done
|
||||||
|
|
||||||
# Initialize Docker Swarm
|
|
||||||
${pkgs.docker}/bin/docker swarm init --advertise-addr ${ip} || true
|
${pkgs.docker}/bin/docker swarm init --advertise-addr ${ip} || true
|
||||||
|
|
||||||
# Install abra for root user
|
|
||||||
export HOME=/root
|
export HOME=/root
|
||||||
if [ ! -f /root/.local/bin/abra ]; then
|
if [ ! -f /root/.local/bin/abra ]; then
|
||||||
echo "📦 Installing abra..."
|
echo "Installing abra..."
|
||||||
${pkgs.curl}/bin/curl -fsSL https://install.abra.coopcloud.tech | ${pkgs.bash}/bin/bash
|
${pkgs.curl}/bin/curl -fsSL https://install.abra.coopcloud.tech | ${pkgs.bash}/bin/bash
|
||||||
echo "✅ Abra installed"
|
echo "Abra installed"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Make abra available globally
|
|
||||||
if ! grep -q "/.local/bin" /root/.bashrc 2>/dev/null; then
|
if ! grep -q "/.local/bin" /root/.bashrc 2>/dev/null; then
|
||||||
echo 'export PATH="$HOME/.local/bin:$PATH"' >> /root/.bashrc
|
echo 'export PATH="$HOME/.local/bin:$PATH"' >> /root/.bashrc
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Create symlink for immediate availability
|
|
||||||
if [ -f /root/.local/bin/abra ]; then
|
if [ -f /root/.local/bin/abra ]; then
|
||||||
ln -sf /root/.local/bin/abra /usr/local/bin/abra 2>/dev/null || true
|
ln -sf /root/.local/bin/abra /usr/local/bin/abra 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add server
|
|
||||||
if [ -f /root/.local/bin/abra ]; then
|
if [ -f /root/.local/bin/abra ]; then
|
||||||
export PATH="/root/.local/bin:$PATH"
|
export PATH="/root/.local/bin:$PATH"
|
||||||
/root/.local/bin/abra server add ${name}.local 2>/dev/null || true
|
/root/.local/bin/abra server add ${name}.local 2>/dev/null || true
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "✅ ${name} container ready!"
|
echo "${name} container ready!"
|
||||||
echo "SSH: ssh root@${ip} (password: root)"
|
echo "SSH: ssh root@${ip} (password: root)"
|
||||||
echo "Abra: Available via 'abra' command"
|
echo "Abra: Available via 'abra' command"
|
||||||
'';
|
'';
|
||||||
@@ -204,7 +311,6 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Ensure abra is in PATH for all sessions
|
|
||||||
environment.sessionVariables = {
|
environment.sessionVariables = {
|
||||||
PATH = [ "/root/.local/bin" ];
|
PATH = [ "/root/.local/bin" ];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,52 +1,49 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
echo "🚀 Deploying Co-op Cloud workshop..."
|
echo "Deploying Co-op Cloud workshop..."
|
||||||
|
|
||||||
cd terraform
|
cd terraform
|
||||||
terraform apply \
|
terraform apply \
|
||||||
-var="hcloud_token=$HCLOUD_TOKEN" \
|
-var="hcloud_token=$HCLOUD_TOKEN" \
|
||||||
|
-var="hetzner_dns_token=$HETZNER_DNS_TOKEN" \
|
||||||
|
-var="dns_zone_id=$DNS_ZONE_ID" \
|
||||||
-var="domain=codecrispi.es" \
|
-var="domain=codecrispi.es" \
|
||||||
-var="ssh_public_key=$(cat ~/.ssh/id_rsa.pub)" \
|
-var="ssh_public_key=$(cat ~/.ssh/id_rsa.pub)" \
|
||||||
-auto-approve
|
-auto-approve
|
||||||
|
|
||||||
echo "⏳ Waiting for servers to be ready..."
|
echo "Waiting for servers to be ready..."
|
||||||
|
|
||||||
# Wait for SSH + Docker to be ready on each server
|
|
||||||
terraform output -json participant_ips | jq -r 'keys[]' | while read participant; do
|
terraform output -json participant_ips | jq -r 'keys[]' | while read participant; do
|
||||||
echo "Checking $participant.codecrispi.es..."
|
echo "Checking $participant.codecrispi.es..."
|
||||||
|
|
||||||
# Wait for SSH to be available
|
|
||||||
while ! ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no workshop@$participant.codecrispi.es "exit" 2>/dev/null; do
|
while ! ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no workshop@$participant.codecrispi.es "exit" 2>/dev/null; do
|
||||||
echo " SSH not ready yet, retrying in 10s..."
|
echo " SSH not ready yet, retrying in 10s..."
|
||||||
sleep 10
|
sleep 10
|
||||||
done
|
done
|
||||||
|
|
||||||
# Wait for Docker + abra to be ready
|
|
||||||
while ! ssh -o StrictHostKeyChecking=no workshop@$participant.codecrispi.es "docker info && which abra" &>/dev/null; do
|
while ! ssh -o StrictHostKeyChecking=no workshop@$participant.codecrispi.es "docker info && which abra" &>/dev/null; do
|
||||||
echo " Docker/abra not ready yet, retrying in 5s..."
|
echo " Docker/abra not ready yet, retrying in 5s..."
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
echo " ✅ $participant ready!"
|
echo " $participant ready!"
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "🔧 Setting up each server..."
|
echo "Setting up each server..."
|
||||||
terraform output -json participant_ips | jq -r 'keys[]' | while read participant; do
|
terraform output -json participant_ips | jq -r 'keys[]' | while read participant; do
|
||||||
echo "Configuring $participant..."
|
echo "Configuring $participant..."
|
||||||
|
|
||||||
ssh -o StrictHostKeyChecking=no workshop@$participant.codecrispi.es << EOF
|
ssh -o StrictHostKeyCheckking=no workshop@$participant.codecrispi.es << EOF
|
||||||
# Deploy Traefik
|
|
||||||
abra app deploy traefik.$participant.codecrispi.es
|
abra app deploy traefik.$participant.codecrispi.es
|
||||||
|
|
||||||
# Wait for Traefik to be ready
|
|
||||||
until curl -f https://traefik.$participant.codecrispi.es/ping 2>/dev/null; do
|
until curl -f https://traefik.$participant.codecrispi.es/ping 2>/dev/null; do
|
||||||
echo "Waiting for Traefik..."
|
echo "Waiting for Traefik..."
|
||||||
sleep 5
|
sleep 5
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "✅ $participant fully configured!"
|
echo "$participant fully configured!"
|
||||||
EOF
|
EOF
|
||||||
done
|
done
|
||||||
|
|
||||||
echo "🎉 Workshop ready! Participants can access their servers."
|
echo "Workshop ready! Participants can access their servers."
|
||||||
|
|||||||
88
terraform/cloud-init.yml
Normal file
88
terraform/cloud-init.yml
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#cloud-config
|
||||||
|
users:
|
||||||
|
- name: workshop
|
||||||
|
groups: [adm, docker]
|
||||||
|
sudo: ['ALL=(ALL) NOPASSWD:ALL']
|
||||||
|
shell: /bin/bash
|
||||||
|
ssh_authorized_keys:
|
||||||
|
- ${ssh_public_key}
|
||||||
|
|
||||||
|
packages:
|
||||||
|
- docker.io
|
||||||
|
- curl
|
||||||
|
- git
|
||||||
|
- jq
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- systemctl enable docker
|
||||||
|
- systemctl start docker
|
||||||
|
- usermod -aG docker workshop
|
||||||
|
- docker swarm init
|
||||||
|
- docker network create -d overlay proxy
|
||||||
|
|
||||||
|
- curl -fsSL https://install.abra.coopcloud.tech | sudo -u workshop bash
|
||||||
|
- ln -sf /home/workshop/.local/bin/abra /usr/local/bin/abra
|
||||||
|
|
||||||
|
- sudo -u workshop mkdir -p /home/workshop/.abra/servers
|
||||||
|
- sudo -u workshop abra server add ${participant_name}.${domain}
|
||||||
|
|
||||||
|
- sudo -u workshop abra app new traefik --domain=traefik.${participant_name}.${domain} --server=${participant_name}.${domain}
|
||||||
|
- sudo -u workshop abra app deploy traefik.${participant_name}.${domain}
|
||||||
|
|
||||||
|
- |
|
||||||
|
cat > /home/workshop/.bashrc << 'EOF'
|
||||||
|
export PATH="$HOME/.local/bin:$PATH"
|
||||||
|
|
||||||
|
recipes() {
|
||||||
|
echo "Available Co-op Cloud Recipes:"
|
||||||
|
echo ""
|
||||||
|
echo "Content Management:"
|
||||||
|
echo " wordpress ghost hedgedoc dokuwiki mediawiki"
|
||||||
|
echo ""
|
||||||
|
echo "File & Collaboration:"
|
||||||
|
echo " nextcloud seafile collabora onlyoffice"
|
||||||
|
echo ""
|
||||||
|
echo "Communication:"
|
||||||
|
echo " jitsi-meet matrix-synapse rocketchat mattermost"
|
||||||
|
echo ""
|
||||||
|
echo "E-commerce & Business:"
|
||||||
|
echo " prestashop invoiceninja kimai pretix"
|
||||||
|
echo ""
|
||||||
|
echo "Development & Tools:"
|
||||||
|
echo " gitea n8n jupyter-lab"
|
||||||
|
echo ""
|
||||||
|
echo "Analytics & Monitoring:"
|
||||||
|
echo " plausible uptime-kuma grafana"
|
||||||
|
echo ""
|
||||||
|
echo "Media & Social:"
|
||||||
|
echo " peertube mastodon jellyfin"
|
||||||
|
echo ""
|
||||||
|
echo "Deploy: abra app new <recipe> -S --domain=myapp.${participant_name}.${domain}"
|
||||||
|
echo "Browse all: https://recipes.coopcloud.tech"
|
||||||
|
}
|
||||||
|
|
||||||
|
help() {
|
||||||
|
echo "CODE CRISPIES Workshop Commands:"
|
||||||
|
echo ""
|
||||||
|
echo "recipes - Show available app recipes"
|
||||||
|
echo "abra app new <recipe> -S --domain=<name>.${participant_name}.${domain}"
|
||||||
|
echo "abra app deploy <domain>"
|
||||||
|
echo "abra app ls - List your apps"
|
||||||
|
echo ""
|
||||||
|
echo "Examples:"
|
||||||
|
echo " abra app new wordpress -S --domain=blog.${participant_name}.${domain}"
|
||||||
|
echo " abra app deploy blog.${participant_name}.${domain}"
|
||||||
|
echo ""
|
||||||
|
echo "Server: ${participant_name}.${domain}"
|
||||||
|
echo "Traefik: https://traefik.${participant_name}.${domain}"
|
||||||
|
}
|
||||||
|
|
||||||
|
export -f recipes help
|
||||||
|
|
||||||
|
echo "CODE CRISPIES Workshop!"
|
||||||
|
echo "Server: ${participant_name}.${domain}"
|
||||||
|
echo "Type 'help' for commands or 'recipes' for available apps"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
final_message:
|
||||||
|
- echo "Workshop server ready! Traefik available at https://traefik.${participant_name}.${domain}"
|
||||||
90
terraform/main.tf
Normal file
90
terraform/main.tf
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
terraform {
|
||||||
|
required_providers {
|
||||||
|
hcloud = {
|
||||||
|
source = "hetznercloud/hcloud"
|
||||||
|
version = "~> 1.48"
|
||||||
|
}
|
||||||
|
hetznerdns = {
|
||||||
|
source = "timohirt/hetznerdns"
|
||||||
|
version = "~> 2.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
locals {
|
||||||
|
domain = var.domain
|
||||||
|
participants = [
|
||||||
|
"hopper", "curie", "lovelace", "noether", "hamilton",
|
||||||
|
"franklin", "johnson", "clarke", "goldberg", "liskov",
|
||||||
|
"wing", "rosen", "shaw", "karp", "rich"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "hcloud" {
|
||||||
|
token = var.hcloud_token
|
||||||
|
}
|
||||||
|
|
||||||
|
provider "hetznerdns" {
|
||||||
|
apitoken = var.hetzner_dns_token
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "hcloud_ssh_key" "workshop" {
|
||||||
|
name = "code-crispies-workshop"
|
||||||
|
public_key = var.ssh_public_key
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "hcloud_server" "participant" {
|
||||||
|
for_each = toset(local.participants)
|
||||||
|
|
||||||
|
name = each.key
|
||||||
|
image = "ubuntu-22.04"
|
||||||
|
server_type = "cx21"
|
||||||
|
location = "nbg1"
|
||||||
|
|
||||||
|
ssh_keys = [hcloud_ssh_key.workshop.id]
|
||||||
|
|
||||||
|
user_data = templatefile("${path.module}/cloud-init.yml", {
|
||||||
|
participant_name = each.key
|
||||||
|
domain = local.domain
|
||||||
|
ssh_public_key = var.ssh_public_key
|
||||||
|
})
|
||||||
|
|
||||||
|
labels = {
|
||||||
|
project = "code-crispies-workshop"
|
||||||
|
participant = each.key
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "hetznerdns_record" "participant_main" {
|
||||||
|
for_each = toset(local.participants)
|
||||||
|
|
||||||
|
zone_id = var.dns_zone_id
|
||||||
|
name = each.key
|
||||||
|
type = "A"
|
||||||
|
value = hcloud_server.participant[each.key].ipv4_address
|
||||||
|
ttl = 60
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "hetznerdns_record" "participant_wildcard" {
|
||||||
|
for_each = toset(local.participants)
|
||||||
|
|
||||||
|
zone_id = var.dns_zone_id
|
||||||
|
name = "*.${each.key}"
|
||||||
|
type = "A"
|
||||||
|
value = hcloud_server.participant[each.key].ipv4_address
|
||||||
|
ttl = 60
|
||||||
|
}
|
||||||
|
|
||||||
|
output "participant_ips" {
|
||||||
|
value = {
|
||||||
|
for name, server in hcloud_server.participant : name => server.ipv4_address
|
||||||
|
}
|
||||||
|
description = "IP addresses of participant servers"
|
||||||
|
}
|
||||||
|
|
||||||
|
output "participant_urls" {
|
||||||
|
value = {
|
||||||
|
for name, server in hcloud_server.participant : name => "https://traefik.${name}.${local.domain}"
|
||||||
|
}
|
||||||
|
description = "Traefik URLs for each participant"
|
||||||
|
}
|
||||||
29
terraform/variables.tf
Normal file
29
terraform/variables.tf
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
variable "hcloud_token" {
|
||||||
|
description = "Hetzner Cloud API token"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "hetzner_dns_token" {
|
||||||
|
description = "Hetzner DNS API token"
|
||||||
|
type = string
|
||||||
|
sensitive = true
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "dns_zone_id" {
|
||||||
|
description = "Hetzner DNS Zone ID"
|
||||||
|
type = string
|
||||||
|
default = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "domain" {
|
||||||
|
description = "Workshop domain"
|
||||||
|
type = string
|
||||||
|
default = "codecrispi.es"
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "ssh_public_key" {
|
||||||
|
description = "SSH public key for server access"
|
||||||
|
type = string
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user