internal static V1beta1CustomResourceDefinition Convert(this V1CustomResourceDefinition crd)
        {
            var crdVersion = crd.Spec.Versions.First();
            var betaCrd    = new V1beta1CustomResourceDefinition(
                new V1beta1CustomResourceDefinitionSpec(),
                $"{V1beta1CustomResourceDefinition.KubeGroup}/{V1beta1CustomResourceDefinition.KubeApiVersion}",
                V1beta1CustomResourceDefinition.KubeKind,
                crd.Metadata);

            betaCrd.Spec.Group = crd.Spec.Group;
            betaCrd.Spec.Names = new V1beta1CustomResourceDefinitionNames
            {
                Kind     = crd.Spec.Names.Kind,
                ListKind = crd.Spec.Names.ListKind,
                Singular = crd.Spec.Names.Singular,
                Plural   = crd.Spec.Names.Plural,
            };
            betaCrd.Spec.Scope    = crd.Spec.Scope;
            betaCrd.Spec.Version  = crdVersion.Name;
            betaCrd.Spec.Versions = new List <V1beta1CustomResourceDefinitionVersion>
            {
                new V1beta1CustomResourceDefinitionVersion(crdVersion.Name, true, true),
            };

            if (crdVersion.Subresources != null)
            {
                betaCrd.Spec.Subresources = new V1beta1CustomResourceSubresources(null, new { });
            }

            betaCrd.Spec.Validation = new V1beta1CustomResourceValidation(crdVersion.Schema.OpenAPIV3Schema.Convert());

            return(betaCrd);
        }
Пример #2
0
        public async Task CreateCustomResourceDefinitionAsync_UsesProtocol_Async()
        {
            var crd = new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name = "my-crd"
                }
            };
            var protocol = new Mock <IKubernetesProtocol>(MockBehavior.Strict);

            protocol
            .Setup(p => p.CreateCustomResourceDefinitionWithHttpMessagesAsync(crd, null, null, null, null, default))
            .ReturnsAsync(new HttpOperationResponse <V1CustomResourceDefinition>()
            {
                Body = crd
            });
            protocol.Setup(p => p.Dispose()).Verifiable();

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                var result = await client.CreateCustomResourceDefinitionAsync(crd, default).ConfigureAwait(false);

                Assert.Same(crd, result);
            }
        }
Пример #3
0
        public async Task InstallOrUpgradeCustomResourceDefinitionAsync_CrdNotInstalled_InstallsCrd_Async()
        {
            var clientMock = new Mock <KubernetesClient>()
            {
                CallBase = true,
            };

            var timeout = TimeSpan.Zero;
            var crd     = new V1CustomResourceDefinition()
            {
                Metadata = new V1ObjectMeta()
                {
                    Name   = "test",
                    Labels = new Dictionary <string, string>()
                    {
                        { Annotations.Version, "0.1" },
                    },
                },
            };

            clientMock.Setup(c => c.TryReadCustomResourceDefinitionAsync("test", default)).ReturnsAsync((V1CustomResourceDefinition)null).Verifiable();
            clientMock.Setup(c => c.CreateCustomResourceDefinitionAsync(crd, default)).ReturnsAsync(crd).Verifiable();

            var client = clientMock.Object;

            await client.InstallOrUpgradeCustomResourceDefinitionAsync(
                crd,
                timeout,
                default).ConfigureAwait(false);

            clientMock.Verify();
        }
Пример #4
0
        private async Task <V1CustomResourceDefinition> CreateOrReplaceCustomResourceDefinitionAsync(
            V1CustomResourceDefinition customResourceDefinition,
            CancellationToken cancellationToken)
        {
            // TODO: log messages from here

            if (customResourceDefinition is null)
            {
                throw new ArgumentNullException(nameof(customResourceDefinition));
            }

            var existingList = await _client.ListCustomResourceDefinitionAsync(
                fieldSelector : $"metadata.name={customResourceDefinition.Name()}",
                cancellationToken : cancellationToken);

            var existingCustomResourceDefinition = existingList?.Items?.SingleOrDefault();

            if (existingCustomResourceDefinition != null)
            {
                customResourceDefinition.Metadata.ResourceVersion = existingCustomResourceDefinition.ResourceVersion();

                return(await _client.ReplaceCustomResourceDefinitionAsync(
                           customResourceDefinition,
                           customResourceDefinition.Name(),
                           cancellationToken : cancellationToken));
            }
            else
            {
                return(await _client.CreateCustomResourceDefinitionAsync(
                           customResourceDefinition,
                           cancellationToken : cancellationToken));
            }
        }
