nix-community / impermanence

Modules to help you handle persistent state on systems with ephemeral root storage [maintainer=@talyz]
MIT License
1.03k stars 77 forks source link

Allow generic environment.persistence attribute names, rather than the persistent path #117

Closed civts closed 12 months ago

civts commented 1 year ago

Hi, first of all thank you for the project 🏆

I have the following use-case: In a NixOS configuration, I defined some paths to persist with the NixOS impermanence module, like this

environment.persistence = {
  "/persist/backup_me" = {
    directories = [ "/foo" "/bar" ];
  };
  "/persist/dont_backup" = {
    directories = [ "/fizz" "/buzz" ];
  };
};

Now, in another part of the configuration, I'd like to backup the content of /persist/backup_me. To do so, I need to get the path of the directory to backup "/persist/backup_me", so I'd write a line like this backup.baseDir = "/persist/backup_me";, duplicating the string "/persist/backup_me".

Would it be possible to change the module so that I did not need to specify the persistent path as the environment.persistence attribute name, but could instead specify an arbitrary value as the name and use a nested attribute, say environment.impermanence.<name>.persistentPath to specify the persistent path?

Impermanence could default to using <name> as the persistent path if persistentPath is not specified (like in NixOS for the fileSystems attributes), so this would not be a breaking change for existing users.

The idea I have in mind is that in the end people could specify a NixOS configuration like this

environment.persistence = {
  backup = {
    persistentPath = "/persist/backup_me";
    directories = [ "/foo" "/bar" ];
  };
  "/persist/dont_backup" = {
    directories = [ "/fizz" "/buzz" ];
  };
};
# Hypothetical backup tool in which I specify the directory to backup
backup.baseDir = config.environment.persistence.backup.persistentPath;

Do you think this would be something worth adding?

civts commented 1 year ago

Actually, the duplication can also be avoided by having a configuration like the following, without any modification to impermanence 🤔.

let backupPath = "/persist/backup_me"; in
{
  environment.persistence = {
    ${backupPath} = {
      directories = [ "/foo" "/bar" ];
    };
    "/persist/dont_backup" = {
      directories = [ "/fizz" "/buzz" ];
    };
  };
  # Hypothetical backup tool in which I specify the directory to backup
  backup.baseDir = ${backupPath};
}
richousrick commented 1 year ago

It is definitely possible to create your own abstraction layer on top of impermanence.
I had a similar need for that kind of structuring but for a different purpose. Wanting multiple endpoints for persistence across multiple partitions: I think I'm at 7 stores and counting, with different permissions no/exec, read only, etc. And being able to store persistence alongside the applications whose state they managed, for a very slimmed down system.

My abstraction allows stores to be defined with optional store wide configuration:

environment.persist.userAppDataStore = {
  persistantStore = "/nix/persist/somewhere";
  pathPrefix = "/home/<some user>";
  user = "<some user>";
  group = "users";
};

And then used for a different groups of resources which can be defined alongside the applications that they manage the state of:

environment.persist.userAppDataStore.resources.firefoxProfile.files = [ ".mozilla/firefox/profiles.ini" ];
environment.persist.userAppDataStore.resources.firefoxProfileData = {
  resourcePrefix = ".mozilla/firefox/default";

  files = [
    # ...
    "places.sqlite"
    "favicons.sqlite"
    "cookies.sqlite"
    # ...
  ];

  directories = [
    # ...
    "storage/default/"
    # ...
  ];
};

The code is not that complicated, basically it just reduces to the relevant impermanence settings. If there is interest I could probably create a MR.

hacker1024 commented 1 year ago

Actually, the duplication can also be avoided by having a configuration like the following, without any modification to impermanence 🤔.

let backupPath = "/persist/backup_me"; in
{
  environment.persistence = {
    ${backupPath} = {
      directories = [ "/foo" "/bar" ];
    };
    "/persist/dont_backup" = {
      directories = [ "/fizz" "/buzz" ];
    };
  };
  # Hypothetical backup tool in which I specify the directory to backup
  backup.baseDir = ${backupPath};
}

This stops working as soon as you want to split things up into multiple files. Allowing the path to be set with an option is necessary for this use case.