forked from fediversity/fediversity
switch to bash deployment
This commit is contained in:
parent
319a6c391c
commit
246380e00f
16 changed files with 84 additions and 312 deletions
|
|
@ -43,10 +43,7 @@
|
||||||
pre-commit.settings.hooks =
|
pre-commit.settings.hooks =
|
||||||
let
|
let
|
||||||
## Add a directory here if pre-commit hooks shouldn't apply to it.
|
## Add a directory here if pre-commit hooks shouldn't apply to it.
|
||||||
optout = [
|
optout = [ "npins" ];
|
||||||
"npins"
|
|
||||||
"launch/.terraform"
|
|
||||||
];
|
|
||||||
excludes = map (dir: "^${dir}/") optout;
|
excludes = map (dir: "^${dir}/") optout;
|
||||||
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
|
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
|
||||||
in
|
in
|
||||||
|
|
|
||||||
4
launch/.gitignore
vendored
4
launch/.gitignore
vendored
|
|
@ -1,4 +0,0 @@
|
||||||
.auto.tfvars.json
|
|
||||||
.terraform/
|
|
||||||
.terraform.tfstate.lock.info
|
|
||||||
terraform.tfstate*
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
# service deployment
|
|
||||||
|
|
||||||
## usage
|
|
||||||
|
|
||||||
### updating TF modules
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ npins update nixos-anywhere
|
|
||||||
$ cd launch/
|
|
||||||
$ echo "{\"nixos-anywhere\": $(nix-instantiate --eval --json -E '(import ../npins).nixos-anywhere.outPath')}" > .auto.tfvars.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### local development
|
|
||||||
|
|
||||||
```sh
|
|
||||||
$ nix-shell
|
|
||||||
$ rm -rf .terraform/
|
|
||||||
$ tofu init
|
|
||||||
```
|
|
||||||
|
|
@ -1,31 +0,0 @@
|
||||||
{
|
|
||||||
system ? builtins.currentSystem,
|
|
||||||
sources ? import ../npins,
|
|
||||||
inputs ? import sources.flake-inputs {
|
|
||||||
root = ../.;
|
|
||||||
},
|
|
||||||
# match the same version of opentofu that is deployed by the root flake
|
|
||||||
pkgs ? import inputs.nixpkgs {
|
|
||||||
inherit system;
|
|
||||||
},
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
inherit (pkgs) lib;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
shell = pkgs.mkShellNoCC {
|
|
||||||
packages = [
|
|
||||||
pkgs.npins
|
|
||||||
pkgs.jq # implicit dep of nixos-anywhere TF: https://github.com/nix-community/nixos-anywhere/issues/416
|
|
||||||
(import ./tf.nix { inherit lib pkgs; })
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
# re-export inputs so they can be overridden granularly
|
|
||||||
# (they can't be accessed from the outside any other way)
|
|
||||||
inherit
|
|
||||||
sources
|
|
||||||
system
|
|
||||||
pkgs
|
|
||||||
;
|
|
||||||
}
|
|
||||||
16
launch/deploy.sh
Executable file
16
launch/deploy.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -uex -o pipefail
|
||||||
|
declare domain hostname config initialUser
|
||||||
|
eval "$(jq -r '@sh "domain=\(.domain) hostname=\(.hostname) config=\(.config) initialUser=\(.initialUser)"')"
|
||||||
|
TARGET_HOST="${hostname}.abundos.eu"
|
||||||
|
TARGET="root@${TARGET_HOST}"
|
||||||
|
wrapper="$(mktemp -d)/wrapper.nix"
|
||||||
|
echo "(import $(readlink -f "./${config}.nix")).extendModules { specialArgs.terraform = { domain = \"${domain}\"; hostname = \"${hostname}\"; initialUser = builtins.fromJSON ''${initialUser}''; }; }" > "$wrapper"
|
||||||
|
NIXOS_SYSTEM=$(nix build --no-link --json --option show-trace true --file "$wrapper" "config.system.build.toplevel" | jq -r '.[].outputs.out')
|
||||||
|
sshOpts=(-p 22 -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no)
|
||||||
|
NIX_SSHOPTS="${sshOpts[*]}" nix copy -s --experimental-features nix-command --to "ssh://$TARGET" "$NIXOS_SYSTEM"
|
||||||
|
switchCommand="nix-env -p /nix/var/nix/profiles/system --set $(printf "%q" "$NIXOS_SYSTEM"); /nix/var/nix/profiles/system/bin/switch-to-configuration switch"
|
||||||
|
deploy_status=0
|
||||||
|
# shellcheck disable=SC2029
|
||||||
|
ssh "${sshOpts[@]}" "$TARGET" "$switchCommand" || deploy_status="$?"
|
||||||
|
exit "$deploy_status"
|
||||||
11
launch/env.nix
Normal file
11
launch/env.nix
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
pkgs.stdenv.mkDerivation {
|
||||||
|
name = "fediversity-repo";
|
||||||
|
src = ../.;
|
||||||
|
installPhase = ''
|
||||||
|
cp -r . $out
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
|
@ -1,92 +0,0 @@
|
||||||
variable "nixos-anywhere" {
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "domain" {
|
|
||||||
type = string
|
|
||||||
default = "fediversity.net"
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "mastodon" {
|
|
||||||
type = object({
|
|
||||||
enable = bool
|
|
||||||
})
|
|
||||||
default = {
|
|
||||||
enable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "pixelfed" {
|
|
||||||
type = object({
|
|
||||||
enable = bool
|
|
||||||
})
|
|
||||||
default = {
|
|
||||||
enable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "peertube" {
|
|
||||||
type = object({
|
|
||||||
enable = bool
|
|
||||||
})
|
|
||||||
default = {
|
|
||||||
enable = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "initialUser" {
|
|
||||||
type = object({
|
|
||||||
displayName = string
|
|
||||||
username = string
|
|
||||||
email = string
|
|
||||||
# TODO: mark (nested) credentials as sensitive
|
|
||||||
# https://discuss.hashicorp.com/t/is-it-possible-to-mark-an-attribute-of-an-object-as-sensitive/24649/2
|
|
||||||
password = string
|
|
||||||
})
|
|
||||||
default = {
|
|
||||||
displayName = "Testy McTestface"
|
|
||||||
username = "test"
|
|
||||||
email = "test@test.com"
|
|
||||||
password = "testtest"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# module "garage" {
|
|
||||||
# source = "./vm"
|
|
||||||
# count = var.mastodon.enable || var.pixelfed.enable || var.peertube.enable ? 1 : 0
|
|
||||||
# domain = var.domain
|
|
||||||
# hostname = "test01"
|
|
||||||
# config = "garage"
|
|
||||||
# initialUser = var.initialUser
|
|
||||||
# nixos-anywhere = var.nixos-anywhere
|
|
||||||
# }
|
|
||||||
|
|
||||||
module "mastodon" {
|
|
||||||
source = "./vm"
|
|
||||||
count = var.mastodon.enable ? 1 : 0
|
|
||||||
domain = var.domain
|
|
||||||
hostname = "test02"
|
|
||||||
config = "mastodon"
|
|
||||||
initialUser = var.initialUser
|
|
||||||
nixos-anywhere = var.nixos-anywhere
|
|
||||||
}
|
|
||||||
|
|
||||||
module "pixelfed" {
|
|
||||||
source = "./vm"
|
|
||||||
count = var.pixelfed.enable ? 1 : 0
|
|
||||||
domain = var.domain
|
|
||||||
hostname = "test04"
|
|
||||||
config = "pixelfed"
|
|
||||||
initialUser = var.initialUser
|
|
||||||
nixos-anywhere = var.nixos-anywhere
|
|
||||||
}
|
|
||||||
|
|
||||||
module "peertube" {
|
|
||||||
source = "./vm"
|
|
||||||
count = var.peertube.enable ? 1 : 0
|
|
||||||
domain = var.domain
|
|
||||||
hostname = "test03"
|
|
||||||
config = "peertube"
|
|
||||||
initialUser = var.initialUser
|
|
||||||
nixos-anywhere = var.nixos-anywhere
|
|
||||||
}
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
#!/usr/bin/env bash
|
|
||||||
export host="$host"
|
|
||||||
|
|
||||||
mkdir -p etc/ssh
|
|
||||||
|
|
||||||
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
|
|
||||||
|
|
||||||
for keyname in ssh_host_ed25519_key ssh_host_ed25519_key.pub; do
|
|
||||||
if [[ $keyname == *.pub ]]; then
|
|
||||||
umask 0133
|
|
||||||
else
|
|
||||||
umask 0177
|
|
||||||
fi
|
|
||||||
cp "$SCRIPT_DIR/../infra/test-machines/${host}/${keyname}" ./etc/ssh/${keyname}
|
|
||||||
done
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
(import ./. { }).shell
|
|
||||||
|
|
@ -1,36 +0,0 @@
|
||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
sources ? import ../npins,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
pkgs.stdenv.mkDerivation {
|
|
||||||
name = "tf-repo";
|
|
||||||
src = ../.;
|
|
||||||
buildInputs = [
|
|
||||||
(import ./tf.nix { inherit lib pkgs; })
|
|
||||||
];
|
|
||||||
buildPhase = ''
|
|
||||||
runHook preBuild
|
|
||||||
pushd launch/
|
|
||||||
|
|
||||||
# pass nixos-anywhere path to TF through variable
|
|
||||||
# when switching TF to nix take this directly from `inputs`
|
|
||||||
# https://codeberg.org/kiara/e2ed-hetzner/commit/84b2a349d3e48ea2a17340bceff762d834fd4046
|
|
||||||
echo "{\"nixos-anywhere\": \"${sources.nixos-anywhere}\"}" > .auto.tfvars.json
|
|
||||||
|
|
||||||
# point to the relevant providers
|
|
||||||
tofu init -input=false
|
|
||||||
|
|
||||||
popd
|
|
||||||
runHook postBuild
|
|
||||||
'';
|
|
||||||
# FIXME: can the above even work without a connection?
|
|
||||||
installPhase = ''
|
|
||||||
runHook preInstall
|
|
||||||
|
|
||||||
cp -r . $out
|
|
||||||
|
|
||||||
runHook postInstall
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
|
|
@ -1,25 +0,0 @@
|
||||||
# FIXME: use overlays so this gets imported just once?
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}:
|
|
||||||
let
|
|
||||||
tofuProvider =
|
|
||||||
provider:
|
|
||||||
provider.override (oldArgs: {
|
|
||||||
provider-source-address =
|
|
||||||
lib.replaceStrings [ "https://registry.terraform.io/providers" ] [ "registry.opentofu.org" ]
|
|
||||||
oldArgs.homepage;
|
|
||||||
});
|
|
||||||
tf = pkgs.opentofu;
|
|
||||||
tfPlugins = (
|
|
||||||
p: [
|
|
||||||
p.null
|
|
||||||
p.external
|
|
||||||
]
|
|
||||||
);
|
|
||||||
in
|
|
||||||
# tf.withPlugins tfPlugins
|
|
||||||
# https://github.com/NixOS/nixpkgs/pull/358522
|
|
||||||
tf.withPlugins (p: pkgs.lib.lists.map tofuProvider (tfPlugins p))
|
|
||||||
|
|
@ -1,51 +0,0 @@
|
||||||
variable "nixos-anywhere" {
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "domain" {
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "hostname" {
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "config" {
|
|
||||||
type = string
|
|
||||||
}
|
|
||||||
|
|
||||||
variable "initialUser" {
|
|
||||||
type = object({
|
|
||||||
displayName = string
|
|
||||||
username = string
|
|
||||||
password = string
|
|
||||||
email = string
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
module "deploy" {
|
|
||||||
# source = "github.com/nix-community/nixos-anywhere//terraform/all-in-one"
|
|
||||||
source = "${var.nixos-anywhere}//terraform/all-in-one"
|
|
||||||
file = "${path.module}/../${var.config}.nix"
|
|
||||||
nixos_system_attr = "config.system.build.toplevel"
|
|
||||||
nixos_partitioner_attr = "config.system.build.diskoScript"
|
|
||||||
# when instance id changes, it will trigger a reinstall
|
|
||||||
instance_id = var.hostname
|
|
||||||
target_user = "root"
|
|
||||||
target_host = "${var.hostname}.abundos.eu"
|
|
||||||
extra_files_script = "${path.module}/../pass-ssh-key.sh"
|
|
||||||
extra_environment = {
|
|
||||||
host = var.hostname
|
|
||||||
}
|
|
||||||
special_args = {
|
|
||||||
terraform = {
|
|
||||||
domain = var.domain
|
|
||||||
hostname = var.hostname
|
|
||||||
initialUser = var.initialUser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nix_options = {
|
|
||||||
show-trace = true
|
|
||||||
}
|
|
||||||
# build_on_remote = true
|
|
||||||
}
|
|
||||||
|
|
@ -11,6 +11,5 @@
|
||||||
pkgs.openssh
|
pkgs.openssh
|
||||||
pkgs.git
|
pkgs.git
|
||||||
pkgs.jq # implicit dep of nixos-anywhere TF: https://github.com/nix-community/nixos-anywhere/issues/416
|
pkgs.jq # implicit dep of nixos-anywhere TF: https://github.com/nix-community/nixos-anywhere/issues/416
|
||||||
(import ../launch/tf.nix { inherit lib pkgs; })
|
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ let
|
||||||
((pkgs.formats.pythonVars { }).generate "settings.py" cfg.settings)
|
((pkgs.formats.pythonVars { }).generate "settings.py" cfg.settings)
|
||||||
(builtins.toFile "extra-settings.py" cfg.extra-settings)
|
(builtins.toFile "extra-settings.py" cfg.extra-settings)
|
||||||
];
|
];
|
||||||
REPO_DIR = import ../../launch/tf-env.nix { inherit lib pkgs; };
|
REPO_DIR = import ../../launch/env.nix { inherit lib pkgs; };
|
||||||
};
|
};
|
||||||
|
|
||||||
python-environment = pkgs.python3.withPackages (
|
python-environment = pkgs.python3.withPackages (
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ python3.pkgs.buildPythonPackage {
|
||||||
cp -v ${src}/manage.py $out/bin/manage.py
|
cp -v ${src}/manage.py $out/bin/manage.py
|
||||||
chmod +x $out/bin/manage.py
|
chmod +x $out/bin/manage.py
|
||||||
wrapProgram $out/bin/manage.py \
|
wrapProgram $out/bin/manage.py \
|
||||||
--set REPO_DIR "${import ../../launch/tf-env.nix { inherit lib pkgs; }}" \
|
--set REPO_DIR "${import ../../launch/env.nix { inherit lib pkgs; }}" \
|
||||||
--prefix PYTHONPATH : "$PYTHONPATH"
|
--prefix PYTHONPATH : "$PYTHONPATH"
|
||||||
cp ${sources.htmx}/dist/htmx.min.js* $out/${python3.sitePackages}/panel/static/
|
cp ${sources.htmx}/dist/htmx.min.js* $out/${python3.sitePackages}/panel/static/
|
||||||
'';
|
'';
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
import json
|
import json
|
||||||
from os.path import expanduser
|
import logging
|
||||||
import subprocess
|
import subprocess
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
@ -14,6 +14,8 @@ from django.shortcuts import render
|
||||||
from panel import models, settings
|
from panel import models, settings
|
||||||
from panel.configuration import forms
|
from panel.configuration import forms
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class Index(TemplateView):
|
class Index(TemplateView):
|
||||||
template_name = 'index.html'
|
template_name = 'index.html'
|
||||||
|
|
@ -103,7 +105,7 @@ class DeploymentStatus(ConfigurationForm):
|
||||||
# Check for deploy button
|
# Check for deploy button
|
||||||
if "deploy" in self.request.POST.keys():
|
if "deploy" in self.request.POST.keys():
|
||||||
deployment_result, deployment_params = self.deployment(obj)
|
deployment_result, deployment_params = self.deployment(obj)
|
||||||
if deployment_result.returncode == 0:
|
if deployment_result == 0:
|
||||||
deployment_status = "Deployment Succeeded"
|
deployment_status = "Deployment Succeeded"
|
||||||
else:
|
else:
|
||||||
deployment_status = "Deployment Failed"
|
deployment_status = "Deployment Failed"
|
||||||
|
|
@ -118,39 +120,60 @@ class DeploymentStatus(ConfigurationForm):
|
||||||
})
|
})
|
||||||
|
|
||||||
def deployment(self, obj):
|
def deployment(self, obj):
|
||||||
submission = obj.parsed_value.model_dump_json()
|
testvms = {
|
||||||
|
"garage": "test01",
|
||||||
|
"mastodon": "test06",
|
||||||
|
"pixelfed": "test04",
|
||||||
|
"peertube": "test03",
|
||||||
|
}
|
||||||
|
submission = json.loads(obj.parsed_value.model_dump_json())
|
||||||
|
logger.debug(f"submission: {submission}")
|
||||||
# FIXME: let the user specify these from the form (#190)
|
# FIXME: let the user specify these from the form (#190)
|
||||||
dummy_user = {
|
dummy_user = {
|
||||||
"initialUser": {
|
"initialUser": json.dumps({
|
||||||
"displayName": "Testy McTestface",
|
"displayName": "Testy McTestface",
|
||||||
"username": "test",
|
"username": "test",
|
||||||
"password": "testtest",
|
"password": "testtest",
|
||||||
"email": "test@test.com",
|
"email": "test@test.com",
|
||||||
},
|
}),
|
||||||
}
|
}
|
||||||
# serialize back and forth now we still need to manually inject the dummy user
|
deployment_result = 0
|
||||||
deployment_params = dummy_user | json.loads(submission)
|
if submission["enable"]:
|
||||||
env = {
|
services = [
|
||||||
"PATH": settings.bin_path,
|
"mastodon",
|
||||||
# used in nixos-anywhere for ssh-copy-id to make `.ssh` in for ssh-copy-id.
|
"pixelfed",
|
||||||
# run thru subprocess, HOME points to the read-only `/var/empty`.
|
"peertube",
|
||||||
# in local dev, it will just reject the `/tmp` and make it in HOME after all.
|
]
|
||||||
"HOME": "/tmp",
|
for service in [
|
||||||
"XDG_CACHE_HOME": "/tmp",
|
"garage",
|
||||||
} | {
|
] + services:
|
||||||
# pass in form info to our deployment
|
if (service == "garage" and any(submission[srvc]["enable"] for srvc in services)) or submission[service]["enable"]:
|
||||||
# FIXME: ensure sensitive info is protected
|
hostname = testvms[service]
|
||||||
f"TF_VAR_{k}": v if isinstance(v, str) else json.dumps(v) for k, v in deployment_params.items()
|
# serialize back and forth now we still need to manually inject the dummy user
|
||||||
}
|
deployment_params = json.dumps({
|
||||||
cwd = f"{settings.repo_dir}/launch"
|
"config": service,
|
||||||
cmd = [
|
"hostname": hostname,
|
||||||
"tofu",
|
"initialUser": json.dumps({
|
||||||
# f"-chdir={cwd}",
|
"displayName": "Testy McTestface",
|
||||||
"apply",
|
"username": "test",
|
||||||
f"-state={cwd}/terraform.tfstate", # FIXME: separate users' state
|
"password": "testtest",
|
||||||
"--auto-approve",
|
"email": "test@test.com",
|
||||||
"-lock=false",
|
}),
|
||||||
]
|
} | submission)
|
||||||
deployment_result = subprocess.run(cmd, cwd=cwd, env=env)
|
logger.debug(f"deployment_params: {deployment_params}")
|
||||||
print(deployment_result)
|
deployment_result = deployment_result or subprocess.run(
|
||||||
return deployment_result, deployment_params
|
["./deploy.sh"],
|
||||||
|
cwd=f"{settings.repo_dir}/launch",
|
||||||
|
env={
|
||||||
|
"PATH": settings.bin_path,
|
||||||
|
},
|
||||||
|
# pass in form info to our deployment
|
||||||
|
input=deployment_params,
|
||||||
|
text=True,
|
||||||
|
).returncode
|
||||||
|
else:
|
||||||
|
logger.debug(f"service {service} disabled")
|
||||||
|
else:
|
||||||
|
# FIXME: implement disable
|
||||||
|
logger.debug("deployment disabled")
|
||||||
|
return deployment_result, json.loads(deployment_params)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue