From 76f8967545821525a0feb3c0302e2069b522a738 Mon Sep 17 00:00:00 2001 From: Nayden Petrov Date: Mon, 26 Oct 2026 15:12:21 +0200 Subject: [PATCH 1/5] fix: rename generated .network file to sort before netplan on Azure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Azure, cloud-init/netplan generates 10-netplan-eth0.network in /run/systemd/network. BOSH's 10_eth0.network (underscore) sorted after it — '_' (0x5F) > '-' (0x2D) — so netplan's file won and BOSH's DNS config (DNSDefaultRoute=no) was silently ignored. Rename to 10-eth0.network so BOSH's file takes precedence after agent restart. --- platform/net/ubuntu_net_manager.go | 2 +- .../net/ubuntu_net_manager_helpers_test.go | 9 ++ platform/net/ubuntu_net_manager_ipv6_test.go | 24 +++--- platform/net/ubuntu_net_manager_test.go | 82 +++++++++---------- 4 files changed, 63 insertions(+), 54 deletions(-) create mode 100644 platform/net/ubuntu_net_manager_helpers_test.go diff --git a/platform/net/ubuntu_net_manager.go b/platform/net/ubuntu_net_manager.go index 325af24c4..b8eb32abf 100644 --- a/platform/net/ubuntu_net_manager.go +++ b/platform/net/ubuntu_net_manager.go @@ -350,7 +350,7 @@ func (net UbuntuNetManager) writeDHCPConfiguration(dnsServers []string, opts bos } func interfaceConfigurationFile(name string) string { - interfaceBasename := fmt.Sprintf("10_%s.network", name) + interfaceBasename := fmt.Sprintf("10-%s.network", name) return filepath.Join(systemdNetworkFolder, interfaceBasename) } diff --git a/platform/net/ubuntu_net_manager_helpers_test.go b/platform/net/ubuntu_net_manager_helpers_test.go new file mode 100644 index 000000000..25c8b4556 --- /dev/null +++ b/platform/net/ubuntu_net_manager_helpers_test.go @@ -0,0 +1,9 @@ +//go:build !windows + +package net_test + +import "fmt" + +func netFile(iface string) string { + return fmt.Sprintf("/etc/systemd/network/10-%s.network", iface) +} diff --git a/platform/net/ubuntu_net_manager_ipv6_test.go b/platform/net/ubuntu_net_manager_ipv6_test.go index efddc7df3..8a4314473 100644 --- a/platform/net/ubuntu_net_manager_ipv6_test.go +++ b/platform/net/ubuntu_net_manager_ipv6_test.go @@ -172,10 +172,10 @@ var _ = Describe("UbuntuNetManager (IPv6)", func() { matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_ethstatic1.network", + netFile("ethstatic1"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network") + networkConfig := fs.GetFileTestStat(netFile("ethstatic1")) Expect(networkConfig).ToNot(BeNil()) Expect(strings.Contains(networkConfig.StringContents(), ` @@ -239,10 +239,10 @@ Gateway=2001:db8::1 matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", + netFile("eth0"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(ContainSubstring(` [Match] @@ -366,11 +366,11 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_ethstatic1.network", - "/etc/systemd/network/10_ethstatic2.network", - "/etc/systemd/network/10_ethstatic3.network", + netFile("ethstatic1"), + netFile("ethstatic2"), + netFile("ethstatic3"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network") + networkConfig := fs.GetFileTestStat(netFile("ethstatic1")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -387,7 +387,7 @@ DNS=9.9.9.9 DNSDefaultRoute=no `)) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic2.network") + networkConfig = fs.GetFileTestStat(netFile("ethstatic2")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -402,7 +402,7 @@ DNS=9.9.9.9 DNSDefaultRoute=no `)) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic3.network") + networkConfig = fs.GetFileTestStat(netFile("ethstatic3")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -450,9 +450,9 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_ethstatic1.network", + netFile("ethstatic1"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic1.network") + networkConfig := fs.GetFileTestStat(netFile("ethstatic1")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] diff --git a/platform/net/ubuntu_net_manager_test.go b/platform/net/ubuntu_net_manager_test.go index 966d16090..1fdb908cc 100644 --- a/platform/net/ubuntu_net_manager_test.go +++ b/platform/net/ubuntu_net_manager_test.go @@ -351,10 +351,10 @@ dns-nameservers 8.8.8.8 9.9.9.9` matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", - "/etc/systemd/network/10_eth1.network", + netFile("eth0"), + netFile("eth1"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -368,7 +368,7 @@ DNS=8.8.8.8 DNSDefaultRoute=no `)) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_eth1.network") + networkConfig = fs.GetFileTestStat(netFile("eth1")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -424,12 +424,12 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", - "/etc/systemd/network/10_eth1.network", + netFile("eth0"), + netFile("eth1"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_eth1.network") + networkConfig = fs.GetFileTestStat(netFile("eth1")) Expect(networkConfig).ToNot(BeNil()) }) @@ -461,9 +461,9 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", + netFile("eth0"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -531,10 +531,10 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).ToNot(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", - "/etc/systemd/network/10_eth1.network", + netFile("eth0"), + netFile("eth1"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -556,7 +556,7 @@ Destination=10.0.1.0/8 Gateway=3.4.5.6 `)) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_eth1.network") + networkConfig = fs.GetFileTestStat(netFile("eth1")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -613,9 +613,9 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).ToNot(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", + netFile("eth0"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -651,7 +651,7 @@ Gateway=3.4.5.6 }, nil, nil) Expect(err).ToNot(HaveOccurred()) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).ToNot(ContainSubstring("UseRoutes=no")) }) @@ -675,9 +675,9 @@ Gateway=3.4.5.6 matches, err := fs.Ls("/etc/systemd/network/") Expect(err).ToNot(HaveOccurred()) - Expect(matches).To(ConsistOf("/etc/systemd/network/10_ethstatic.network")) + Expect(matches).To(ConsistOf(netFile("ethstatic"))) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic.network") + networkConfig := fs.GetFileTestStat(netFile("ethstatic")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -709,9 +709,9 @@ Gateway=3.4.5.6 matches, err := fs.Ls("/etc/systemd/network/") Expect(err).ToNot(HaveOccurred()) - Expect(matches).To(ConsistOf("/etc/systemd/network/10_ethdhcp.network")) + Expect(matches).To(ConsistOf(netFile("ethdhcp"))) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethdhcp.network") + networkConfig := fs.GetFileTestStat(netFile("ethdhcp")) Expect(networkConfig).ToNot(BeNil()) // With no cloud-config dns the global scope is empty, so the DHCP-provided // DNS on this link is the only external path and must stay the DNS default @@ -1024,11 +1024,11 @@ prepend domain-name-servers 8.8.8.8, 9.9.9.9; matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_ethdhcp.network", - "/etc/systemd/network/10_ethstatic.network", + netFile("ethdhcp"), + netFile("ethstatic"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethdhcp.network") + networkConfig := fs.GetFileTestStat(netFile("ethdhcp")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1047,7 +1047,7 @@ UseRoutes=no `)) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_ethstatic.network") + networkConfig = fs.GetFileTestStat(netFile("ethstatic")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1089,7 +1089,7 @@ DNSDefaultRoute=no }, nil, nil) Expect(err).ToNot(HaveOccurred()) - primaryConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethprimary.network") + primaryConfig := fs.GetFileTestStat(netFile("ethprimary")) Expect(primaryConfig).ToNot(BeNil()) Expect(primaryConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1106,7 +1106,7 @@ UseMTU=yes `)) - secondaryConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethsecondary.network") + secondaryConfig := fs.GetFileTestStat(netFile("ethsecondary")) Expect(secondaryConfig).ToNot(BeNil()) Expect(secondaryConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1148,7 +1148,7 @@ UseRoutes=no }, nil, nil) Expect(err).ToNot(HaveOccurred()) - sharedConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethshared.network") + sharedConfig := fs.GetFileTestStat(netFile("ethshared")) Expect(sharedConfig).ToNot(BeNil()) Expect(sharedConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1202,9 +1202,9 @@ UseMTU=yes matches, err := fs.Ls("/etc/systemd/network/") Expect(err).ToNot(HaveOccurred()) - Expect(matches).To(ConsistOf("/etc/systemd/network/10_ethshared.network")) + Expect(matches).To(ConsistOf(netFile("ethshared"))) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethshared.network") + networkConfig := fs.GetFileTestStat(netFile("ethshared")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1267,9 +1267,9 @@ DNSDefaultRoute=no matches, err := fs.Ls("/etc/systemd/network/") Expect(err).ToNot(HaveOccurred()) - Expect(matches).To(ConsistOf("/etc/systemd/network/10_ethshared.network")) + Expect(matches).To(ConsistOf(netFile("ethshared"))) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethshared.network") + networkConfig := fs.GetFileTestStat(netFile("ethshared")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1340,10 +1340,10 @@ Gateway=10.20.0.1 matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_ethstatic.network", + netFile("ethstatic"), )) - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_ethstatic.network") + networkConfig := fs.GetFileTestStat(netFile("ethstatic")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1433,13 +1433,13 @@ nameserver 10.0.80.12 matches, err := fs.Ls("/etc/systemd/network/") Expect(err).NotTo(HaveOccurred()) Expect(matches).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", - "/etc/systemd/network/10_eth1.network", + netFile("eth0"), + netFile("eth1"), )) format.TruncatedDiff = false - networkConfig := fs.GetFileTestStat("/etc/systemd/network/10_eth0.network") + networkConfig := fs.GetFileTestStat(netFile("eth0")) Expect(networkConfig).ToNot(BeNil()) Expect(networkConfig.StringContents()).To(Equal(`# Generated by bosh-agent [Match] @@ -1459,7 +1459,7 @@ DNS=10.0.80.12 DNSDefaultRoute=no `)) - networkConfig = fs.GetFileTestStat("/etc/systemd/network/10_eth1.network") + networkConfig = fs.GetFileTestStat(netFile("eth1")) Expect(networkConfig).ToNot(BeNil()) Expect(scrubMultipleLines(networkConfig.StringContents())).To(Equal(`# Generated by bosh-agent [Match] @@ -1526,7 +1526,7 @@ DNSDefaultRoute=no } Expect(files).To(ConsistOf( - "/etc/systemd/network/10_eth0.network", + netFile("eth0"), "/etc/systemd/network/02_custom_unmanaged.network", "/etc/systemd/network/04_special_unmanaged.network", )) @@ -1545,9 +1545,9 @@ DNSDefaultRoute=no }) It("returns networks that are defined in /etc/network/interfaces", func() { - err := fs.WriteFileString("/etc/systemd/network/10_fake-eth0.network", ``) + err := fs.WriteFileString(netFile("fake-eth0"), ``) Expect(err).NotTo(HaveOccurred()) - err = fs.WriteFileString("/etc/systemd/network/10_fake-eth2.network", ``) + err = fs.WriteFileString(netFile("fake-eth2"), ``) Expect(err).NotTo(HaveOccurred()) interfaces, err := netManager.GetConfiguredNetworkInterfaces() From 4480383267cbdd9c45dfbca7f4d121f189061866 Mon Sep 17 00:00:00 2001 From: Nayden Petrov Date: Mon, 26 Oct 2026 15:19:11 +0200 Subject: [PATCH 2/5] test: assert BOSH .network filename sorts before netplan --- platform/net/ubuntu_net_manager_test.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/platform/net/ubuntu_net_manager_test.go b/platform/net/ubuntu_net_manager_test.go index 1fdb908cc..3c68b9d02 100644 --- a/platform/net/ubuntu_net_manager_test.go +++ b/platform/net/ubuntu_net_manager_test.go @@ -656,6 +656,23 @@ Gateway=3.4.5.6 Expect(networkConfig.StringContents()).ToNot(ContainSubstring("UseRoutes=no")) }) + It("generates a .network filename that sorts before the netplan file", func() { + stubInterfaces(map[string]boshsettings.Network{ + "eth0": dhcpNetwork, + }) + fs.WriteFileString("/run/systemd/network/10-netplan-eth0.network", "") + + err := netManager.SetupNetworking(boshsettings.Networks{ + "dhcp-1": dhcpNetwork, + }, nil, nil) + Expect(err).ToNot(HaveOccurred()) + + boshBase := strings.TrimPrefix(netFile("eth0"), "/etc/systemd/network/") + netplanBase := "10-netplan-eth0.network" + Expect(boshBase < netplanBase).To(BeTrue(), + "BOSH file %q must sort before netplan file %q", boshBase, netplanBase) + }) + It("writes /etc/network/interfaces without dns-namservers if there are no dns servers", func() { staticNetworkWithoutDNS := boshsettings.Network{ Type: "manual", From 09e1b05a3537aa47b2983a9523727e8f81dae28f Mon Sep 17 00:00:00 2001 From: Nayden Petrov Date: Mon, 26 Oct 2026 15:25:38 +0200 Subject: [PATCH 3/5] fix lint: check error from WriteFileString --- platform/net/ubuntu_net_manager_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/net/ubuntu_net_manager_test.go b/platform/net/ubuntu_net_manager_test.go index 3c68b9d02..ffbab61e2 100644 --- a/platform/net/ubuntu_net_manager_test.go +++ b/platform/net/ubuntu_net_manager_test.go @@ -660,7 +660,7 @@ Gateway=3.4.5.6 stubInterfaces(map[string]boshsettings.Network{ "eth0": dhcpNetwork, }) - fs.WriteFileString("/run/systemd/network/10-netplan-eth0.network", "") + Expect(fs.WriteFileString("/run/systemd/network/10-netplan-eth0.network", "")).To(Succeed()) err := netManager.SetupNetworking(boshsettings.Networks{ "dhcp-1": dhcpNetwork, From fbb33908753c061e13715aa3a81af67811182629 Mon Sep 17 00:00:00 2001 From: Ned Petrov Date: Tue, 27 Oct 2026 09:39:15 +0200 Subject: [PATCH 4/5] Update platform/net/ubuntu_net_manager.go Co-authored-by: Ivaylo Ivanov --- platform/net/ubuntu_net_manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/net/ubuntu_net_manager.go b/platform/net/ubuntu_net_manager.go index b8eb32abf..86502a40b 100644 --- a/platform/net/ubuntu_net_manager.go +++ b/platform/net/ubuntu_net_manager.go @@ -350,7 +350,7 @@ func (net UbuntuNetManager) writeDHCPConfiguration(dnsServers []string, opts bos } func interfaceConfigurationFile(name string) string { - interfaceBasename := fmt.Sprintf("10-%s.network", name) + interfaceBasename := fmt.Sprintf("09-%s.network", name) return filepath.Join(systemdNetworkFolder, interfaceBasename) } From 111cb62278c46fb6497766bdff8b0324e9a8b004 Mon Sep 17 00:00:00 2001 From: Nayden Petrov Date: Tue, 27 Oct 2026 09:51:58 +0200 Subject: [PATCH 5/5] Fix expected file name in tests - Clarify why we use 09- for the config file --- platform/net/ubuntu_net_manager.go | 2 ++ platform/net/ubuntu_net_manager_helpers_test.go | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/platform/net/ubuntu_net_manager.go b/platform/net/ubuntu_net_manager.go index 86502a40b..d73536c96 100644 --- a/platform/net/ubuntu_net_manager.go +++ b/platform/net/ubuntu_net_manager.go @@ -349,6 +349,8 @@ func (net UbuntuNetManager) writeDHCPConfiguration(dnsServers []string, opts bos return changed, nil } +// The bosh-agent expects this file to be used for the network configuration +// Using 09- to make sure it takes priority over any other files func interfaceConfigurationFile(name string) string { interfaceBasename := fmt.Sprintf("09-%s.network", name) return filepath.Join(systemdNetworkFolder, interfaceBasename) diff --git a/platform/net/ubuntu_net_manager_helpers_test.go b/platform/net/ubuntu_net_manager_helpers_test.go index 25c8b4556..fa4c8d01d 100644 --- a/platform/net/ubuntu_net_manager_helpers_test.go +++ b/platform/net/ubuntu_net_manager_helpers_test.go @@ -5,5 +5,5 @@ package net_test import "fmt" func netFile(iface string) string { - return fmt.Sprintf("/etc/systemd/network/10-%s.network", iface) + return fmt.Sprintf("/etc/systemd/network/09-%s.network", iface) }