Beispiel #1
0
        private Task <WatchExitReason> WatchObjectAsync <TObject, TList>(
            TObject value,
            ListObjectWithHttpMessagesAsync <TObject, TList> listOperation,
            WatchEventDelegate <TObject> eventHandler,
            CancellationToken cancellationToken)
            where TObject : IKubernetesObject <V1ObjectMeta>
            where TList : IItems <TObject>
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (value.Metadata == null)
            {
                throw new ValidationException(ValidationRules.CannotBeNull, "value.Metadata");
            }

            if (value.Metadata.Name == null)
            {
                throw new ValidationException(ValidationRules.CannotBeNull, "value.Metadata.Name");
            }

            return(this.WatchObjectAsync <TObject, TList>(
                       fieldSelector: $"metadata.name={value.Metadata.Name}",
                       value.Metadata.ResourceVersion,
                       listOperation,
                       eventHandler,
                       cancellationToken));
        }
Beispiel #2
0
        public async Task DeletePodAsync_ApiDisconnects_Errors_Async()
        {
            var pod =
                new V1Pod()
            {
                Kind     = V1Pod.KubeKind,
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-pod",
                    NamespaceProperty = "default",
                },
                Status = new V1PodStatus()
                {
                    Phase = "Pending",
                },
            };

            WatchEventDelegate <V1Pod>             callback  = null;
            TaskCompletionSource <WatchExitReason> watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.DeleteNamespacedPodWithHttpMessagesAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, null, null, null, null, null, null, null, default))
            .Returns(Task.FromResult(new HttpOperationResponse <V1Pod>()
            {
                Body = pod, Response = new HttpResponseMessage(HttpStatusCode.OK)
            })).Verifiable();

            protocol
            .Setup(p => p.WatchNamespacedObjectAsync(pod, protocol.Object.ListNamespacedPodWithHttpMessagesAsync, It.IsAny <WatchEventDelegate <V1Pod> >(), It.IsAny <CancellationToken>()))
            .Returns <V1Pod, ListNamespacedObjectWithHttpMessagesAsync <V1Pod, V1PodList>, WatchEventDelegate <V1Pod>, CancellationToken>((pod, list, watcher, ct) =>
            {
                Assert.NotNull(list);
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.DeletePodAsync(pod, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // Simulate the watch task stopping
                watchTask.SetResult(WatchExitReason.ServerDisconnected);

                // The watch completes with an exception.
                var ex = await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);

                Assert.Equal("The API server unexpectedly closed the connection while watching Pod 'my-pod'.", ex.Message);
            }

            protocol.Verify();
        }
Beispiel #3
0
 /// <summary>
 /// Asynchronously watches a pod.
 /// </summary>
 /// <param name="value">
 /// The pod being watched.
 /// </param>
 /// <param name="eventHandler">
 /// An handler which processes a watch event, and lets the watcher know whether
 /// to continue watching or not.
 /// </param>
 /// <param name="cancellationToken">
 /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous
 /// operation.
 /// </param>
 /// <returns>
 /// A <see cref="Task"/> which represents the watch operation. The task completes
 /// when the watcher stops watching for events. The <see cref="WatchExitReason"/>
 /// return value describes why the watcher stopped. The task errors if the watch
 /// loop errors.
 /// </returns>
 public virtual Task <WatchExitReason> WatchPodAsync(
     V1Pod value,
     WatchEventDelegate <V1Pod> eventHandler,
     CancellationToken cancellationToken)
 {
     return(this.protocol.WatchNamespacedObjectAsync(
                value,
                this.protocol.ListNamespacedPodWithHttpMessagesAsync,
                eventHandler,
                cancellationToken));
 }
Beispiel #4
0
 /// <inheritdoc/>
 public Task <WatchExitReason> WatchCustomResourceDefinitionAsync(
     V1CustomResourceDefinition value,
     WatchEventDelegate <V1CustomResourceDefinition> eventHandler,
     CancellationToken cancellationToken)
 {
     return(this.WatchObjectAsync(
                value,
                this.ListCustomResourceDefinitionWithHttpMessagesAsync,
                eventHandler,
                cancellationToken));
 }
