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
Allow specifying interface for default gateway #21875
Conversation
@abbradar, thanks for your PR! By analyzing the history of the files in this pull request, we identified @wkennington, @tk-ecotelecom and @edolstra to be potential reviewers. |
Could also add an example for ipv6 static address configuration + default gateway to the manual? |
An example would pretty much duplicate one for IPv4 sans option names and addresses, so is with the gateway. I've, however, added mentions of needed options and a reference to IPv4 section for examples -- do you think that's OK or it's better to describe things in more detail? |
I think at least the
|
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.
👍 but what I would really love to see is a generic networking.routes
module.
63aa092
to
29743f7
Compare
I added some examples to the documentation. @fpletz Me too but I don't have enough time to work on it now D: When/if it's done we could just redefine |
You've used I'd like input from @nbp for the |
29743f7
to
6be45fa
Compare
@globin Indeed, fixed to |
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.
Nice addition, I will note that the module system has a way to post-process options with the apply
function of option definitions, but as the either
type does not check for submodules, this could be a bit complicated.
One good thing is that the coerceTo
function should keep the option definition locations, even after they got converted into modules.
I have one concern about this coerceTo
function, which is that the error messages can report errors to code that the user did not wrote, which would generate potentially confusing error messages. In which case I would agree that this would be a NixOS issue, if this happens.
In addition to the following comments, I recommend that you add test cases for this new coerceTo
type. Including failing cases. (see lib/tests/modules.sh
)
lib/types.nix
Outdated
coercedTo = coercedType: coerceFunc: finalType: mkOptionType rec { | ||
name = "coercedTo"; | ||
description = "${finalType.description} or ${coercedType.description} coerced to it"; | ||
check = x: coercedType.check x || finalType.check x; |
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 does not matter much here, but I would swap the 2 conditions. (see why below)
lib/types.nix
Outdated
@@ -352,6 +352,25 @@ rec { | |||
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; }; | |||
}; | |||
|
|||
coercedTo = coercedType: coerceFunc: finalType: mkOptionType rec { | |||
name = "coercedTo"; | |||
description = "${finalType.description} or ${coercedType.description} coerced to it"; |
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.
These can be quite complex expressions, I am not sure that coerce to it
would make sense in all cases.
The fact that some data are coerce is an implementation detail which does not matter from the documentation perspective of the description. I would recommend using the same description as types.either
.
lib/types.nix
Outdated
check = x: coercedType.check x || finalType.check x; | ||
merge = loc: defs: | ||
let | ||
coerceVal = val: if coercedType.check val |
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 code will always attempt to coerce. If there is an overlap between the types, I am not sure this would be the expected behavior. Checking for finalType.check
before coercedType.check
would make more sense to prevent coercing data which already have the final type.
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 catch, I haven't thought that types could overlap.
lib/types.nix
Outdated
@@ -352,6 +352,25 @@ rec { | |||
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; }; | |||
}; | |||
|
|||
coercedTo = coercedType: coerceFunc: finalType: mkOptionType rec { |
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.
Assert that coercedType
does not contain any submodule by checking that getSubmodule
is null
.
You mean assertions to check that types are correct? Yeah, not sure what can we do about that other than carefully writing coercing functions... I'll address other comments and add tests, thank you for the review! |
Hmm, I think we can greatly improve error handling if define
This should catch nearly all misbehaving coercion functions, presenting them as type errors. It can be a bit expensive however, requiring two |
For catching misbehaving coercion function, I think you can just add them as part of an assertion in the merge function. A coercion issue would be something for a NixOS module developer, not a new NixOS user. |
6be45fa
to
873d890
Compare
Changed |
@nbp would you care to review the changes again? If that's fine I'd be happy to merge 👍 |
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.
Fix the multiple option declaration issues, and add tests cases in lib/tests/modules.sh
.
lib/types.nix
Outdated
let | ||
coerceVal = val: if finalType.check val | ||
then val | ||
else 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.
nit: indent these lines properly:
let
coerceVal = val:
if finalType.check val then val
else let
lib/types.nix
Outdated
@@ -352,6 +352,25 @@ rec { | |||
functor = (defaultFunctor name) // { wrapped = [ t1 t2 ]; }; | |||
}; | |||
|
|||
coercedTo = coercedType: coerceFunc: finalType: assert coercedType.getSubModules == null; mkOptionType rec { |
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.
nit: move the assert
and mkOptionType
on new lines.
lib/types.nix
Outdated
getSubOptions = finalType.getSubOptions; | ||
getSubModules = finalType.getSubModules; | ||
substSubModules = m: coercedTo coercedType coerceFunc (finalType.substSubModules m); | ||
functor = (defaultFunctor name) // { wrapped = finalType; }; |
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 would not work if multiple options are declared with the same type.
Also, merging multiple coerceTo
types or even a coerceTo
with a finalType
might not be easy, add a failing test case and a nicer error message.
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.
Sorry, I don't fully understand how functor
and typeMerge
work and what are their purpose -- how are types merged in the module system and where does this occur? I don't want to blindly fix things by example :D
Augh, yes, I forgot about tests! Very sorry, this was not intentional -- I'll take a look at them (and your other comments) ASAP. |
873d890
to
645387c
Compare
@abbradar Do you need help for writing and running the test cases? |
Test cases should be okay (I haven't tried but think I can handle) however first I want to understand |
|
Thanks, I understand what's going on better now. After some reading I have the next question about |
@abbradar , exactly, the order in which the I would say, the easiest way forward I would recommend is only make this type mergeable with its identical counter-part, and not with the |
Oh, I have realized only now that type merging of |
645387c
to
4feb0a9
Compare
Pushed new version, along with tests. @nbp, please re-review! |
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.
Nice work, thanks!
# Check coerced value. | ||
checkConfigOutput "\"42\"" config.value ./declare-coerced-value.nix | ||
checkConfigOutput "\"24\"" config.value ./declare-coerced-value.nix ./define-value-string.nix | ||
checkConfigError 'The option value .* in .* is not a coercedTo.' config.value ./declare-coerced-value.nix ./define-value-list.nix |
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 you open a follow-up issue to fix this error message.
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 sure, what do you mean? This error message is expected because I try to use a list.
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 think I understand what you mean -- "is not a coercedTo" is not a helpful message ;). I think we can fix this easily by outputting description instead of type in the error message -- how's 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.
That sounds good, and unrelated to the current patch as well, why I suggested to make a follow-up patch ;)
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.
Indeed. Let's merge this and I'll make another PR. Thank you again for your reviews!
Motivation for this change
This is needed for particular network configurations. For example, Hetzner provides me with /64 IPv6 network, but requires to set default gateway
fe80::1
. This won't work if I just set the address because Linux wouldn't know where should it send packets. Specifying the interface explicitly fixes that.To accomplish this, I turned
networking.defaultGateway{,6}
into a submodule with optionsaddress
andgateway
. To hold backwards compatibility I introduce new typecoercedTo coercedType coerceFunc finalType
. It allows one to specify either something offinalType
(merged as is), orcoercedType
(would be converted tofinalType
during merge usingcoerceFunc
). For example, option:configuration-1.nix
:configuration-2.nix
:We'd get resulting
foo.value
containing both"value1"
and"value2"
.cc @nbp as the module system author -- this was my first encounter with it and I'm unsure if I've defined
coercedTo
correctly.Things done
(nix.useSandbox on NixOS,
or option
build-use-sandbox
innix.conf
on non-NixOS)
nix-shell -p nox --run "nox-review wip"
./result/bin/
)