Пример #5
0
        private async Task <string> DescribeObject(Kubernetes client, V1CustomResourceDefinition o, StringBuilder buffer)
        {
            var fetched = await client.ReadCustomResourceDefinitionAsync(o.Metadata.Name).ConfigureAwait(false);

            buffer.AppendLine($"API Veresion: {fetched.ApiVersion}");
            buffer.AppendLine($"Kind: {fetched.Kind}");
            buffer.AppendLine(DescribeMetadata(fetched.Metadata));
            return($"Custom Resource Definition - {fetched.Metadata.Name}");
        }
Пример #6
0
 /// <summary>
 /// Asynchronously deletes a custom resource definition.
 /// </summary>
 /// <param name="crd">
 /// The custom resource definition to delete.
 /// </param>
 /// <param name="timeout">
 /// The amount of time in which the pod should be deleted.
 /// </param>
 /// <param name="cancellationToken">
 /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
 /// </param>
 /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
 public virtual Task DeleteCustomResourceDefinitionAsync(V1CustomResourceDefinition crd, TimeSpan timeout, CancellationToken cancellationToken)
 {
     return(this.DeleteObjectAsync <V1CustomResourceDefinition>(
                crd,
                this.protocol.DeleteCustomResourceDefinitionAsync,
                this.protocol.WatchCustomResourceDefinitionAsync,
                timeout,
                cancellationToken));
 }
Пример #7
0
        internal static V1CustomResourceDefinition CreateCrd(this Type entityType)
        {
            var entityDefinition = entityType.CreateResourceDefinition();

            var crd = new V1CustomResourceDefinition(
                new V1CustomResourceDefinitionSpec(),
                $"{V1CustomResourceDefinition.KubeGroup}/{V1CustomResourceDefinition.KubeApiVersion}",
                V1CustomResourceDefinition.KubeKind,
                new V1ObjectMeta {
                Name = $"{entityDefinition.Plural}.{entityDefinition.Group}"
            });

            var spec = crd.Spec;

            spec.Group = entityDefinition.Group;
            spec.Names = new V1CustomResourceDefinitionNames
            {
                Kind     = entityDefinition.Kind,
                ListKind = entityDefinition.ListKind,
                Singular = entityDefinition.Singular,
                Plural   = entityDefinition.Plural,
            };
            spec.Scope = entityDefinition.Scope.ToString();

            var version = new V1CustomResourceDefinitionVersion();

            spec.Versions = new[] { version };

            version.Name    = entityDefinition.Version;
            version.Served  = true;
            version.Storage = true;

            if (entityType.GetProperty("Status") != null || entityType.GetProperty("status") != null)
            {
                version.Subresources = new V1CustomResourceSubresources(null, new object());
            }

            var columns = new List <V1CustomResourceColumnDefinition>();

            version.Schema = new V1CustomResourceValidation(MapType(entityType, columns, string.Empty));

            version.AdditionalPrinterColumns = entityType
                                               .GetCustomAttributes <GenericAdditionalPrinterColumnAttribute>(true)
                                               .Select(a => a.ToAdditionalPrinterColumn())
                                               .Concat(columns)
                                               .ToList();

            if (version.AdditionalPrinterColumns.Count == 0)
            {
                version.AdditionalPrinterColumns = null;
            }

            return(crd);
        }
Пример #8
0
        private static bool IsEstablished(V1CustomResourceDefinition value)
        {
            if (value?.Status?.Conditions == null)
            {
                return(false);
            }

            return(value.Status.Conditions.Any(
                       c => string.Equals(c.Type, "Established", StringComparison.OrdinalIgnoreCase) &&
                       string.Equals(c.Status, "True", StringComparison.OrdinalIgnoreCase)));
        }
Пример #9
0
 /// <inheritdoc/>
 public Task <WatchExitReason> WatchCustomResourceDefinitionAsync(
     V1CustomResourceDefinition value,
     WatchEventDelegate <V1CustomResourceDefinition> eventHandler,
     CancellationToken cancellationToken)
 {
     return(this.WatchObjectAsync(
                value,
                this.ListCustomResourceDefinitionWithHttpMessagesAsync,
                eventHandler,
                cancellationToken));
 }
