public Watcher(StreamReader streamReader, Action <WatchEventType, T> onEvent, Action <Exception> onError) { _streamReader = streamReader; OnEvent += onEvent; OnError += onError; _cts = new CancellationTokenSource(); var token = _cts.Token; Task.Run(async() => { try { Watching = true; while (!streamReader.EndOfStream) { if (token.IsCancellationRequested) { return; } var line = await streamReader.ReadLineAsync(); try { var genericEvent = SafeJsonConvert.DeserializeObject <k8s.Watcher <KubernetesObject> .WatchEvent>(line); if (genericEvent.Object.Kind == "Status") { var statusEvent = SafeJsonConvert.DeserializeObject <k8s.Watcher <V1Status> .WatchEvent>(line); var exception = new KubernetesException(statusEvent.Object); this.OnError?.Invoke(exception); } else { var @event = SafeJsonConvert.DeserializeObject <k8s.Watcher <T> .WatchEvent>(line); this.OnEvent?.Invoke(@event.Type, @event.Object); } } catch (Exception e) { // error if deserialized failed or onevent throws OnError?.Invoke(e); } } } catch (Exception e) { // error when transport error, IOException ect OnError?.Invoke(e); } finally { Watching = false; } }, token); }
internal static async IAsyncEnumerable <(WatchEventType, T)> CreateWatchEventEnumerator( Func <Task <TextReader> > streamReaderCreator, Action <Exception> onError = null, [EnumeratorCancellation] CancellationToken cancellationToken = default) { Task <TR> AttachCancellationToken <TR>(Task <TR> task) { if (!task.IsCompleted) { // here to pass cancellationToken into task return(task.ContinueWith(t => t.GetAwaiter().GetResult(), cancellationToken)); } return(task); } using var streamReader = await AttachCancellationToken(streamReaderCreator ()).ConfigureAwait(false); for (; ;) { // ReadLineAsync will return null when we've reached the end of the stream. var line = await AttachCancellationToken(streamReader.ReadLineAsync()).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); if (line == null) { yield break; } WatchEvent @event = null; try { var genericEvent = KubernetesJson.Deserialize <Watcher <KubernetesObject> .WatchEvent>(line); if (genericEvent.Object.Kind == "Status") { var statusEvent = KubernetesJson.Deserialize <Watcher <V1Status> .WatchEvent>(line); var exception = new KubernetesException(statusEvent.Object); onError?.Invoke(exception); } else { @event = KubernetesJson.Deserialize <WatchEvent>(line); } } catch (Exception e) { onError?.Invoke(e); } if (@event != null) { yield return(@event.Type, @event.Object); } } }
private async Task WatcherLoop(CancellationToken cancellationToken) { // Make sure we run async await Task.Yield(); try { Watching = true; string line; _streamReader = await _streamReaderCreator(); // ReadLineAsync will return null when we've reached the end of the stream. while ((line = await _streamReader.ReadLineAsync().ConfigureAwait(false)) != null) { if (cancellationToken.IsCancellationRequested) { return; } try { var genericEvent = SafeJsonConvert.DeserializeObject <k8s.Watcher <KubernetesObject> .WatchEvent>(line); if (genericEvent.Object.Kind == "Status") { var statusEvent = SafeJsonConvert.DeserializeObject <k8s.Watcher <V1Status> .WatchEvent>(line); var exception = new KubernetesException(statusEvent.Object); this.OnError?.Invoke(exception); } else { var @event = SafeJsonConvert.DeserializeObject <k8s.Watcher <T> .WatchEvent>(line); this.OnEvent?.Invoke(@event.Type, @event.Object); } } catch (Exception e) { // error if deserialized failed or onevent throws OnError?.Invoke(e); } } } catch (Exception e) { // error when transport error, IOException ect OnError?.Invoke(e); } finally { Watching = false; OnClosed?.Invoke(); } }