Just had an idea I wanted to drop, maybe someone p...
# general
h
Just had an idea I wanted to drop, maybe someone picks it up. I find configuring servers isn't the easiest task with pulumi, simply because server configuration isn't easily reflected with resource declarations. I know there is
remote
provider, but that's more of an escape hatch (and then
cloud-init
but only for initialization). How about immutable distributions like NixOS (or just Nix)? They have well defined states and well defined state transition points, I think they should work great with pulumi
m
Can you give a bit more details of what you are thinking about? I don't see any need for Pulumi when Nix comes into the equation 🤔
h
I guess it might be a resource that represents installed package. Also nix allows its nix files to install multiple packages, so there would be a resource to represent it too. Applying such resource would run nix on a target host, atomically installing, upgrading or removing packages, i.e. bring the OS to the specified state. On one hand it sounds similar to what Ansible does, of which pulumi positions itself as a competitor, but on the other hand it also sounds similar to kubernetes provider (e.g. patching k8s resources), only working on one layer lower (k8s cluster => OS).
🤔 1
m
I am sorry, I don't want to discourage you or anything, just want to better understand the workflow. Nix already has a declarative language (Nix) to configure all the knobs, and with use of flakes it gives us fully reproducible environments. What would Pulumi bring to the table here?
h
same that pulumi brings to kubernetes - integration. Otherwise I could just run
kubectl apply
. Or at the very least it would bring a simpler syntax and typed model., like pulumi-coudinit provider.
I read a bit on difference between nix and nixos, and I think nixos would be a better fit here, since applying a new state also restarts services
there even exists nixos terraform provider, but it hasn't been updated for three years :/
I don't really understand your question, what pulumi brings to the table. I can do manually everything pulumi does
m
I don't really understand your question, what pulumi brings to the table. I can do manually everything pulumi does
My bad, should have been clearer here. When you are saying I want nix integration with Pulumi do you want: a. A provider that can install Nix package manager onto a system? b. A provider that can bootstrap a (cloud) machine on NixOS? c. A provider that can load and evaluate flake outputs? d. A provider that can produce and run Nix expressions? e. A provider that can drop configuration.nix onto already provisioned machine and just run that?
h
oh, ok 🙂 I was thinking of managing packages and configuration on NixOS machine. I guess that would involve generating configuration.nix from pulumi resources.
m
Yes, correct and this will be pretty involved process —
configuration.nix
is built using the Nix language, so to build something like this we would have to translate between your Pulumi language of choice and Nix language...
h
After a quick glance, it looks to me like json with shortcuts (dot paths) and expressions. Shortcuts could be always expanded, expressions could be evaluated in the host language, so it would boil down to emitting json, wouldn't it?
m
It is not a JSON by any stretch — it is a fully fledged functional, lazy evaluated programming language. https://nix.dev/tutorials/nix-language.html
h
Ah, so we'd need to bring all the output values as pulumi outputs, that's what you mean?
m
I am not even sure how one would approach something like this — when it comes to managing YAML or JSON, we at least have a defined schema and can just use some sort of templating to fill in the values / blanks. When it comes to Nix, packages that are available, options that you can tweak and so forth are programmatically defined and don't really exist apart from the run time.
One way perhaps would be to define
configuration.nix
for a system one tries to provision and then just let pulumi kick-off
nixos-rebuild
?
h
Nix docs says that it fails on invalid names, so I guess it must have some schema
m
Yup, it does, defined programmatically using Nix! Let me find a good example perhaps.
h
I'm working on something like that with command provider, using simple template literals to generate configuration.nix and kick-off rebuild when it changes
m
^ that might actually work, as long as you have very limited set of things to do...
h
as long as you have very limited set of things to do.
how do you mean
m
for example, to configure console, you need to understand what this module produces: https://github.com/NixOS/nixpkgs/blob/master/nixos/modules/config/console.nix
h
you mean, if I want to use its output? My intention was to make it one way data pass from pulumi, say nixos channel version, or users ssh keys, and build the config file, not reading nixos values back into pulumi, that's why I thought this way (template literals+remote command) would work
m
if you are working with a well-known small template, then yes, and that's what I meant when I said:
as long as you have very limited set of things to do.
because Nix is a programming language and not configuration / markup / structured data representation language, there is no way to do something like:
pkgs.wezterm.overrideAttrs
which is a function call.
Unless we have some transpiler that generates Nix from Pulumi language of choice...
for example, I am using Pantheon desktop from ElementaryOS on my NixOS system. But I don't like that it comes with all packages installed by default, so I do:
Copy code
{ ... }:
(self: super: {
  pantheon = super.pantheon.overrideScope (
    sself: ssuper: {
      # Remove some of the Wingpanel indicators that I never use
      wingpanelIndicators = builtins.filter (
        val: val != ssuper.wingpanel-indicator-bluetooth && val != ssuper.wingpanel-indicator-network
      ) ssuper.wingpanelIndicators;
      # Remove some of the Switchboard plugs that I never use
      switchboardPlugs = builtins.filter (
        val:
        val != ssuper.switchboard-plug-applications
        && val != ssuper.switchboard-plug-bluetooth
        && val != ssuper.switchboard-plug-network
        && val != ssuper.switchboard-plug-onlineaccounts
        && val != ssuper.switchboard-plug-printers
        && val != ssuper.switchboard-plug-sharing
        && val != ssuper.switchboard-plug-wacom
      ) ssuper.switchboardPlugs;
      switchboard-plug-sound = ssuper.switchboard-plug-sound.override {
        # The switchboard plugin depends on PulseAudio directly, even though it
        # only actually needs libraries and works just fine with pipewire.
        pulseaudio = self.libpulseaudio;
      };
      wingpanel-indicator-sound = ssuper.wingpanel-indicator-sound.override {
        # The sound indicator depends on PulseAudio directly, even though it
        # only actually needs libraries and works just fine with pipewire.
        pulseaudio = self.libpulseaudio;
      };
      wingpanel-indicator-datetime = ssuper.wingpanel-indicator-datetime.override {
        # This allows to drop duplicate evolution data server packages
        evolution-data-server = self.evolution-data-server-gtk4;
        # I don't want calendar, but want the calendar indicator. This is a
        # simple hack to point the applet to launch a `true` binary when double-
        # clicking on the date. Without actual legitimate binary, this will fail
        # and crash the entire session — why elementary, why?..
        elementary-calendar = "true ";
      };
    }
  );
})
h
I'm not sure if I follow, it's still a text file
m
Sorry, I suck at explaining perhaps! It is a text file, but if you want to generate it from the template and you want configurable set of packages to exclude, you would need something like:
Copy code
switchboardPlugs = builtins.filter (
        val:
        ---> Some templating language that takes a list of Pulumi inputs and transforms them into the following statements:
        val != ssuper.switchboard-plug-applications
        && val != ssuper.switchboard-plug-bluetooth
        && val != ssuper.switchboard-plug-network
        && val != ssuper.switchboard-plug-onlineaccounts
        && val != ssuper.switchboard-plug-printers
        && val != ssuper.switchboard-plug-sharing
        && val != ssuper.switchboard-plug-wacom
      ) ssuper.switchboardPlugs;