Пример #10
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();
        }
Пример #11
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();
        }
Пример #12
0
        public CustomResourceDefinitionContext BuildDefinition(Type resourceType)
        {
            if (!resourceType.Implements <CustomResource>())
            {
                throw new ArgumentException("Custom Resource Definitions can only be generated for custom resources", nameof(resourceType));
            }

            try
            {
                var apiVersion = GetAttribute <ApiVersionAttribute>(resourceType).ApiVersion;
                var plural     = GetAttribute <PluralNameAttribute>(resourceType).PluralName.ToLower();
                var scope      = GetAttribute <ResourceScopeAttribute>(resourceType).ResourceScope;
                var kind       = GetAttribute <KindAttribute>(resourceType).Kind;
                var shortNames = new List <string> {
                    GetAttribute <ShortNameAttribute>(resourceType).ShortName
                };

                var crd = new V1CustomResourceDefinition
                {
                    ApiVersion = "apiextensions.k8s.io/v1",
                    Kind       = "CustomResourceDefinition",
                    Metadata   = new V1ObjectMeta
                    {
                        Name = $"{plural}.{apiVersion.Group}"
                    },
                    Spec = new V1CustomResourceDefinitionSpec
                    {
                        Group = apiVersion.Group,
                        Scope = scope.ToString(),
                        Names = new V1CustomResourceDefinitionNames(kind, plural, shortNames: shortNames, singular: kind.ToLower()),
                        PreserveUnknownFields = true,
                        Versions = new List <V1CustomResourceDefinitionVersion>
                        {
                            BuildVersion(apiVersion, resourceType)
                        }
                    },
                };

                return(new CustomResourceDefinitionContext <V1CustomResourceDefinition>(crd.Metadata.Name, crd));
            }
            catch (ArgumentException argEx)
            {
                throw new MissingMetadataAttributeException(argEx.Message, argEx);
            }
        }
Пример #13
0
        internal static V1CustomResourceDefinition CreateCrd(Type entityType)
        {
            var entityDefinition = CustomEntityDefinitionExtensions.CreateResourceDefinition(entityType);

            var crd = new V1CustomResourceDefinition(
                new V1CustomResourceDefinitionSpec(),
                $"{V1CustomResourceDefinition.KubeGroup}/{V1CustomResourceDefinition.KubeApiVersion}",
                V1CustomResourceDefinition.KubeKind,
                new V1ObjectMeta {
                Name = $"{entityDefinition.Plural}.{entityDefinition.Group}"
            });

            var spec = crd.Spec;

            spec.Group = entityDefinition.Group;
            spec.Names = new V1CustomResourceDefinitionNames
            {
                Kind     = entityDefinition.Kind,
                ListKind = entityDefinition.ListKind,
                Singular = entityDefinition.Singular,
                Plural   = entityDefinition.Plural,
            };
            spec.Scope = entityDefinition.Scope.ToString();

            var version = new V1CustomResourceDefinitionVersion();

            spec.Versions = new[] { version };

            // TODO: versions?
            version.Name    = entityDefinition.Version;
            version.Served  = true;
            version.Storage = true;

            if (entityType.GetProperty("Status") != null || entityType.GetProperty("status") != null)
            {
                version.Subresources = new V1CustomResourceSubresources(null, new { });
            }

            version.Schema = new V1CustomResourceValidation(MapType(entityType));

            return(crd);
        }
Пример #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();
        }
Пример #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();
        }
Пример #16
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();
        }
Пример #17
0
        public async Task TryReadCustomResourceDefinitionAsync_Found_ReturnsPod_Async()
        {
            var crd = new V1CustomResourceDefinition();

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

            protocol
            .Setup(p => p.ListCustomResourceDefinitionWithHttpMessagesAsync(null, null, "metadata.name=my-crd", null, null, null, null, null, null, null, null, default))
            .ReturnsAsync(new HttpOperationResponse <V1CustomResourceDefinitionList>()
            {
                Body = new V1CustomResourceDefinitionList()
                {
                    Items = new V1CustomResourceDefinition[] { crd }
                }
            });

            protocol.Setup(p => p.Dispose()).Verifiable();

            using (var client = new KubernetesClient(protocol.Object, KubernetesOptions.Default, NullLogger <KubernetesClient> .Instance, NullLoggerFactory.Instance))
            {
                Assert.Equal(crd, await client.TryReadCustomResourceDefinitionAsync("my-crd", default).ConfigureAwait(false));
            }
        }
