trigger nixops from panel #253
6 changed files with 85 additions and 19 deletions
|
|
@ -58,9 +58,13 @@
|
||||||
packages = [
|
packages = [
|
||||||
pkgs.nil
|
pkgs.nil
|
||||||
inputs'.agenix.packages.default
|
inputs'.agenix.packages.default
|
||||||
inputs'.nixops4.packages.default
|
pkgs.openssh
|
||||||
pkgs.httpie
|
pkgs.httpie
|
||||||
pkgs.jq
|
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;
|
shellHook = config.pre-commit.installationScript;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,17 @@ in
|
||||||
## - We add a “test” deployment with all test machines.
|
## - We add a “test” deployment with all test machines.
|
||||||
nixops4Deployments = genAttrs machines makeDeployment' // {
|
nixops4Deployments = genAttrs machines makeDeployment' // {
|
||||||
default = makeDeployment machines;
|
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 =
|
flake.nixosConfigurations =
|
||||||
genAttrs machines (makeConfiguration false)
|
genAttrs machines (makeConfiguration false)
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,17 @@ in
|
||||||
];
|
];
|
||||||
env = {
|
env = {
|
||||||
NPINS_DIRECTORY = toString ../npins;
|
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 = ''
|
shellHook = ''
|
||||||
# in production, secrets are passed via CREDENTIALS_DIRECTORY by systemd.
|
# in production, secrets are passed via CREDENTIALS_DIRECTORY by systemd.
|
||||||
# use this directory for testing with local secrets
|
# use this directory for testing with local secrets
|
||||||
mkdir -p .credentials
|
mkdir -p $CREDENTIALS_DIRECTORY
|
||||||
echo secret > ${builtins.toString ./.credentials}/SECRET_KEY
|
echo secret > ${builtins.toString ./.credentials}/SECRET_KEY
|
||||||
export CREDENTIALS_DIRECTORY=${builtins.toString ./.credentials}
|
|
||||||
export DATABASE_URL="sqlite:///${toString ./src}/db.sqlite3"
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -173,14 +173,26 @@ DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
# Customization via user settings
|
# Customization via user settings
|
||||||
# This must be at the end, as it must be able to override the above
|
# 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):
|
||||||
# this would make it more obvious which moving parts there are, if that environment is specified for development/staging/production in a visible place.
|
# we may want to do this with a flat environment instead, and get all values from `os.environ.get()`.
|
||||||
user_settings_file = env.get("USER_SETTINGS_FILE", None)
|
# this would make it more obvious which moving parts there are, if that environment is specified for development/staging/production in a visible place.
|
||||||
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)
|
spec = importlib.util.spec_from_file_location("user_settings", user_settings_file)
|
||||||
if spec is None or spec.loader is None:
|
if spec is None or spec.loader is None:
|
||||||
raise RuntimeError("User settings specification failed!")
|
raise RuntimeError("User settings specification failed!")
|
||||||
module = importlib.util.module_from_spec(spec)
|
module = importlib.util.module_from_spec(spec)
|
||||||
spec.loader.exec_module(module)
|
spec.loader.exec_module(module)
|
||||||
sys.modules["user_settings"] = module
|
sys.modules["user_settings"] = module
|
||||||
from user_settings import * # noqa: F403 # pyright: ignore [reportMissingImports]
|
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"]
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
{{ form.as_p }}
|
{{ form.as_p }}
|
||||||
|
|
||||||
<button class="button" disabled>Deploy</button>
|
<button class="button" type="submit" name="deploy">Deploy</button>
|
||||||
<button class="button" type="submit" >Save</button>
|
<button class="button" type="submit" name="save">Save</button>
|
||||||
</form>
|
</form>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
import json
|
||||||
|
import subprocess
|
||||||
|
|
||||||
from django.urls import reverse_lazy
|
from django.urls import reverse_lazy
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
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 import TemplateView, DetailView
|
||||||
from django.views.generic.edit import FormView
|
from django.views.generic.edit import FormView
|
||||||
|
|
||||||
from panel import models
|
from panel import models, settings
|
||||||
from panel.configuration import forms
|
from panel.configuration import forms
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -41,6 +43,41 @@ class ConfigurationForm(LoginRequiredMixin, FormView):
|
||||||
operator=self.request.user,
|
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",
|
||||||
|
|
|||||||
|
"username": "test",
|
||||||
|
"password": "testtest",
|
||||||
|
fricklerhandwerk
commented
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.
fricklerhandwerk
commented
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.
kiara
commented
Fediversity/Fediversity#215/files indeed rendered https://git.fediversity.eu/Fediversity/Fediversity/pulls/215/files#diff-c959e516a3e7e73b06277e1352072cd290f6d96b indeed rendered `initialUser` a mandatory property there, unfortunately
kiara
commented
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
fricklerhandwerk
commented
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
|
return obj
|
||||||
|
|
||||||
# TODO(@fricklerhandwerk):
|
# TODO(@fricklerhandwerk):
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue
Instead of polluting business logic with a conditional dealing with inessential details, let's instead set
REPO_DIRin 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.pushed an update for this