so you would be generating Nix syntax code from Pulumi
h
by generating from template, I meant template literal for settings basic things like user's ssh keys. I didn't mean it for any logic
m
Yup, and that's why I stated:
as long as you have very limited set of things to do.
so you can inject strings, numbers etc into prebaked template
you can not generate a template for the system with a bunch of customizations
h
ah, ok 🙂 Sounds kinda like what cloud-init does, there isn't even a data model for cloud-init user data in that package, all it does is gzip+base64 strings.
m
I think we are on the same page, I just was imagining a much more sophisticated use-case.
h
I'm a noob when it comes to nixos, I just realised that immutable OS should be a good fit for pulumi, being declarative. All I know about nix syntax is that people complain a lot about it 🙂
thank you for the conversation
m
I have started using Nix as a package manager for work 3ish years ago, then I switched my home system from Gentoo into Nix about 2 years ago... and I concur — the Nix syntax is pain the b*tt, but for me the benefits of declarative, reproducible and immutable system overweight the pain from Nix.
I honestly wish Eelco just took another existing functional programming language and use that for the same purpose with maybe a different standard library or something...
And you are most welcome. Feel free to reach out if you have questions / confusions. I am no expert still, but I had my fair share debugging and fiddling with Nix that I can sometimes answer some questions!
h
cheers mate! 🙂