Пример #18
0
        public static async Task <V1CustomResourceDefinition> CreateOrReplaceCustomResourceDefinitionAsync(
            this IKubernetes client,
            V1CustomResourceDefinition customResourceDefinition,
            CancellationToken cancellationToken)
        {
            if (client is null)
            {
                throw new ArgumentNullException(nameof(client));
            }

            if (customResourceDefinition is null)
            {
                throw new ArgumentNullException(nameof(customResourceDefinition));
            }

            var existingList = await client.ListCustomResourceDefinitionAsync(
                fieldSelector : $"metadata.name={customResourceDefinition.Name()}",
                cancellationToken : cancellationToken);

            var existingCustomResourceDefinition = existingList?.Items?.SingleOrDefault();

            if (existingCustomResourceDefinition != null)
            {
                customResourceDefinition.Metadata.ResourceVersion = existingCustomResourceDefinition.ResourceVersion();

                return(await client.ReplaceCustomResourceDefinitionAsync(
                           customResourceDefinition,
                           customResourceDefinition.Name(),
                           cancellationToken : cancellationToken));
            }
            else
            {
                return(await client.CreateCustomResourceDefinitionAsync(
                           customResourceDefinition,
                           cancellationToken : cancellationToken));
            }
        }
Пример #19
0
        /// <summary>
        /// Asynchronously creates a new custom resource definition.
        /// </summary>
        /// <param name="value">
        /// The custom resource definition to create.
        /// </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 asynchronous operation, and returns the newly created custom resource definition
        /// when completed.
        /// </returns>
        public virtual Task <V1CustomResourceDefinition> CreateCustomResourceDefinitionAsync(V1CustomResourceDefinition value, CancellationToken cancellationToken)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            return(this.RunTaskAsync(this.protocol.CreateCustomResourceDefinitionAsync(value, cancellationToken: cancellationToken)));
        }
Пример #20
0
        /// <summary>
        /// Asynchronously waits for a CRD to enter be established.
        /// </summary>
        /// <param name="value">
        /// The CRD which should be established.
        /// </param>
        /// <param name="timeout">
        /// The amount of time in which the CRD should be established.
        /// </param>
        /// <param name="cancellationToken">
        /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous operation.
        /// </param>
        /// <returns>
        /// The <see cref="V1CustomResourceDefinition"/>.
        /// </returns>
        public virtual async Task <V1CustomResourceDefinition> WaitForCustomResourceDefinitionEstablishedAsync(V1CustomResourceDefinition value, TimeSpan timeout, CancellationToken cancellationToken)
        {
            if (value == null)
            {
                throw new ArgumentNullException(nameof(value));
            }

            if (IsEstablished(value))
            {
                return(value);
            }

            CancellationTokenSource cts = new CancellationTokenSource();

            cancellationToken.Register(cts.Cancel);

            var watchTask = this.protocol.WatchCustomResourceDefinitionAsync(
                value,
                eventHandler: (type, updatedCrd) =>
            {
                value = updatedCrd;

                if (type == WatchEventType.Deleted)
                {
                    throw new KubernetesException($"The CRD '{value.Metadata.Name}' was deleted.");
                }

                if (IsEstablished(updatedCrd))
                {
                    return(Task.FromResult(WatchResult.Stop));
                }

                return(Task.FromResult(WatchResult.Continue));
            },
                cts.Token);

            if (await Task.WhenAny(watchTask, Task.Delay(timeout)).ConfigureAwait(false) != watchTask)
            {
                cts.Cancel();
                throw new KubernetesException($"The CRD '{value.Metadata.Name}' was not established within a timeout of {timeout.TotalSeconds} seconds.");
            }

            var result = await watchTask.ConfigureAwait(false);

            if (result != WatchExitReason.ClientDisconnected)
            {
                throw new KubernetesException($"The API server unexpectedly closed the connection while watching CRD '{value.Metadata.Name}'.");
            }

            return(value);
        }