Beispiel #5
0
        public async Task DeletePodAsync_PodDeleted_Returns_Async()
        {
            var pod =
                new V1Pod()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-pod",
                    NamespaceProperty = "default",
                },
                Status = new V1PodStatus()
                {
                    Phase = "Pending",
                },
            };

            WatchEventDelegate <V1Pod>             callback  = null;
            TaskCompletionSource <WatchExitReason> watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.DeleteNamespacedPodWithHttpMessagesAsync(pod.Metadata.Name, pod.Metadata.NamespaceProperty, null, null, null, null, null, null, null, default))
            .Returns(Task.FromResult(new HttpOperationResponse <V1Pod>()
            {
                Body = pod, Response = new HttpResponseMessage(HttpStatusCode.OK)
            })).Verifiable();

            protocol
            .Setup(p => p.WatchNamespacedObjectAsync(pod, protocol.Object.ListNamespacedPodWithHttpMessagesAsync, It.IsAny <WatchEventDelegate <V1Pod> >(), It.IsAny <CancellationToken>()))
            .Returns <V1Pod, ListNamespacedObjectWithHttpMessagesAsync <V1Pod, V1PodList>, WatchEventDelegate <V1Pod>, CancellationToken>((pod, list, watcher, ct) =>
            {
                Assert.NotNull(list);
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.DeletePodAsync(pod, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // The callback continues watching until the pod is deleted
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, pod).ConfigureAwait(false));
                Assert.Equal(WatchResult.Stop, await callback(WatchEventType.Deleted, pod).ConfigureAwait(false));
                watchTask.SetResult(WatchExitReason.ClientDisconnected);

                await task.ConfigureAwait(false);
            }

            protocol.Verify();
        }
Beispiel #6
0
        private async Task <WatchExitReason> WatchAsync <TObject, TList>(
            HttpOperationResponse <TList> response,
            WatchEventDelegate <TObject> eventHandler,
            CancellationToken cancellationToken)
            where TObject : IKubernetesObject <V1ObjectMeta>
            where TList : IItems <TObject>
        {
            using (var watchContent = (WatchHttpContent)response.Response.Content)
                using (var content = watchContent.OriginalContent)
                    using (var stream = await content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false))
                        using (var reader = new StreamReader(stream))
                        {
                            string?line;

                            // ReadLineAsync will return null when we've reached the end of the stream.
                            try
                            {
                                while ((line = await reader.ReadLineAsync(cancellationToken).ConfigureAwait(false)) != null)
                                {
                                    var genericEvent =
                                        SafeJsonConvert.DeserializeObject <Watcher <KubernetesObject> .WatchEvent>(line);

                                    if (genericEvent.Object.Kind == "Status")
                                    {
                                        var statusEvent = SafeJsonConvert.DeserializeObject <Watcher <V1Status> .WatchEvent>(line);
                                        this.logger.LogInformation("Stopped watching '{kind}' objects because of a status event with payload {status}", typeof(TObject).Name, statusEvent.Object);
                                        throw new KubernetesException(statusEvent.Object);
                                    }
                                    else
                                    {
                                        var @event = SafeJsonConvert.DeserializeObject <Watcher <TObject> .WatchEvent>(line);
                                        this.logger.LogDebug("Got an {event} event for a {kind} object", @event.Type, typeof(TObject).Name);

                                        if (await eventHandler(@event.Type, @event.Object).ConfigureAwait(false) == WatchResult.Stop)
                                        {
                                            this.logger.LogInformation("Stopped watching '{kind}' objects because the client requested to stop watching.", typeof(TObject).Name);
                                            return(WatchExitReason.ClientDisconnected);
                                        }
                                    }
                                }
                            }
                            catch (Exception ex) when(cancellationToken.IsCancellationRequested)
                            {
                                this.logger.LogInformation("Stopped watching '{kind}' objects because a cancellation request was received.", typeof(TObject).Name);
                                throw new TaskCanceledException("The watch operation was cancelled.", ex);
                            }

                            this.logger.LogInformation("Stopped watching '{kind}' objects because the server closed the connection.", typeof(TObject).Name);
                            return(WatchExitReason.ServerDisconnected);
                        }
        }
