diff --git a/launch/.gitignore b/launch/.gitignore deleted file mode 100644 index 6ff139e0..00000000 --- a/launch/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.auto.tfvars.json -.terraform/ -.terraform.tfstate.lock.info -terraform.tfstate* diff --git a/launch/README.md b/launch/README.md deleted file mode 100644 index 8bed71b8..00000000 --- a/launch/README.md +++ /dev/null @@ -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 -``` diff --git a/launch/default.nix b/launch/default.nix index 8518f575..98cb7861 100644 --- a/launch/default.nix +++ b/launch/default.nix @@ -9,15 +9,11 @@ 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; }) ]; }; diff --git a/launch/deploy.sh b/launch/deploy.sh new file mode 100755 index 00000000..efdc75b5 --- /dev/null +++ b/launch/deploy.sh @@ -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" diff --git a/launch/main.tf b/launch/main.tf deleted file mode 100644 index 3fd5a5b5..00000000 --- a/launch/main.tf +++ /dev/null @@ -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 -} diff --git a/launch/pass-ssh-key.sh b/launch/pass-ssh-key.sh deleted file mode 100755 index 80f17dec..00000000 --- a/launch/pass-ssh-key.sh +++ /dev/null @@ -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 diff --git a/launch/tf-env.nix b/launch/tf-env.nix deleted file mode 100644 index 817c6e23..00000000 --- a/launch/tf-env.nix +++ /dev/null @@ -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 - ''; -} diff --git a/launch/tf.nix b/launch/tf.nix deleted file mode 100644 index 14ad72a7..00000000 --- a/launch/tf.nix +++ /dev/null @@ -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)) diff --git a/launch/vm/main.tf b/launch/vm/main.tf deleted file mode 100644 index e4eb9b88..00000000 --- a/launch/vm/main.tf +++ /dev/null @@ -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 -} diff --git a/panel/env.nix b/panel/env.nix index 99a286d8..ad184cad 100644 --- a/panel/env.nix +++ b/panel/env.nix @@ -11,6 +11,5 @@ pkgs.openssh pkgs.git pkgs.jq # implicit dep of nixos-anywhere TF: https://github.com/nix-community/nixos-anywhere/issues/416 - (import ../launch/tf.nix { inherit lib pkgs; }) ]; } diff --git a/panel/nix/configuration.nix b/panel/nix/configuration.nix index 8b7cd95d..fcc17f71 100644 --- a/panel/nix/configuration.nix +++ b/panel/nix/configuration.nix @@ -29,7 +29,7 @@ let ((pkgs.formats.pythonVars { }).generate "settings.py" cfg.settings) (builtins.toFile "extra-settings.py" cfg.extra-settings) ]; - REPO_DIR = import ../../launch/tf-env.nix { inherit lib pkgs; }; + REPO_DIR = ./..; }; python-environment = pkgs.python3.withPackages ( diff --git a/panel/nix/package.nix b/panel/nix/package.nix index 0acd9796..49242b04 100644 --- a/panel/nix/package.nix +++ b/panel/nix/package.nix @@ -60,7 +60,7 @@ python3.pkgs.buildPythonPackage { cp -v ${src}/manage.py $out/bin/manage.py chmod +x $out/bin/manage.py wrapProgram $out/bin/manage.py \ - --set REPO_DIR "${import ../../launch/tf-env.nix { inherit lib pkgs; }}" \ + --set REPO_DIR "${./..}" \ --prefix PYTHONPATH : "$PYTHONPATH" cp ${sources.htmx}/dist/htmx.min.js* $out/${python3.sitePackages}/panel/static/ ''; diff --git a/panel/src/panel/views.py b/panel/src/panel/views.py index 2299751b..be640930 100644 --- a/panel/src/panel/views.py +++ b/panel/src/panel/views.py @@ -1,6 +1,6 @@ from enum import Enum import json -from os.path import expanduser +import logging import subprocess import os @@ -14,6 +14,8 @@ from django.shortcuts import render from panel import models, settings from panel.configuration import forms +logger = logging.getLogger(__name__) + class Index(TemplateView): template_name = 'index.html' @@ -103,54 +105,77 @@ class DeploymentStatus(ConfigurationForm): # Check for deploy button if "deploy" in self.request.POST.keys(): deployment_result, deployment_params = self.deployment(obj) - if deployment_result.returncode == 0: - deployment_status = "Deployment Succeeded" - else: - deployment_status = "Deployment Failed" + deployment_succeeded = deployment_result == 0 return render(self.request, "partials/deployment_result.html", { - "deployment_status": deployment_status, + "deployment_succeeded": deployment_succeeded, "services": { - "peertube": deployment_params['peertube']['enable'], - "pixelfed": deployment_params['pixelfed']['enable'], - "mastodon": deployment_params['mastodon']['enable'] - } - }) + "peertube": { + "enable": deployment_params['peertube']['enable'], + "url": f"https://peertube.{deployment_params['domain']}", + }, + "pixelfed":{ + "enable": deployment_params['pixelfed']['enable'], + "url": f"https://pixelfed.{deployment_params['domain']}", + }, + "mastodon": { + "enable": deployment_params['mastodon']['enable'], + "url": f"https://mastodon.{deployment_params['domain']}", + }, + }, + }) def deployment(self, obj): - submission = obj.parsed_value.model_dump_json() + testvms = { + "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) dummy_user = { - "initialUser": { + "initialUser": json.dumps({ "displayName": "Testy McTestface", "username": "test", "password": "testtest", "email": "test@test.com", - }, + }), } - # serialize back and forth now we still need to manually inject the dummy user - deployment_params = dummy_user | json.loads(submission) - env = { - "PATH": settings.bin_path, - # used in nixos-anywhere for ssh-copy-id to make `.ssh` in for ssh-copy-id. - # run thru subprocess, HOME points to the read-only `/var/empty`. - # in local dev, it will just reject the `/tmp` and make it in HOME after all. - "HOME": "/tmp", - "XDG_CACHE_HOME": "/tmp", - } | { - # pass in form info to our deployment - # FIXME: ensure sensitive info is protected - f"TF_VAR_{k}": v if isinstance(v, str) else json.dumps(v) for k, v in deployment_params.items() - } - cwd = f"{settings.repo_dir}/launch" - cmd = [ - "tofu", - # f"-chdir={cwd}", - "apply", - f"-state={cwd}/terraform.tfstate", # FIXME: separate users' state - "--auto-approve", - "-lock=false", - ] - deployment_result = subprocess.run(cmd, cwd=cwd, env=env) - print(deployment_result) - return deployment_result, deployment_params + deployment_result = 0 + if submission["enable"]: + for service in [ + "mastodon", + "pixelfed", + "peertube", + ]: + if submission[service]["enable"]: + hostname = testvms[service] + # serialize back and forth now we still need to manually inject the dummy user + deployment_params = json.dumps({ + "config": service, + "hostname": hostname, + "initialUser": json.dumps({ + "displayName": "Testy McTestface", + "username": "test", + "password": "testtest", + "email": "test@test.com", + }), + } | submission) + logger.debug(f"deployment_params: {deployment_params}") + deployment_result = deployment_result or subprocess.run( + ["./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)