Пример #21
0
        /// <summary>
        /// Asynchronously installs a <see cref="V1CustomResourceDefinition"/> in the cluster, or, if the <see cref="V1CustomResourceDefinition"/>
        /// is already installed, upgrades the <see cref="V1CustomResourceDefinition"/> if required.
        /// </summary>
        /// <param name="crd">
        /// The custom resource definition to install into the cluster.
        /// </param>
        /// <param name="timeout">
        /// The amount of time alotted to the operation.
        /// </param>
        /// <param name="cancellationToken">
        /// A <see cref="CancellationToken"/> which can be used to cancel the asynchronous task.
        /// </param>
        /// <returns>A <see cref="Task"/> representing the asynchronous operation.</returns>
        public virtual async Task <V1CustomResourceDefinition> InstallOrUpgradeCustomResourceDefinitionAsync(V1CustomResourceDefinition crd, TimeSpan timeout, CancellationToken cancellationToken)
        {
            if (crd == null)
            {
                throw new ArgumentNullException(nameof(crd));
            }

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

            if (crd.Metadata.Labels == null || !crd.Metadata.Labels.ContainsKey(Annotations.Version))
            {
                throw new ValidationException(ValidationRules.CannotBeNull, $"value.Metadata.Labels[{Annotations.Version}]");
            }

            var installedCrd = await this.TryReadCustomResourceDefinitionAsync(
                crd.Metadata.Name,
                cancellationToken : cancellationToken).ConfigureAwait(false);

            if (installedCrd == null)
            {
                // Deploy the CRD
                return(await this.CreateCustomResourceDefinitionAsync(
                           crd,
                           cancellationToken : cancellationToken).ConfigureAwait(false));
            }
            else
            {
                var version = new Version(crd.Metadata.Labels[Annotations.Version]);

                // Get the version of the installed CRD; set the version to 0.0 if not version
                // annotation could be found.
                var installedVersion = new Version(0, 0);
                if (installedCrd.Metadata.Labels.ContainsKey(Annotations.Version))
                {
                    installedVersion = new Version(installedCrd.Metadata.Labels[Annotations.Version]);
                }

                if (version > installedVersion)
                {
                    // Upgrade: delete and reinstall
                    await this.DeleteCustomResourceDefinitionAsync(
                        installedCrd,
                        timeout,
                        cancellationToken : cancellationToken).ConfigureAwait(false);

                    return(await this.CreateCustomResourceDefinitionAsync(
                               crd,
                               cancellationToken : cancellationToken).ConfigureAwait(false));
                }
                else
                {
                    return(installedCrd);
                }
            }
        }
        public async Task CRDTest()
        {
            const string crdName = "crontabs.stable.example.com";
            var          crds    = await this._kubeClient.ListCustomResourceDefinitionAsync();

            var crdToCleanUp = crds.Items.SingleOrDefault(c => c.Metadata.Name == crdName);

            if (crdToCleanUp != null)
            {
                await this._kubeClient.DeleteCustomResourceDefinitionAsync(crdName);
            }

            var newCrd = new V1CustomResourceDefinition
            {
                ApiVersion = "apiextensions.k8s.io/v1",
                Kind       = "CustomResourceDefinition",
                Metadata   = new V1ObjectMeta
                {
                    Name = "crontabs.stable.example.com"
                },
                Spec = new V1CustomResourceDefinitionSpec
                {
                    Group    = "stable.example.com",
                    Versions = new List <V1CustomResourceDefinitionVersion> {
                        new V1CustomResourceDefinitionVersion
                        {
                            Name    = "v1",
                            Served  = true,
                            Storage = true,
                            Schema  = new V1CustomResourceValidation {
                                OpenAPIV3Schema = new V1JSONSchemaProps
                                {
                                    Required = new List <string> {
                                        "CronSpec", "Image"
                                    },
                                    Type       = "object",
                                    Properties = new Dictionary <string, V1JSONSchemaProps>
                                    {
                                        { "CronSpec", new V1JSONSchemaProps {
                                              Type = "string"
                                          } },
                                        { "Image", new V1JSONSchemaProps {
                                              Type = "string"
                                          } }
                                    }
                                }
                            }
                        }
                    },
                    Scope = "Namespaced",
                    Names = new V1CustomResourceDefinitionNames
                    {
                        Plural     = "crontabs",
                        Singular   = "crontab",
                        Kind       = "CronTab",
                        ShortNames = new List <string> {
                            "ct"
                        }
                    }
                }
            };

            var crdCreated = await this._kubeClient.CreateCustomResourceDefinitionAsync(newCrd);

            crds = await this._kubeClient.ListCustomResourceDefinitionAsync();

            Assert.NotNull(crds);
            Assert.NotNull(crds.Items.SingleOrDefault(c => c.Metadata.Name == crdName));

            var newCustomObj = new TestCustomObject
            {
                ApiVersion = "stable.example.com/v1",
                Kind       = "CronTab",
                Metadata   = new V1ObjectMeta
                {
                    Name = "my-new-cron-object"
                },
                CronSpec = "* * * * */5",
                Image    = "my-awesome-cron-image"
            };

            var customObjCreated = ((JObject)await this._kubeClient.CreateNamespacedCustomObjectAsync(newCustomObj, "stable.example.com", "v1", "default", "crontabs")).ToObject <TestCustomObject>();

            Assert.NotNull(customObjCreated);

            var a = await this._kubeClient.ListNamespacedCustomObjectAsync("stable.example.com", "v1", "default", "crontabs");

            var customObjs = ((JObject)await this._kubeClient.ListNamespacedCustomObjectAsync("stable.example.com", "v1", "default", "crontabs"))["items"].ToObject <TestCustomObject[]>();

            Assert.NotNull(customObjs);
            Assert.True(customObjs.Length == 1);

            var customObjFound = ((JObject)await this._kubeClient.GetNamespacedCustomObjectAsync("stable.example.com", "v1", "default", "crontabs", "my-new-cron-object")).ToObject <TestCustomObject>();

            Assert.NotNull(customObjFound);

            await this._kubeClient.DeleteNamespacedCustomObjectAsync("stable.example.com", "v1", "default", "crontabs", "my-new-cron-object");

            await this._kubeClient.DeleteCustomResourceDefinitionAsync(crdName);
        }