Beispiel #7
0
        public async Task WaitForPodRunning_PodFails_Returns_Async()
        {
            var pod =
                new V1Pod()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-pod",
                },
                Status = new V1PodStatus()
                {
                    Phase = "Pending",
                },
            };

            WatchEventDelegate <V1Pod>             callback  = null;
            TaskCompletionSource <WatchExitReason> watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchNamespacedObjectAsync(pod, protocol.Object.ListNamespacedPodWithHttpMessagesAsync, It.IsAny <WatchEventDelegate <V1Pod> >(), It.IsAny <CancellationToken>()))
            .Returns <V1Pod, ListNamespacedObjectWithHttpMessagesAsync <V1Pod, V1PodList>, WatchEventDelegate <V1Pod>, CancellationToken>((pod, list, watcher, ct) =>
            {
                Assert.NotNull(list);
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForPodRunningAsync(pod, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // The callback throws an exception when a deleted event was received.
                pod.Status.Phase  = "Failed";
                pod.Status.Reason = "Something went wrong!";

                var ex = await Assert.ThrowsAsync <KubernetesException>(() => callback(WatchEventType.Modified, pod)).ConfigureAwait(false);

                watchTask.SetException(ex);
                Assert.Equal("The pod my-pod has failed: Something went wrong!", ex.Message);

                // The watch task propagates this exception.
                await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);
            }

            protocol.Verify();
        }
Beispiel #8
0
        public async Task DeleteCustomResourceDefinitionAsync_CustomResourceDefinitionDeleted_Returns_Async()
        {
            var customResourceDefinition =
                new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd",
                },
                Status = new V1CustomResourceDefinitionStatus()
                {
                },
            };

            WatchEventDelegate <V1CustomResourceDefinition> callback  = null;
            TaskCompletionSource <WatchExitReason>          watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.DeleteCustomResourceDefinitionWithHttpMessagesAsync(customResourceDefinition.Metadata.Name, null, null, null, null, null, null, null, default))
            .Returns(Task.FromResult(new HttpOperationResponse <V1Status>()
            {
                Body = new V1Status(), Response = new HttpResponseMessage(HttpStatusCode.OK)
            })).Verifiable();

            protocol
            .Setup(p => p.WatchCustomResourceDefinitionAsync(customResourceDefinition, It.IsAny <WatchEventDelegate <V1CustomResourceDefinition> >(), It.IsAny <CancellationToken>()))
            .Returns <V1CustomResourceDefinition, WatchEventDelegate <V1CustomResourceDefinition>, CancellationToken>((customResourceDefinition, watcher, ct) =>
            {
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.DeleteCustomResourceDefinitionAsync(customResourceDefinition, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // The callback continues watching until the CustomResourceDefinition is deleted
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, customResourceDefinition).ConfigureAwait(false));
                Assert.Equal(WatchResult.Stop, await callback(WatchEventType.Deleted, customResourceDefinition).ConfigureAwait(false));
                watchTask.SetResult(WatchExitReason.ClientDisconnected);

                await task.ConfigureAwait(false);
            }

            protocol.Verify();
        }
