@@ -19,6 +19,7 @@ package hostclaim
1919import (
2020 "context"
2121 "crypto/rand"
22+ "encoding/json"
2223 "errors"
2324 "math/big"
2425 "regexp"
@@ -200,6 +201,11 @@ func (m *HostManager) Update(ctx context.Context) error {
200201 if syncReboot (m .HostClaim .Annotations , bmh .Annotations ) {
201202 updated = true
202203 }
204+ if upd , err2 := m .syncDetached (ctx , bmh .Namespace , m .HostClaim .Annotations , bmh .Annotations ); err2 != nil {
205+ return err2
206+ } else if upd {
207+ updated = true
208+ }
203209 if updated {
204210 m .Log .Info ("Update the BareMetalHost spec: changes detected." )
205211 err = m .client .Update (ctx , bmh )
@@ -535,56 +541,59 @@ func (m *HostManager) acceptableNamespaces(ctx context.Context, namespace string
535541 nsLabels = map [string ]string {}
536542 }
537543 namespaces := NewSet [string ]()
538- LOOP_POLICY:
539544 for _ , hostDeployPolicy := range hostdeploypolicies .Items {
540- log := m .Log . WithValues ( "policyNamespace" , hostDeployPolicy . Namespace , "policyName" , hostDeployPolicy . Name )
541- if SetContains ( namespaces , hostDeployPolicy . Namespace ) {
542- // namespace already added, no reason to check this hdp.
543- continue
545+ if accept , err := m .checkPolicy ( & hostDeployPolicy , hostNs , nsLabels ); err != nil {
546+ return nil , err
547+ } else if accept {
548+ AddSet ( namespaces , hostDeployPolicy . Namespace )
544549 }
545- constraints := hostDeployPolicy .Spec .HostClaimNamespaces
546- if constraints == nil {
547- log .V (1 ).Info ("Ignoring HostDeployPolicy without constraint" )
548- continue
550+ }
551+ m .Log .Info ("Acceptable namespaces" , "namespaces" , namespaces )
552+ return namespaces , nil
553+ }
554+
555+ func (m * HostManager ) checkPolicy (hostDeployPolicy * metal3api.HostDeployPolicy , hostNs string , hostNsLabels map [string ]string ) (bool , error ) {
556+ log := m .Log .WithValues ("policyNamespace" , hostDeployPolicy .Namespace , "policyName" , hostDeployPolicy .Name )
557+ constraints := hostDeployPolicy .Spec .HostClaimNamespaces
558+ if constraints == nil {
559+ m .Log .V (1 ).Info ("Ignoring HostDeployPolicy without constraint" )
560+ return false , nil
561+ }
562+ if constraints .Names != nil && ! slices .Contains (constraints .Names , hostNs ) {
563+ log .V (1 ).Info ("Ignoring HostDeployPolicy because claim namespace not in names" , "names" , constraints .Names )
564+ return false , nil
565+ }
566+ if constraints .NameMatches != "" {
567+ b , err := regexp .MatchString (constraints .NameMatches , hostNs )
568+ if err != nil {
569+ log .Error (
570+ err , "Error during regexp matching on HostClaim namespace (bad regexp)" ,
571+ "regexp" , constraints .NameMatches )
572+ return false , err
549573 }
550- if constraints . Names != nil && ! slices . Contains ( constraints . Names , hostNs ) {
551- log .V (1 ).Info ("Ignoring HostDeployPolicy because claim namespace not in names" , "names" , constraints . Names )
552- continue
574+ if ! b {
575+ log .V (1 ).Info ("Ignoring HostDeployPolicy because claim namespace does not match regex" )
576+ return false , nil
553577 }
554- if constraints .NameMatches != "" {
555- b , err := regexp .MatchString (constraints .NameMatches , hostNs )
556- if err != nil {
557- log .Error (
558- err , "Error during regexp matching on HostClaim namespace (bad regexp)" ,
559- "regexp" , constraints .NameMatches )
560- return nil , err
561- }
562- if ! b {
563- log .V (1 ).Info ("Ignoring HostDeployPolicy because claim namespace does not match regex" )
564- continue
565- }
566- }
567- // Behaves as a 'forall' on labels
568- for _ , pair := range constraints .HasLabels {
569- if v , ok := nsLabels [pair .Name ]; ok {
570- if pair .Value != "" && v != pair .Value {
571- log .V (1 ).Info (
572- "Ignoring HostDeployPolicy because claim namespace labels does not have correct value" ,
573- "label" , pair .Name , "value" , v , "expected" , pair .Value )
574- continue LOOP_POLICY
575- }
576- } else {
578+ }
579+ // Behaves as a 'forall' on labels
580+ for _ , pair := range constraints .HasLabels {
581+ if v , ok := hostNsLabels [pair .Name ]; ok {
582+ if pair .Value != "" && v != pair .Value {
577583 log .V (1 ).Info (
578- "Ignoring HostDeployPolicy because claim namespace labels does not have a required label " ,
579- "label" , pair .Name )
580- continue LOOP_POLICY
584+ "Ignoring HostDeployPolicy because claim namespace labels does not have correct value " ,
585+ "label" , pair .Name , "value" , v , "expected" , pair . Value )
586+ return false , nil
581587 }
588+ } else {
589+ log .V (1 ).Info (
590+ "Ignoring HostDeployPolicy because claim namespace labels does not have a required label" ,
591+ "label" , pair .Name )
592+ return false , nil
582593 }
583- log .V (1 ).Info ("Accepting namespace because of HostDeployPolicy" , "namespace" , hostDeployPolicy .Namespace )
584- AddSet (namespaces , hostDeployPolicy .Namespace )
585594 }
586- m . Log .Info ("Acceptable namespaces " , "namespaces " , namespaces )
587- return namespaces , nil
595+ log . V ( 1 ) .Info ("Accepting namespace because of HostDeployPolicy " , "namespace " , hostDeployPolicy . Namespace )
596+ return true , nil
588597}
589598
590599func (m * HostManager ) hostLabelSelectorForHostClaim () (labels.Selector , error ) {
@@ -740,6 +749,43 @@ func syncReboot(hostMap, bmhMap map[string]string) bool {
740749 return updated
741750}
742751
752+ // synchronize detached annotation from hostMap to bmhMap.
753+ func (m * HostManager ) syncDetached (ctx context.Context , bmhNs string , hostMap , bmhMap map [string ]string ) (bool , error ) {
754+ if annotValue , ok := hostMap [metal3api .DetachedAnnotation ]; ok {
755+ if _ , ok := bmhMap [metal3api .DetachedAnnotation ]; ok {
756+ return false , nil
757+ }
758+ if compatiblePolicies , err := m .GetCompatiblePolicies (ctx , bmhNs ); err != nil {
759+ return false , err
760+ } else if ! slices .ContainsFunc (
761+ compatiblePolicies ,
762+ func (hdp * metal3api.HostDeployPolicy ) bool {
763+ return hdp .Spec .AllowsDetachedMode
764+ },
765+ ) {
766+ m .Log .Info ("HostClaim not allowed to detach underlying BareMetalHost" )
767+ return false , err
768+ }
769+ inputArgs := metal3api.DetachedAnnotationArguments {}
770+ _ = json .Unmarshal ([]byte (annotValue ), & inputArgs )
771+ args := metal3api.DetachedAnnotationArguments {DeleteAction : inputArgs .DeleteAction }
772+ annot , err := json .Marshal (args )
773+ if err != nil {
774+ return false , err
775+ }
776+ bmhMap [metal3api .DetachedAnnotation ] = string (annot )
777+ return true , nil
778+ } else if annotValue , ok := bmhMap [metal3api .DetachedAnnotation ]; ok {
779+ args := metal3api.DetachedAnnotationArguments {}
780+ if err := json .Unmarshal ([]byte (annotValue ), & args ); err != nil || args .Manager {
781+ return false , nil //nolint:nilerr // arbitrary value of annotation mean they are not handled by hostclaim
782+ }
783+ delete (bmhMap , metal3api .DetachedAnnotation )
784+ return true , nil
785+ }
786+ return false , nil
787+ }
788+
743789type Set [T comparable ] = map [T ]struct {}
744790
745791func NewSet [T comparable ]() Set [T ] {
@@ -754,3 +800,33 @@ func SetContains[T comparable](m Set[T], s T) bool {
754800 _ , ok := m [s ]
755801 return ok
756802}
803+
804+ // GetCompatiblePolicies find all the HostDeployPolicies in the namespace of the bareMetalHost that
805+ // accept the namespace of the current HostClaim.
806+ func (m * HostManager ) GetCompatiblePolicies (ctx context.Context , bmhNs string ) ([]* metal3api.HostDeployPolicy , error ) {
807+ policiesInNs := & metal3api.HostDeployPolicyList {}
808+ if err := m .client .List (ctx , policiesInNs , client .InNamespace (bmhNs )); err != nil {
809+ m .Log .Error (err , "Cannot access HostDeployPolicies" , "bmhNamespace" , bmhNs )
810+ return nil , err
811+ }
812+ hostNs := m .HostClaim .Namespace
813+ hostNsResource := & corev1.Namespace {}
814+ if err := m .client .Get (ctx , client.ObjectKey {Name : hostNs }, hostNsResource ); err != nil {
815+ m .Log .Error (err , "cannot access the namespace of the claim" )
816+ return nil , err
817+ }
818+ nsLabels := hostNsResource .Labels
819+ if nsLabels == nil {
820+ nsLabels = map [string ]string {}
821+ }
822+
823+ var compatiblePolicies []* metal3api.HostDeployPolicy
824+ for _ , hdp := range policiesInNs .Items {
825+ if accept , err := m .checkPolicy (& hdp , hostNs , nsLabels ); err != nil {
826+ return nil , err
827+ } else if accept {
828+ compatiblePolicies = append (compatiblePolicies , & hdp )
829+ }
830+ }
831+ return compatiblePolicies , nil
832+ }
0 commit comments