github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/pkg/dataprotection/utils/periodical_enqueue_source.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package utils
    21  
    22  import (
    23  	"context"
    24  	"fmt"
    25  	"reflect"
    26  	"time"
    27  
    28  	"github.com/go-logr/logr"
    29  	"k8s.io/apimachinery/pkg/api/meta"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/types"
    32  	"k8s.io/apimachinery/pkg/util/wait"
    33  	"k8s.io/client-go/util/workqueue"
    34  	ctrl "sigs.k8s.io/controller-runtime"
    35  	"sigs.k8s.io/controller-runtime/pkg/client"
    36  	"sigs.k8s.io/controller-runtime/pkg/event"
    37  	"sigs.k8s.io/controller-runtime/pkg/handler"
    38  	"sigs.k8s.io/controller-runtime/pkg/log"
    39  	"sigs.k8s.io/controller-runtime/pkg/predicate"
    40  )
    41  
    42  // PeriodicalEnqueueSource is an implementation of interface sigs.k8s.io/controller-runtime/pkg/source/Source
    43  // It reads the specific resources from cache and enqueue them into the queue to trigger
    44  // the reconcile procedure periodically.
    45  type PeriodicalEnqueueSource struct {
    46  	client.Client
    47  	log     logr.Logger
    48  	objList client.ObjectList
    49  	period  time.Duration
    50  	option  PeriodicalEnqueueSourceOption
    51  }
    52  
    53  type PeriodicalEnqueueSourceOption struct {
    54  	OrderFunc func(objList client.ObjectList) client.ObjectList
    55  }
    56  
    57  func NewPeriodicalEnqueueSource(
    58  	client client.Client,
    59  	objList client.ObjectList,
    60  	period time.Duration,
    61  	option PeriodicalEnqueueSourceOption) *PeriodicalEnqueueSource {
    62  	return &PeriodicalEnqueueSource{
    63  		log:     log.Log.WithValues("resource", reflect.TypeOf(objList).String()),
    64  		Client:  client,
    65  		objList: objList,
    66  		period:  period,
    67  		option:  option,
    68  	}
    69  }
    70  
    71  func (p *PeriodicalEnqueueSource) Start(
    72  	ctx context.Context,
    73  	_ handler.EventHandler,
    74  	q workqueue.RateLimitingInterface,
    75  	predicates ...predicate.Predicate) error {
    76  	go wait.Until(func() {
    77  		p.log.V(1).Info("enqueueing resources ...")
    78  		if err := p.List(ctx, p.objList); err != nil {
    79  			p.log.Error(err, "error listing resources")
    80  			return
    81  		}
    82  
    83  		if meta.LenList(p.objList) == 0 {
    84  			p.log.V(1).Info("no resources found, skip")
    85  			return
    86  		}
    87  
    88  		if p.option.OrderFunc != nil {
    89  			p.objList = p.option.OrderFunc(p.objList)
    90  		}
    91  
    92  		if err := meta.EachListItem(p.objList, func(object runtime.Object) error {
    93  			obj, ok := object.(client.Object)
    94  			if !ok {
    95  				p.log.Error(nil, "object is not a client.Object", "object", object)
    96  				return nil
    97  			}
    98  			e := event.GenericEvent{Object: obj}
    99  			for _, pred := range predicates {
   100  				if !pred.Generic(e) {
   101  					p.log.V(1).Info("skip enqueue object due to the predicate", "object", obj)
   102  					return nil
   103  				}
   104  			}
   105  
   106  			q.Add(ctrl.Request{
   107  				NamespacedName: types.NamespacedName{
   108  					Namespace: obj.GetNamespace(),
   109  					Name:      obj.GetName(),
   110  				},
   111  			})
   112  			p.log.V(1).Info("resource enqueued", "object", obj)
   113  			return nil
   114  		}); err != nil {
   115  			p.log.Error(err, "error enqueueing resources")
   116  			return
   117  		}
   118  	}, p.period, ctx.Done())
   119  
   120  	return nil
   121  }
   122  
   123  func (p *PeriodicalEnqueueSource) String() string {
   124  	if p.objList != nil {
   125  		return fmt.Sprintf("periodical enqueue source: %T", p.objList)
   126  	}
   127  	return "periodical enqueue source: unknown type"
   128  }