Beispiel #9
0
        public async Task DeleteCustomResourceDefinitionAsync_RespectsTimeout_Async()
        {
            var customResourceDefinition =
                new V1CustomResourceDefinition()
            {
                Kind     = V1CustomResourceDefinition.KubeKind,
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd",
                },
                Status = new V1CustomResourceDefinitionStatus()
                {
                },
            };

            WatchEventDelegate <V1CustomResourceDefinition> callback  = null;
            TaskCompletionSource <WatchExitReason>          watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.DeleteCustomResourceDefinitionWithHttpMessagesAsync(customResourceDefinition.Metadata.Name, null, null, null, null, null, null, null, default))
            .Returns(Task.FromResult(new HttpOperationResponse <V1Status>()
            {
                Body = new V1Status(), Response = new HttpResponseMessage(HttpStatusCode.OK)
            })).Verifiable();

            protocol
            .Setup(p => p.WatchCustomResourceDefinitionAsync(customResourceDefinition, It.IsAny <WatchEventDelegate <V1CustomResourceDefinition> >(), It.IsAny <CancellationToken>()))
            .Returns <V1CustomResourceDefinition, WatchEventDelegate <V1CustomResourceDefinition>, CancellationToken>((customResourceDefinition, watcher, ct) =>
            {
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.DeleteCustomResourceDefinitionAsync(customResourceDefinition, TimeSpan.Zero, default);
                Assert.NotNull(callback);

                // The watch completes with an exception.
                var ex = await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);

                Assert.Equal("The CustomResourceDefinition 'my-crd' was not deleted within a timeout of 0 seconds.", ex.Message);
            }

            protocol.Verify();
        }
Beispiel #10
0
        public async Task <string> Watch(string collectionname, string aggregates, WatchEventDelegate onWatchEvent)
        {
            WatchMessage q = new WatchMessage();

            q.collectionname = collectionname; q.aggregates = JArray.Parse(aggregates);
            q = await q.SendMessage <WatchMessage>(this);

            if (!string.IsNullOrEmpty(q.error))
            {
                throw new Exception(q.error);
            }
            if (!watches.ContainsKey(q.id))
            {
                watches.Add(q.id, onWatchEvent);
            }
            return(q.id);
        }
Beispiel #11
0
 /// <summary>
 /// Asynchronously watches pods.
 /// </summary>
 /// <param name="fieldSelector">
 /// A selector to restrict the list of returned objects by their fields. Defaults
 /// to everything.
 /// </param>
 /// <param name="labelSelector">
 /// A selector to restrict the list of returned objects by their labels. Defaults
 /// to everything.
 /// </param>
 /// <param name="resourceVersion">
 /// resourceVersion sets a constraint on what resource versions a request may be
 /// served from. See <see href="https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions"/>
 /// for details. Defaults to unset.
 /// </param>
 /// <param name="resourceVersionMatch">
 /// <paramref name="resourceVersionMatch"/> determines how <paramref name="resourceVersion"/> is applied to list calls.
 /// It is highly recommended that <paramref name="resourceVersionMatch"/> be set for list calls where
 /// <paramref name="resourceVersion"/> is set. See <see href="https://kubernetes.io/docs/reference/using-api/api-concepts/#resource-versions"/>
 /// for details. Defaults to <see langword="null"/>.
 /// </param>
 /// <param name="eventHandler">
 /// An handler which processes a watch event, and lets the watcher know whether
 /// to continue watching or not.
 /// </param>
 /// <param name="cancellationToken">
 /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous
 /// operation.
 /// </param>
 /// <returns>
 /// A <see cref="Task"/> which represents the watch operation. The task completes
 /// when the watcher stops watching for events. The <see cref="WatchExitReason"/>
 /// return value describes why the watcher stopped. The task errors if the watch
 /// loop errors.
 /// </returns>
 public virtual Task <WatchExitReason> WatchPodAsync(
     string fieldSelector,
     string labelSelector,
     string resourceVersion,
     string resourceVersionMatch,
     WatchEventDelegate <V1Pod> eventHandler,
     CancellationToken cancellationToken)
 {
     return(this.protocol.WatchNamespacedObjectAsync(
                this.options.Value.Namespace,
                fieldSelector: fieldSelector,
                labelSelector: labelSelector,
                resourceVersion: resourceVersion,
                resourceVersionMatch: resourceVersionMatch,
                this.protocol.ListNamespacedPodWithHttpMessagesAsync,
                eventHandler,
                cancellationToken));
 }
