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
TSM client #61312
TSM client #61312
Conversation
Updated the branch following the suggestions of #59891 . Also added some examples to module options. |
options.services.tsmBackup.includeExclude.default; | ||
environment.systemPackages = [ tsmPkg ]; | ||
environment.etc."${etcDir}/cli.dsm.sys".text = dsmSysText; | ||
systemd.services.tsm-systembackup = service; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please inline the service here
preFixup = '' | ||
# Symlinks to dms.sys outside of the package: | ||
ln --verbose --symbolic --no-target-directory \ | ||
"/etc/${etcDir}/cli.dsm.sys" $out/dsm_dir/dsm.sys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure if I like this passing around of etcDir
too much - I guess we can just create symlinks to /etc/tsm-client
and use that in the module consistently - no need to make the location overridable.
--prefix PATH : "${lib.strings.makeBinPath [ procps jdk ]}:$out/dsm_dir" \ | ||
--set DSM_DIR $out/dsm_dir \ | ||
--set-default DSM_CONFIG /dev/null \ | ||
--run 'export DSM_LOG=''${DSM_LOG-$HOME}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what does DSM_LOG do? The "''" also looks like a typo…
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inlining your comment…
Regarding DSM_LOG, this variable tells the software where to write logfiles to. It must point to a directory where the invoking user has write access. The way it is declared in the wrapper allows the user to override it at will. However, if the user doesn't touch it, the wrapper lets it point to the user's HOME directory, as that is the only directory where it is very likely the invoking user has write access to. I wanted to use --set-default to implement the override effect, but makeWrapper uses single quotes to enclose the argument in the resulting wrapper script which prevents $HOME from being resolved. Therefore I implemented a command that's similar to --set-default but actually uses the value of $HOME here.
I guess if DSM_LOG
is not set, we log to whatever is defined in errorlogname
from dsmSysText
, and this is a way to override it for manual operation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@flokli ''${...}
ensures that the ${...}
is passed to bash, not interpreted by nix. It's thus necessary for any kind of parameter expansion that can't be done in shell without curly brackets, including the expansion-with-default-on-unset PE done here (used to default to $HOME
should there be no DSM_LOG
variable).
(Edit: Oh, I see this was already discussed elsewhere, in a thread not attached to the per-line comment).
# https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.7/client/c_sched_rtncode.html | ||
serviceConfig.SuccessExitStatus = "4 8"; | ||
serviceConfig.ExecStart = "${tsmPkg}/bin/dsmc backup"; | ||
serviceConfig.StateDirectory = etcDir; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will create /var/lib/tsm-client
.
Can you explain a bit what lands where, and why HOME
is set like above?
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some more comments, shaping up well already :-)
IBM Spectrum Protect (Tivoli Storage Manager, TSM) | ||
client system configuration and backup service | ||
''; | ||
ip6 = lib.options.mkEnableOption |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ip6 = lib.options.mkEnableOption | |
enableIPv6 = lib.options.mkEnableOption |
The default value excludes compressed | ||
formats from being recompressed by TSM. | ||
To disable the default value, | ||
you have to use <function>mkForce</function>. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't the user just pass an empty string?
default = ""; | ||
example = '' | ||
encryptiontype AES256 | ||
encryptkey save |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if the config file format stays flat like this, what about letting the user pass an attrset, and convert it to the TSM config syntax, with something similar to toKeyValue
from lib/generators.nix
? (with space as separator)
(see NixOS/rfcs#42 for why)
**** Do not edit! | ||
**** This file is generated by NixOS configuration. | ||
servername systembackup | ||
commmethod ${if cfg.ip6 then "v6tcpip" else "tcpip"} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
commmethod ${if cfg.ip6 then "v6tcpip" else "tcpip"} | |
commmethod ${if cfg.enableIPv6 then "v6tcpip" else "tcpip"} |
It's a shame the client doesn't just use getaddrinfo and getnameinfo, and IPv6 needs to explicitly be enabled :-/
description = "IBM Spectrum Protect (Tivoli Storage Manager) System Backup"; | ||
environment.DSM_CONFIG = "/dev/null"; | ||
# TSM needs a HOME dir to store certificates. | ||
environment.HOME = "/var/lib/${serviceConfig.StateDirectory}/home"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
serviceConfig
isn't defined here - just hardcode it to /var/lib/tsm-client/home
, or even better, /var/lib/tsm-client
, if there's no reason to put it inside another subfolder.
inclexcl ${pkgs.writeText "dsm.sys-inclexcl" cfg.includeExclude} | ||
compression yes | ||
passworddir /var/lib/tsm-client/password/ | ||
errorlogname /var/log/tsm-client.log |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this be configured to log via syslog or to stdout/stderr instead?
That way, we wouldn't need to worry about rotating the logs, and could easily look at the logs from the systemd journal (and handle log forwarding there as well, if desired).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some quick googling, the TSM client seems to log to syslog and stderr "when it's not able to log into a file" - I guess this translates to when the log destination isn't configured, too?
environment.DSM_CONFIG = "/dev/null"; | ||
# TSM needs a HOME dir to store certificates. | ||
environment.HOME = "/var/lib/${serviceConfig.StateDirectory}/home"; | ||
serviceConfig.ExecStartPre = "${pkgs.coreutils}/bin/mkdir --parents $HOME"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This can be accomplished by either setting HOME to /var/lib/tsm-client
as suggested above, or by setting serviceConfig.StateDirectory = "tsm-client tsm-client/home";
.
--prefix PATH : "${lib.strings.makeBinPath [ procps jdk ]}:$out/dsm_dir" \ | ||
--set DSM_DIR $out/dsm_dir \ | ||
--set-default DSM_CONFIG /dev/null \ | ||
--run 'export DSM_LOG=''${DSM_LOG-$HOME}' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Inlining your comment…
Regarding DSM_LOG, this variable tells the software where to write logfiles to. It must point to a directory where the invoking user has write access. The way it is declared in the wrapper allows the user to override it at will. However, if the user doesn't touch it, the wrapper lets it point to the user's HOME directory, as that is the only directory where it is very likely the invoking user has write access to. I wanted to use --set-default to implement the override effect, but makeWrapper uses single quotes to enclose the argument in the resulting wrapper script which prevents $HOME from being resolved. Therefore I implemented a command that's similar to --set-default but actually uses the value of $HOME here.
I guess if DSM_LOG
is not set, we log to whatever is defined in errorlogname
from dsmSysText
, and this is a way to override it for manual operation?
I did some research on the syntax of First off, some notes on server options:
I also tried to feed logging into syslog/journal, ultimately to no avail:
Further notable changes:
|
@spacefrogg could you take a look at this? You seem to be familiar with this ecosystem ;-) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for your effort! Looks fine for most part but binary wrapping should be changed to comply with nixos necessities.
, stdenv | ||
, fetchurl | ||
, gnutar | ||
, jdk # optional (needed for `dsmj` GUI application) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does it support any (also conceivable future) jdk? Do we need version restriction here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! The documentation https://www-01.ibm.com/support/docview.wss?uid=swg21052223#Version%208.1 asks for "JRE 7 or JRE 8", so I'm now using "jdk8".
For certain functionality, it also recommends "the acl package" and "libdevmapper.so v1.01 or higher".
- Grepping through the result of the build process and using the
strings
utility shows that there are indeed somesetfacl
andgetfacl
command lines in binary files. While I never tested/used any acl functionality of TSM, I guess it's wiser to put{s,g}etfacl
in thePATH
. The increase in closure size should be minimal and it might help other users. - The only file referencing
libdevmapper.so
seems to beopt/tivoli/tsm/client/ba/bin/plugins/libPiIMG.so
(apart from many language-specific help files). However, autopatchelf doesn't complain about a missing library. Addinglvm2
tobuiltInputs
also does not generate a dependency onlvm2
. So I'm ignoring this (optional) dependency for now.
for debfile in *.deb | ||
do | ||
ar -xv "$debfile" | ||
tar --verbose --xz --extract --file=data.tar.xz |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove all --verbose
flags. They clutter the logs without providing useful information.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
ln --verbose --symbolic --no-target-directory \ | ||
"/etc/tsm-client/cli.dsm.sys" $out/dsm_dir/dsm.sys | ||
ln --verbose --symbolic --no-target-directory \ | ||
"/etc/tsm-client/api.dsm.sys" $out/dsmi_dir/dsm.sys |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove. Nix packages must not depend on outside state. See nixos module for resolution.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implies that all external binaries that use TSM via its libraries must also set environment variables appropriately. Maybe, add a note about this at the top of this derivation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove. Nix packages must not depend on outside state. See nixos module for resolution.
Please see my general comment.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This implies that all external binaries that use TSM via its libraries must also set environment variables appropriately. Maybe, add a note about this at the top of this derivation.
There is a comment about the client system-options file dsm.sys
at the top of the derivation. I extended the comment to hint on DSMI_DIR
and possible further environment variables.
rm --verbose "$bin" | ||
makeWrapper "$target" "$bin" \ | ||
--prefix PATH : "${lib.strings.makeBinPath [ procps jdk ]}:$out/dsm_dir" \ | ||
--set DSM_DIR $out/dsm_dir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove wrapper, here. This will be done in nixos module.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my general comment.
} // lib.attrsets.optionalAttrs (config.includeExclude!="") { | ||
inclexcl = ''"${pkgs.writeText "inclexcl.dsm.sys" config.includeExclude}"''; | ||
} | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only works if options' effects are independent of the order in which they appear. Is that true for this config file?
It is normally easier (on future maintenance), less error prone and less likely to cause user frustration to not abstract the config file format at all and leave configuration to the user than to abstract only parts of the config file language.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This only works if options' effects are independent of the order in which they appear. Is that true for this config file?
I don't know -- see below for a resolution.
It is normally easier (on future maintenance), less error prone and less likely to cause user frustration to not abstract the config file format at all and leave configuration to the user than to abstract only parts of the config file language.
I wouldn't consider that a problem here as it concerns two different configuration files with different syntax. Abstracting the "inclexcl" file would certainly be quite hard.
However, I agree with the objection above: the order of options in dsm.sys
is a problem. I haven't found a statement about ordering options in dsm.sys
in the doucmentation. Coarsely looking for order-dependent options didn't give me any suspects, but there are too many options to be sure.
Completely "unabstracting" the dsm.sys
seems to be hard as well as we still need to place some options there for the system backup. I was tempted to change the type of the "extraConfig" option to contain "lines" of text instead of an "attrsOf" strings. That way users could just juse mkBefore
and mkAfter
to add lines in arbitrary order around those generated by the predefined config keys. That approach, however, precludes the user from overwriting or suppressing config lines generated by the predefined config keys. In light of this, I added another config option "text" that contains the textual result of "extraConfig" that forms the stanza. So, if the user wants to overwrite/suppress a config line that is generated by a predefined option, s/he can use "extraConfig"; if the user wants to add lines in specific order, s/he can use "text".
This solution might appear a bit over-engineered, but it's the best one I could come up with: It provides predefined options for common configuration keys and still gives the user full control over the content of each server stanza.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can still use lines
to achieve your goal. A user can completely overwrite generated content by setting the variable with mkForce
.
}; | ||
systemd.services.tsm-backup = lib.modules.mkIf (cfgSvc.servername!=null) { | ||
description = "IBM Spectrum Protect (Tivoli Storage Manager) Backup"; | ||
environment.DSM_CONFIG = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not needed with wrapped binaries. Also, please add a config option for writing a dsm.opt
. I specify more options than the servername in there. In this implementation, I couldn't communicate this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you sure that is needed? While I couldn't find an explict statement, my understanding of the documentation is that each option in dsm.opt
can also be used in dsm.sys
. A noteable -- but not crucial -- exception are the servername
and defaultserver
options, but those options just control which server stanza in dsm.sys
is to be used.
If my idea of the inner workings of the TSM client is correct, this leaves us with two scenarios, neither of which requires dsm.opt
to be defined in the NixOS module:
- The system backup is completely controlled by the settings in
dsm.sys
. Any options in a hypotheticaldsm.opt
might as well be stated indsm.sys
. - Users of the command line client want to define additional options that the system administrator didn't add to the system-wide
dsm.sys
. They can do so in their owndsm.opt
file and provide that one to the client with the-optfile=/path/to/dsm.opt
command line option, or with theDSM_CONFIG
environment variable.
@spacefrogg: Can you check if your use cases fit in the scenarios described above and whether they can be handled without dsm.opt
or with a user-provided dsm.opt
, respectively? I'm curious if I missed something and there is actually a use case (e.g. an option I overlooked) that must be stated in dsm.opt
for the system backup service.
Note: Researching on the interplay of dsm.sys
and dsm.opt
led me to the -se
command line option. This option can fully replace the dsm.opt
file for the backup systemd service, so I removed the file in the recent incarnation of the pull request at hand.
# for exit status description see | ||
# https://www.ibm.com/support/knowledgecenter/en/SSEQVQ_8.1.7/client/c_sched_rtncode.html | ||
serviceConfig.SuccessExitStatus = "4 8"; | ||
serviceConfig.ExecStart = "${pkgs.tsm-client}/bin/dsmc backup"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make the arguments configurable. I use incr
, here, so this seems to depend on usage scenario. :)
Also, refer to the wrapped binary instead of the original.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added the services.tsmClient.service.command
option now, that should do the trick.
for link in $out/lib/* $out/bin/* | ||
do | ||
target=$(readlink "$link") | ||
test "$(cut -b -6 <<< "$target")" == "../../" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use proper if [ ... ]; then ... fi
construct, here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
, jdk # optional (needed for `dsmj` GUI application) | ||
, procps | ||
, zlib | ||
, makeWrapper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dito. Remove.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see my general comment.
Thanks for your suggestions, @spacefrogg. It is really good to see that I will not be the sole user of that package! I applied most of your requested changes. Considering your suggestion to create a wrapper script in a NixOS module, I beg to differ. Since creating that wrapper concerns several of your per-line requests, I will concentrate my response to that concept here. I am convinced that the symlink to
On a sidenote: I used to have a similar setup on my machines -- I reassembled the |
@Yarny0 Thank you for your effort! I understand your reasoning and will address each of your remarks in turn. Please note, that I am in no authority over merging this PR or demanding any changes of you. My code reviews are based on my experience and current understanding of how nixpkgs PRs are supposed to be designed to acceptable to people that actually do have the authority. So, feel free to ignore me.
|
Dear @spacefrogg, I will concentrate on the question of the (im)possibility of the symlink into
I'm not sure if your argument goes against a symlinks in particular or against any kind of reference that points outside of the Nix store. Please forgive if I'm bringing forward unnecessary arguments. Anyway, here's my stance on references that point out of the Nix store:
Is there any documentation or otherwise documented consensus that explicitely forbids -- or frowns upon -- references pointing out of the Nix store? What is the rational to avoid such references in general? Although I still consider myself a novice w.r.t. Nix packaging, the ban on outside references stands in stark contrast to everything in nixpkgs I've seen so far. On the other hand, not using the symlink would hamstring many of my use cases. So please understand my skepticism and excuse my insistence. While looking for statements about the (in)validity of references pointing out of
The safety and comfort of a NixOS module is independent of the usage of
Do you refer to Apache's httpd? I'm not sure it fully compares to the problem at hand: httpd accepts a config file on its command line, and it is a server process not ment to be invoked by ordinary users. However, I tried it, and httpd from nixpkgs runs fine without a wrapper script -- even when called by non-root (on a port beyond 1024) and even on a non-NixOS machine. I'd like to reach a similar solution with
The software would need it as TSM's API needs the
Why would I add Let me add and emphasize that I'm very greatful of this discussion, @spacefrogg. Irrespective of the outcome of this particular question, I'm aiming at a solution that covers as much use cases as possible, in a way that is as user/admin friendly as possible. So I would be very inclined if the version that is finally merged covers your use cases as well as mine. I'm still convinced that the symlink to |
My argument goes against what I wrote just above. File-based references, i.e., softlinks and hardlinks.
These are not references of the same kind. The main difference between the two is in expectations. Resolving a configuration file from inside a running program is in the responsibility of the program itself. Providing a filesystem without broken links is not. You may not like this distinction, but I, myself, regard this as common consensus (by mere observation) among Linux distribution maintaining folks.
Maybe, but this similarity is not relevant. See my previous remark.
True, and the term you are looking for is purity, impure package and descendants. We try to keep packages pure as much and as often as possible. A package should not reference anything outside the known scope of the nix package manager with very few exceptions, e.g., runtime data (usually databases of any kind). And even that is a concession to practicality. I give you the very idea of NixOS:
The consequence from 3. is, that applications cannot have state, which is impractical, which is why local application state is treated separately. But even then, look at the postgres NixOS module and you will find that it uses versioned directories for its application state. This way, you at least have the possibility to downgrade again, should the database upgrade mess up your end-user application. On a 'normal' Linux distribution you would have literally messed up when you upgraded without making backups first. As a sidenote: With
Okay, now to the other side of the story... As mentioned, NixOS allows application state, although it strives for purity through statelessness. If it would have been practically achievable and sustainable (i.e., maintainable), there would be no
We even support copying the file from the So... stepping back, looking at the infrastructure NixOS actually provides you with to avoid statefulness. No, breaking this rule lightly and uncalled for is not acceptable.
I can only refer you to IRC or the discourse forum for further guidance. But anyway, could you please concisely list which of your use cases it would hinder to not set up the symlink but to follow the approach I showed you? I think, I haven't understood your problem domain, yet.
I hope, I could prove to you, that your conclusions are wrong, here. Sidestepping
But it is the very responsibility of a package maintainer to correctly instate the configuration of a package that depends on another one. If we could automate that task, we would do so. If it is as easy as providing every descendent with the right DSMI_DIR environment variable, it's even a no-brainer (maybe not trivial to write, just the architectural idea is). You could write a package setup hook in
Because it adds
I am not, but I think, it would cover my use cases. I am always willing to share my insights. |
943cba5
to
5b51e3a
Compare
Thanks for your response that helped me understand your objections more clearly. I updated the request in order to tweak some minor details (explained below). However, irrespective of other packaging details, I am still confused by the interplay of symlinks and purity:
This leaves me with the impression that it is considered alright when a program looks into
These definitions are independent of the usage of symlinks/hardlinks. In particular, by this definition, the programs listed above (and many more) suffer impurity. E.g. the command I still can't grasp why dependence on outside state is acceptable unless a symlink is involved. While a filesystem with a "broken symlink" (as criticised above) isn't nice, it won't do any additional harm when compared to a missing config file that is referenced without passing through a symlink. For instance:
All those behaviours are certainly impure to the degree that local configuration is not included in the package itself. Yet, it matches most users' expectations, and it works flawlessly on NixOS and on non-NixOS systems. How the config file is discovered in the end is an implementation detail that is of no interest to the admin as long as where the file is to be placed is documented. To aid the admin, I added a hint about the configuration file's placement in the
This leaves me confused. This pull request uses the method advertised in the third bullet point of yours to spare the admin the hassle of having to prepare the config file by hand. So, if you want to ensure that TSM's config file is in the Nix store, this pull request satisfies your needs. By using the NixOS module in this pull request, you will have a config file that is generated in the store and then referenced by a symlink in
As I'm still not sure how to balance practicability and purity here, I will consult Discourse. Regarding the use cases I'd like to cover, they represent most combinations of
in addition to
The combination 2b+3a is of no real interest to me, the combination 2a+3b is used occasionally only. All other combinations are very important. Talking about expectations, non-NixOS systems are a concern for me in particular, as admins are used to just modifying the config file and expecting the change to be active immediately (like adding a certificate to
When trying what you suggested, Yet I want to thank you for reminding me of the
Thanks for you offer, and moreover thanks for the assessment of your use cases. This feeds me with optimism regarding this pull request. |
This pull request has been mentioned on Nix community. There might be relevant details there: https://discourse.nixos.org/t/symlink-to-etc-from-within-a-package-vs-purity/3257/1 |
Executive summary, see below for details:
This should suite both, NixOS and non-NixOS cases without relying on this broken symlink.
It is considered acceptable, because it is not sustainable to make all applications work without hardcoded configuration files. Maybe it helps to look at it from a different angle. You actually don't care where a configuration file is stored on the harddrive. What you do care about is, making the application access the right configuration when it is run. Having a hardcoded location for a configuration file, is a broken, old-school way of providing this functionality. Whenever you can tell your application where to find the right configuration at calltime, you don't need a hardcoded location. This is what we do on NixOS, if possible. There are usage scenarios, where it is important for a user to know what's inside a configuration file. But this is an abuse of hardcoded paths for providing a stable interface altogether. Ideally you'd have a way to ask your application or operating system for providing this information. Again, the question is, why are hardcoded paths a broken interface? Answer, they lack the notion of multiple (and different) consistent application states. This cannot be done using hardcoded paths. This is the reason why there is a separate
Yes, they are independent of symlinks/hardlinks. On NixOS, programs generally do not suffer from impurity, because, to stay with you example, the administrator cannot write into
What I wanted to convey with my previous remarks, was the following: Nothing you say is plain wrong, but on a NixOS system, we want more guarantees. You are arguing towards not providing these guarantees in the way that I seem acceptable (aka. what I think the NixOS community finds acceptable). Nothing more, nothing less.
You're comparing apples and oranges, here. On a NixOS system this is exactly not how you use
This said, if you want a PR to be merged into NixOS, you should at least accept that this is the way software is used. No ad hoc hacking in
This is not good enough. If possible and sustainable,
True, not good enough. NixOS provides functionality, not inconsistent stuff that must be made consistent as an afterthought. This is where almost all other operating systems fail today.
True, I never argued against this part of your implementation. I merely shared my insights into the architecture of NixOS with you. I argued against putting a broken symlink into the nix store.
Thanks for sharing.
The expectation of ad hoc hacking configuration is not the focus of NixOS. If possible without violating purity, you may write the package in a way to also provide useful non-NixOS behaviour. If not, non-NixOS is not the focus, and folks there have to find a way around the issues. For your particular case, there is no such problem if you follow my suggestions. Non-NixOS systems can happily use
This is true only for non-NixOS systems. On NixOS, both, the application and the configuration are provided consistently at the same time. It is, thus, in the responsibility of the package maintainer to provide the necessary nixos module mechanics. Other distributions like Debian also expect from you to provide sane default configuration for a service you want to integrate in their system.
True. The decision has been made, wrap the binaries. No symlinks pointing outside the store. This decision hinges on filesystems not providing multiple consistent states on hardcoded paths.
Good, good! Do this! I was not sure myself. :)
Sounds good! |
The pull request received a major overhaul:
Details of changes in the package:
Details of changes in the module:
Dear @spacefrogg, first of all, thanks again for your hints towards a better implementation. I revised the pull request based on ideas I received on discourse and on what I distilled from your latest response. Could you have a look at the current revision to judge whether the design satisfies purity requirements? I tried to understand and follow your suggestions, but I'm still not sure if I graps your goals correctly. In case the current design is not acceptable, this is, essentially, where I'm struggling to follow your response:
Just to be sure so we're not talking cross purposes: In my initial design the files |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very neat! Thank you very much, @Yarny0, for your efforts! I still have some requests, but the overall architecture is very much in the right spirit, now. :)
Regarding your last remark: This difference was, that you wanted to put a, probably broken, symlink into the nix store. This is something, sshd
doesn't do.
, gnutar | ||
, makeWrapper | ||
, procps | ||
, runCommand |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not used, remove.
, autoPatchelfHook | ||
, buildEnv | ||
, fetchurl | ||
, gnutar |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this would be unnecessary if you would depend on stdenv
instead of stdenv.cc.cc
later. This way, you make sure to use the tar package from stdenv
. So, unless you need a tar feature, that only gnutar
has, this would be more general.
I am not sure about the impact on autoPatchelfHook
, though...
# The `-se` option must come after the command. | ||
# The `-optfile` option suppresses a `dsm.opt`-not-found warning. | ||
serviceConfig.ExecStart = | ||
"${tsmClientPkg}/bin/dsmc ${cfgSvc.command} -se='${cfgSvc.servername}' -optfile=/dev/null"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the dsm.opt
not-found guard still necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. It merely hides a warning, but a long and confusing one.
{ config, lib, pkgs, ... }: | ||
|
||
let | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use
inherit (lib) mkOption, strMatching,...;
inherit (builtins) ...;
lines, here. Using full-qualified names throughout the code affects readability a lot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Applied, with two exceptions
- I still refer to the sub-namespaces in lib, like
inherit (lib.options) mkOption
. I faintly recall having read somewhere that putting all library elements in thelib
namespace was a bad idea (I can't find it and I might be wrong about this). While I guess this will never be reverted, I don't want to stand in the way of doing so. - I didn't add an
inherit
line for libraries with only one element to be imported. In such cases I feel short code weight more than readability (but that's probably a matter of taste).
IBM Spectrum Protect (Tivoli Storage Manager, TSM) | ||
client system-options file "dsm.sys" | ||
used by TSM command line applications | ||
''; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Intertwining enable
and servername
this way is unexpected and confusing. The semantics you described warrant, that you split the module in two (code may stay in the same file). One programs.tsmClient.enable
that draws in the right wrapped binary, and one services.tsmClient.enable
which enables the service, forcing that servername
has to be configured and activating programs.tsmClient.enable
.
Rationale: The way you constructed this module, the tsm-client binaries are expected to work without configuration. Then you should put them into programs
instead of services
. The user's expectation is that, when (s)he does services.*.enable
, the service gets enabled. This silently fails now, if servername
is not set. I know, that you correctly documented this behavior. Still, this is unexpected and, thus, confusing.
EDIT: typo
Dear @spacefrogg, I updated the pull request and applied your requests (and responded to non-request comments). Two further remarks are in order:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks ready to merge from my side. @infinisil @flokli
IBM Spectrum Protect (former name: Tivoli Storage Manager) provides a single point of control for backup and recovery. This package contains the client software, that is, a command line client and linkable libraries. This commit adds two packages to nixpkgs: The TSM client software contains a Java GUI that, naturally, requires Java to be installed. To keep the closure size low, we provide the packages `tsm-client` and `tsm-client-withGUI`. The former comes without the Java GUI. While the product has been renamed, its old name is still alive in filenames and as package name used by other distros.
This commit brings a module that installs the IBM Spectrum Protect (Tivoli Storage Manager) command-line client together with its system-wide client system-options file `dsm.sys`.
Based on the programs/tsm-client module, this commit introduces a systemd service that uses the tsm-client to create regular backups of the machine.
Tiny update: By looking at |
looks really nice so far, thanks for the hard work! |
I saw Docbook tags being used in NixOS module descriptions - I assumed that was also possible for package descriptions - cc @grahamc |
It seems thaht a subset of the XML is supported for options: https://github.com/NixOS/nixos-homepage/blob/master/nixos/options.tt#L279 but is not applied to packages. It'd be nice to port this over and support it in both! |
It would be lovely if this could be integrated into NixOS/nixos-homepage#209 (or a followup PR after that) |
Motivation for this change
Tivoli Storage Manager is IBM's backup solution. This pull request brings the client software and a NixOS module to use the client for regular system backups. Note that the client also provides an API to be linked against.
This solftware was renamed to IBM Spectrum Protect recently, but the previous name prevails in file names of the software as well as in package names of other distros. A I would expect possible users to look for the old name, I stick to it.
Things done
sandbox
innix.conf
on non-NixOS)x86_64-linux
nix-shell -p nix-review --run "nix-review wip"
(none)./result/bin/
)nix path-info -S
before and after)