Complete the data model with a runtime environment and end-to-end test #481
Labels
No labels
0 points
0.5 points
1 point
13 points
2 points
21 points
3 points
34 points
5 points
55 points
8 points
api service
blocked
component: fediversity panel
component: nixops4
documentation
estimation high: >3d
estimation low: <2h
estimation mid: <8h
infinite points
productisation
project-management
question
role: application developer
role: application operator
role: hosting provider
role: maintainer
security
technical debt
testing
type unclear
type: bug
type: deliverable
type: key result
type: objective
type: task
type: user story
user experience
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: fediversity/fediversity#481
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "fricklerhandwerk/Fediversity:deployment-data-model-with-tests"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes #103
At last, a fully fledged data model for what Fediversity really is and does. This comes with a test that exercises a very simple but functionally complete arrangement with all ingredients fo the business logic: a dummy resource (login shell), a dummy application (
hello, which needs a shell to live in), a dummy environment (a single NixOS VM that allows for one, the operator's, login shell), and a deployment of that environment given a dummy configuration (that enableshello).The next step will be to lift this purely evaluation-level test into a VM test which verifies that the resulting VM indeed has
hellodeployed to the operator's user account.Caveats:
types.rawfor resources such as NixOS users settings which could be more finely delineated@ -15,3 +14,3 @@options = {input-type = mkOption {type = deferredModule;type = optionType;while this change better aligns the semantics with the naming (
input-type,output-type), from the DX perspective this seems to make for a slightly clunkier interface, having to manually invokesubmoduleon these arguments.(to be fair, unlike the old version, the current one does also work with say
raw, which indeed is a type rather than a module - tho as you noted, we might find more elegant alternatives to typing NixOS modules thanraw.)i don't think it's clunkier. after letting it rest for a couple of days I actually was surprised why the field is not of type
optionType, because why can a function not bestr -> str?@ -35,1 +98,3 @@enable = lib.mkEnableOption "Hello in the shell";options.enable = lib.mkEnableOption "Hello in the shell";};implementation = cfg: {our demo code currently has modules depend on other parts of
configas well - down the road we may need to consider if those might function as say resources instead, tho for the purpose of this PR this may dothe purpose of this PR is exactly to show a minimal example that one can still understand in limited time. in practice you will probably want a resource of the size and complexity of an entire NixOS VM, but here it would be in the way to asserting that the mechanism is sane.
@ -19,2 +21,4 @@test-eval = {/**This tests a very simple arrangement that features all ingredients of the Fediversity business logic:one might argue the business logic could include portability aspects as well, but sure
oh yeah, maybe it should also say "as far as it is currently encoded".
@ -36,0 +108,4 @@{ config, ... }:{resources.operator-environment.login-shell.username = "operator";implementation = requests: {this explicit interface to module functions (specifying
outputandinputboth) feels a bit clunky, is this not something that could be handled behind the scenes instead (see my branch)?it sure is clunky, but that's what we have to ensure type safety. we don't strictly require it, we may as well reduce
implementationtofunctionTo output-type, but it makes things more explicit. I'm open to loosening that strictness if we find a way to make sure that application/policy authors don't get confronted with even more inscrutable errors than we have already.so, i might wanna check if handling this wiring in
data-model.nix(1) enforces type-safety and (2) doesn't complicate error messages too much - is that correct?hmm i'd say it's more about practicality. will application authors be able to work fluently with an interface that doesn't force type checks on them?
i mean, hence i proposed i'd verify that'd still work 😅
in fact, even this type-checks while it shouldn't, implying it doesn't work as intended? like i'd be inclined to maybe leave it out until we get it to do what we wanted out of it.
i.e. simplification
Yes I see. While I can't prove it at the moment, I think skipping the types loses us a valuable opportunity to generate rich documentation for application authors and hosting providers. And that would matter, because both need a very clear understanding of the objects of interest here, and how to put things together.
thanks. i'm sympathetic to facilitating generation of type-based documentation.
while we don't have an indication the clunky developer interface is necessary to achieve either type-checking or doc gen tho, i may try my hand at a version balancing those two. that said, i wouldn't see that as blocking for the purpose of the current PR.
opened at fricklerhandwerk/Fediversity#9
i see that at
module-interfacesinput types do successfully verify input values, so we may need further investigation on why it didn't here.my hunch now is our
inputs remained unchecked since we never referenced them after assignment, whereasmodule-interfacesdid for both consumer and provider, explicitly making their outputs depend on their (module-provided) inputs.it achieves this by having the interface'sproviderfor the function's abstract type, while using theconsumeras the instantiation where concrete values for the function (here:string-provider) and its input (here: fromstring-consumer) may be plugged in to compute the output (here: checked in the test).on the other hand, our functionimplementations (parallel to the aboveproviders) do not yet get module-providedinputs (as aboveconsumer's) for their arguments:forapplications'implementation,resourcesis hampered from switching to such a modular imperative invocation due to its function-based invocation in amapAttrsinenvironments'deployment.forenvironments'implementation, so far invocation remains functional, thru functiondeployment, in turn called from the tests.a solution would seem to move their relevant inputs to a deployment entity, such that invocations could be switched from functional to modular.fix at fricklerhandwerk/Fediversity#13 on the input type checks noted at Fediversity/Fediversity#481/files (comment)
@ -27,0 +70,4 @@};config.resource-type = types.raw; # TODO: splice out the user type from NixOSconfig.apply =requests:so these functions now are exposed to requests for other resources as well. did we find a use-case for that? otherwise this may mostly unnecessarily complicate the interface here.
yes, this is why I left the TODO to consider transposing the resource
attrsOf, so we could pass just the request(s) for that same resource to this function. we supposedly wouldn't (want to) care or even know about other resources. so arguably this exact formulation is more or less accidental.@ -54,3 +150,4 @@};});resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello;what's up with the mentioned
resourcesbeing a function - could it take multiple inputs instead, and how might that affect the interface here?why would it take multiple inputs? here in the test we only touch it in order to do some granular analysis of intermediate steps. in practice this is not part of the public interface. each application's resource-mapping takes one configuration value for that application.
@ -56,1 +152,4 @@);resources = fediversity.applications.hello.resources fediversity.example-configuration.applications.hello;hello-shell = (resources).resources.hello.login-shell;environment = fediversity.environments.single-nixos-vm.resources.operator-environment.login-shell;is a shell an environment?
in this example, the "single NixOS VM" environment has a shell as a resource.
divergent thought: if we could deploy single users to NixOS VMs (why not, in principle), a shell could play the role of the environment, but I'm out of creativity for what a resource inside that shell may be.
@ -59,3 +163,1 @@inherit (fediversity)example-configuration;rec {i recall in the past sidestepping the simplest test of straight-up verifying the full contents of a deployment had been a cope for unresolved evaluation errors. i imagine
mkDeployment/evalModulesmay complicate full inspection, but out of curiosity, is the set-up here still to any extent a cope?no,
mkDeploymentis simply required to at least get something out of the deployment's module expression. looking inside is not very helpful though, as you see from the test. it has a bunch of functions in there that are called by nixops4 at runtime, but we can't do much about them here.@ -31,0 +57,4 @@};request = mkOption {description = "Options for declaring resource requirements by an application, a description of how the resource is consumed or accessed";type = deferredModuleWith { staticModules = [ { _class = "fediversity-resource-request"; } ]; };why not use
deferredModuleWith'sclassparameter?oh yes, good point
tackled in fricklerhandwerk/Fediversity#4
(my earlier comment was off btw - it isn't
deferredModuleWiththat has aclassparameter, justsubModuleWithdoes)@ -27,0 +69,4 @@};};config.resource-type = types.raw; # TODO: splice out the user type from NixOSconfig.apply =config = { ... }to contain these two?yes I left it to keep some intermediate diff small, will fix up...
see fricklerhandwerk/Fediversity#3
@ -27,0 +74,4 @@let# Filter out requests that need wheel if policy doesn't allow itvalidRequests = lib.filterAttrs (_name: req: !req.login-shell.wheel || config.wheelwhen i tested on my branch, i thought that
confighere seemed to refer to the configuration set by this policy in the abstract (i.e. withapplyandresource-type), rather than the actual instantiated configuration of the policy (i.e. withwheelandusername). how did you handle that here?I'm not sure I get the question. Yes, the mapping deals with the specific instance of a policy, as one can (hopefully) observe in the test, where we set the username to
"operator".@ -68,0 +125,4 @@environments = mkOption {description = "Run-time environments for Fediversity applications to be deployed to";type = attrsOf (submodule (environment: {maybe
submoduleWithwithclassarg?I don't think it makes a difference here, except more braces?
maybe? technically its implementation mentions the class a bit more, but you may understand better to what extent that impacts our use-cases
see fricklerhandwerk/Fediversity#7
@ -68,0 +134,4 @@Setting this is optional, but provides a place to declare that information for programmatic use in the resource mapping.'';# TODO: maybe transpose, and group the resources by type insteadhad we revisited this consideration already? what do we see as arguments for either option?
transposing may be slightly easier on the eyes in the two places where we iterate over resources. not sure, it's just a thought we can ignore until it starts hurting. at the moment we don't have to guarantee interface stability and need to experiment with it in practice to get an idea what works well
@ -68,0 +156,4 @@};# TODO(@fricklerhandwerk): maybe this should be a separate thing such as `fediversity-setup`,# which makes explicit which applications and environments are available.# then the deployments can simply be the result of the function application baked into this module.👍
i'll probably still try and compare with my branch later. either way tho, this looks like a clear improvement over
main.@ -68,0 +151,4 @@readOnly = true;default = {input-type = application-resources;output-type = nixops4Deployment;we may want to un-hardcode this deployment method in a future iteration, in line with earlier terminology involving multiple distinct run-time environments with their own providers. this could maybe be handled with an
attrTagtype.which maybe also begs the question, could deployment providers be handled like resources, similarly to how a host's fediversity setup might provide configured resources for the deployments?
the deployment method is decidedly not hard-coded here at all. the environment owner specifies that by providing an
implementation, where they can use any provider just the way they want. environments are alreadyattrsOf, so you can have as many ways as you wish to realise some configuration.also, the decoupling of providers and deployments already happens at the nixops4 layer (via the
providersfield). other than that, how you arrange the code on your end as an environment owner is your responsibility.what of
_class = "nixops4Deployment";?
ah that. but that's just a data structure you can convert to for use by any deployment method.
it seems just out of place here tho. surely, it wasn't our top-level that would correspond to their structure in the first place? like, should this line not just be part of our
nixops4Deploymentdefinition up a few lines?fricklerhandwerk/Fediversity#6
configstuff in an attrsetNew commits pushed, approval review dismissed automatically according to repository settings
New commits pushed, approval review dismissed automatically according to repository settings
maybe i can try this now, having gone over the other stuff
edit: wip branch
hereherekiara referenced this pull request2025-08-02 16:12:06 +02:00