Beispiel #12
0
        public async Task WaitForPodRunning_RespectsTimeout_Async()
        {
            var pod =
                new V1Pod()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-pod",
                },
                Status = new V1PodStatus()
                {
                    Phase = "Pending",
                },
            };

            WatchEventDelegate <V1Pod>             callback  = null;
            TaskCompletionSource <WatchExitReason> watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchNamespacedObjectAsync(pod, protocol.Object.ListNamespacedPodWithHttpMessagesAsync, It.IsAny <WatchEventDelegate <V1Pod> >(), It.IsAny <CancellationToken>()))
            .Returns <V1Pod, ListNamespacedObjectWithHttpMessagesAsync <V1Pod, V1PodList>, WatchEventDelegate <V1Pod>, CancellationToken>((pod, list, watcher, ct) =>
            {
                Assert.NotNull(list);
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForPodRunningAsync(pod, TimeSpan.Zero, default);
                Assert.NotNull(callback);

                // The watch completes with an exception.
                var ex = await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);

                Assert.Equal("The pod my-pod did not transition to the completed state within a timeout of 0 seconds.", ex.Message);
            }

            protocol.Verify();
        }
Beispiel #13
0
        public void WatchPodAsync_ForwardRequests()
        {
            var tcs          = new TaskCompletionSource <WatchExitReason>();
            var eventHandler = new WatchEventDelegate <V1Pod>((type, pod) => Task.FromResult(WatchResult.Continue));

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol
            .Setup(p => p.WatchNamespacedObjectAsync("default", "fieldSelector", "labelSelector", "resourceVersion", "resourceVersionMatch", protocol.Object.ListNamespacedPodWithHttpMessagesAsync, eventHandler, default))
            .Returns(tcs.Task);
            protocol.Setup(p => p.Dispose()).Verifiable();

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                Assert.Same(tcs.Task, client.WatchPodAsync("fieldSelector", "labelSelector", "resourceVersion", "resourceVersionMatch", eventHandler, default));
            }

            protocol.Verify();
        }
Beispiel #14
0
        public async Task WaitForCustomResourceDefinitionEstablishedAsync_PodDeleted_Returns_Async()
        {
            var crd =
                new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd",
                },
            };

            WatchEventDelegate <V1CustomResourceDefinition> callback  = null;
            TaskCompletionSource <WatchExitReason>          watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchCustomResourceDefinitionAsync(crd, It.IsAny <WatchEventDelegate <V1CustomResourceDefinition> >(), It.IsAny <CancellationToken>()))
            .Returns <V1CustomResourceDefinition, WatchEventDelegate <V1CustomResourceDefinition>, CancellationToken>((crd, watcher, ct) =>
            {
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForCustomResourceDefinitionEstablishedAsync(crd, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // The callback throws an exception when a deleted event was received.
                var ex = await Assert.ThrowsAsync <KubernetesException>(() => callback(WatchEventType.Deleted, crd)).ConfigureAwait(false);

                watchTask.SetException(ex);
                Assert.Equal("The CRD 'my-crd' was deleted.", ex.Message);

                // The watch task propagates this exception.
                await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);
            }

            protocol.Verify();
        }
Beispiel #15
0
        public async Task WaitForCustomResourceDefinitionEstablishedAsync_ApiDisconnects_Errors_Async()
        {
            var crd =
                new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd",
                },
            };

            WatchEventDelegate <V1CustomResourceDefinition> callback  = null;
            TaskCompletionSource <WatchExitReason>          watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchCustomResourceDefinitionAsync(crd, It.IsAny <WatchEventDelegate <V1CustomResourceDefinition> >(), It.IsAny <CancellationToken>()))
            .Returns <V1CustomResourceDefinition, WatchEventDelegate <V1CustomResourceDefinition>, CancellationToken>((pod, watcher, ct) =>
            {
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForCustomResourceDefinitionEstablishedAsync(crd, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // Simulate the watch task stopping
                watchTask.SetResult(WatchExitReason.ServerDisconnected);

                // The watch completes with an exception.
                var ex = await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);

                Assert.Equal("The API server unexpectedly closed the connection while watching CRD 'my-crd'.", ex.Message);
            }

            protocol.Verify();
        }
