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); }
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); } }
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(); }
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)); } }
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}"); }
/// <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)); }
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); }
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))); }
/// <inheritdoc/> public Task <WatchExitReason> WatchCustomResourceDefinitionAsync( V1CustomResourceDefinition value, WatchEventDelegate <V1CustomResourceDefinition> eventHandler, CancellationToken cancellationToken) { return(this.WatchObjectAsync( value, this.ListCustomResourceDefinitionWithHttpMessagesAsync, eventHandler, cancellationToken)); }
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(); }
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(); }
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); } }
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); }
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(); }
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(); }
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(); }
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)); } }
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)); } }
/// <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))); }
/// <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); }
/// <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); }
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(); }
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}"); } }
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); }