@@ -21,12 +21,17 @@ import (
2121 "fmt"
2222 "time"
2323
24+ apimeta "k8s.io/apimachinery/pkg/api/meta"
2425 "k8s.io/apimachinery/pkg/runtime"
26+ "k8s.io/apimachinery/pkg/types"
2527 "k8s.io/client-go/kubernetes"
2628 ctrl "sigs.k8s.io/controller-runtime"
2729 "sigs.k8s.io/controller-runtime/pkg/client"
2830 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
31+ "sigs.k8s.io/controller-runtime/pkg/handler"
2932 "sigs.k8s.io/controller-runtime/pkg/log"
33+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
34+ "sigs.k8s.io/controller-runtime/pkg/source"
3035
3136 "github.com/go-logr/logr"
3237 keystonev1 "github.com/openstack-k8s-operators/keystone-operator/api/v1beta1"
@@ -42,7 +47,6 @@ import (
4247 labels "github.com/openstack-k8s-operators/lib-common/modules/common/labels"
4348 nad "github.com/openstack-k8s-operators/lib-common/modules/common/networkattachment"
4449 common_rbac "github.com/openstack-k8s-operators/lib-common/modules/common/rbac"
45- oko_secret "github.com/openstack-k8s-operators/lib-common/modules/common/secret"
4650 "github.com/openstack-k8s-operators/lib-common/modules/common/service"
4751 util "github.com/openstack-k8s-operators/lib-common/modules/common/util"
4852
@@ -58,11 +62,133 @@ import (
5862 k8s_errors "k8s.io/apimachinery/pkg/api/errors"
5963)
6064
65+ type conditionUpdater interface {
66+ Set (c * condition.Condition )
67+ MarkTrue (t condition.Type , messageFormat string , messageArgs ... interface {})
68+ }
69+
70+ type GetSecret interface {
71+ GetSecret () string
72+ client.Object
73+ }
74+
75+ // ensureSecret - ensures that the Secret object exists and the expected fields
76+ // are in the Secret. It returns a hash of the values of the expected fields.
77+ func ensureSecret (
78+ ctx context.Context ,
79+ secretName types.NamespacedName ,
80+ expectedFields []string ,
81+ reader client.Reader ,
82+ conditionUpdater conditionUpdater ,
83+ ) (string , ctrl.Result , corev1.Secret , error ) {
84+ secret := & corev1.Secret {}
85+ err := reader .Get (ctx , secretName , secret )
86+ if err != nil {
87+ if k8s_errors .IsNotFound (err ) {
88+ conditionUpdater .Set (condition .FalseCondition (
89+ condition .InputReadyCondition ,
90+ condition .RequestedReason ,
91+ condition .SeverityInfo ,
92+ fmt .Sprintf ("Input data resources missing: %s" , "secret/" + secretName .Name )))
93+ return "" ,
94+ ctrl.Result {},
95+ * secret ,
96+ fmt .Errorf ("Secret %s not found" , secretName )
97+ }
98+ conditionUpdater .Set (condition .FalseCondition (
99+ condition .InputReadyCondition ,
100+ condition .ErrorReason ,
101+ condition .SeverityWarning ,
102+ condition .InputReadyErrorMessage ,
103+ err .Error ()))
104+ return "" , ctrl.Result {}, * secret , err
105+ }
106+
107+ // collect the secret values the caller expects to exist
108+ values := [][]byte {}
109+ for _ , field := range expectedFields {
110+ val , ok := secret .Data [field ]
111+ if ! ok {
112+ err := fmt .Errorf ("field '%s' not found in secret/%s" , field , secretName .Name )
113+ conditionUpdater .Set (condition .FalseCondition (
114+ condition .InputReadyCondition ,
115+ condition .ErrorReason ,
116+ condition .SeverityWarning ,
117+ condition .InputReadyErrorMessage ,
118+ err .Error ()))
119+ return "" , ctrl.Result {}, * secret , err
120+ }
121+ values = append (values , val )
122+ }
123+
124+ // TODO(gibi): Do we need to watch the Secret for changes?
125+
126+ hash , err := util .ObjectHash (values )
127+ if err != nil {
128+ conditionUpdater .Set (condition .FalseCondition (
129+ condition .InputReadyCondition ,
130+ condition .ErrorReason ,
131+ condition .SeverityWarning ,
132+ condition .InputReadyErrorMessage ,
133+ err .Error ()))
134+ return "" , ctrl.Result {}, * secret , err
135+ }
136+
137+ return hash , ctrl.Result {}, * secret , nil
138+ }
139+
61140// GetLog returns a logger object with a prefix of "controller.name" and additional controller context fields
62141func (r * PlacementAPIReconciler ) GetLogger (ctx context.Context ) logr.Logger {
63142 return log .FromContext (ctx ).WithName ("Controllers" ).WithName ("PlacementAPI" )
64143}
65144
145+ func (r * PlacementAPIReconciler ) GetSecretMapperFor (crs client.ObjectList , ctx context.Context ) func (client.Object ) []reconcile.Request {
146+ Log := r .GetLogger (ctx )
147+ mapper := func (secret client.Object ) []reconcile.Request {
148+ var namespace string = secret .GetNamespace ()
149+ var secretName string = secret .GetName ()
150+ result := []reconcile.Request {}
151+
152+ listOpts := []client.ListOption {
153+ client .InNamespace (namespace ),
154+ }
155+ if err := r .Client .List (ctx , crs , listOpts ... ); err != nil {
156+ Log .Error (err , "Unable to retrieve the list of CRs" )
157+ panic (err )
158+ }
159+
160+ err := apimeta .EachListItem (crs , func (o runtime.Object ) error {
161+ // NOTE(gibi): intentionally let the failed cast panic to catch
162+ // this implementation error as soon as possible.
163+ cr := o .(GetSecret )
164+ if cr .GetSecret () == secretName {
165+ name := client.ObjectKey {
166+ Namespace : namespace ,
167+ Name : cr .GetName (),
168+ }
169+ Log .Info (
170+ "Requesting reconcile due to secret change" ,
171+ "Secret" , secretName , "CR" , name .Name ,
172+ )
173+ result = append (result , reconcile.Request {NamespacedName : name })
174+ }
175+ return nil
176+ })
177+
178+ if err != nil {
179+ Log .Error (err , "Unable to iterate the list of CRs" )
180+ panic (err )
181+ }
182+
183+ if len (result ) > 0 {
184+ return result
185+ }
186+ return nil
187+ }
188+
189+ return mapper
190+ }
191+
66192// PlacementAPIReconciler reconciles a PlacementAPI object
67193type PlacementAPIReconciler struct {
68194 client.Client
@@ -207,6 +333,8 @@ func (r *PlacementAPIReconciler) SetupWithManager(mgr ctrl.Manager) error {
207333 Owns (& corev1.ServiceAccount {}).
208334 Owns (& rbacv1.Role {}).
209335 Owns (& rbacv1.RoleBinding {}).
336+ Watches (& source.Kind {Type : & corev1.Secret {}},
337+ handler .EnqueueRequestsFromMapFunc (r .GetSecretMapperFor (& placementv1.PlacementAPIList {}, context .TODO ()))).
210338 Complete (r )
211339}
212340
@@ -591,7 +719,14 @@ func (r *PlacementAPIReconciler) reconcileNormal(ctx context.Context, instance *
591719 //
592720 // check for required OpenStack secret holding passwords for service/admin user and add hash to the vars map
593721 //
594- ospSecret , hash , err := oko_secret .GetSecret (ctx , helper , instance .Spec .Secret , instance .Namespace )
722+ hash , result , ospSecret , err := ensureSecret (
723+ ctx ,
724+ types.NamespacedName {Namespace : instance .Namespace , Name : instance .Spec .Secret },
725+ []string {
726+ instance .Spec .PasswordSelectors .Service ,
727+ },
728+ helper .GetClient (),
729+ & instance .Status .Conditions )
595730 if err != nil {
596731 if k8s_errors .IsNotFound (err ) {
597732 instance .Status .Conditions .Set (condition .FalseCondition (
@@ -607,7 +742,7 @@ func (r *PlacementAPIReconciler) reconcileNormal(ctx context.Context, instance *
607742 condition .SeverityWarning ,
608743 condition .InputReadyErrorMessage ,
609744 err .Error ()))
610- return ctrl. Result {} , err
745+ return result , err
611746 }
612747 configMapVars [ospSecret .Name ] = env .SetValue (hash )
613748 instance .Status .Conditions .MarkTrue (condition .InputReadyCondition , condition .InputReadyMessage )
0 commit comments