Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC 0078] System-agnostic configuration file generators #78

Closed
wants to merge 4 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
167 changes: 167 additions & 0 deletions rfcs/0078-configuration-file-generators.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
feature: configuration_file_generators
start-date: 2020-10-18
author: Michael Raskin @7c6f434c
co-authors: (in-progress)
shepherd-team: @ehmry, @edolstra, @roberth
shepherd-leader: @edolstra
related-issues: (will contain links to implementation PRs)
---

# Summary
[summary]: #summary

Provide the configuration file generation functionality separately from NixOS
Copy link
Contributor

@blaggacao blaggacao Oct 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I want to add, that in my use case, I would have an interest in that configuration management (as a shared resource) in nix would compound different APIs, like:

  • filesystem API (configuration files)
  • socket based APIs (fully dynamic service configuration)

→ It might still be useful to apply fully dynamic service configuration declaratively through nix.

Examples of such configuration APIs are:

  • network gear TCP endpoints that respond to yang or netconf
  • k8s APIs
  • ldap configuration (set of boostrapping data after handing off data management to the ldap specific UIs)

It might be out of scope for this particular RFC, though it might be worth including in future work.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not even sure it is in scope for future work!

Sure, that's a good idea in general, but this RFC is about sharing something that is already written now, and in a format that has seen some use by now. Inventing on-start configuration for things without persistent configuration is a good direction of work, but it is somewhat different type of work than described here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right maybe "future work" is too normative. Would you like to add a "broader context" (or a better naming!) section at the end (as an after thought)?

as a whole, and using the module system without the global namespace.

# Motivation
[motivation]: #motivation

There is currently a lot of code duplication between
* NixOS
* home-manager
* nix-darwin
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another target that would benefit from this are containers that need configuration independent from NixOS modules since they don't support systemd etc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True; I am not sure what publically available codebase should be referenced as an example. My real use case is not even in the list and it is closer to what you say about containers; on the other hand, highly-visible existing parallel efforts are more convenient to discuss.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Option generation is also useful for override, such as pkgs.rofi.override = { theme = <file>; }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. But the packages are shared just fine already anyway, so not a strong motivation. Wrapper packages can already be done easily enough. If implementation turns out to be a huge success we might discuss this on a case by case basis whether to move stuff to the configuration file generator library, but let's get there first.


and obviously growing code duplication with any effort running on below-Tier-2
platforms. That leads both to effort duplication, and to confusion among users
using multiple Nix ecosystem tools for service generation,

While fully resolving the problem is likely not fully compatible with the core
preferences of maintainers on each specific platform, we believe that config
file generation could be a shared resource just like Nixpkgs based on the
following properties:
* unlike, say, upstream `systemd` unit files we typically do not
reuse-and-override upstream configs, but generate our own (both in NixOS and
other service collections)
* the configs for a piece of software often have few dependencies on the
general environment
* it can be defined as a purely functional library, simplifying integration

We aim to reduce duplication of effort and code while also having a high level
of inspection and tweaking possibilities without needing to patch module
source. An additional goal is to move the more package-like configuration file
generation code closer to the Nixpkgs model with scope/visibility rules. At
the same time we aim to avoid or minimise code in the NixOS subtree that
cannot be tested on NixOS.
Comment on lines +42 to +45
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a goal but an implementation choice.


# Detailed design
[design]: #detailed-design

This RFC proposal builds upon RFCs#42 defining a configuration file abstraction
approach.

A top-level directory is added to the Nixpkgs repository, and a library of
functions implementing program configuration file generation is created within
this directory. Each such generator takes as an input a NixOS module system
based attribute set «subtree», e.g. the attribute set typically bound to `cfg`
variable in the current NixOS service modules.

The output of each such function is an attribute set. The keys are relative
file names of configuration files, and the values are attribute sets of RFCs#42
settings abstraction and serialiser.
Comment on lines +60 to +61
Copy link
Contributor

@blaggacao blaggacao Oct 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
file names of configuration files, and the values are attribute sets of RFCs#42
settings abstraction and serialiser.
file names of configuration files, and the values are attribute sets with one or several attributes conforming to RFCs#42 settings notation and other required metadata.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

«well-known» is factually wrong here for some of the more interesting configuration file formats.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, that was my interpretation of logSettings question on RFC42.

I changed the suggestion and eliminated well-known. It should be still a little clearer that way.


The function is provided as a member of an attribute set, which also contains
the corresponding type specifications for input and output modules are defined.
The specifications for the input should be suitable for importing as a part of
the NixOS service module options.

In some cases we only provide low-level overridable default configuration. In
this case the input may have the type that is always an empty attribute set,
`{}`.

# Examples and Interactions
[examples-and-interactions]: #examples-and-interactions

A minimalistic silly example is:

