@@ -35,7 +35,9 @@ const (
3535 TokenSecretPullSecretHashKey = "pull-secret-hash"
3636 TokenSecretHCConfigurationHashKey = "hc-configuration-hash"
3737 TokenSecretAdditionalTrustBundleHashKey = "additional-trust-bundle-hash"
38+ TokenSecretCloudConfigHashKey = "cloud-config-hash"
3839 InvalidConfigReason = "InvalidConfig"
40+ CloudConfigPendingReason = "CloudConfigPending"
3941 TokenSecretReasonKey = "reason"
4042 // TokenSecretOSStreamKey is intentionally duplicated from nodepool/token.go
4143 // to avoid a dependency from ignition-server → hypershift-operator.
@@ -88,7 +90,7 @@ func NewPayloadStore() *ExpiringCache {
8890type IgnitionProvider interface {
8991 // GetPayload returns the ignition payload content for
9092 // the provided release image and a config string containing 0..N MachineConfig yaml definitions.
91- GetPayload (ctx context.Context , payloadImage , config , pullSecretHash , additionalTrustBundleHash , hcConfigurationHash , osStream string ) ([]byte , error )
93+ GetPayload (ctx context.Context , payloadImage , config , pullSecretHash , additionalTrustBundleHash , hcConfigurationHash , osStream , cloudConfigHash string ) ([]byte , error )
9294}
9395
9496// TokenSecretReconciler watches token Secrets
@@ -231,25 +233,35 @@ func (r *TokenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request)
231233 }
232234
233235 token := string (tokenSecret .Data [TokenSecretTokenKey ])
236+ cloudConfigHash := string (tokenSecret .Data [TokenSecretCloudConfigHashKey ])
234237 if value , ok := r .PayloadStore .Get (token ); ok {
235- log .Info ("Payload found in cache" )
236-
237- if tokenNeedRotation (timeLived ) {
238- log .Info ("Rotating token ID" )
239- if err := r .rotateToken (ctx , tokenSecret , value , now ); err != nil {
240- return ctrl.Result {}, err
238+ if value .CloudConfigHash != cloudConfigHash {
239+ log .Info ("Cloud config hash changed, invalidating cached payload" )
240+ r .PayloadStore .Delete (token )
241+ } else {
242+ log .Info ("Payload found in cache" )
243+
244+ if tokenNeedRotation (timeLived ) {
245+ log .Info ("Rotating token ID" )
246+ if err := r .rotateToken (ctx , tokenSecret , value , now ); err != nil {
247+ return ctrl.Result {}, err
248+ }
249+ TokenRotationTotal .Inc ()
241250 }
242- TokenRotationTotal . Inc ()
251+ return ctrl. Result { RequeueAfter : ttl / 2 - durationDeref ( timeLived )}, nil
243252 }
244- return ctrl.Result {RequeueAfter : ttl / 2 - durationDeref (timeLived )}, nil
245253 }
246254
247255 // If something else rotated the token (e.g. running in HA), we fall back to set the cache value from the old one.
248256 oldToken , ok := tokenSecret .Data [TokenSecretOldTokenKey ]
249257 if ok {
250258 if value , ok := r .PayloadStore .Get (string (oldToken )); ok {
251- r .PayloadStore .Set (token , value )
252- return ctrl.Result {RequeueAfter : ttl / 2 - durationDeref (timeLived )}, nil
259+ if value .CloudConfigHash == cloudConfigHash {
260+ r .PayloadStore .Set (token , value )
261+ return ctrl.Result {RequeueAfter : ttl / 2 - durationDeref (timeLived )}, nil
262+ }
263+ log .Info ("Cloud config hash changed, invalidating old token cached payload" )
264+ r .PayloadStore .Delete (string (oldToken ))
253265 }
254266 }
255267
@@ -279,7 +291,7 @@ func (r *TokenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request)
279291 osStream := string (tokenSecret .Data [TokenSecretOSStreamKey ])
280292 payload , err := func () ([]byte , error ) {
281293 start := time .Now ()
282- payload , err := r .IgnitionProvider .GetPayload (ctx , releaseImage , config .String (), pullSecretHash , additionalTrustBundleHash , hcConfigurationHash , osStream )
294+ payload , err := r .IgnitionProvider .GetPayload (ctx , releaseImage , config .String (), pullSecretHash , additionalTrustBundleHash , hcConfigurationHash , osStream , cloudConfigHash )
283295 if err != nil {
284296 return nil , fmt .Errorf ("error getting ignition payload: %w" , err )
285297 }
@@ -292,12 +304,16 @@ func (r *TokenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request)
292304 // This patch could flood the API server, so we should only do it when the reason or message is different from the current one.
293305 // More info here: https://issues.redhat.com/browse/OCPBUGS-42320.
294306 errWithFullMsg := fmt .Errorf ("failed to generate payload: %w" , err )
295- if hasSameReasonAndMessage (tokenSecret , InvalidConfigReason , errWithFullMsg ) {
307+ reason := InvalidConfigReason
308+ if strings .Contains (err .Error (), "hash mismatch" ) {
309+ reason = CloudConfigPendingReason
310+ }
311+ if hasSameReasonAndMessage (tokenSecret , reason , errWithFullMsg ) {
296312 return ctrl.Result {}, errWithFullMsg
297313 }
298314
299315 patch := tokenSecret .DeepCopy ()
300- patch .Data [TokenSecretReasonKey ] = []byte (InvalidConfigReason )
316+ patch .Data [TokenSecretReasonKey ] = []byte (reason )
301317 patch .Data [TokenSecretMessageKey ] = []byte (errWithFullMsg .Error ())
302318 if err := r .Client .Patch (ctx , patch , client .MergeFrom (tokenSecret )); err != nil {
303319 return ctrl.Result {}, fmt .Errorf ("failed to patch tokenSecret with payload content: %w" , err )
@@ -307,12 +323,13 @@ func (r *TokenSecretReconciler) Reconcile(ctx context.Context, req ctrl.Request)
307323 }
308324
309325 log .Info ("IgnitionProvider generated payload" )
310- r .PayloadStore .Set (token , CacheValue {Payload : payload , SecretName : tokenSecret .Name })
326+ cacheValue := CacheValue {Payload : payload , SecretName : tokenSecret .Name , CloudConfigHash : cloudConfigHash }
327+ r .PayloadStore .Set (token , cacheValue )
311328 oldToken , ok = tokenSecret .Data [TokenSecretOldTokenKey ]
312329 if ok {
313330 // If we got here and there's an old token e.g. ignition server pod was restarted, then we set it as well
314331 // So Machines that were given that token right before the restart can succeed.
315- r .PayloadStore .Set (string (oldToken ), CacheValue { Payload : payload , SecretName : tokenSecret . Name } )
332+ r .PayloadStore .Set (string (oldToken ), cacheValue )
316333 }
317334
318335 patch := tokenSecret .DeepCopy ()
0 commit comments