Beispiel #16
0
        /// <inheritdoc/>
        public async Task <WatchExitReason> WatchNamespacedObjectAsync <TObject, TList>(
            string @namespace,
            string?fieldSelector,
            string?labelSelector,
            string?resourceVersion,
            string?resourceVersionMatch,
            ListNamespacedObjectWithHttpMessagesAsync <TObject, TList> listOperation,
            WatchEventDelegate <TObject> eventHandler,
            CancellationToken cancellationToken)
            where TObject : IKubernetesObject <V1ObjectMeta>
            where TList : IItems <TObject>
        {
            if (@namespace == null)
            {
                throw new ArgumentNullException(nameof(@namespace));
            }

            if (listOperation == null)
            {
                throw new ArgumentNullException(nameof(listOperation));
            }

            if (eventHandler == null)
            {
                throw new ArgumentNullException(nameof(eventHandler));
            }

            using (var response = await listOperation(
                       namespaceParameter: @namespace,
                       allowWatchBookmarks: true,
                       fieldSelector: fieldSelector,
                       labelSelector: labelSelector,
                       resourceVersion: resourceVersion,
                       resourceVersionMatch: resourceVersionMatch,
                       watch: true,
                       cancellationToken: cancellationToken).ConfigureAwait(false))
            {
                return(await this.WatchAsync(response, eventHandler, cancellationToken).ConfigureAwait(false));
            }
        }
Beispiel #17
0
        public async Task WaitForCustomResourceDefinitionEstablishedAsync_RespectsTimeout_Async()
        {
            var crd =
                new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd",
                },
            };

            WatchEventDelegate <V1CustomResourceDefinition> callback  = null;
            TaskCompletionSource <WatchExitReason>          watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchCustomResourceDefinitionAsync(crd, It.IsAny <WatchEventDelegate <V1CustomResourceDefinition> >(), It.IsAny <CancellationToken>()))
            .Returns <V1CustomResourceDefinition, WatchEventDelegate <V1CustomResourceDefinition>, CancellationToken>((pod, watcher, ct) =>
            {
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForCustomResourceDefinitionEstablishedAsync(crd, TimeSpan.Zero, default);
                Assert.NotNull(callback);

                // The watch completes with an exception.
                var ex = await Assert.ThrowsAsync <KubernetesException>(() => task).ConfigureAwait(false);

                Assert.Equal("The CRD 'my-crd' was not established within a timeout of 0 seconds.", ex.Message);
            }

            protocol.Verify();
        }
Beispiel #18
0
        private async Task <WatchExitReason> WatchObjectAsync <TObject, TList>(
            string fieldSelector,
            string resourceVersion,
            ListObjectWithHttpMessagesAsync <TObject, TList> listOperation,
            WatchEventDelegate <TObject> eventHandler,
            CancellationToken cancellationToken)
            where TObject : IKubernetesObject <V1ObjectMeta>
            where TList : IItems <TObject>
        {
            if (eventHandler == null)
            {
                throw new ArgumentNullException(nameof(eventHandler));
            }

            using (var response = await listOperation(
                       fieldSelector: fieldSelector,
                       resourceVersion: resourceVersion,
                       watch: true,
                       cancellationToken: cancellationToken).ConfigureAwait(false))
            {
                return(await this.WatchAsync(response, eventHandler, cancellationToken).ConfigureAwait(false));
            }
        }
