From b574c1ed678a89930b3edf4f244b9531c590ef67 Mon Sep 17 00:00:00 2001 From: Florian Haftmann Date: Tue, 26 Sep 2023 17:10:02 +0200 Subject: [PATCH] Enable mac address retrieved from annotations This addresses issue #1208. Signed-off-by: Florian Haftmann --- api/v1alpha5/conversion.go | 14 ++ api/v1alpha5/zz_generated.conversion.go | 124 +++++++++++++++--- api/v1beta1/metal3datatemplate_types.go | 15 +++ api/v1beta1/zz_generated.deepcopy.go | 20 +++ baremetal/metal3data_manager.go | 84 ++++++++---- baremetal/metal3data_manager_test.go | 95 +++++++++++++- ....cluster.x-k8s.io_metal3datatemplates.yaml | 60 +++++++++ config/default/capm3/manager_image_patch.yaml | 2 +- docs/api.md | 7 +- 9 files changed, 368 insertions(+), 53 deletions(-) diff --git a/api/v1alpha5/conversion.go b/api/v1alpha5/conversion.go index bbe52183d0..d206443b38 100644 --- a/api/v1alpha5/conversion.go +++ b/api/v1alpha5/conversion.go @@ -196,6 +196,15 @@ func (src *Metal3DataTemplate) ConvertTo(dstRaw conversion.Hub) error { } } if dst.Spec.NetworkData != nil && restored.Spec.NetworkData != nil { + for k := range dst.Spec.NetworkData.Links.Ethernets { + dst.Spec.NetworkData.Links.Ethernets[k].MACAddress = restored.Spec.NetworkData.Links.Ethernets[k].MACAddress + } + for k := range dst.Spec.NetworkData.Links.Vlans { + dst.Spec.NetworkData.Links.Vlans[k].MACAddress = restored.Spec.NetworkData.Links.Vlans[k].MACAddress + } + for k := range dst.Spec.NetworkData.Links.Bonds { + dst.Spec.NetworkData.Links.Bonds[k].MACAddress = restored.Spec.NetworkData.Links.Bonds[k].MACAddress + } for k := range dst.Spec.NetworkData.Networks.IPv4 { dst.Spec.NetworkData.Networks.IPv4[k].FromPoolRef = restored.Spec.NetworkData.Networks.IPv4[k].FromPoolRef } @@ -219,6 +228,11 @@ func (dst *Metal3DataTemplate) ConvertFrom(srcRaw conversion.Hub) error { return utilconversion.MarshalData(src, dst) } +func Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(in *v1beta1.NetworkLinkEthernetMac, out *NetworkLinkEthernetMac, s apiconversion.Scope) error { + // fromAnnotation was added with v1betaX. + return autoConvert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(in, out, s) +} + func Convert_v1beta1_NetworkDataIPv6_To_v1alpha5_NetworkDataIPv6(in *v1beta1.NetworkDataIPv6, out *NetworkDataIPv6, s apiconversion.Scope) error { // fromPoolRef was added with v1beta1. return autoConvert_v1beta1_NetworkDataIPv6_To_v1alpha5_NetworkDataIPv6(in, out, s) diff --git a/api/v1alpha5/zz_generated.conversion.go b/api/v1alpha5/zz_generated.conversion.go index 949f2be6d9..4944e2ecb7 100644 --- a/api/v1alpha5/zz_generated.conversion.go +++ b/api/v1alpha5/zz_generated.conversion.go @@ -658,11 +658,6 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } - if err := s.AddGeneratedConversionFunc((*v1beta1.NetworkLinkEthernetMac)(nil), (*NetworkLinkEthernetMac)(nil), func(a, b interface{}, scope conversion.Scope) error { - return Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(a.(*v1beta1.NetworkLinkEthernetMac), b.(*NetworkLinkEthernetMac), scope) - }); err != nil { - return err - } if err := s.AddGeneratedConversionFunc((*RemediationStrategy)(nil), (*v1beta1.RemediationStrategy)(nil), func(a, b interface{}, scope conversion.Scope) error { return Convert_v1alpha5_RemediationStrategy_To_v1beta1_RemediationStrategy(a.(*RemediationStrategy), b.(*v1beta1.RemediationStrategy), scope) }); err != nil { @@ -703,6 +698,11 @@ func RegisterConversions(s *runtime.Scheme) error { }); err != nil { return err } + if err := s.AddConversionFunc((*v1beta1.NetworkLinkEthernetMac)(nil), (*NetworkLinkEthernetMac)(nil), func(a, b interface{}, scope conversion.Scope) error { + return Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(a.(*v1beta1.NetworkLinkEthernetMac), b.(*NetworkLinkEthernetMac), scope) + }); err != nil { + return err + } return nil } @@ -2208,7 +2208,17 @@ func Convert_v1beta1_NetworkDataIPv6DHCP_To_v1alpha5_NetworkDataIPv6DHCP(in *v1b } func autoConvert_v1alpha5_NetworkDataLink_To_v1beta1_NetworkDataLink(in *NetworkDataLink, out *v1beta1.NetworkDataLink, s conversion.Scope) error { - out.Ethernets = *(*[]v1beta1.NetworkDataLinkEthernet)(unsafe.Pointer(&in.Ethernets)) + if in.Ethernets != nil { + in, out := &in.Ethernets, &out.Ethernets + *out = make([]v1beta1.NetworkDataLinkEthernet, len(*in)) + for i := range *in { + if err := Convert_v1alpha5_NetworkDataLinkEthernet_To_v1beta1_NetworkDataLinkEthernet(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Ethernets = nil + } if in.Bonds != nil { in, out := &in.Bonds, &out.Bonds *out = make([]v1beta1.NetworkDataLinkBond, len(*in)) @@ -2220,7 +2230,17 @@ func autoConvert_v1alpha5_NetworkDataLink_To_v1beta1_NetworkDataLink(in *Network } else { out.Bonds = nil } - out.Vlans = *(*[]v1beta1.NetworkDataLinkVlan)(unsafe.Pointer(&in.Vlans)) + if in.Vlans != nil { + in, out := &in.Vlans, &out.Vlans + *out = make([]v1beta1.NetworkDataLinkVlan, len(*in)) + for i := range *in { + if err := Convert_v1alpha5_NetworkDataLinkVlan_To_v1beta1_NetworkDataLinkVlan(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Vlans = nil + } return nil } @@ -2230,7 +2250,17 @@ func Convert_v1alpha5_NetworkDataLink_To_v1beta1_NetworkDataLink(in *NetworkData } func autoConvert_v1beta1_NetworkDataLink_To_v1alpha5_NetworkDataLink(in *v1beta1.NetworkDataLink, out *NetworkDataLink, s conversion.Scope) error { - out.Ethernets = *(*[]NetworkDataLinkEthernet)(unsafe.Pointer(&in.Ethernets)) + if in.Ethernets != nil { + in, out := &in.Ethernets, &out.Ethernets + *out = make([]NetworkDataLinkEthernet, len(*in)) + for i := range *in { + if err := Convert_v1beta1_NetworkDataLinkEthernet_To_v1alpha5_NetworkDataLinkEthernet(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Ethernets = nil + } if in.Bonds != nil { in, out := &in.Bonds, &out.Bonds *out = make([]NetworkDataLinkBond, len(*in)) @@ -2242,7 +2272,17 @@ func autoConvert_v1beta1_NetworkDataLink_To_v1alpha5_NetworkDataLink(in *v1beta1 } else { out.Bonds = nil } - out.Vlans = *(*[]NetworkDataLinkVlan)(unsafe.Pointer(&in.Vlans)) + if in.Vlans != nil { + in, out := &in.Vlans, &out.Vlans + *out = make([]NetworkDataLinkVlan, len(*in)) + for i := range *in { + if err := Convert_v1beta1_NetworkDataLinkVlan_To_v1alpha5_NetworkDataLinkVlan(&(*in)[i], &(*out)[i], s); err != nil { + return err + } + } + } else { + out.Vlans = nil + } return nil } @@ -2255,7 +2295,15 @@ func autoConvert_v1alpha5_NetworkDataLinkBond_To_v1beta1_NetworkDataLinkBond(in out.BondMode = in.BondMode out.Id = in.Id out.MTU = in.MTU - out.MACAddress = (*v1beta1.NetworkLinkEthernetMac)(unsafe.Pointer(in.MACAddress)) + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(v1beta1.NetworkLinkEthernetMac) + if err := Convert_v1alpha5_NetworkLinkEthernetMac_To_v1beta1_NetworkLinkEthernetMac(*in, *out, s); err != nil { + return err + } + } else { + out.MACAddress = nil + } out.BondLinks = *(*[]string)(unsafe.Pointer(&in.BondLinks)) return nil } @@ -2270,7 +2318,15 @@ func autoConvert_v1beta1_NetworkDataLinkBond_To_v1alpha5_NetworkDataLinkBond(in // WARNING: in.BondXmitHashPolicy requires manual conversion: does not exist in peer-type out.Id = in.Id out.MTU = in.MTU - out.MACAddress = (*NetworkLinkEthernetMac)(unsafe.Pointer(in.MACAddress)) + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(NetworkLinkEthernetMac) + if err := Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(*in, *out, s); err != nil { + return err + } + } else { + out.MACAddress = nil + } out.BondLinks = *(*[]string)(unsafe.Pointer(&in.BondLinks)) return nil } @@ -2279,7 +2335,15 @@ func autoConvert_v1alpha5_NetworkDataLinkEthernet_To_v1beta1_NetworkDataLinkEthe out.Type = in.Type out.Id = in.Id out.MTU = in.MTU - out.MACAddress = (*v1beta1.NetworkLinkEthernetMac)(unsafe.Pointer(in.MACAddress)) + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(v1beta1.NetworkLinkEthernetMac) + if err := Convert_v1alpha5_NetworkLinkEthernetMac_To_v1beta1_NetworkLinkEthernetMac(*in, *out, s); err != nil { + return err + } + } else { + out.MACAddress = nil + } return nil } @@ -2292,7 +2356,15 @@ func autoConvert_v1beta1_NetworkDataLinkEthernet_To_v1alpha5_NetworkDataLinkEthe out.Type = in.Type out.Id = in.Id out.MTU = in.MTU - out.MACAddress = (*NetworkLinkEthernetMac)(unsafe.Pointer(in.MACAddress)) + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(NetworkLinkEthernetMac) + if err := Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(*in, *out, s); err != nil { + return err + } + } else { + out.MACAddress = nil + } return nil } @@ -2305,7 +2377,15 @@ func autoConvert_v1alpha5_NetworkDataLinkVlan_To_v1beta1_NetworkDataLinkVlan(in out.VlanID = in.VlanID out.Id = in.Id out.MTU = in.MTU - out.MACAddress = (*v1beta1.NetworkLinkEthernetMac)(unsafe.Pointer(in.MACAddress)) + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(v1beta1.NetworkLinkEthernetMac) + if err := Convert_v1alpha5_NetworkLinkEthernetMac_To_v1beta1_NetworkLinkEthernetMac(*in, *out, s); err != nil { + return err + } + } else { + out.MACAddress = nil + } out.VlanLink = in.VlanLink return nil } @@ -2319,7 +2399,15 @@ func autoConvert_v1beta1_NetworkDataLinkVlan_To_v1alpha5_NetworkDataLinkVlan(in out.VlanID = in.VlanID out.Id = in.Id out.MTU = in.MTU - out.MACAddress = (*NetworkLinkEthernetMac)(unsafe.Pointer(in.MACAddress)) + if in.MACAddress != nil { + in, out := &in.MACAddress, &out.MACAddress + *out = new(NetworkLinkEthernetMac) + if err := Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(*in, *out, s); err != nil { + return err + } + } else { + out.MACAddress = nil + } out.VlanLink = in.VlanLink return nil } @@ -2589,14 +2677,10 @@ func Convert_v1alpha5_NetworkLinkEthernetMac_To_v1beta1_NetworkLinkEthernetMac(i func autoConvert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(in *v1beta1.NetworkLinkEthernetMac, out *NetworkLinkEthernetMac, s conversion.Scope) error { out.String = (*string)(unsafe.Pointer(in.String)) out.FromHostInterface = (*string)(unsafe.Pointer(in.FromHostInterface)) + // WARNING: in.FromAnnotation requires manual conversion: does not exist in peer-type return nil } -// Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac is an autogenerated conversion function. -func Convert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(in *v1beta1.NetworkLinkEthernetMac, out *NetworkLinkEthernetMac, s conversion.Scope) error { - return autoConvert_v1beta1_NetworkLinkEthernetMac_To_v1alpha5_NetworkLinkEthernetMac(in, out, s) -} - func autoConvert_v1alpha5_RemediationStrategy_To_v1beta1_RemediationStrategy(in *RemediationStrategy, out *v1beta1.RemediationStrategy, s conversion.Scope) error { out.Type = v1beta1.RemediationType(in.Type) out.RetryLimit = in.RetryLimit diff --git a/api/v1beta1/metal3datatemplate_types.go b/api/v1beta1/metal3datatemplate_types.go index efd4081896..0f4801d1db 100644 --- a/api/v1beta1/metal3datatemplate_types.go +++ b/api/v1beta1/metal3datatemplate_types.go @@ -191,6 +191,16 @@ type MetaData struct { FromAnnotations []MetaDataFromAnnotation `json:"fromAnnotations,omitempty"` } +// NetworkLinkEthernetMacFromAnnotation contains the information to fetch an annotation +// content, if the label does not exist, it is rendered as empty string. +type NetworkLinkEthernetMacFromAnnotation struct { + // +kubebuilder:validation:Enum=machine;metal3machine;baremetalhost + // Object is the type of the object from which we retrieve the name + Object string `json:"object"` + // Annotation is the key of the Annotation to fetch + Annotation string `json:"annotation"` +} + // NetworkLinkEthernetMac represents the Mac address content. type NetworkLinkEthernetMac struct { // String contains the MAC address given as a string @@ -201,6 +211,11 @@ type NetworkLinkEthernetMac struct { // Introspection details from which to fetch the MAC address // +optional FromHostInterface *string `json:"fromHostInterface,omitempty"` + + // FromAnnotation references an object Annotation to retrieve the + // MAC address from + // +optional + FromAnnotation *NetworkLinkEthernetMacFromAnnotation `json:"fromAnnotation,omitempty"` } // NetworkDataLinkEthernet represents an ethernet link object. diff --git a/api/v1beta1/zz_generated.deepcopy.go b/api/v1beta1/zz_generated.deepcopy.go index bf100ce784..6e7a2a362b 100644 --- a/api/v1beta1/zz_generated.deepcopy.go +++ b/api/v1beta1/zz_generated.deepcopy.go @@ -1667,6 +1667,11 @@ func (in *NetworkLinkEthernetMac) DeepCopyInto(out *NetworkLinkEthernetMac) { *out = new(string) **out = **in } + if in.FromAnnotation != nil { + in, out := &in.FromAnnotation, &out.FromAnnotation + *out = new(NetworkLinkEthernetMacFromAnnotation) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkLinkEthernetMac. @@ -1679,6 +1684,21 @@ func (in *NetworkLinkEthernetMac) DeepCopy() *NetworkLinkEthernetMac { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *NetworkLinkEthernetMacFromAnnotation) DeepCopyInto(out *NetworkLinkEthernetMacFromAnnotation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NetworkLinkEthernetMacFromAnnotation. +func (in *NetworkLinkEthernetMacFromAnnotation) DeepCopy() *NetworkLinkEthernetMacFromAnnotation { + if in == nil { + return nil + } + out := new(NetworkLinkEthernetMacFromAnnotation) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RemediationStrategy) DeepCopyInto(out *RemediationStrategy) { *out = *in diff --git a/baremetal/metal3data_manager.go b/baremetal/metal3data_manager.go index b50be971ea..23afba9c58 100644 --- a/baremetal/metal3data_manager.go +++ b/baremetal/metal3data_manager.go @@ -275,7 +275,7 @@ func (m *DataManager) createSecrets(ctx context.Context) error { // The NetworkData secret must be created if apierrors.IsNotFound(networkDataErr) { m.Log.Info("Creating Networkdata secret") - networkData, err := renderNetworkData(m3dt, bmh, poolAddresses) + networkData, err := renderNetworkData(m.Data, m3dt, m3m, capiMachine, bmh, poolAddresses) if err != nil { return err } @@ -922,8 +922,9 @@ func (m *DataManager) releaseAddressFromPool(ctx context.Context, poolRef corev1 // renderNetworkData renders the networkData into an object that will be // marshalled into the secret. -func renderNetworkData(m3dt *infrav1.Metal3DataTemplate, - bmh *bmov1alpha1.BareMetalHost, poolAddresses map[string]addressFromPool, +func renderNetworkData(m3d *infrav1.Metal3Data, m3dt *infrav1.Metal3DataTemplate, + m3m *infrav1.Metal3Machine, machine *clusterv1.Machine, bmh *bmov1alpha1.BareMetalHost, + poolAddresses map[string]addressFromPool, ) ([]byte, error) { if m3dt.Spec.NetworkData == nil { return nil, nil @@ -932,7 +933,7 @@ func renderNetworkData(m3dt *infrav1.Metal3DataTemplate, networkData := map[string][]interface{}{} - networkData["links"], err = renderNetworkLinks(m3dt.Spec.NetworkData.Links, bmh) + networkData["links"], err = renderNetworkLinks(m3dt.Spec.NetworkData.Links, m3m, machine, bmh) if err != nil { return nil, err } @@ -978,12 +979,13 @@ func renderNetworkServices(services infrav1.NetworkDataService, poolAddresses ma } // renderNetworkLinks renders the different types of links. -func renderNetworkLinks(networkLinks infrav1.NetworkDataLink, bmh *bmov1alpha1.BareMetalHost) ([]interface{}, error) { +func renderNetworkLinks(networkLinks infrav1.NetworkDataLink, + m3m *infrav1.Metal3Machine, machine *clusterv1.Machine, bmh *bmov1alpha1.BareMetalHost) ([]interface{}, error) { data := []interface{}{} // Bond links for _, link := range networkLinks.Bonds { - macAddress, err := getLinkMacAddress(link.MACAddress, bmh) + macAddress, err := getLinkMacAddress(link.MACAddress, m3m, machine, bmh) if err != nil { return nil, err } @@ -1000,7 +1002,7 @@ func renderNetworkLinks(networkLinks infrav1.NetworkDataLink, bmh *bmov1alpha1.B // Ethernet links for _, link := range networkLinks.Ethernets { - macAddress, err := getLinkMacAddress(link.MACAddress, bmh) + macAddress, err := getLinkMacAddress(link.MACAddress, m3m, machine, bmh) if err != nil { return nil, err } @@ -1014,7 +1016,7 @@ func renderNetworkLinks(networkLinks infrav1.NetworkDataLink, bmh *bmov1alpha1.B // Vlan links for _, link := range networkLinks.Vlans { - macAddress, err := getLinkMacAddress(link.MACAddress, bmh) + macAddress, err := getLinkMacAddress(link.MACAddress, m3m, machine, bmh) if err != nil { return nil, err } @@ -1032,7 +1034,8 @@ func renderNetworkLinks(networkLinks infrav1.NetworkDataLink, bmh *bmov1alpha1.B } // renderNetworkNetworks renders the different types of network. -func renderNetworkNetworks(networks infrav1.NetworkDataNetwork, poolAddresses map[string]addressFromPool, +func renderNetworkNetworks(networks infrav1.NetworkDataNetwork, + poolAddresses map[string]addressFromPool, ) ([]interface{}, error) { data := []interface{}{} @@ -1232,22 +1235,27 @@ func translateMask(maskInt int, ipv4 bool) interface{} { } // getLinkMacAddress returns the mac address. -func getLinkMacAddress(mac *infrav1.NetworkLinkEthernetMac, bmh *bmov1alpha1.BareMetalHost) ( +func getLinkMacAddress(mac *infrav1.NetworkLinkEthernetMac, + m3m *infrav1.Metal3Machine, machine *clusterv1.Machine, bmh *bmov1alpha1.BareMetalHost) ( string, error, ) { - macAddress := "" - var err error - // if a string was given if mac.String != nil { - macAddress = *mac.String + return *mac.String, nil + } - // Otherwise fetch the mac from the interface name - } else if mac.FromHostInterface != nil { - macAddress, err = getBMHMacByName(*mac.FromHostInterface, bmh) + // if a host interface is given + if mac.FromHostInterface != nil { + return getBMHMacByName(*mac.FromHostInterface, bmh) } - return macAddress, err + // if an annotation reference is given + if mac.FromAnnotation != nil { + return getValueFromAnnotationStrict(mac.FromAnnotation.Object, + mac.FromAnnotation.Annotation, m3m, machine, bmh) + } + + return "", errors.New("no MAC address given") } // renderMetaData renders the MetaData items. @@ -1339,16 +1347,12 @@ func renderMetaData(m3d *infrav1.Metal3Data, m3dt *infrav1.Metal3DataTemplate, // Annotations for _, entry := range m3dt.Spec.MetaData.FromAnnotations { - switch strings.ToLower(entry.Object) { - case m3machine: - metadata[entry.Key] = m3m.Annotations[entry.Annotation] - case capimachine: - metadata[entry.Key] = machine.Annotations[entry.Annotation] - case host: - metadata[entry.Key] = bmh.Annotations[entry.Annotation] - default: - return nil, errors.New("Unknown object type") + value, err := getValueFromAnnotation(entry.Object, + entry.Annotation, m3m, machine, bmh) + if err != nil { + return nil, err } + metadata[entry.Key] = value } // Strings @@ -1373,6 +1377,32 @@ func getBMHMacByName(name string, bmh *bmov1alpha1.BareMetalHost) (string, error return "", fmt.Errorf("nic name not found %v", name) } +func getValueFromAnnotation(object string, annotation string, + m3m *infrav1.Metal3Machine, machine *clusterv1.Machine, bmh *bmov1alpha1.BareMetalHost) (string, error) { + switch strings.ToLower(object) { + case m3machine: + return m3m.Annotations[annotation], nil + case capimachine: + return machine.Annotations[annotation], nil + case host: + return bmh.Annotations[annotation], nil + default: + return "", errors.New("Unknown object type") + } +} + +func getValueFromAnnotationStrict(object string, annotation string, + m3m *infrav1.Metal3Machine, machine *clusterv1.Machine, bmh *bmov1alpha1.BareMetalHost) (string, error) { + value, err := getValueFromAnnotation(object, annotation, m3m, machine, bmh) + if err != nil { + return value, err + } + if value == "" { + return "", fmt.Errorf("no annotation %s on object %s", object, annotation) + } + return value, err +} + func (m *DataManager) getM3Machine(ctx context.Context, m3dt *infrav1.Metal3DataTemplate) (*infrav1.Metal3Machine, error) { if m.Data.Spec.Claim.Name == "" { return nil, errors.New("Claim name not set") diff --git a/baremetal/metal3data_manager_test.go b/baremetal/metal3data_manager_test.go index 1edd92517c..0e10d1b3f2 100644 --- a/baremetal/metal3data_manager_test.go +++ b/baremetal/metal3data_manager_test.go @@ -2119,6 +2119,8 @@ var _ = Describe("Metal3Data manager", func() { type testCaseRenderNetworkData struct { m3d *infrav1.Metal3Data m3dt *infrav1.Metal3DataTemplate + m3m *infrav1.Metal3Machine + machine *clusterv1.Machine bmh *bmov1alpha1.BareMetalHost poolAddresses map[string]addressFromPool expectError bool @@ -2127,7 +2129,7 @@ var _ = Describe("Metal3Data manager", func() { DescribeTable("Test renderNetworkData", func(tc testCaseRenderNetworkData) { - result, err := renderNetworkData(tc.m3dt, tc.bmh, tc.poolAddresses) + result, err := renderNetworkData(tc.m3d, tc.m3dt, tc.m3m, tc.machine, tc.bmh, tc.poolAddresses) if tc.expectError { Expect(err).To(HaveOccurred()) return @@ -2362,6 +2364,8 @@ var _ = Describe("Metal3Data manager", func() { ) type testCaseRenderNetworkLinks struct { links infrav1.NetworkDataLink + m3m *infrav1.Metal3Machine + machine *clusterv1.Machine bmh *bmov1alpha1.BareMetalHost expectError bool expectedOutput []interface{} @@ -2369,7 +2373,7 @@ var _ = Describe("Metal3Data manager", func() { DescribeTable("Test renderNetworkLinks", func(tc testCaseRenderNetworkLinks) { - result, err := renderNetworkLinks(tc.links, tc.bmh) + result, err := renderNetworkLinks(tc.links, tc.m3m, tc.machine, tc.bmh) if tc.expectError { Expect(err).To(HaveOccurred()) return @@ -2991,6 +2995,8 @@ var _ = Describe("Metal3Data manager", func() { type testCaseGetLinkMacAddress struct { mac *infrav1.NetworkLinkEthernetMac + m3m *infrav1.Metal3Machine + machine *clusterv1.Machine bmh *bmov1alpha1.BareMetalHost expectError bool expectedMAC string @@ -2998,7 +3004,7 @@ var _ = Describe("Metal3Data manager", func() { DescribeTable("Test getLinkMacAddress", func(tc testCaseGetLinkMacAddress) { - result, err := getLinkMacAddress(tc.mac, tc.bmh) + result, err := getLinkMacAddress(tc.mac, tc.m3m, tc.machine, tc.bmh) if tc.expectError { Expect(err).To(HaveOccurred()) return @@ -3006,7 +3012,7 @@ var _ = Describe("Metal3Data manager", func() { Expect(err).NotTo(HaveOccurred()) Expect(result).To(Equal(tc.expectedMAC)) }, - Entry("String", testCaseGetLinkMacAddress{ + Entry("string", testCaseGetLinkMacAddress{ mac: &infrav1.NetworkLinkEthernetMac{ String: pointer.String("XX:XX:XX:XX:XX:XX"), }, @@ -3062,6 +3068,87 @@ var _ = Describe("Metal3Data manager", func() { }, expectError: true, }), + Entry("from machine annotation", testCaseGetLinkMacAddress{ + mac: &infrav1.NetworkLinkEthernetMac{ + FromAnnotation: &infrav1.NetworkLinkEthernetMacFromAnnotation{ + Object: "machine", + Annotation: "mac-address", + }, + }, + machine: &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: machineName, + Annotations: map[string]string{ + "mac-address": "XX:XX:XX:XX:XX:XX", + }, + }, + }, + expectedMAC: "XX:XX:XX:XX:XX:XX", + }), + Entry("from metal3machine annotation", testCaseGetLinkMacAddress{ + mac: &infrav1.NetworkLinkEthernetMac{ + FromAnnotation: &infrav1.NetworkLinkEthernetMacFromAnnotation{ + Object: "metal3machine", + Annotation: "mac-address", + }, + }, + m3m: &infrav1.Metal3Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: metal3machineName, + Namespace: namespaceName, + UID: m3muid, + Annotations: map[string]string{ + "mac-address": "XX:XX:XX:XX:XX:XX", + }, + }, + }, + expectedMAC: "XX:XX:XX:XX:XX:XX", + }), + Entry("from baremetalhost annotation", testCaseGetLinkMacAddress{ + mac: &infrav1.NetworkLinkEthernetMac{ + FromAnnotation: &infrav1.NetworkLinkEthernetMacFromAnnotation{ + Object: "baremetalhost", + Annotation: "mac-address", + }, + }, + bmh: &bmov1alpha1.BareMetalHost{ + ObjectMeta: metav1.ObjectMeta{ + Name: baremetalhostName, + Namespace: namespaceName, + UID: "", + Annotations: map[string]string{ + "mac-address": "XX:XX:XX:XX:XX:XX", + }, + }, + }, + expectedMAC: "XX:XX:XX:XX:XX:XX", + }), + Entry("from annotation on unknown object", testCaseGetLinkMacAddress{ + mac: &infrav1.NetworkLinkEthernetMac{ + FromAnnotation: &infrav1.NetworkLinkEthernetMacFromAnnotation{ + Object: "wrflbrmpfd", + Annotation: "mac-address", + }, + }, + expectError: true, + }), + Entry("from unknown annotation", testCaseGetLinkMacAddress{ + mac: &infrav1.NetworkLinkEthernetMac{ + FromAnnotation: &infrav1.NetworkLinkEthernetMacFromAnnotation{ + Object: "machine", + Annotation: "wrflbrmpfd", + }, + }, + machine: &clusterv1.Machine{ + ObjectMeta: metav1.ObjectMeta{ + Name: machineName, + Annotations: map[string]string{ + "mac-address": "XX:XX:XX:XX:XX:XX", + }, + }, + }, + expectError: true, + }), ) type testCaseRenderMetaData struct { diff --git a/config/crd/bases/infrastructure.cluster.x-k8s.io_metal3datatemplates.yaml b/config/crd/bases/infrastructure.cluster.x-k8s.io_metal3datatemplates.yaml index 5967075e69..0dad2c0c84 100644 --- a/config/crd/bases/infrastructure.cluster.x-k8s.io_metal3datatemplates.yaml +++ b/config/crd/bases/infrastructure.cluster.x-k8s.io_metal3datatemplates.yaml @@ -1212,6 +1212,26 @@ spec: description: MACAddress is the MAC address of the interface, containing the object used to render it. properties: + fromAnnotation: + description: FromAnnotation references an object + Annotation to retrieve the MAC address from + properties: + annotation: + description: Annotation is the key of the Annotation + to fetch + type: string + object: + description: Object is the type of the object + from which we retrieve the name + enum: + - machine + - metal3machine + - baremetalhost + type: string + required: + - annotation + - object + type: object fromHostInterface: description: FromHostInterface contains the name of the interface in the BareMetalHost Introspection @@ -1247,6 +1267,26 @@ spec: description: MACAddress is the MAC address of the interface, containing the object used to render it. properties: + fromAnnotation: + description: FromAnnotation references an object + Annotation to retrieve the MAC address from + properties: + annotation: + description: Annotation is the key of the Annotation + to fetch + type: string + object: + description: Object is the type of the object + from which we retrieve the name + enum: + - machine + - metal3machine + - baremetalhost + type: string + required: + - annotation + - object + type: object fromHostInterface: description: FromHostInterface contains the name of the interface in the BareMetalHost Introspection @@ -1297,6 +1337,26 @@ spec: description: MACAddress is the MAC address of the interface, containing the object used to render it. properties: + fromAnnotation: + description: FromAnnotation references an object + Annotation to retrieve the MAC address from + properties: + annotation: + description: Annotation is the key of the Annotation + to fetch + type: string + object: + description: Object is the type of the object + from which we retrieve the name + enum: + - machine + - metal3machine + - baremetalhost + type: string + required: + - annotation + - object + type: object fromHostInterface: description: FromHostInterface contains the name of the interface in the BareMetalHost Introspection diff --git a/config/default/capm3/manager_image_patch.yaml b/config/default/capm3/manager_image_patch.yaml index 360fa70b17..e6c0c9ecd3 100644 --- a/config/default/capm3/manager_image_patch.yaml +++ b/config/default/capm3/manager_image_patch.yaml @@ -8,5 +8,5 @@ spec: spec: containers: # Change the value of image field below to your controller image URL - - image: quay.io/metal3-io/cluster-api-provider-metal3:main + - image: quay.io/metal3-io/cluster-api-provider-metal3 name: manager diff --git a/docs/api.md b/docs/api.md index 308ec0c72f..f074deeef4 100644 --- a/docs/api.md +++ b/docs/api.md @@ -630,7 +630,9 @@ spec: id: "enp1s0" mtu: 1500 macAddress: - fromHostInterface: "eth0" + fromAnnotation: + object: machine + annotation: primary-mac - type: "phy" id: "enp2s0" mtu: 1500 @@ -791,6 +793,9 @@ The **links/ethernets/type** can be one of : The **links/ethernets/macAddress** object can be one of: - **string**: with the desired Mac given as a string +- **fromAnnotation**: with the desired Mac retrieved from an annotation. It + takes an `object` attribute to specify the type of the object where to fetch + the annotation, and an `annotation` attribute that contains the annotation key. - **fromHostInterface**: with the interface name from BareMetalHost hardware details.