@@ -22,13 +22,15 @@ import (
2222 . "github.com/onsi/ginkgo/v2" //revive:disable:dot-imports
2323 . "github.com/onsi/gomega" //revive:disable:dot-imports
2424 baremetalv1 "github.com/openstack-k8s-operators/openstack-baremetal-operator/api/v1beta1"
25+ corev1 "k8s.io/api/core/v1"
2526 k8s_errors "k8s.io/apimachinery/pkg/api/errors"
2627
2728 //revive:disable-next-line:dot-imports
2829 "github.com/openstack-k8s-operators/lib-common/modules/common/condition"
2930 . "github.com/openstack-k8s-operators/lib-common/modules/common/test/helpers"
30- corev1 "k8s.io/api/core/v1 "
31+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured "
3132 "k8s.io/apimachinery/pkg/types"
33+ "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
3234)
3335
3436var _ = Describe ("BaremetalSet Test" , func () {
@@ -267,8 +269,8 @@ var _ = Describe("BaremetalSet Test", func() {
267269 When ("BMH provisioned with generated userdata and networkdata" , func () {
268270 BeforeEach (func () {
269271 DeferCleanup (th .DeleteInstance , CreateBaremetalHost (bmhName ))
270- bmh := GetBaremetalHost (bmhName )
271272 Eventually (func (g Gomega ) {
273+ bmh := GetBaremetalHost (bmhName )
272274 bmh .Status .Provisioning .State = metal3v1 .StateAvailable
273275 g .Expect (th .K8sClient .Status ().Update (th .Ctx , bmh )).To (Succeed ())
274276 }, th .Timeout , th .Interval ).Should (Succeed ())
@@ -327,6 +329,11 @@ var _ = Describe("BaremetalSet Test", func() {
327329 })
328330 Expect (networkDataSecret .Data ).To (HaveKey ("networkData" ))
329331 networkData := string (networkDataSecret .Data ["networkData" ])
332+ // Verify proper YAML formatting - links: should be on its own line
333+ Expect (networkData ).To (MatchRegexp ("(?m)^links:\n " ))
334+ Expect (networkData ).To (MatchRegexp ("(?m)^networks:\n " ))
335+ Expect (networkData ).NotTo (ContainSubstring ("links:- " ))
336+ Expect (networkData ).NotTo (ContainSubstring ("networks:- " ))
330337 Expect (networkData ).To (ContainSubstring ("links:" ))
331338 Expect (networkData ).To (ContainSubstring ("name: eth0" ))
332339 Expect (networkData ).To (ContainSubstring ("ip_address: 10.0.0.1" ))
@@ -454,6 +461,9 @@ var _ = Describe("BaremetalSet Test", func() {
454461 Namespace : bmhName .Namespace ,
455462 })
456463 networkData := string (networkDataSecret .Data ["networkData" ])
464+ // Verify proper YAML formatting for routes
465+ Expect (networkData ).To (MatchRegexp ("(?m)^ routes:\n " ))
466+ Expect (networkData ).NotTo (ContainSubstring ("routes: - network:" ))
457467 Expect (networkData ).To (ContainSubstring ("gateway: 10.0.0.254" ))
458468 Expect (networkData ).To (ContainSubstring ("routes:" ))
459469 })
@@ -472,6 +482,11 @@ var _ = Describe("BaremetalSet Test", func() {
472482 Namespace : bmhName .Namespace ,
473483 })
474484 networkData := string (networkDataSecret .Data ["networkData" ])
485+ // Verify proper YAML formatting for DNS sections
486+ Expect (networkData ).To (MatchRegexp ("(?m)^ dns_nameservers:\n " ))
487+ Expect (networkData ).NotTo (ContainSubstring ("dns_nameservers: - " ))
488+ Expect (networkData ).To (MatchRegexp ("(?m)^ dns_search:\n " ))
489+ Expect (networkData ).NotTo (ContainSubstring ("dns_search: - " ))
475490 Expect (networkData ).To (ContainSubstring ("dns_nameservers:" ))
476491 Expect (networkData ).To (ContainSubstring ("8.8.8.8" ))
477492 Expect (networkData ).To (ContainSubstring ("8.8.4.4" ))
@@ -947,4 +962,150 @@ var _ = Describe("BaremetalSet Test", func() {
947962 )
948963 })
949964 })
965+
966+ When ("A BaremetalSet with bonding configuration generates network data" , func () {
967+ BeforeEach (func () {
968+ DeferCleanup (th .DeleteInstance , CreateBaremetalHost (bmhName ))
969+ bmh := GetBaremetalHost (bmhName )
970+ Eventually (func (g Gomega ) {
971+ bmh .Status .Provisioning .State = metal3v1 .StateAvailable
972+ g .Expect (th .K8sClient .Status ().Update (th .Ctx , bmh )).To (Succeed ())
973+ }, th .Timeout , th .Interval ).Should (Succeed ())
974+
975+ DeferCleanup (th .DeleteInstance , CreateSSHSecret (deploymentSecretName ))
976+ })
977+
978+ // Helper to patch provision server to be ready
979+ patchProvisionServerReady := func () {
980+ provServerName := types.NamespacedName {
981+ Name : strings .Join ([]string {baremetalSetName .Name , "provisionserver" }, "-" ),
982+ Namespace : namespace ,
983+ }
984+
985+ // Wait for provision server to be created by the controller
986+ // and patch it to be ready
987+ Eventually (func (g Gomega ) {
988+ provServer := & baremetalv1.OpenStackProvisionServer {}
989+ g .Expect (th .K8sClient .Get (th .Ctx , provServerName , provServer )).To (Succeed ())
990+ provServer .Status .ProvisionIP = "192.168.122.100"
991+ provServer .Status .LocalImageURL = "http://192.168.122.100:6190/images/edpm-hardened-uefi.qcow2"
992+ provServer .Status .LocalImageChecksumURL = "http://192.168.122.100:6190/images/edpm-hardened-uefi.qcow2.sha256sum"
993+ provServer .Status .ReadyCount = 1
994+
995+ g .Expect (th .K8sClient .Status ().Update (th .Ctx , provServer )).To (Succeed ())
996+ }, th .Timeout , th .Interval ).Should (Succeed ())
997+ }
998+
999+ It ("Should generate networkdata secret with bonding configuration" , func () {
1000+ bondSpec := DefaultBaremetalSetSpec (bmhName , true ) // Need provision server
1001+ bondSpec ["ctlplaneInterface" ] = "bond0"
1002+ bondSpec ["ctlplaneBond" ] = map [string ]any {
1003+ "bondInterfaces" : []string {"eno1" , "eno2" },
1004+ "bondMode" : "active-backup" ,
1005+ }
1006+ DeferCleanup (th .DeleteInstance , CreateBaremetalSet (baremetalSetName , bondSpec ))
1007+
1008+ // Verify default was applied
1009+ baremetalSetInstance := GetBaremetalSet (baremetalSetName )
1010+ Expect (baremetalSetInstance .Spec .CtlplaneBond .BondMode ).Should (Equal ("active-backup" ))
1011+
1012+ //patch provision server to be ready
1013+ patchProvisionServerReady ()
1014+
1015+ // Wait for network data secret with bonding config
1016+ Eventually (func (g Gomega ) {
1017+ secretName := types.NamespacedName {
1018+ Name : strings .Join ([]string {baremetalSetName .Name , "cloudinit-networkdata" , "compute-0" }, "-" ),
1019+ Namespace : namespace ,
1020+ }
1021+ secret := & corev1.Secret {}
1022+ g .Expect (th .K8sClient .Get (th .Ctx , secretName , secret )).To (Succeed ())
1023+
1024+ networkData := string (secret .Data ["networkData" ])
1025+ // Verify proper YAML formatting
1026+ g .Expect (networkData ).To (MatchRegexp ("(?m)^links:\n " ))
1027+ g .Expect (networkData ).NotTo (ContainSubstring ("links:- " ))
1028+ g .Expect (networkData ).To (MatchRegexp ("(?m)^ bond_interfaces:\n " ))
1029+ g .Expect (networkData ).NotTo (ContainSubstring ("bond_interfaces: - " ))
1030+ g .Expect (networkData ).To (MatchRegexp ("bond_mode: [^\n ]+\n " ))
1031+ g .Expect (networkData ).NotTo (MatchRegexp ("bond_mode: [^\n ]+ params:" ))
1032+ g .Expect (networkData ).Should (ContainSubstring ("type: bond" ))
1033+ g .Expect (networkData ).Should (ContainSubstring ("bond_mode: active-backup" ))
1034+ g .Expect (networkData ).Should (ContainSubstring ("eno1" ))
1035+ g .Expect (networkData ).Should (ContainSubstring ("eno2" ))
1036+ }, th .Timeout , th .Interval ).Should (Succeed ())
1037+ })
1038+
1039+ It ("Should reject bonding with less than 2 interfaces" , func () {
1040+ bondSpec := DefaultBaremetalSetSpec (bmhName , true )
1041+ bondSpec ["ctlplaneInterface" ] = "bond0"
1042+ bondSpec ["ctlplaneBond" ] = map [string ]any {
1043+ "bondInterfaces" : []string {"eno1" }, // Only one interface - should fail
1044+ "bondMode" : "active-backup" ,
1045+ }
1046+
1047+ object := DefaultBaremetalSetTemplate (baremetalSetName , bondSpec )
1048+ unstructuredObj := & unstructured.Unstructured {Object : object }
1049+ _ , err := controllerutil .CreateOrPatch (
1050+ th .Ctx , th .K8sClient , unstructuredObj , func () error { return nil })
1051+ Expect (err ).Should (HaveOccurred ())
1052+ Expect (err .Error ()).Should (ContainSubstring ("bondInterfaces" ))
1053+ })
1054+
1055+ It ("Should generate networkdata without bonding (backward compatibility)" , func () {
1056+ noBondSpec := DefaultBaremetalSetSpec (bmhName , true )
1057+ noBondSpec ["ctlplaneInterface" ] = "eth0"
1058+
1059+ DeferCleanup (th .DeleteInstance , CreateBaremetalSet (baremetalSetName , noBondSpec ))
1060+
1061+ // Verify provision server to be ready
1062+ patchProvisionServerReady ()
1063+
1064+ // Wait for network data secret without bonding
1065+ Eventually (func (g Gomega ) {
1066+ secretName := types.NamespacedName {
1067+ Name : strings .Join ([]string {baremetalSetName .Name , "cloudinit-networkdata" , "compute-0" }, "-" ),
1068+ Namespace : namespace ,
1069+ }
1070+ secret := & corev1.Secret {}
1071+ g .Expect (th .K8sClient .Get (th .Ctx , secretName , secret )).To (Succeed ())
1072+
1073+ networkData := string (secret .Data ["networkData" ])
1074+ g .Expect (networkData ).ShouldNot (ContainSubstring ("type: bond" ))
1075+ g .Expect (networkData ).Should (ContainSubstring ("type: vif" ))
1076+ }, th .Timeout , th .Interval ).Should (Succeed ())
1077+ })
1078+
1079+ It ("Should generate networkdata with bonding and VLAN" , func () {
1080+ bondVlanSpec := DefaultBaremetalSetSpec (bmhName , true )
1081+ bondVlanSpec ["ctlplaneInterface" ] = "bond0"
1082+ bondVlanSpec ["ctlplaneVlan" ] = 100
1083+ bondVlanSpec ["ctlplaneBond" ] = map [string ]any {
1084+ "bondInterfaces" : []string {"eno1" , "eno2" },
1085+ "bondMode" : "802.3ad" ,
1086+ }
1087+
1088+ DeferCleanup (th .DeleteInstance , CreateBaremetalSet (baremetalSetName , bondVlanSpec ))
1089+
1090+ // Verify provision server to be ready
1091+ patchProvisionServerReady ()
1092+
1093+ // Wait for network data secret with bonding + VLAN
1094+ Eventually (func (g Gomega ) {
1095+ secretName := types.NamespacedName {
1096+ Name : strings .Join ([]string {baremetalSetName .Name , "cloudinit-networkdata" , "compute-0" }, "-" ),
1097+ Namespace : namespace ,
1098+ }
1099+ secret := & corev1.Secret {}
1100+ g .Expect (th .K8sClient .Get (th .Ctx , secretName , secret )).To (Succeed ())
1101+
1102+ networkData := string (secret .Data ["networkData" ])
1103+ g .Expect (networkData ).Should (ContainSubstring ("type: bond" ))
1104+ g .Expect (networkData ).Should (ContainSubstring ("bond_mode: 802.3ad" ))
1105+ g .Expect (networkData ).Should (ContainSubstring ("type: vlan" ))
1106+ g .Expect (networkData ).Should (ContainSubstring ("vlan_id: 100" ))
1107+ g .Expect (networkData ).Should (ContainSubstring ("bond0.100" ))
1108+ }, th .Timeout , th .Interval ).Should (Succeed ())
1109+ })
1110+ })
9501111})
0 commit comments