Input:
```
{
port = 1234;
nodeName = "localhost";
content = "Hello";
Comment on lines +80 to +82
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this typed?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the type is provided next to the generator function.

}
```

Output:
```
{
"subdir/listen.conf" = {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An example how this would be integrated into NixOS modules might be helpful.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I am not sure it can be done easily without integrating an entire example from #42 and then extending it…

serialiser = toJSON;
settings = {
listenPort = "1234";
serverGreet = "I am localhost";
};
type = …;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this type for?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the output is intended for use with an RFCs#42 implementing NixOS module, to put into (in the simplest case) services.<service>.settings. In the simplest cases one can just have a static type for the settings entry, but I guess it doesn't hurt to repeat it here and to allow, when necessary, some variation (depending on configuration format input option, I guess)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That deserves introduction and propper reasoning in the detailed section, then?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, we promise to provide type specification and serialiser.

};
"subdir/content.conf" = {
serialiser = toINI;
settings = {
greeting = {
greeter = "localhost";
};
payload = {
value = "I say to you: ‘Hello’";
};
};
type = …;
};
}
```

# Drawbacks
[drawbacks]: #drawbacks

The proposed approach increases the spreading of the related code, from package
and service module to package and configuration generator and module.

The proposed refactoring incurs a cost in terms of implementation effort across
NixOS, and likely to create a medium-term situation of partial migration.

# Alternatives
[alternatives]: #alternatives

Do nothing, continuing with the current code duplication.

Same, but put configuration generators closer to packages. This would mean
widespread use of the module system inside `pkgs/`. There is also no guarantee
that all the configuration files describing interaction of multiple software
packages will have a clear choice of reference package.

Introduce a guideline on making configuration files available to the module
system after generation (i.e. avoidance of only putting them in a `let` to add
a path reference inside some string; maybe with best practices on naming the
corresponding options). Does not help with scoping, still needs a non-trivial
amount of refactoring. Might make other desirable refactorings such as
multiple instances of a module marginally harder.

A complete or partial merge of the module collections of the major currently
existing module system based code bases, with internal options for platform
specific implementation details. Unfortunately, this would force more coupling
Comment on lines +139 to +140
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with internal options for platform
specific implementation details

This misses the point entirely. The dependencies are inverted, with the internal options serving the role of the function output, so the internal options are platform agnostic.

of the development cycle for the platform-specific parts. It also does not
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So does the function approach.

create a clean inspection/override point, and mixes the code with different
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a matter of calling lib.evalModules, nix repl '<nixpkgs/nixos>', arion repl or whatnot, where the latter variations have the advantage of not having to decipher the logic that produces the inputs. Using options for this is an inspectability improvement over the usual let bindings and such that will be used with the function approach.

platform requirements for testing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, the common module isn't platform specific and can stand alone.
It's really just like a function, except you get to reuse the option declarations for its inputs.

The two approaches are isomorphic if it wasn't for the fact that going back to a simple function loses the option metadata and checking.

inputs <-> normal options
outputs <-> internal options
multiple invocations <-> submodules
function without free variables <-> module that only defines self-declared options
function with small closure <-> module that doesn't import much


There also have been many solutions proposed based on a significant rework of
the module system.

Abstract generation of configuration files with package-like flat arguments and
plain text file outputs. This approach will need less code as long as it we do
not want type checks, or ease of overriding values.

Implement a complete service abstraction not tied to global system-wide
assumptions.
Comment on lines +152 to +153
Copy link
Contributor

@blaggacao blaggacao Oct 23, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can understand the abstract motion, but am not capable of visualizing this alternative before my inner eye. Maybe a complementary statement can make it easier to reconstruct the argument behind it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single statement would not be enough to do much good. A good description will overtake the rest of the RFC.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, maybe you want to define at least the system boundaries so it becomes clear what "system-wide" refers to?

If you can rephrase I can re-try if it's — while applying utmost care while reading — intelligible (to me).


# Unresolved questions
[unresolved]: #unresolved-questions

Full proof-of-concept implementation for some module.

# Future work
[future]: #future-work

Define generic service abstraction for non-`systemd` systems, possibly with a
convertor from `systemd` units.

Consider some way of referring from package to related configuration file
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? (what for) → you might want to clarify to improve intelligibility here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anyone interested in this future work item now is likely to be aware of the context like passthru.tests; those who are not familiar with that will wait anyway until some detailed work is started

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your response is a little bit enigmatic with regard to rhe matter of the question, but let's make some assumptions at the risk of requiring further clarification:

Do you consider here the possibility of moving service configuration still closer to the packaging itself?

If that is the case, the (new) proposed module system for packaging might come into play. And (very) early coordination with this RFC might benefit future development.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll see from feedback, but currently the RFC argues against moving configuration generators close to packages. However, we might consider developing a way of having some kind of cross-references available at evaluation time.

generators via `meta` or `passthru`, in a way similar to `passthru.tests`.