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

nixos/tsm-client: migrate to freeform settings (RFC42) #253428

Merged
merged 9 commits into from
Dec 3, 2023

Conversation

Yarny0
Copy link
Contributor

@Yarny0 Yarny0 commented Sep 5, 2023

Description of changes

This pull request reworks the tsm-client module to be compliant with RFC42 a.k.a. freeform settings.

tsm-client uses a global configuration file that must contain coordinates for each server that it is supposed to contact, similar to Host entries in ~/.ssh/config. Each server section contains several key-value pairs that can easily be represented by a freeform attribute set. Details of the change are explained in the corresponding commit message.

Adding migration code for existing configurations is not easy, as the migration happens inside an attrsOf submodule ... where mkRemovedOptionModule and mkRenamedOptionModule don't work out of the box. This pull request adds additional options inside the submodule that are required by the migration modules to work properly. All this migration code is added in a separate commit so it can easily be reverted later when it is no longer deemed necessary. Details on how the migration mechanism works are also explained in the commit message.

Besides, this pull request adds some assertions for more thorough checks on the validity of the server configuration settings and performs some minor cleanups.

As soon as this is merged, the tsm-client checkbox in #144575 can be checked.

Things done

  • Built on platform(s)
    • x86_64-linux
    • aarch64-linux
    • x86_64-darwin
    • aarch64-darwin
  • For non-Linux: Is sandbox = true set in nix.conf? (See Nix manual)
  • Tested, as applicable:
  • Tested compilation of all packages that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review rev HEAD". Note: all changes have to be committed, also see nixpkgs-review usage
  • Tested basic functionality of all binary files (usually in ./result/bin/)
  • 23.11 Release Notes (or backporting 23.05 Release notes)
    • (Package updates) Added a release notes entry if the change is major or breaking
    • (Module updates) Added a release notes entry if the change is significant
    • (Module addition) Added a release notes entry if adding a new NixOS module
  • Fits CONTRIBUTING.md.

Result of nixpkgs-review run on x86_64-linux 1

1 package blacklisted:
  • nixos-install-tools

