providers/hetzner: add network configuration and public IPv6 attribute#1266
providers/hetzner: add network configuration and public IPv6 attribute#1266Rolv-Apneseth wants to merge 2 commits intocoreos:mainfrom
Conversation
There was a problem hiding this comment.
Code Review
This pull request introduces network configuration support for the Hetzner provider, including systemd-networkd and netplan configurations, and adds a HETZNER_PUBLIC_IPV6 attribute. The changes are comprehensive, covering documentation, implementation, and extensive tests. My review focuses on a few areas to improve code readability, maintainability, and robustness.
| fn netplan_config(&self) -> Result<Option<String>> { | ||
| let networks = self.networks()?; | ||
|
|
||
| let mut ethernets = serde_yaml::Mapping::new(); | ||
|
|
||
| for iface in networks { | ||
| let mut eth_config = serde_yaml::Mapping::new(); | ||
|
|
||
| // Add DHCP settings | ||
| if let Some(dhcp) = iface.dhcp { | ||
| match dhcp { | ||
| DhcpSetting::V4 => { | ||
| eth_config.insert("dhcp4".into(), true.into()); | ||
| } | ||
| DhcpSetting::V6 => { | ||
| eth_config.insert("dhcp6".into(), true.into()); | ||
| } | ||
| DhcpSetting::Both => { | ||
| eth_config.insert("dhcp4".into(), true.into()); | ||
| eth_config.insert("dhcp6".into(), true.into()); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if !iface.ip_addresses.is_empty() { | ||
| let addresses: Vec<String> = iface | ||
| .ip_addresses | ||
| .iter() | ||
| .map(std::string::ToString::to_string) | ||
| .collect(); | ||
| eth_config.insert("addresses".into(), addresses.into()); | ||
| } | ||
|
|
||
| if !iface.routes.is_empty() { | ||
| let routes: Vec<serde_yaml::Value> = iface | ||
| .routes | ||
| .iter() | ||
| .map(|route| { | ||
| let mut route_map = serde_yaml::Mapping::new(); | ||
| route_map.insert("to".into(), route.destination.to_string().into()); | ||
| route_map.insert("via".into(), route.gateway.to_string().into()); | ||
| serde_yaml::Value::Mapping(route_map) | ||
| }) | ||
| .collect(); | ||
| eth_config.insert("routes".into(), routes.into()); | ||
| } | ||
|
|
||
| if !iface.nameservers.is_empty() { | ||
| let nameservers: Vec<_> = iface | ||
| .nameservers | ||
| .iter() | ||
| .map(std::string::ToString::to_string) | ||
| .collect(); | ||
| eth_config.insert( | ||
| "nameservers".into(), | ||
| serde_yaml::Value::Mapping(serde_yaml::Mapping::from_iter([( | ||
| "addresses".into(), | ||
| nameservers.into(), | ||
| )])), | ||
| ); | ||
| } | ||
|
|
||
| if let Some(name) = iface.name { | ||
| ethernets.insert(name.into(), eth_config.into()); | ||
| } | ||
| } | ||
|
|
||
| let network = serde_yaml::Mapping::from_iter([ | ||
| ("version".into(), 2.into()), | ||
| ("ethernets".into(), ethernets.into()), | ||
| ]); | ||
| let netplan = serde_yaml::Mapping::from_iter([("network".into(), network.into())]); | ||
|
|
||
| Ok(Some(serde_yaml::to_string(&netplan)?)) | ||
| } |
There was a problem hiding this comment.
This function manually constructs the YAML structure using serde_yaml::Mapping. While this works, it's quite verbose and can be hard to maintain. Consider defining structs that derive serde::Serialize to represent the Netplan configuration. This would allow you to build the configuration in a more structured, type-safe way and then serialize it, leading to more declarative and maintainable code.
There was a problem hiding this comment.
Basically copied from an existing provider and this is how it was done, though I kind of agree
There was a problem hiding this comment.
Might be more useful with a default implementation though, rather than in this PR
a8ed60f to
63844d3
Compare
63844d3 to
09ca71d
Compare
prestist
left a comment
There was a problem hiding this comment.
Overall this looks really good @Rolv-Apneseth great work! I love the test coverage!
| }); | ||
| } | ||
|
|
||
| "dhcp" | "dhcp4" | "dhcp6" => { |
There was a problem hiding this comment.
hmm I think this is correct, but what a weird state lol... if subnet is set to "dhcp6" but our ipv6=false we end up with dhcp:4?
So by this statement we are saying that ipv6 takes the lead not dhcp.
There was a problem hiding this comment.
Yeah... not ideal. Those ipv4 and ipv6 booleans are a deviation from cloud-init v1 network config and I've only seen them set to true. I don't even know if they actually use dhcp6, I just thought it should be covered in case.
This should enable the generation of systemd-networkd unit and netplan configuration files for Hetzner systems. Note that a network connection is required to request network metadata (no alternative on Hetzner as far as I can tell).
09ca71d to
6a91e5b
Compare
|
@apricote, in relation to flatcar/Flatcar#1968, if possible could you confirm this would work for Flatcar? Edit: @tormath1 might be more relevant actually, sorry. |
Closes #1141
This adds support for configuring networking on Hetzner systems, both via
systemd-networkdunit files andnetplanconfigurations, as well as providing aHETZNER_PUBLIC_IPV6attribute. This should be enough for, e.g. Flatcar, but Fedora CoreOS (NetworkManager) requires some more work.The main issue is that unlike other providers, it seems (as far as I can tell from everywhere I've looked) that Hetzner only provides the networking metadata from a HTTP endpoint, meaning you need some form of networking up first before further configuration. To my understanding, this means we can't use the
kargsapproach like with other providers, and need to instead fallback to the automatic network configuration before making a request to the metadata endpoint (which is available locally even on systems with only a public IPv6).I've confirmed that the
netplanconfiguration can work on FCOS using:And I'm planning on creating a separate draft PR for generating NetworkManager configurations, which I've also confirmed works with these changes and which I believe would be very nice to have in
afterburnregardless. I'll create an issue for discussion though.On a side note, the
netplanconfiguration was largely copied from another provider in the codebase. I have some code ready for a default implementation, if we think that's a good change.