nix-openwrt-imagebuilder
nix-openwrt-imagebuilder copied to clipboard
PR to support modular interface?
Hello, and thanks for this neat and useful toolchain. I'm trying to do a better job of managing my OpenWRT-based router and APs, and as a confirmed Nixaholic this repository is just what I needed.
Would you be interested in a PR introducing a modular interface to the OpenWRT image builder code? I have a (very) rough cut at such an interface up on SourceHut here.
The high-level view:
{
openwrtImages.default = self.lib.openwrtImage {
modules = [
{
image = {
profileName = "avm_fritz7412";
packages.include = ["tcpdump"];
packages.exclude = ["ppp"];
disabledServices = ["dnsmasq"];
snippets."etc/uci-defaults/99-custom".text = ''
uci -q batch << EOI
set system.@system[0].hostname='testap'
commit
EOI
'';
};
}
];
};
}
If specified as a flake output, the image and associated assets could then be built with nix build '.#openwrtImages.default.config.system.build.image' . I plan to add a wrapper command for this that would permit doing, say, nix-openwrt-imagebuild '.#default'.
Any interest in integrating something like this in your project?
Thank you!
In general, great idea! I already use the NixOS module system for networking configuration.
However, this piece of code already has a few ways of calling it. I prefer the managability and flexibility of putting lib.evalModules in my own code, passing the nix-openwrt-imagebuilder parameters together in one place. Are there use-cases where doing so would be inconvenient?
To me it starts to make sense if we start defining all the OpenWRT options as sensible NixOS module options. @ehmry launched a fork into this direction starting in #9. Although it would be nice to have, I am not sure I am ready to maintain and update such a bulk of declarations. Can it be automated just like I've done with the download hashes?
I'm not working with OpenWRT anymore so I'm not maintain my module fork. As I recall @mic92 has similar configuration work that is relevant.
https://github.com/Mic92/dotfiles/blob/main/openwrt/example.nix
There is also https://git.eno.space/dewclaw.git/tree/ now.
Hey, @tomeon! I noticed that your sourcehut repository is active and that you have implemented a very comprehensive module system, including the flake-parts module ❤️. Are you still interested in creating a PR to port it to this repository? 👀
I believe everyone would benefit from a more modular system that allows for atomic changes, better type-safety, and auto-generated documentation similar to NixOS modules.
Regarding @astro's comment:
Although it would be nice to have, I am not sure I am ready to maintain and update such a bulk of declarations.
I think @tomeon's approach to modules is simpler and easier to maintain than what was proposed in https://github.com/astro/nix-openwrt-imagebuilder/pull/9. While it would be great to have many individual services wrapped into Nix expressions like NixOS service modules, I agree that it would be hard to maintain.
In my opinion, wrapping UCI expressions into NIX and exposing packages, disabledServices, and files makes more sense, as it aligns more closely with what is already implemented in this repository and with ImageBuilder's interface.
@astro -- apologies for taking so long to reply here. Maybe it is superfluous now that #48 appears to have traction, but I'd like to put my thoughts down anyway.
I agree with @pedorich-n's remark that
everyone would benefit from a more modular system that allows for atomic changes, better type-safety, and auto-generated documentation similar to NixOS modules.
However, since it's likely everyone here is already convinced of the benefits of modular systems in general and the NixOS module system in particular, let's talk about the benefits specific to my implementation of a modular interface to this project.
I prefer the managability and flexibility of putting lib.evalModules in my own code, passing the nix-openwrt-imagebuilder parameters together in one place. Are there use-cases where doing so would be inconvenient?
It's not inconvenient for me personally, or (as far as I can guess) for any single person. However, there is the potential for the collective inconvenience of repeated wheel-reinvention. One of my goals in implementing a modular interface is to expose interfaces at various levels of abstraction, reducing the likelihood of needing to re-implement low-level plumbing while still making it possible to futz with the plumbing when necessary.
A case in point here is the layering of options that eventually get composed into the derivation that's passed as the files argument to the image builder function. From highest- to lowest-level:
- The (semi-)declarative UCI defaults options, which compose into
- UCI instructions, which compose into
- Text snippets representing the bodies of scripts to be installed in
/etc/uci-defaults, which compose into - Derivations built with
pkgs.writeTextDir, which compose into - A final, single derivation passed as the
filesargument to the image builder function.
At each of these levels, barring the lowest,[^direct-files-control] it is possible to take the reins directly:
- The (semi-)declarative UCI defaults options are where I'd hope the bulk of the configuration will go; they are at least intended for that purpose.
- UCI instructions can be specified explicitly if (for instance) the user needs fine-grained control over instruction sequencing.
- Text snippets can be defined directly if (for instance) the user needs to do something other than run
uci batchin an/etc/uci-defaultsscript. - Derivations can be added directly to the list composed into the
filesargument.
I believe this represents a reasonably thorough hierarchy that balances power and convenience.
Regarding
To me it starts to make sense if we start defining all the OpenWRT options as sensible NixOS module options. [...] Although it would be nice to have, I am not sure I am ready to maintain and update such a bulk of declarations. Can it be automated just like I've done with the download hashes?
I again concur with @pedorich-n's remarks.
I do not think that it would be straightforward or even possible to implement a comprehensive modeling of "all the OpenWRT options". For one, I don't think there is any canonical representation of all available options, other than their implementation in the OpenWRT codebase. I certainly have not found any API that can be queried to get all of these options and their default values for a given OpenWRT-supported device.
Instead, I've focused on building a more-or-less comprehensive interface to the UCI defaults system.[^uci-defaults] This way, consumers of the modular interface can declare and define just the settings that they need for the target device. It might not be as polished an experience as working with NixOS itself -- however, if the modeling of the UCI defaults system is reasonably comprehensive, then it could be used as the basis for implementing even-higher-level interfaces if and when it looks like it would be useful to do so.
[^direct-files-control]: This is probably a mistake; users should be able to directly control the contents of the files argument. I'll update the module accordingly.
[^uci-defaults]: I am not entirely satisfied with the implementation, particularly the interface to "unnamed" options. I've represented sparse lists as Nix attribute sets whose keys are list indices, and provided library functions to help with defining such sparse lists, but the experience is clumsier than I'd like.
I like the module system, and there seem to be even more gems in your fork. I'm happy to merge them.