From 34e4cfdd8b58a044a97aa511e7816e3acf519132 Mon Sep 17 00:00:00 2001 From: Erin Allison Date: Thu, 12 Aug 2021 15:46:32 -0500 Subject: [PATCH 1/2] Add ability to specify event type when requeueing --- .../ManagedResourceController{TEntity}.cs | 45 ++++++++++++++----- .../Controller/Results/RequeueEventResult.cs | 6 +++ .../Results/ResourceControllerResult.cs | 34 +++++++++++++- 3 files changed, 73 insertions(+), 12 deletions(-) diff --git a/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs b/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs index 0848ea70..c2e82970 100644 --- a/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs +++ b/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs @@ -33,7 +33,7 @@ internal class ManagedResourceController : IManagedResourceController private readonly OperatorSettings _settings; private readonly IFinalizerManager _finalizerManager; - private readonly Subject<(TEntity Resource, TimeSpan Delay)> + private readonly Subject _requeuedEvents = new(); private readonly Subject @@ -85,9 +85,17 @@ public ManagedResourceController( private IObservable RequeuedEvents => _requeuedEvents .Do(_ => _metrics.RequeuedEvents.Inc()) .Select( - data => Observable.Return(data.Resource).Delay(data.Delay)) + data => Observable.Return(data).Delay(data.Delay)) .Switch() - .Select(data => Observable.FromAsync(() => UpdateResourceData(data))) + .Select(data => + Observable.FromAsync(async () => + { + var queuedEvent = await UpdateResourceData(data.Resource); + + return data.ResourceEvent.HasValue && queuedEvent != null + ? queuedEvent with { ResourceEvent = data.ResourceEvent.Value } + : queuedEvent; + })) .Switch() .Where(data => data != null) .Do( @@ -269,13 +277,28 @@ protected async Task HandleResourceEvent(QueuedEvent? data) resource.Name()); return; case RequeueEventResult requeue: - _logger.LogInformation( - @"Event type ""{eventType}"" on resource ""{kind}/{name}"" successfully reconciled. Requeue requested with delay ""{requeue}"".", - @event, - resource.Kind, - resource.Name(), - requeue.RequeueIn); - _requeuedEvents.OnNext((resource, requeue.RequeueIn)); + // TODO Add a setting to default to requeue as the same type event. + if (requeue.EventType.HasValue) + { + _logger.LogInformation( + @"Event type ""{eventType}"" on resource ""{kind}/{name}"" successfully reconciled. Requeue requested as type ""{requeueType}"" with delay ""{requeue}"".", + @event, + resource.Kind, + resource.Name(), + requeue.EventType, + requeue.RequeueIn); + } + else + { + _logger.LogInformation( + @"Event type ""{eventType}"" on resource ""{kind}/{name}"" successfully reconciled. Requeue requested with delay ""{requeue}"".", + @event, + resource.Kind, + resource.Name(), + requeue.RequeueIn); + } + + _requeuedEvents.OnNext(new RequeuedEvent(requeue.EventType, resource, requeue.RequeueIn)); break; } } @@ -401,5 +424,7 @@ private TimeSpan ExponentialBackoff(int retryCount) => TimeSpan .Add(TimeSpan.FromMilliseconds(_rnd.Next(0, 1000))); internal record QueuedEvent(ResourceEventType ResourceEvent, TEntity Resource, int RetryCount = 0); + + private record RequeuedEvent(ResourceEventType? ResourceEvent, TEntity Resource, TimeSpan Delay); } } diff --git a/src/KubeOps/Operator/Controller/Results/RequeueEventResult.cs b/src/KubeOps/Operator/Controller/Results/RequeueEventResult.cs index 74bfbbe7..a22b20cf 100644 --- a/src/KubeOps/Operator/Controller/Results/RequeueEventResult.cs +++ b/src/KubeOps/Operator/Controller/Results/RequeueEventResult.cs @@ -1,4 +1,5 @@ using System; +using KubeOps.Operator.Kubernetes; namespace KubeOps.Operator.Controller.Results { @@ -8,5 +9,10 @@ public RequeueEventResult(TimeSpan requeueIn) : base(requeueIn) { } + + public RequeueEventResult(TimeSpan requeueIn, ResourceEventType eventType) + : base(requeueIn, eventType) + { + } } } diff --git a/src/KubeOps/Operator/Controller/Results/ResourceControllerResult.cs b/src/KubeOps/Operator/Controller/Results/ResourceControllerResult.cs index e9521a91..dbb3a00f 100644 --- a/src/KubeOps/Operator/Controller/Results/ResourceControllerResult.cs +++ b/src/KubeOps/Operator/Controller/Results/ResourceControllerResult.cs @@ -1,4 +1,5 @@ using System; +using KubeOps.Operator.Events; using KubeOps.Operator.Kubernetes; namespace KubeOps.Operator.Controller.Results @@ -10,13 +11,27 @@ namespace KubeOps.Operator.Controller.Results /// public abstract class ResourceControllerResult { - internal ResourceControllerResult(TimeSpan delay) => RequeueIn = delay; + internal ResourceControllerResult(TimeSpan delay) + { + RequeueIn = delay; + } + + internal ResourceControllerResult(TimeSpan delay, ResourceEventType eventType) + { + RequeueIn = delay; + EventType = eventType; + } /// /// Time that should be waited for a requeue. /// public TimeSpan RequeueIn { get; } + /// + /// Type of the event to be queued. + /// + public ResourceEventType? EventType { get; } + /// /// Create a that requeues a resource /// with a given delay. When the event fires (after the delay) the resource @@ -31,6 +46,21 @@ public abstract class ResourceControllerResult public static ResourceControllerResult RequeueEvent(TimeSpan delay) => new RequeueEventResult(delay); - // TODO: Requeue with forced event method + /// + /// Create a that requeues a resource + /// with a given delay. When the event fires (after the delay) the resource + /// cache is ignored in favor the specified . + /// Based on the specified type, the new event triggers the according function. + /// + /// + /// The delay. Please note, that a delay of + /// will result in an immediate trigger of the function. This can lead to infinite circles. + /// + /// + /// The event type to queue. + /// + /// The with the configured delay and event type. + public static ResourceControllerResult RequeueEvent(TimeSpan delay, ResourceEventType eventType) + => new RequeueEventResult(delay, eventType); } } From bdfadaa592089b669cc53046274a9310c4ab906e Mon Sep 17 00:00:00 2001 From: Erin Allison Date: Thu, 12 Aug 2021 15:55:11 -0500 Subject: [PATCH 2/2] Add setting to have requeue use the same callback as originally used --- .../Controller/ManagedResourceController{TEntity}.cs | 6 +++++- src/KubeOps/Operator/OperatorSettings.cs | 12 ++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs b/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs index c2e82970..448783f7 100644 --- a/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs +++ b/src/KubeOps/Operator/Controller/ManagedResourceController{TEntity}.cs @@ -277,7 +277,11 @@ protected async Task HandleResourceEvent(QueuedEvent? data) resource.Name()); return; case RequeueEventResult requeue: - // TODO Add a setting to default to requeue as the same type event. + if (_settings.DefaultRequeueAsSameType) + { + requeue = new RequeueEventResult(requeue.RequeueIn, @event); + } + if (requeue.EventType.HasValue) { _logger.LogInformation( diff --git a/src/KubeOps/Operator/OperatorSettings.cs b/src/KubeOps/Operator/OperatorSettings.cs index 62dd7bf8..694ea66a 100644 --- a/src/KubeOps/Operator/OperatorSettings.cs +++ b/src/KubeOps/Operator/OperatorSettings.cs @@ -78,5 +78,17 @@ public sealed class OperatorSettings /// The search will be performed on each "Start" of the controller. /// public bool PreloadCache { get; set; } + + /// + /// + /// If set to true, returning `ResourceControllerResult.RequeueEvent` will + /// automatically requeue the event as the same type. + /// + /// + /// For example, if done from a "Created" event, the event will be queued + /// again as "Created" instead of (for example) "NotModified". + /// + /// + public bool DefaultRequeueAsSameType { get; set; } = false; } }