Compare commits

...

14 commits

Author SHA1 Message Date
caa497c4a2
get TF in prod to the same 'installable ... does not correspond to a Nix language value' for non-flakes
seemingly gets further when a similar command is tried from terminal.
as per https://github.com/NixOS/nix/issues/8752#issuecomment-1694714693,
this may have to do with aligning the current working directory.
2025-03-28 21:14:16 +01:00
659a3593b5
document updating TF module 2025-03-28 21:14:16 +01:00
7e24b9e478
update 2025-03-28 21:14:16 +01:00
7048058d6b
specify XDG_CACHE_HOME, workaround to error writing to /var/empty/.cache 2025-03-28 21:14:16 +01:00
ed63b582ed
skip tf lock in views.py over read-only nix env 2025-03-28 21:14:16 +01:00
f04e1d0f40
move tf init out of python over read-only nix env 2025-03-28 21:14:16 +01:00
2365d9a044
properly pass repo dir for prod, be it with hard-coded TF init 2025-03-28 21:14:16 +01:00
ecc41a7dfd
use flake-sourced nixos-anywhere in tf, to reproduce modules for nix 2025-03-28 21:14:16 +01:00
0419ec38f5
switch launch shell to root flake's nixpkgs, see #279 2025-03-28 21:14:16 +01:00
65bba16d83
Revert "deduplicate flake inputs"
This reverts commit 95769084ce.
2025-03-28 21:14:16 +01:00
264fbf8729
make re-exports explicit again 2025-03-28 21:14:16 +01:00
65159cdc18
deduplicate flake inputs 2025-03-28 21:14:16 +01:00
6bb5768ddc
tf 2025-03-28 21:14:16 +01:00
88674c8efc Show if deployment succeeded (#283)
Show which services deployed and if so, the urls

Co-authored-by: kevin <kevin@procolix.com>
Reviewed-on: Fediversity/Fediversity#283
Reviewed-by: kiara Grouwstra <kiara@procolix.eu>
Co-authored-by: lois <lois@procolix.eu>
Co-committed-by: lois <lois@procolix.eu>
2025-03-28 13:36:02 +01:00
36 changed files with 982 additions and 40 deletions

View file

@ -43,7 +43,10 @@
pre-commit.settings.hooks =
let
## Add a directory here if pre-commit hooks shouldn't apply to it.
optout = [ "npins" ];
optout = [
"npins"
"launch/.terraform"
];
excludes = map (dir: "^${dir}/") optout;
addExcludes = lib.mapAttrs (_: c: c // { inherit excludes; });
in

View file

@ -19,6 +19,24 @@ in
enable = true;
production = true;
domain = "demo.fediversity.eu";
# FIXME: make it work without this duplication
settings =
let
cfg = config.services.${name};
in
{
STATIC_ROOT = "/var/lib/${name}/static";
DEBUG = false;
ALLOWED_HOSTS = [
cfg.domain
cfg.host
"localhost"
"[::1]"
];
CSRF_TRUSTED_ORIGINS = [ "https://${cfg.domain}" ];
COMPRESS_OFFLINE = true;
LIBSASS_OUTPUT_STYLE = "compressed";
};
secrets = {
SECRET_KEY = config.age.secrets.panel-secret-key.path;
};

1
launch/.auto.tfvars.json Normal file
View file

@ -0,0 +1 @@
{"nixos-anywhere": "/nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source"}

10
launch/.envrc Normal file
View file

@ -0,0 +1,10 @@
#!/usr/bin/env bash
# the shebang is ignored, but nice for editors
# shellcheck shell=bash
if type -P lorri &>/dev/null; then
eval "$(lorri direnv --flake .)"
else
echo 'while direnv evaluated .envrc, could not find the command "lorri" [https://github.com/nix-community/lorri]'
use flake
fi

3
launch/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
# .terraform/
.terraform.tfstate.lock.info
terraform.tfstate*

16
launch/.terraform.lock.hcl generated Normal file
View file

@ -0,0 +1,16 @@
# This file is maintained automatically by "tofu init".
# Manual edits may be lost in future updates.
provider "registry.opentofu.org/hashicorp/external" {
version = "2.3.4"
hashes = [
"h1:HfVaWMC7Tz+tRfoWZtGCX2MATcgX3HsexoirWdi/voo=",
]
}
provider "registry.opentofu.org/hashicorp/null" {
version = "3.2.3"
hashes = [
"h1:qTlGDGC3RmXIPLgwsIh4LHG/DrAR6T6L+Wn6egnQnwE=",
]
}

View file

@ -0,0 +1 @@
{"Modules":[{"Key":"","Source":"","Dir":"."},{"Key":"peertube","Source":"./vm","Dir":"vm"},{"Key":"peertube.deploy","Source":"file:///nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source//terraform/all-in-one","Dir":".terraform/modules/peertube.deploy/terraform/all-in-one"},{"Key":"peertube.deploy.install","Source":"../install","Dir":"/nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source/terraform/install"},{"Key":"peertube.deploy.nixos-rebuild","Source":"../nixos-rebuild","Dir":"/nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source/terraform/nixos-rebuild"},{"Key":"peertube.deploy.partitioner-build","Source":"../nix-build","Dir":"/nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source/terraform/nix-build"},{"Key":"peertube.deploy.system-build","Source":"../nix-build","Dir":"/nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source/terraform/nix-build"}]}

View file

@ -0,0 +1 @@
/nix/store/q68mw6lmjjhrvcrhb0pcm9i0v6m3v7cn-source

View file

@ -0,0 +1,3 @@
[
"/nix/store/mnqkwjg5v6sx86an34b4cn075h0lapz3-opentofu-1.8.7/libexec/terraform-providers"
]

View file

@ -0,0 +1 @@
/nix/store/mnqkwjg5v6sx86an34b4cn075h0lapz3-opentofu-1.8.7/libexec/terraform-providers/registry.opentofu.org/hashicorp/external/2.3.4/linux_amd64

View file

@ -0,0 +1 @@
/nix/store/mnqkwjg5v6sx86an34b4cn075h0lapz3-opentofu-1.8.7/libexec/terraform-providers/registry.opentofu.org/hashicorp/null/3.2.3/linux_amd64

19
launch/README.md Normal file
View file

@ -0,0 +1,19 @@
# 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
```

31
launch/default.nix Normal file
View file

@ -0,0 +1,31 @@
{
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
;
}

158
launch/flake.lock generated Normal file
View file

@ -0,0 +1,158 @@
{
"nodes": {
"agenix": {
"inputs": {
"darwin": "darwin",
"home-manager": "home-manager",
"nixpkgs": "nixpkgs",
"systems": "systems"
},
"locked": {
"lastModified": 1736955230,
"narHash": "sha256-uenf8fv2eG5bKM8C/UvFaiJMZ4IpUFaQxk9OH5t/1gA=",
"owner": "ryantm",
"repo": "agenix",
"rev": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
"type": "github"
},
"original": {
"owner": "ryantm",
"repo": "agenix",
"type": "github"
}
},
"darwin": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1700795494,
"narHash": "sha256-gzGLZSiOhf155FW7262kdHo2YDeugp3VuIFb4/GGng0=",
"owner": "lnl7",
"repo": "nix-darwin",
"rev": "4b9b83d5a92e8c1fbfd8eb27eda375908c11ec4d",
"type": "github"
},
"original": {
"owner": "lnl7",
"ref": "master",
"repo": "nix-darwin",
"type": "github"
}
},
"disko": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1741786315,
"narHash": "sha256-VT65AE2syHVj6v/DGB496bqBnu1PXrrzwlw07/Zpllc=",
"owner": "nix-community",
"repo": "disko",
"rev": "0d8c6ad4a43906d14abd5c60e0ffe7b587b213de",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "disko",
"type": "github"
}
},
"home-manager": {
"inputs": {
"nixpkgs": [
"agenix",
"nixpkgs"
]
},
"locked": {
"lastModified": 1703113217,
"narHash": "sha256-7ulcXOk63TIT2lVDSExj7XzFx09LpdSAPtvgtM7yQPE=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "3bfaacf46133c037bb356193bd2f1765d9dc82c1",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1703013332,
"narHash": "sha256-+tFNwMvlXLbJZXiMHqYq77z/RfmpfpiI3yjL6o/Zo9M=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "54aac082a4d9bb5bbc5c4e899603abfb76a3f6d6",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1741402956,
"narHash": "sha256-y2hByvBM03s9T2fpeLjW6iprbxnhV9mJMmSwCHc41ZQ=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "ed0b1881565c1ffef490c10d663d4f542031dad3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_3": {
"locked": {
"lastModified": 1743046730,
"narHash": "sha256-3ON6kKBQ4pz/IVZylcbw28K/Jm5cym4V/Z+VmPzR9/4=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "67545051fd77a131ebab477fbf2bb4d9473edd35",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "release-24.11",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"agenix": "agenix",
"disko": "disko",
"nixpkgs": "nixpkgs_3"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

125
launch/flake.nix Normal file
View file

@ -0,0 +1,125 @@
{
inputs = {
agenix.url = "github:ryantm/agenix";
disko.url = "github:nix-community/disko";
nixpkgs.url = "github:nixos/nixpkgs/release-24.11";
};
outputs =
inputs@{ nixpkgs, ... }:
let
system = "x86_64-linux";
inherit (nixpkgs) lib;
in
{
nixosConfigurations =
let
## NOTE: All of these secrets are publicly available in this source file
## and will end up in the Nix store. We don't care as they are only ever
## used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
in
lib.mapAttrs
(
_: module:
lib.nixosSystem {
inherit system;
specialArgs = { inherit system inputs; };
modules = [
inputs.disko.nixosModules.default
inputs.agenix.nixosModules.default
../services/fediversity
./resource.nix
module
{
nixpkgs = { inherit system; };
}
(
{ pkgs, terraform, ... }:
let
inherit (terraform) hostname;
in
{
imports = [
# FIXME: get VM details from TF
../infra/test-machines/${hostname}
];
fediversityVm.name = hostname;
fediversity = {
inherit (terraform) domain;
temp.initialUser = {
inherit (terraform.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" terraform.initialUser.password;
};
};
}
)
];
}
)
{
garage =
{ pkgs, ... }:
{
fediversity = {
garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; };
peertube = peertubeS3KeyConfig { inherit pkgs; };
};
};
mastodon =
{ pkgs, ... }:
{
fediversity = {
mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
enable = true;
};
temp.cores = 1; # FIXME: should come from NixOps4 eventually
};
};
peertube =
{ pkgs, ... }:
{
fediversity = {
peertube = peertubeS3KeyConfig { inherit pkgs; } // {
enable = true;
## NOTE: Only ever used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24";
};
};
};
pixelfed =
{ pkgs, ... }:
{
fediversity = {
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
enable = true;
};
};
};
};
};
}

37
launch/garage.nix Normal file
View file

@ -0,0 +1,37 @@
let
## NOTE: All of these secrets are publicly available in this source file
## and will end up in the Nix store. We don't care as they are only ever
## used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
in
import ./shared.nix {
module =
{ pkgs, ... }:
{
fediversity = {
garage.enable = true;
pixelfed = pixelfedS3KeyConfig { inherit pkgs; };
mastodon = mastodonS3KeyConfig { inherit pkgs; };
peertube = peertubeS3KeyConfig { inherit pkgs; };
};
};
}

92
launch/main.tf Normal file
View file

@ -0,0 +1,92 @@
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
}

20
launch/mastodon.nix Normal file
View file

@ -0,0 +1,20 @@
let
mastodonS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK3515373e4c851ebaad366558";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7d37d093435a41f2aab8f13c19ba067d9776c90215f56614adad6ece597dbb34";
};
in
import ./shared.nix {
module =
{ pkgs, ... }:
{
fediversity = {
mastodon = mastodonS3KeyConfig { inherit pkgs; } // {
enable = true;
};
temp.cores = 1; # FIXME: should come from NixOps4 eventually
};
};
}

15
launch/pass-ssh-key.sh Executable file
View file

@ -0,0 +1,15 @@
#!/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

23
launch/peertube.nix Normal file
View file

@ -0,0 +1,23 @@
let
peertubeS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GK1f9feea9960f6f95ff404c9b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "7295c4201966a02c2c3d25b5cea4a5ff782966a2415e3a196f91924631191395";
};
in
import ./shared.nix {
module =
{ pkgs, ... }:
{
fediversity = {
peertube = peertubeS3KeyConfig { inherit pkgs; } // {
enable = true;
## NOTE: Only ever used for testing anyway.
##
## FIXME: Generate and store in NixOps4's state.
secretsFile = pkgs.writeText "secret" "574e093907d1157ac0f8e760a6deb1035402003af5763135bae9cbd6abe32b24";
};
};
};
}

19
launch/pixelfed.nix Normal file
View file

@ -0,0 +1,19 @@
let
pixelfedS3KeyConfig =
{ pkgs, ... }:
{
s3AccessKeyFile = pkgs.writeText "s3AccessKey" "GKb5615457d44214411e673b7b";
s3SecretKeyFile = pkgs.writeText "s3SecretKey" "5be6799a88ca9b9d813d1a806b64f15efa49482dbe15339ddfaf7f19cf434987";
};
in
import ./shared.nix {
module =
{ pkgs, ... }:
{
fediversity = {
pixelfed = pixelfedS3KeyConfig { inherit pkgs; } // {
enable = true;
};
};
};
}

41
launch/resource.nix Normal file
View file

@ -0,0 +1,41 @@
{
lib,
config,
...
}:
let
inherit (lib) attrValues elem mkDefault;
inherit (lib.attrsets) concatMapAttrs optionalAttrs;
inherit (lib.strings) removeSuffix;
secretsPrefix = ../secrets;
secrets = import (secretsPrefix + "/secrets.nix");
keys = import ../keys;
in
{
fediversityVm.hostPublicKey = mkDefault keys.systems.${config.fediversityVm.name};
## The configuration of the machine. We strive to keep in this file only the
## options that really need to be injected from the resource. Everything else
## should go into the `./nixos` subdirectory.
imports = [
../infra/common/options.nix
../infra/common/nixos
];
## Read all the secrets, filter the ones that are supposed to be readable
## with this host's public key, and add them correctly to the configuration
## as `age.secrets.<name>.file`.
age.secrets = concatMapAttrs (
name: secret:
optionalAttrs (elem config.fediversityVm.hostPublicKey secret.publicKeys) {
${removeSuffix ".age" name}.file = secretsPrefix + "/${name}";
}
) secrets;
## FIXME: Remove direct root authentication once the NixOps4 NixOS provider
## supports users with password-less sudo.
users.users.root.openssh.authorizedKeys.keys = attrValues keys.contributors;
}

45
launch/shared.nix Normal file
View file

@ -0,0 +1,45 @@
{
system ? "x86_64-linux",
sources ? import ../npins,
pkgs ? import sources.nixpkgs {
inherit system;
config = { };
overlays = [ (import ../panel/nix/overlay.nix) ];
},
module,
...
}:
import "${sources.nixpkgs}/nixos/lib/eval-config.nix" {
modules = [
"${sources.disko}/module.nix"
"${sources.agenix}/modules/age.nix"
../services/fediversity
./resource.nix
# FIXME: get VM details from TF
module
(
{
terraform,
...
}:
let
inherit (terraform) hostname;
in
{
imports = [
../infra/test-machines/${hostname}
];
fediversityVm.name = hostname;
fediversity = {
inherit (terraform) domain;
temp.initialUser = {
inherit (terraform.initialUser) username email displayName;
# FIXME: disgusting, but nvm, this is going to be replaced by
# proper central authentication at some point
passwordFile = pkgs.writeText "password" terraform.initialUser.password;
};
};
}
)
];
}

1
launch/shell.nix Normal file
View file

@ -0,0 +1 @@
(import ./. { }).shell

36
launch/tf-env.nix Normal file
View file

@ -0,0 +1,36 @@
{
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
'';
}

25
launch/tf.nix Normal file
View file

@ -0,0 +1,25 @@
# 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))

51
launch/vm/main.tf Normal file
View file

@ -0,0 +1,51 @@
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 = "kiara"
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
}

View file

@ -1,5 +1,44 @@
{
"pins": {
"agenix": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "ryantm",
"repo": "agenix"
},
"branch": "main",
"revision": "e600439ec4c273cf11e06fe4d9d906fb98fa097c",
"url": "https://github.com/ryantm/agenix/archive/e600439ec4c273cf11e06fe4d9d906fb98fa097c.tar.gz",
"hash": "006ngydiykjgqs85cl19h9klq8kaqm5zs0ng51dnwy7nzgqxzsdr"
},
"disko": {
"type": "GitRelease",
"repository": {
"type": "GitHub",
"owner": "nix-community",
"repo": "disko"
},
"pre_releases": false,
"version_upper_bound": null,
"release_prefix": null,
"version": "v1.11.0",
"revision": "cdf8deded8813edfa6e65544f69fdd3a59fa2bb4",
"url": "https://api.github.com/repos/nix-community/disko/tarball/v1.11.0",
"hash": "13brimg7z7k9y36n4jc1pssqyw94nd8qvgfjv53z66lv4xkhin92"
},
"flake-inputs": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "fricklerhandwerk",
"repo": "flake-inputs"
},
"branch": "main",
"revision": "559574c9cbb8af262f3944b67d60fbf0f6ad03c3",
"url": "https://github.com/fricklerhandwerk/flake-inputs/archive/559574c9cbb8af262f3944b67d60fbf0f6ad03c3.tar.gz",
"hash": "0gbhmp6x2vdzvfnsvqzal3g8f8hx2ia6r73aibc78kazf78m67x6"
},
"htmx": {
"type": "GitRelease",
"repository": {
@ -27,6 +66,18 @@
"url": "https://github.com/nix-community/nix-unit/archive/2071bbb765681ac3d8194ec560c8b27ff2a3b541.tar.gz",
"hash": "0blz1kcmn9vnr9q3iqp2mv13hv3pdccljmmc54f8j7ybf5v0wgmp"
},
"nixos-anywhere": {
"type": "Git",
"repository": {
"type": "GitHub",
"owner": "KiaraGrouwstra",
"repo": "nixos-anywhere"
},
"branch": "special-args-nested-flake-fixed",
"revision": "6986a78788d9b32354ab11b3881edf0caf1ae727",
"url": "https://github.com/KiaraGrouwstra/nixos-anywhere/archive/6986a78788d9b32354ab11b3881edf0caf1ae727.tar.gz",
"hash": "13j1l8dj9w22ch5z3iyzx5z6hrrx6c75m5gnkpa4d4wgiznx90r9"
},
"nixpkgs": {
"type": "Channel",
"name": "nixpkgs-unstable",

View file

@ -20,11 +20,18 @@ in
pkgs.npins
manage
];
env = import ./env.nix { inherit lib pkgs; } // {
NPINS_DIRECTORY = toString ../npins;
CREDENTIALS_DIRECTORY = toString ./.credentials;
DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3";
};
env =
let
inherit (builtins) toString;
in
import ./env.nix { inherit lib pkgs; }
// {
NPINS_DIRECTORY = toString ../npins;
CREDENTIALS_DIRECTORY = toString ./.credentials;
DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3";
# locally: use a fixed relative reference, so we can use our newest files without copying to the store
REPO_DIR = toString ../.;
};
shellHook = ''
ln -sf ${sources.htmx}/dist/htmx.js src/panel/static/htmx.min.js
# in production, secrets are passed via CREDENTIALS_DIRECTORY by systemd.

View file

@ -3,16 +3,14 @@
pkgs,
...
}:
let
inherit (builtins) toString;
in
{
REPO_DIR = toString ../.;
# explicitly use nix, as e.g. lix does not have configurable-impure-env
BIN_PATH = lib.makeBinPath [
# explicitly use nix, as e.g. lix does not have configurable-impure-env
pkgs.nix
# nixops error maybe due to our flake git hook: executing 'git': No such file or directory
pkgs.lix
pkgs.bash
pkgs.coreutils
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; })
];
}

View file

@ -29,6 +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; };
};
python-environment = pkgs.python3.withPackages (

View file

@ -1,5 +1,6 @@
{
lib,
pkgs,
sqlite,
python3,
sources ? import ../../npins,
@ -11,7 +12,7 @@ let
root = ../src;
fileset = intersection (gitTracked ../../.) ../src;
};
pyproject = with lib; fromTOML pyproject-toml;
pyproject = fromTOML pyproject-toml;
# TODO: define this globally
name = "panel";
# TODO: we may want this in a file so it's easier to read statically
@ -58,7 +59,9 @@ python3.pkgs.buildPythonPackage {
mkdir -p $out/bin
cp -v ${src}/manage.py $out/bin/manage.py
chmod +x $out/bin/manage.py
wrapProgram $out/bin/manage.py --prefix PYTHONPATH : "$PYTHONPATH"
wrapProgram $out/bin/manage.py \
--set REPO_DIR "${import ../../launch/tf-env.nix { inherit lib pkgs; }}" \
--prefix PYTHONPATH : "$PYTHONPATH"
cp ${sources.htmx}/dist/htmx.min.js* $out/${python3.sitePackages}/panel/static/
'';
}

View file

@ -10,7 +10,9 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
import re
import sys
import subprocess
import os
import importlib.util
import dj_database_url
@ -18,6 +20,8 @@ import dj_database_url
from os import environ as env
from pathlib import Path
STORE_PATTERN = re.compile("^/nix/store/[^/]+$")
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
@ -171,6 +175,54 @@ COMPRESS_PRECOMPILERS = [
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
"filters": {
"require_debug_false": {
"()": "django.utils.log.RequireDebugFalse",
},
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue",
},
},
"formatters": {
"django.server": {
"()": "django.utils.log.ServerFormatter",
"format": "[{server_time}] {message}",
"style": "{",
}
},
"handlers": {
"console": {
"level": "INFO",
# "filters": ["require_debug_true"],
"class": "logging.StreamHandler",
},
"django.server": {
"level": "INFO",
"class": "logging.StreamHandler",
"formatter": "django.server",
},
"mail_admins": {
"level": "ERROR",
"filters": ["require_debug_false"],
"class": "django.utils.log.AdminEmailHandler",
},
},
"loggers": {
"django": {
"handlers": ["console", "mail_admins"],
"level": "INFO",
},
"django.server": {
"handlers": ["django.server"],
"level": "INFO",
"propagate": False,
},
},
}
# Customization via user settings
# This must be at the end, as it must be able to override the above
# TODO(@fricklerhandwerk):

View file

@ -9,7 +9,8 @@
hx-trigger="click"
hx-indicator="#spinner-container"
hx-disabled-elt="this"
hx-swap="none"
hx-target="#deployment-result"
hx-swap="innerHTML"
name="deploy">
Deploy
</button>
@ -19,5 +20,8 @@
<div id="spinner-container" class="htmx-indicator">
<span class="loader"></span>
</div>
<div id="deployment-result">
</div>
</form>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% if deployment_status %}
<p>{{ deployment_status }}</p>
<ul>
{% for service, state in services.items %}
{% if state %}
<li>
✓ {{ service }}
</li>
{% endif %}
{% endfor %}
</ul>
{% endif %}

View file

@ -1,5 +1,6 @@
from enum import Enum
import json
from os.path import expanduser
import subprocess
import os
@ -9,10 +10,11 @@ from django.contrib.auth.models import User
from django.views.generic import TemplateView, DetailView
from django.views.generic.edit import FormView
from django.shortcuts import render
from panel import models, settings
from panel import models
from panel.configuration import forms
class Index(TemplateView):
template_name = 'index.html'
@ -82,6 +84,7 @@ class ConfigurationForm(LoginRequiredMixin, FormView):
initial.update(self.convert_enums_to_names(config_dict))
return initial
class Save(ConfigurationForm):
def form_valid(self, form):
obj = self.get_object()
@ -90,6 +93,7 @@ class Save(ConfigurationForm):
return super().form_valid(form)
class DeploymentStatus(ConfigurationForm):
def form_valid(self, form):
obj = self.get_object()
@ -98,41 +102,54 @@ class DeploymentStatus(ConfigurationForm):
# Check for deploy button
if "deploy" in self.request.POST.keys():
self.deployment(obj)
deployment_result, deployment_params = self.deployment(obj)
if deployment_result.returncode == 0:
deployment_status = "Deployment Succeeded"
else:
deployment_status = "Deployment Failed"
return super().form_valid(form)
return render(self.request, "partials/deployment_result.html", {
"deployment_status": deployment_status,
"services": {
"peertube": deployment_params['peertube']['enable'],
"pixelfed": deployment_params['pixelfed']['enable'],
"mastodon": deployment_params['mastodon']['enable']
}
})
def deployment(self, obj):
submission = obj.parsed_value.model_dump_json()
# FIXME: let the user specify these from the form (#190)
dummy_user = {
"initialUser": {
"displayName": "Testy McTestface",
"username": "test",
"password": "testtest",
"email": "test@test.com",
"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 = json.dumps(dummy_user | json.loads(submission))
deployment_params = dummy_user | json.loads(submission)
env = {
"PATH": settings.bin_path,
# used in nixos-anywhere for ssh-copy-id
"HOME": expanduser("~"),
"XDG_CACHE_HOME": "/tmp",
} | {
# pass in form info to our deployment
"DEPLOYMENT": 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"
# FIXME: move init to packaging phase
cmd = [
"nix",
"develop",
"--extra-experimental-features",
"configurable-impure-env",
"--command",
"nixops4",
"tofu",
# f"-chdir={cwd}",
"apply",
"test",
f"-state={cwd}/terraform.tfstate", # FIXME: separate users' state
"--auto-approve",
"-lock=false",
]
deployment_result = subprocess.run(
cmd,
cwd=settings.repo_dir,
env=env,
)
return deployment_result
deployment_result = subprocess.run(cmd, cwd=cwd, env=env)
print(deployment_result)
return deployment_result, deployment_params