From 8fd1e2b75cae1216abfe419f58bf5a13e7da632c Mon Sep 17 00:00:00 2001 From: Valentin Gagarin <valentin.gagarin@tweag.io> Date: Mon, 31 Mar 2025 15:44:23 +0200 Subject: [PATCH] WIP: simplify deployment code --- deployment/options.nix | 42 +++++++++++++++++++-------------- panel/src/panel/views.py | 50 ++++++++++++++++++++++------------------ 2 files changed, 52 insertions(+), 40 deletions(-) diff --git a/deployment/options.nix b/deployment/options.nix index df04b994..27d38bce 100644 --- a/deployment/options.nix +++ b/deployment/options.nix @@ -2,7 +2,7 @@ Deployment options as to be presented in the front end. These are converted to JSON schema in order to generate front-end forms etc. - For this to work, options must not have types `functionTo` or `package` (or `submodule` until [submodule introspection](https://github.com/NixOS/nixpkgs/pull/391544) is merged), and must not access `config` for their default values. + For this to work, options must not have types `functionTo` or `package`, and must not access `config` for their default values. */ { lib, @@ -34,22 +34,30 @@ in mastodon = { enable = lib.mkEnableOption "Mastodon"; }; - initialUser = { - displayName = mkOption { - type = types.str; - description = "Display name of the user"; - }; - username = mkOption { - type = types.str; - description = "Username for login"; - }; - email = mkOption { - type = types.str; - description = "User's email address"; - }; - password = mkOption { - type = types.str; - description = "Password for login"; + initialUser = mkOption { + description = '' + Some services require an initial user to access them. + This option sets the credentials for such an initial user. + ''; + type = types.submodule { + options = { + displayName = mkOption { + type = types.str; + description = "Display name of the user"; + }; + username = mkOption { + type = types.str; + description = "Username for login"; + }; + email = mkOption { + type = types.str; + description = "User's email address"; + }; + password = mkOption { + type = types.str; + description = "Password for login"; + }; + }; }; }; }; diff --git a/panel/src/panel/views.py b/panel/src/panel/views.py index 9c7e9de1..771289d7 100644 --- a/panel/src/panel/views.py +++ b/panel/src/panel/views.py @@ -14,6 +14,8 @@ from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.response import Response from rest_framework.views import APIView +from pydantic import BaseModel + from panel import models, settings from panel.configuration import schema @@ -39,7 +41,6 @@ class ConfigurationForm(LoginRequiredMixin, APIView): success_url = reverse_lazy('configuration_form') def get_object(self): - """Get or create the configuration object for the current user""" obj, created = models.Configuration.objects.get_or_create( operator=self.request.user, ) @@ -67,31 +68,36 @@ class ConfigurationForm(LoginRequiredMixin, APIView): config.save() return redirect(self.success_url) +# TODO(@fricklerhandwerk): +# this is broken after changing the form view. +# but if there's no test for it, how do we know it ever worked in the first place? class DeploymentStatus(ConfigurationForm): - def form_valid(self, form): - obj = self.get_object() - obj.value = form.to_python().model_dump_json() - obj.save() - # 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" + def post(self, request): + config = self.get_object() + serializer = schema.Model.drf_serializer( + instance=config.parsed_value, + data=request.data + ) + + if not serializer.is_valid(): + return Response({'serializer': serializer}) + + config.value = json.dumps(serializer.validated_data) + config.save() + + deployment_result, deployment_params = self.deployment(config.parsed_value) + if deployment_result.returncode == 0: + deployment_status = "Deployment Succeeded" + else: + deployment_status = "Deployment Failed" 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'] - } + "services": deployment_params.json(), }) - def deployment(self, obj): - submission = obj.parsed_value.model_dump_json() + def deployment(self, config: BaseModel): # FIXME: let the user specify these from the form (#190) dummy_user = { "initialUser": { @@ -101,12 +107,10 @@ class DeploymentStatus(ConfigurationForm): "email": "test@test.com", }, } - # serialize back and forth now we still need to manually inject the dummy user - deployment_params = json.dumps(dummy_user | json.loads(submission)) env = { "PATH": settings.bin_path, # pass in form info to our deployment - "DEPLOYMENT": deployment_params, + "DEPLOYMENT": config.json() } cmd = [ "nix", @@ -123,4 +127,4 @@ class DeploymentStatus(ConfigurationForm): cwd=settings.repo_dir, env=env, ) - return deployment_result, json.loads(deployment_params) + return deployment_result, config