trigger nixops from panel #253

Merged
kiara merged 4 commits from kiara/Fediversity:deploy-button into main 2025-03-19 10:09:07 +01:00
6 changed files with 85 additions and 19 deletions

View file

@ -58,9 +58,13 @@
packages = [
pkgs.nil
inputs'.agenix.packages.default
inputs'.nixops4.packages.default
pkgs.openssh
pkgs.httpie
pkgs.jq
# exposing this env var as a hack to pass info in from form
(inputs'.nixops4.packages.default.overrideAttrs {
impureEnvVars = [ "DEPLOYMENT" ];
})
];
shellHook = config.pre-commit.installationScript;
};

View file

@ -143,7 +143,17 @@ in
## - We add a “test” deployment with all test machines.
nixops4Deployments = genAttrs machines makeDeployment' // {
default = makeDeployment machines;
test = makeTestDeployment (fromJSON (readFile ./test-machines/configuration.json));
test = makeTestDeployment (
fromJSON (
let
env = builtins.getEnv "DEPLOYMENT";
in
if env != "" then
env
else
builtins.trace "env var DEPLOYMENT not set, falling back to ./test-machines/configuration.json!" (readFile ./test-machines/configuration.json)
)
);
};
flake.nixosConfigurations =
genAttrs machines (makeConfiguration false)

View file

@ -21,14 +21,17 @@ in
];
env = {
NPINS_DIRECTORY = toString ../npins;
# explicitly use nix, as e.g. lix does not have configurable-impure-env
NIX_DIR = pkgs.nix;
REPO_DIR = toString ../.;
CREDENTIALS_DIRECTORY = builtins.toString ./.credentials;
DATABASE_URL = "sqlite:///${toString ./src}/db.sqlite3";
};
shellHook = ''
# in production, secrets are passed via CREDENTIALS_DIRECTORY by systemd.
# use this directory for testing with local secrets
mkdir -p .credentials
mkdir -p $CREDENTIALS_DIRECTORY
echo secret > ${builtins.toString ./.credentials}/SECRET_KEY
export CREDENTIALS_DIRECTORY=${builtins.toString ./.credentials}
export DATABASE_URL="sqlite:///${toString ./src}/db.sqlite3"
'';
};

View file

@ -173,10 +173,10 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# Customization via user settings
# This must be at the end, as it must be able to override the above
# TODO: we may want to do this with a flat environment instead, and get all values from `os.environ.get()`.
# TODO(@fricklerhandwerk):
# we may want to do this with a flat environment instead, and get all values from `os.environ.get()`.
# this would make it more obvious which moving parts there are, if that environment is specified for development/staging/production in a visible place.
user_settings_file = env.get("USER_SETTINGS_FILE", None)
if user_settings_file is not None:
user_settings_file = env["USER_SETTINGS_FILE"]
spec = importlib.util.spec_from_file_location("user_settings", user_settings_file)
if spec is None or spec.loader is None:
raise RuntimeError("User settings specification failed!")
@ -184,3 +184,15 @@ if user_settings_file is not None:
spec.loader.exec_module(module)
sys.modules["user_settings"] = module
from user_settings import * # noqa: F403 # pyright: ignore [reportMissingImports]
# non-Django application settings
# TODO(@fricklerhandwerk):
# The correct thing to do here would be using a helper function such as with `get_secret()` that will catch the exception and explain what's wrong and where to put the right values.
# Replacing the `USER_SETTINGS_FILE` mechanism following the comment there would probably be a good thing.
# a dir of nix supporting experimental feature `configurable-impure-env`.
nix_bin_dir=f"{env['NIX_DIR']}/bin/"
# path of the root flake to trigger nixops from, see #94.
# to deploy this should be specified, for dev just use a relative path.
repo_dir = env["REPO_DIR"]

View file

@ -5,7 +5,7 @@
{{ form.as_p }}
<button class="button" disabled>Deploy</button>
<button class="button" type="submit" >Save</button>
<button class="button" type="submit" name="deploy">Deploy</button>
<button class="button" type="submit" name="save">Save</button>
</form>
{% endblock %}

View file

@ -1,4 +1,6 @@
from enum import Enum
import json
import subprocess
from django.urls import reverse_lazy
from django.contrib.auth.mixins import LoginRequiredMixin
@ -6,7 +8,7 @@ from django.contrib.auth.models import User
from django.views.generic import TemplateView, DetailView
from django.views.generic.edit import FormView
from panel import models
from panel import models, settings
from panel.configuration import forms
@ -41,6 +43,41 @@ class ConfigurationForm(LoginRequiredMixin, FormView):
operator=self.request.user,
)
# Check for deploy button
if "deploy" in self.request.POST.keys():
submission = obj.parsed_value.model_dump_json()
# FIXME: let the user specify these from the form (#190)
dummy_user = {
"initialUser": {
"displayName": "Testy McTestface",

Instead of polluting business logic with a conditional dealing with inessential details, let's instead set REPO_DIR in the devhell. Also don't grab the environment variable directly, but please go via config, where all of these knobs are defined in a central place, and will make the application error out early if not handled correctly.

Instead of polluting business logic with a conditional dealing with inessential details, let's instead set `REPO_DIR` in the devhell. Also don't grab the environment variable directly, but please go via config, where all of these knobs are defined in a central place, and will make the application error out early if not handled correctly.
Outdated
Review

pushed an update for this

pushed an update for this
"username": "test",
"password": "testtest",

Why do we need the user here? In any case we can get it from the request context.

Why do we need the user here? In any case we can get it from the request context.

I've seen this comment Fediversity/Fediversity#190 (comment)

Do we strictly require that already merely to conclude the user story this implements? If we can avoid it let's not touch that part here.

I've seen this comment https://git.fediversity.eu/Fediversity/Fediversity/issues/190#issuecomment-4986 Do we strictly require that already merely to conclude the user story this implements? If we can avoid it let's not touch that part here.
Outdated
Review

Fediversity/Fediversity#215/files indeed rendered initialUser a mandatory property there, unfortunately

https://git.fediversity.eu/Fediversity/Fediversity/pulls/215/files#diff-c959e516a3e7e73b06277e1352072cd290f6d96b indeed rendered `initialUser` a mandatory property there, unfortunately
Outdated
Review

fwiw i have not checked whether this pixelfed cli command accepts empty values for these properties, or which of its flags are even considered mandatory

fwiw i have not checked whether [this pixelfed cli command](https://pixelfed.github.io/docs-next/running-pixelfed/cli-cheatsheet.html#user-create) accepts empty values for these properties, or which of its [flags](https://git.fediversity.eu/Fediversity/Fediversity/src/commit/8f0bcc35f0c0f5909215db7dea01b5ef8c2bc9a9/services/fediversity/pixelfed/default.nix#L158-L161) are even considered mandatory

Alright, please add this as an in-code comment so we don't lose track of it.

Alright, please add this as an in-code comment so we don't lose track of it.
"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))
env = {
"PATH": settings.nix_bin_dir,
# pass in form info to our deployment
"DEPLOYMENT": deployment,
}
cmd = [
"nix",
"develop",
# workaround to pass in info to nixops4 thru env vars, tho impure :(
"--extra-experimental-features",
"configurable-impure-env",
"--command",
"nixops4",
"apply",
"test",
]
subprocess.run(
cmd,
cwd=settings.repo_dir,
env=env,
)
return obj
# TODO(@fricklerhandwerk):