@@ -23,6 +23,7 @@ import (
2323 "errors"
2424 "fmt"
2525 "hash"
26+ "strings"
2627
2728 corev1 "k8s.io/api/core/v1"
2829 apierrors "k8s.io/apimachinery/pkg/api/errors"
@@ -39,7 +40,10 @@ import (
3940 "sigs.k8s.io/controller-runtime/pkg/controller"
4041 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
4142 "sigs.k8s.io/controller-runtime/pkg/handler"
43+ "sigs.k8s.io/controller-runtime/pkg/log"
4244 "sigs.k8s.io/controller-runtime/pkg/reconcile"
45+
46+ utilyaml "sigs.k8s.io/cluster-api/util/yaml"
4347)
4448
4549type GenericProviderReconciler struct {
@@ -56,6 +60,7 @@ type GenericProviderReconciler struct {
5660
5761const (
5862 appliedSpecHashAnnotation = "operator.cluster.x-k8s.io/applied-spec-hash"
63+ cacheOwner = "capi-operator"
5964)
6065
6166func (r * GenericProviderReconciler ) BuildWithManager (ctx context.Context , mgr ctrl.Manager ) (* ctrl.Builder , error ) {
@@ -173,8 +178,15 @@ func (r *GenericProviderReconciler) Reconcile(ctx context.Context, req reconcile
173178 return ctrl.Result {}, err
174179 }
175180
176- if r .Provider .GetAnnotations ()[appliedSpecHashAnnotation ] == specHash {
181+ // Check provider config map for changes
182+ cacheUsed , err := applyFromCache (ctx , r .Client , r .Provider )
183+ if err != nil {
184+ return ctrl.Result {}, err
185+ }
186+
187+ if r .Provider .GetAnnotations ()[appliedSpecHashAnnotation ] == specHash || cacheUsed {
177188 log .Info ("No changes detected, skipping further steps" )
189+
178190 return ctrl.Result {}, nil
179191 }
180192
@@ -316,17 +328,140 @@ func addObjectToHash(hash hash.Hash, object interface{}) error {
316328 return nil
317329}
318330
331+ // providerHash calculates hash for provider and referenced objects.
332+ func providerHash (ctx context.Context , client client.Client , hash hash.Hash , provider genericprovider.GenericProvider ) error {
333+ log := log .FromContext (ctx )
334+
335+ err := addObjectToHash (hash , provider .GetSpec ())
336+ if err != nil {
337+ log .Error (err , "failed to calculate provider hash" )
338+
339+ return err
340+ }
341+
342+ if err := addConfigSecretToHash (ctx , client , hash , provider ); err != nil {
343+ log .Error (err , "failed to calculate secret hash" )
344+
345+ return err
346+ }
347+
348+ return nil
349+ }
350+
319351func calculateHash (ctx context.Context , k8sClient client.Client , provider genericprovider.GenericProvider ) (string , error ) {
320352 hash := sha256 .New ()
321353
322- err := addObjectToHash (hash , provider .GetSpec ())
354+ err := providerHash (ctx , k8sClient , hash , provider )
355+
356+ return fmt .Sprintf ("%x" , hash .Sum (nil )), err
357+ }
358+
359+ // applyFromCache applies provider configuration from cache and returns true if the cache did not change.
360+ func applyFromCache (ctx context.Context , cl client.Client , provider genericprovider.GenericProvider ) (bool , error ) {
361+ log := log .FromContext (ctx )
362+
363+ configMap , err := providerConfigMap (ctx , cl , provider )
323364 if err != nil {
324- return "" , err
365+ log .Error (err , "failed to get provider config map" )
366+
367+ return false , err
368+ }
369+
370+ // config map does not exist, nothing to apply
371+ if configMap == nil {
372+ return false , nil
373+ }
374+
375+ // calculate combined hash for provider and config map cache
376+ hash := sha256 .New ()
377+ if err := providerHash (ctx , cl , hash , provider ); err != nil {
378+ log .Error (err , "failed to calculate provider hash" )
379+
380+ return false , err
381+ }
382+
383+ if err := addObjectToHash (hash , configMap .Data ); err != nil {
384+ log .Error (err , "failed to calculate config map hash" )
385+
386+ return false , err
325387 }
326388
327- if err := addConfigSecretToHash (ctx , k8sClient , hash , provider ); err != nil {
328- return "" , err
389+ cacheHash := fmt .Sprintf ("%x" , hash .Sum (nil ))
390+ if configMap .GetAnnotations ()[appliedSpecHashAnnotation ] != cacheHash {
391+ log .Info ("Provider or cache state has changed" , "cacheHash" , cacheHash , "providerHash" , configMap .GetAnnotations ()[appliedSpecHashAnnotation ])
392+
393+ return false , nil
394+ }
395+
396+ components , err := getComponentsData (* configMap )
397+ if err != nil {
398+ log .Error (err , "failed to get provider components" )
399+
400+ return false , err
329401 }
330402
331- return fmt .Sprintf ("%x" , hash .Sum (nil )), nil
403+ additionalManifests , err := fetchAdditionalManifests (ctx , cl , provider )
404+ if err != nil {
405+ log .Error (err , "failed to get additional manifests" )
406+
407+ return false , err
408+ }
409+
410+ if additionalManifests != "" {
411+ components = components + "\n ---\n " + additionalManifests
412+ }
413+
414+ for _ , manifests := range strings .Split (components , "---" ) {
415+ manifests , err := utilyaml .ToUnstructured ([]byte (manifests ))
416+ if err != nil {
417+ log .Error (err , "failed to convert yaml to unstructured" )
418+
419+ return false , err
420+ }
421+
422+ if err := cl .Patch (ctx , & manifests [0 ], client .Apply , client .ForceOwnership , client .FieldOwner (cacheOwner )); err != nil {
423+ log .Error (err , "failed to apply object from cache" )
424+
425+ return false , nil
426+ }
427+ }
428+
429+ log .Info ("Applied all objects from cache" )
430+
431+ return true , nil
432+ }
433+
434+ // setCacheHash calculates current provider and configMap hash, and updates it on the configMap.
435+ func setCacheHash (ctx context.Context , cl client.Client , provider genericprovider.GenericProvider ) error {
436+ configMap , err := providerConfigMap (ctx , cl , provider )
437+ if err != nil {
438+ return err
439+ }
440+
441+ helper , err := patch .NewHelper (configMap , cl )
442+ if err != nil {
443+ return err
444+ }
445+
446+ hash := sha256 .New ()
447+
448+ if err := providerHash (ctx , cl , hash , provider ); err != nil {
449+ return err
450+ }
451+
452+ if err := addObjectToHash (hash , configMap .Data ); err != nil {
453+ return err
454+ }
455+
456+ cacheHash := fmt .Sprintf ("%x" , hash .Sum (nil ))
457+
458+ annotations := configMap .GetAnnotations ()
459+ if annotations == nil {
460+ annotations = map [string ]string {}
461+ }
462+
463+ annotations [appliedSpecHashAnnotation ] = cacheHash
464+ configMap .SetAnnotations (annotations )
465+
466+ return helper .Patch (ctx , configMap )
332467}
0 commit comments