Beispiel #19
0
        public async Task WaitForCustomResourceDefinitionEstablishedAsync_CrdEstablished_Returns_Async()
        {
            var crd =
                new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd",
                },
            };

            WatchEventDelegate <V1CustomResourceDefinition> callback  = null;
            TaskCompletionSource <WatchExitReason>          watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchCustomResourceDefinitionAsync(crd, It.IsAny <WatchEventDelegate <V1CustomResourceDefinition> >(), It.IsAny <CancellationToken>()))
            .Returns <V1CustomResourceDefinition, WatchEventDelegate <V1CustomResourceDefinition>, CancellationToken>((pod, watcher, ct) =>
            {
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForCustomResourceDefinitionEstablishedAsync(crd, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // Simulate a first callback where nothing changes. The task keeps listening.
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, crd).ConfigureAwait(false));

                // Another condition is present, and True
                crd.Status = new V1CustomResourceDefinitionStatus()
                {
                    Conditions = new V1CustomResourceDefinitionCondition[]
                    {
                        new V1CustomResourceDefinitionCondition()
                        {
                            Type   = "Terminating",
                            Status = "True",
                        },
                    },
                };
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, crd).ConfigureAwait(false));

                // The established condition is present, but false.
                crd.Status = new V1CustomResourceDefinitionStatus()
                {
                    Conditions = new V1CustomResourceDefinitionCondition[]
                    {
                        new V1CustomResourceDefinitionCondition()
                        {
                            Type   = "Established",
                            Status = "False",
                        },
                    },
                };
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, crd).ConfigureAwait(false));

                // The callback signals to stop watching when the CRD is established.
                crd.Status = new V1CustomResourceDefinitionStatus()
                {
                    Conditions = new V1CustomResourceDefinitionCondition[]
                    {
                        new V1CustomResourceDefinitionCondition()
                        {
                            Type   = "Established",
                            Status = "True",
                        },
                    },
                };
                Assert.Equal(WatchResult.Stop, await callback(WatchEventType.Modified, crd).ConfigureAwait(false));

                // The watch completes successfully.
                watchTask.SetResult(WatchExitReason.ClientDisconnected);
                await task.ConfigureAwait(false);
            }

            protocol.Verify();
        }
Beispiel #20
0
        public async Task WaitForPodRunning_PodStarts_Returns_Async()
        {
            var pod =
                new V1Pod()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-pod",
                },
                Status = new V1PodStatus()
                {
                    Phase = "Pending",
                },
            };

            WatchEventDelegate <V1Pod>             callback  = null;
            TaskCompletionSource <WatchExitReason> watchTask = new TaskCompletionSource <WatchExitReason>();

            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol.Setup(p => p.Dispose()).Verifiable();
            protocol
            .Setup(p => p.WatchNamespacedObjectAsync(pod, protocol.Object.ListNamespacedPodWithHttpMessagesAsync, It.IsAny <WatchEventDelegate <V1Pod> >(), It.IsAny <CancellationToken>()))
            .Returns <V1Pod, ListNamespacedObjectWithHttpMessagesAsync <V1Pod, V1PodList>, WatchEventDelegate <V1Pod>, CancellationToken>((pod, list, watcher, ct) =>
            {
                Assert.NotNull(list);
                callback = watcher;
                return(watchTask.Task);
            });

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var task = client.WaitForPodRunningAsync(pod, TimeSpan.FromMinutes(1), default);
                Assert.NotNull(callback);

                // Simulate a first callback where nothing changes. The task keeps listening.
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, pod).ConfigureAwait(false));

                // The callback signals to continue watching when the pod is running but not all containers are ready.
                pod.Status.Phase             = "Running";
                pod.Status.ContainerStatuses =
                    new V1ContainerStatus[]
                {
                    new V1ContainerStatus()
                    {
                        Ready = false,
                    },
                };
                Assert.Equal(WatchResult.Continue, await callback(WatchEventType.Modified, pod).ConfigureAwait(false));

                // The callback signals to stop watching when the pod starts.
                pod.Status.ContainerStatuses[0].Ready = true;
                Assert.Equal(WatchResult.Stop, await callback(WatchEventType.Modified, pod).ConfigureAwait(false));

                // The watch completes successfully.
                watchTask.SetResult(WatchExitReason.ClientDisconnected);
                await task.ConfigureAwait(false);
            }

            protocol.Verify();
        }