Пример #23
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();
        }
Пример #24
0
        private async Task EnsureWebPingerCrdAsync()
        {
            var crds = await _kubernetes.ListCustomResourceDefinitionAsync(
                fieldSelector : $"metadata.name={WebPinger.Definition.Plural}.{WebPinger.Definition.Group}");

            if (!crds.Items.Any())
            {
                var crd = new V1CustomResourceDefinition
                {
                    Metadata = new V1ObjectMeta
                    {
                        Name   = $"{WebPinger.Definition.Plural}.{WebPinger.Definition.Group}",
                        Labels = new Dictionary <string, string>()
                        {
                            { "kiamol", "ch20" },
                            { "operator", "web-ping" }
                        }
                    },
                    Spec = new V1CustomResourceDefinitionSpec
                    {
                        Group = WebPinger.Definition.Group,
                        Scope = "Namespaced",
                        Names = new V1CustomResourceDefinitionNames
                        {
                            Plural     = WebPinger.Definition.Plural,
                            Singular   = WebPinger.Definition.Singular,
                            Kind       = WebPinger.Definition.Kind,
                            ShortNames = new List <string>
                            {
                                WebPinger.Definition.ShortName
                            }
                        },
                        Versions = new List <V1CustomResourceDefinitionVersion>
                        {
                            new V1CustomResourceDefinitionVersion
                            {
                                Name    = "v1",
                                Served  = true,
                                Storage = true,
                                Schema  = new V1CustomResourceValidation
                                {
                                    OpenAPIV3Schema = new V1JSONSchemaProps
                                    {
                                        Type       = "object",
                                        Properties = new Dictionary <string, V1JSONSchemaProps>
                                        {
                                            {
                                                "spec",
                                                new V1JSONSchemaProps
                                                {
                                                    Type       = "object",
                                                    Properties = new Dictionary <string, V1JSONSchemaProps>
                                                    {
                                                        {
                                                            "target",
                                                            new V1JSONSchemaProps
                                                            {
                                                                Type = "string"
                                                            }
                                                        },
                                                        {
                                                            "interval",
                                                            new V1JSONSchemaProps
                                                            {
                                                                Type = "string"
                                                            }
                                                        },
                                                        {
                                                            "method",
                                                            new V1JSONSchemaProps
                                                            {
                                                                Type = "string"
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                };

                await _kubernetes.CreateCustomResourceDefinitionAsync(crd);

                Console.WriteLine($"** Created CRD for Kind: {WebPinger.Definition.Kind}; ApiVersion: {WebPinger.Definition.Group}/{WebPinger.Definition.Version}");
            }
            else
            {
                Console.WriteLine($"** CRD already exists for Kind: {WebPinger.Definition.Kind}; ApiVersion: {WebPinger.Definition.Group}/{WebPinger.Definition.Version}");
            }
        }
Пример #25
0
        public async Task InstallUpgradeDeleteCrd_IntegrationTest_Async()
        {
            string name = $"{FormatName(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async))}s.kaponata.io";

            using (var client = this.CreateKubernetesClient())
            {
                var v1crd = new V1CustomResourceDefinition()
                {
                    Metadata = new V1ObjectMeta()
                    {
                        Name   = name,
                        Labels = new Dictionary <string, string>()
                        {
                            { Annotations.Version, "0.1" },
                        },
                    },
                    Spec = new V1CustomResourceDefinitionSpec()
                    {
                        Group = "kaponata.io",
                        Scope = "Namespaced",
                        Names = new V1CustomResourceDefinitionNames()
                        {
                            Plural   = $"{FormatName(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async))}s",
                            Singular = FormatName(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async)),
                            Kind     = FormatNameCamelCase(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async)),
                        },
                        Versions = new V1CustomResourceDefinitionVersion[]
                        {
                            new V1CustomResourceDefinitionVersion()
                            {
                                Name    = "v1alpha1",
                                Served  = true,
                                Storage = true,
                                Schema  = new V1CustomResourceValidation()
                                {
                                    OpenAPIV3Schema = new V1JSONSchemaProps()
                                    {
                                        Type = "object",
                                    },
                                },
                            },
                        },
                    },
                };

                var v2crd = new V1CustomResourceDefinition()
                {
                    Metadata = new V1ObjectMeta()
                    {
                        Name   = name,
                        Labels = new Dictionary <string, string>()
                        {
                            { Annotations.Version, "0.2" },
                        },
                    },
                    Spec = new V1CustomResourceDefinitionSpec()
                    {
                        Group = "kaponata.io",
                        Scope = "Namespaced",
                        Names = new V1CustomResourceDefinitionNames()
                        {
                            Plural   = $"{FormatName(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async))}s",
                            Singular = FormatName(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async)),
                            Kind     = FormatNameCamelCase(nameof(this.InstallUpgradeDeleteCrd_IntegrationTest_Async)),
                        },
                        Versions = new V1CustomResourceDefinitionVersion[]
                        {
                            new V1CustomResourceDefinitionVersion()
                            {
                                Name    = "v1alpha1",
                                Served  = true,
                                Storage = true,
                                Schema  = new V1CustomResourceValidation()
                                {
                                    OpenAPIV3Schema = new V1JSONSchemaProps()
                                    {
                                        Type = "object",
                                    },
                                },
                            },
                        },
                    },
                };

                var installedCrd = await client.InstallOrUpgradeCustomResourceDefinitionAsync(
                    v1crd,
                    TimeSpan.FromMinutes(1),
                    default).ConfigureAwait(false);

                installedCrd = await client.WaitForCustomResourceDefinitionEstablishedAsync(
                    installedCrd,
                    TimeSpan.FromMinutes(1),
                    default).ConfigureAwait(false);

                Assert.Equal("0.1", installedCrd.Metadata.Labels[Annotations.Version]);

                // Re-read to be sure.
                installedCrd = await client.TryReadCustomResourceDefinitionAsync(name, default);

                Assert.Equal("0.1", installedCrd.Metadata.Labels[Annotations.Version]);

                installedCrd = await client.InstallOrUpgradeCustomResourceDefinitionAsync(
                    v2crd,
                    TimeSpan.FromMinutes(1),
                    default).ConfigureAwait(false);

                installedCrd = await client.WaitForCustomResourceDefinitionEstablishedAsync(
                    installedCrd,
                    TimeSpan.FromMinutes(1),
                    default).ConfigureAwait(false);

                Assert.Equal("0.2", installedCrd.Metadata.Labels[Annotations.Version]);

                // Re-read to be sure.
                installedCrd = await client.TryReadCustomResourceDefinitionAsync(name, default);

                Assert.Equal("0.2", installedCrd.Metadata.Labels[Annotations.Version]);

                await client.DeleteCustomResourceDefinitionAsync(
                    installedCrd,
                    TimeSpan.FromMinutes(1),
                    default).ConfigureAwait(false);
            }
        }
 private async Task Delete(V1CustomResourceDefinition crd)
 {
     await State.Client.DeleteCustomResourceDefinitionAsync(crd.Metadata.Name);
 }