@github-actions github-actions bot added 6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` labels Sep 5, 2023
@ofborg ofborg bot added 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10 labels Sep 5, 2023
@Yarny0
Copy link
Contributor Author

Yarny0 commented Sep 15, 2023

Updated branch:

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/2655

Copy link
Contributor

@Majiir Majiir left a comment

Choose a reason for hiding this comment

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

Clean work!

The migration approach is unusual. It would be great to follow up by creating some kind of mkRenamedAttrsOptionModule to solve this for everyone. I don't think it poses any risks to merge it as-is, though.

I don't use tsm-client and I don't know how to test it, but code changes LGTM.

Comment on lines 214 to 258
makeDsmSysLines = key: value:
# Turn a key-value pair from the server options attrset
# into zero (value==null), one (scalar value) or
# more (value is list) configuration stanza lines.
if isList value then map (makeDsmSysLines key) value else # recurse into list
if value == null then [ ] else # skip `null` value
[ (" ${key}${
if value == true then "" else # just output key if value is `true`
if isInt value then " ${builtins.toString value}" else
if path.check value then " \"${value}\"" else # enclose path in ".."
if singleLineStr.check value then " ${value}" else
throw "assertion failed: cannot convert type" # should never happen
}") ];

makeDsmSysStanza = {servername, ... }@serverCfg:
let
# drop special values that should not go into server config block
attrs = removeAttrs serverCfg [ "servername" "genPasswd" ];
in
''
servername ${servername}
${concatLines (concatLists (mapAttrsToList makeDsmSysLines attrs))}
'';

Copy link
Contributor

Choose a reason for hiding this comment

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

Did you look into using formats.keyValue? It looks like it would need to be adapted to skip null values. That option would be a great addition to it anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but the format required here has more deviations from what formats.keyValue (and also formats.ini) supports:

  • null should be skipped
  • true should yield a key-only line
  • The servername key must be stated before other keys; it acts like a section head.

I don't think one could (or should) accomplish this by extending keyValue; it's just too specialised. Like, e.g., to gitIni, one could add a dsmSys format, but I feel tsm-client doesn't deserve its own (global) format definition in nixpkgs.

}) cfg.servers);
}) cfg.servers)
# XXX migration code for freeform settings, this can be removed after 23.11:
++ (enrichMigrationInfos "assertions" (addText: { assertion, message }: { inherit assertion; message = addText message; }));
Copy link
Contributor

Choose a reason for hiding this comment

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

This line took me a few minutes to figure out. I finally realized that it's equivalent to this:

   ++ (enrichMigrationInfos "assertions" (addText: assertion: assertion // { message = addText assertion.message; }));

I think your style here is fine - just commenting in case others are similarly confused!

Comment on lines 109 to 101
# XXX migration code for freeform settings, these can be removed after 23.11:
options.warnings = optionsGlobal.warnings;
options.assertions = optionsGlobal.assertions;
Copy link
Contributor

Choose a reason for hiding this comment

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

This won't be the only module where we want to rename options inside attrsOf submodule. So that we don't lose track of this need, could you create an issue and link to this PR as an example use case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Such an issue exists already: #96006

I was thinking about a general solution, but the tricky part is "transporting" assertions and warning out of the submodule into the main configuration area. Here, in the PR at hand, I'm doing it manually, by defining assertions and warnings options in the submodule. Then I have to evaluate both of them outside of the submodule (with mapAttrsToList...) and I have to filter them out of the submodule's result where it is used to generate the configuration. I ultimatelly failed at encapsulating all this inside a mkRenamedAttrsOptionModule, it just requires too many assumptions about the design and usage of the submodule where it would be applied.

Note: I also encountered these PRs that try/tried to implement assertions and warnings at submodule level. Such an addition would probably be a good step towards a general solution for submodules.

@@ -1,4 +1,5 @@
{ config, lib, pkgs, ... }:
{ config, lib, options, pkgs, ... }: # XXX migration code for freeform settings: `options` can be removed after 23.11
Copy link
Contributor

Choose a reason for hiding this comment

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

Since there is (as far as I know) no formal deprecation policy, it may be best to keep the migration code around for a release or three.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I changed the comment to indicate the year 2025. That should be plenty of time.

(Also rebased onto current nixos-unstable and re-ran all tests)

Comment on lines +265 to +251
"extraConfig" "text"
"name" "server" "port" "node" "passwdDir" "includeExclude"
Copy link
Contributor

Choose a reason for hiding this comment

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

Did you consider adding a settings option on the server submodule, rather than commingling the freeform options with the existing ones? It might eliminate the need to ignore options here and simplify logic in a few places.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, but I don't think it's worth the effort: Most attribute keys listed here are due to the migration mechanism; as soon as the migration code gets removed, there are only two attributes left. The servername is usually defined with the attribute key below servers. So we would add another attribute set level only for a single option.

@delroth delroth added the 12.approvals: 1 This PR was reviewed and approved by one reputable person label Sep 21, 2023
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-already-reviewed/2617/1114

@delroth delroth removed the 12.approvals: 1 This PR was reviewed and approved by one reputable person label Sep 25, 2023
@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-ready-for-review/3032/2728

@Yarny0
Copy link
Contributor Author

Yarny0 commented Oct 15, 2023

Is there anything I can do that might help getting this merged before breaking changes are restricted for NixOS 23.11 ?

@Yarny0
Copy link
Contributor Author

Yarny0 commented Nov 22, 2023

Rebased onto current nixos-unstable and added three commits that employ some of the more recent additions to nixpkgs library functions to simplify the code a bit:

  • use lib.lists.allUnique to drop checkIUnique in the tsm-client module,
  • use lib.meta.getExe' for the dsmc binary in the tsm backup service,
  • use lib.options.mkPackageOption for package options in the tsm-client module.

Tests still pass.

Is there anything I can do that might help getting this merged?

With the tsm-client 8.1.19.0 release,
IBM renamed the product brand from
"IBM Spectrum Protect" to "IBM Storage Protect":
https://www.ibm.com/support/pages/node/6964770 .

The package already got updated in commits
5ff5b2a and
a4b7a62 .

The commit at hand updates the modules accordingly.
`tsm-client` uses a global configuration
file that must contain coordinates for each
server that it is supposed to contact.
This configuration consists of text
lines with key-value pairs.

In the NixOS module, these servers may be declared
with an attribute set, where the attribute name
defines an alias for the server, and the value
is again an attribute set with the settings for
the respective server.
This is organized as an option of type `attrsOf submodule...`.

Before this commit:

Important settings have their own option within
the submodule.  For everything else, there is
the "catch-all" option `extraConfig` that may
be used to declare any key-value pairs.
There is also `text` that can be used to
add arbitrary text to each server's
section in the global config file.

After this commit:

`extraConfig` and `text` are gone,
the attribute names and values of each server's attribute
set are translated directly into key-value pairs,
with the following notable rules:

* Lists are translated into multiple lines
  with the same key, as such is permitted by
  the software for certain keys.
* `null` may be used to override/shadow a value that
  is defined elsewhere and hides the corresponding key.

Those "important settings" that have previously been
defined as dedicated options are still defined as such,
but they have been renamed to match their
corresponding key names in the configuration file.
There is a notable exception:
"Our" boolean option `genPasswd` influences the "real"
option `passwordaccess', but the latter one is
uncomfortable to use and might lead
to undesirable outcome if used the wrong way.
So it seems advisable to keep the boolean option
and the warning in its description.
To this end, the value of `getPasswd` itself is
later filtered out when the config file is generated.

The tsm-backup service module and the vm test are adapted.

Migration code will be added in a separate
commit to permit easy reversal later, when the
migration code is no longer deemed necessary.
Check for spaces or duplicate names in server config keys.
Since server config keys are case insensitive,
a setting like

```
{
  compression = "yes";
  Compression = "no";
}
```

would lead to an ambiguous configuration.
To help users migrate from the previous
settings to new freeform settings type,
the commit at hand adds some
`mkRemovedOptionModule` and `mkRenamedOptionModule`.

These modules are not designed to work
inside an attribute set of submodules.
They create values for `assertions` and
`warnings` to inform the user of required changes.
Also, these informational texts do not contain
the full attribute path of the changed options.
To work around these deficiencies,
we define the required options `assertions` and `warnings`
inside the submodule and later add the values collected
inside these options to the corresponding top-level options.
In the course of doing so, we also add the full attribute path
to the informational texts so the user knows these warning
and error messages refer to the `tsmClient.servers` option.

Also, we have to filter out `warnings`, `assertions`, and
the "old" options when rendering the target config file.
@Yarny0
Copy link
Contributor Author

Yarny0 commented Dec 2, 2023

Rebased again onto upcoming nixos-unstable, as there occured a merge conflict with #261702, which also changed one of the two package options into mkPackageOption; so "my" commit now only handles the other one.

No changes otherwise. All tests still pass.

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/prs-in-distress/3604/82

@wegank wegank merged commit d1fc3a5 into NixOS:master Dec 3, 2023
19 checks passed
@Yarny0 Yarny0 deleted the tsm-freeform branch December 20, 2023 11:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.topic: nixos Issues or PRs affecting NixOS modules, or package usability issues specific to NixOS 8.has: module (update) This PR changes an existing module in `nixos/` 10.rebuild-darwin: 0 This PR does not cause any packages to rebuild on Darwin 10.rebuild-linux: 1-10
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants