public ValueTask <CustomResourceDefinition> CreateCustomResourceDefinition(CustomResourceDefinition customResourceDefinition, CancellationToken cancellationToken = default) { if (customResourceDefinition == null) { throw new ArgumentNullException(nameof(customResourceDefinition)); } return(this.PostAsync <CustomResourceDefinition, CustomResourceDefinition>(CustomResourceDefinitionUrls.BaseUrl, payload: customResourceDefinition, cancellationToken: cancellationToken)); }
public CustomResourceWatcherTest() { _logger = new Mock <ILogger>(); _k8sClient = new Mock <IKubernetesWrapper>(); _cancellationTokenSource = new CancellationTokenSource(); _customResourceDefinition = new CustomResourceDefinition { ApiVersion = "test.nvidia.com/v1", PluralName = "Tests", Kind = "Test", Namespace = "test.nvidia.com" }; }
public ValueTask <CustomResourceDefinition> ReplaceCustomResourceDefinition(CustomResourceDefinition customResourceDefinition, CancellationToken cancellationToken = default) { if (customResourceDefinition == null) { throw new ArgumentNullException(nameof(customResourceDefinition)); } if (customResourceDefinition.Metadata == null || string.IsNullOrWhiteSpace(customResourceDefinition.Metadata.Name)) { throw new ArgumentNullException(nameof(customResourceDefinition.Metadata.Name), "Metadata.Name is required."); } return(this.PutAsync <CustomResourceDefinition, CustomResourceDefinition>(CustomResourceDefinitionUrls.ObjectUrl(customResourceDefinition.Metadata.Name), payload: customResourceDefinition, cancellationToken: cancellationToken)); }
public CrdCrudControllerBase( IHttpContextAccessor httpContextAccessor, ILogger <S> logger, IKubernetesWrapper kubernetesClient, CustomResourceDefinition customResourceDefinition, ConfigurationValidator configurationValidator, IOptions <DicomAdapterConfiguration> dicomAdapterConfiguration) { _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); _logger = logger; _kubernetesClient = kubernetesClient ?? throw new ArgumentNullException(nameof(kubernetesClient)); _customResourceDefinition = customResourceDefinition ?? throw new ArgumentNullException(nameof(customResourceDefinition)); _configurationValidator = configurationValidator ?? throw new ArgumentNullException(nameof(configurationValidator)); _dicomAdapterConfiguration = dicomAdapterConfiguration ?? throw new ArgumentNullException(nameof(dicomAdapterConfiguration)); }
public CrdCrudControllerBase( IServiceProvider serviceProvider, ILogger <S> logger, IKubernetesWrapper kubernetesClient, CustomResourceDefinition customResourceDefinition, ConfigurationValidator configurationValidator, IOptions <DicomAdapterConfiguration> dicomAdapterConfiguration) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _kubernetesClient = kubernetesClient ?? throw new ArgumentNullException(nameof(kubernetesClient)); _customResourceDefinition = customResourceDefinition ?? throw new ArgumentNullException(nameof(customResourceDefinition)); _configurationValidator = configurationValidator ?? throw new ArgumentNullException(nameof(configurationValidator)); _dicomAdapterConfiguration = dicomAdapterConfiguration ?? throw new ArgumentNullException(nameof(dicomAdapterConfiguration)); }
public static async Task Main() { var crd = new CustomResourceDefinition() { ApiVersion = "engineerd.dev/v1alpha1", PluralName = "examples", Kind = "Example", Namespace = "kubecontroller" }; var controller = new Controller <ExampleCRD>( new Kubernetes(KubernetesClientConfiguration.BuildConfigFromConfigFile()), crd, (WatchEventType eventType, ExampleCRD example) => Console.WriteLine("Event type: {0} for {1}", eventType, example.Metadata.Name)); var cts = new CancellationTokenSource(); await controller.StartAsync(cts.Token).ConfigureAwait(false); }
public MyStack() { var testNamespace = new Namespace("test-namespace"); var ct = new CustomResourceDefinition("crontab", new CustomResourceDefinitionArgs { Metadata = new ObjectMetaArgs { Name = "crontabs.dotnet.example.com" }, Spec = new CustomResourceDefinitionSpecArgs { Group = "dotnet.example.com", Version = "v1", Scope = "Namespaced", Names = new CustomResourceDefinitionNamesArgs { Plural = "crontabs", Singular = "crontab", Kind = "CronTab", ShortNames = { { "ct" } } } } }); new Pulumi.Kubernetes.ApiExtensions.CustomResource("my-new-cron-object", new CronTabArgs { Metadata = new ObjectMetaArgs { Namespace = testNamespace.Metadata.Apply(m => m.Name), Name = "my-new-cron-object2" }, Spec = new CronTabSpecArgs { CronSpec = "* * * * */5", Image = "my-awesome-cron-image" } }, new CustomResourceOptions { DependsOn = { ct } }); }
private async Task TryCreateResources() { try { var clusterVersionDefinition = new CustomResourceDefinition { ApiVersion = KUBE_API_VERSION, Kind = CustomResourceDefinition.KIND, Metadata = new ObjectMetadata { Name = this.GetClusterVersionObjectDefinitionName() }, Spec = new CustomResourceDefinitionSpec { Group = this._group, Version = PROVIDER_MODEL_VERSION, Scope = NAMESPACED, Names = new CustomResourceDefinitionNames { Plural = ClusterVersionEntity.PLURAL, Singular = ClusterVersionEntity.SINGULAR, Kind = ClusterVersionEntity.KIND, ShortNames = new List <string> { ClusterVersionEntity.SHORT_NAME } } } }; await this._kubeClient.CreateCRD(clusterVersionDefinition); var siloDefinition = new CustomResourceDefinition { ApiVersion = KUBE_API_VERSION, Kind = CustomResourceDefinition.KIND, Metadata = new ObjectMetadata { Name = this.GetSiloObjectDefinitionName() }, Spec = new CustomResourceDefinitionSpec { Group = this._group, Version = PROVIDER_MODEL_VERSION, Scope = NAMESPACED, Names = new CustomResourceDefinitionNames { Plural = SiloEntity.PLURAL, Singular = SiloEntity.SINGULAR, Kind = SiloEntity.KIND, ShortNames = new List <string> { SiloEntity.SHORT_NAME } } } }; await this._kubeClient.CreateCRD(siloDefinition); } catch (Exception exc) { // TODO: Handle conflicts better when the schema is already deployed this._logger?.LogWarning(exc, "We tried to create the resources but fail. Ignoring for now..."); } }
public async Task CRDTest() { var crds = await this._kubeClient.ListCRDs(); var crdToCleanUp = crds.SingleOrDefault(c => c.Metadata.Name == "crontabs.stable.example.com"); if (crdToCleanUp != null) { await this._kubeClient.DeleteCRD(crdToCleanUp); } var newCrd = new CustomResourceDefinition { ApiVersion = "apiextensions.k8s.io/v1beta1", Kind = "CustomResourceDefinition", Metadata = new ObjectMetadata { Name = "crontabs.stable.example.com" }, Spec = new CustomResourceDefinitionSpec { Group = "stable.example.com", Version = "v1", Scope = "Namespaced", Names = new CustomResourceDefinitionNames { Plural = "crontabs", Singular = "crontab", Kind = "CronTab", ShortNames = new List <string> { "ct" } } } }; var crdCreated = await this._kubeClient.CreateCRD(newCrd); crds = await this._kubeClient.ListCRDs(); Assert.NotNull(crds); Assert.True(crds.Count == 1); var newCustomObj = new TestCustomObject { ApiVersion = "stable.example.com/v1", Kind = "CronTab", Metadata = new ObjectMetadata { Name = "my-new-cron-object" }, CronSpec = "* * * * */5", Image = "my-awesome-cron-image" }; var customObjCreated = await this._kubeClient.CreateCustomObject("v1", "crontabs", newCustomObj); Assert.NotNull(customObjCreated); var customObjs = await this._kubeClient.ListCustomObjects <TestCustomObject>("v1", "crontabs"); Assert.NotNull(customObjs); Assert.True(customObjs.Count == 1); var customObjFound = await this._kubeClient.GetCustomObject <TestCustomObject>("my-new-cron-object", "v1", "crontabs"); Assert.NotNull(customObjFound); await this._kubeClient.DeleteCustomObject(customObjCreated.Metadata.Name, "v1", "crontabs"); crdToCleanUp = crds.SingleOrDefault(c => c.Metadata.Name == "crontabs.stable.example.com"); Assert.NotNull(crdToCleanUp); await this._kubeClient.DeleteCRD(crdToCleanUp); }
static async Task Main(string[] args) { if (!string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("IN_CLUSTER"))) { _configuration = KubernetesClientConfiguration.InClusterConfig(); Console.WriteLine($"== {Environment.GetEnvironmentVariable("OPERATOR_NAME")} will be started within cluster config... =="); } else { _configuration = KubernetesClientConfiguration.BuildConfigFromConfigFile(); Console.WriteLine($"== Debug environment will be started from config file... =="); } _client = new Kubernetes(_configuration); var resource = new CustomResourceDefinition() { ApiVersion = "selcukusta.com/v1alpha1", PluralName = "linkedconfigmaps", Kind = "LinkedConfigMap", Namespace = Environment.GetEnvironmentVariable("WATCH_NAMESPACE") ?? "default" }; var controller = new Controller <LinkedConfigMapCRD>( _client, resource, (WatchEventType type, LinkedConfigMapCRD item) => { Console.WriteLine($"== Event Type: {type}, Item Type: {item.Kind}, Item Name: {item.Metadata.Name} =="); switch (type) { case WatchEventType.Added: item.CreateConfigMapAsync(_client, resource.Namespace).ConfigureAwait(false); foreach (var deployment in item.Spec.LinkedDeployments) { item.AddMountToDeploymentAsync(_client, resource.Namespace, deployment.Name, deployment.ContainerName, deployment.MountPath).ConfigureAwait(false); } break; case WatchEventType.Modified: { item.UpdateConfigMapAsync(_client, resource.Namespace).ConfigureAwait(false); foreach (var deployment in item.Spec.LinkedDeployments) { item.UpdateDeploymentAsync(_client, resource.Namespace, deployment.Name).ConfigureAwait(false); } break; } case WatchEventType.Deleted: item.DeleteConfigMapAsync(_client, resource.Namespace).ConfigureAwait(false); foreach (var deployment in item.Spec.LinkedDeployments) { item.RemoveMountFromDeploymentAsync(_client, resource.Namespace, deployment.Name, deployment.ContainerName).ConfigureAwait(false); } break; } }); var cts = new CancellationTokenSource(); await controller.StartAsync(cts).ConfigureAwait(false); }
public async Task CustomObjectApiTests() { var namespaceName = this.fixture.Namespace; // // Create the namespace specified in ApiClient options. // var @namespace = new Namespace { ApiVersion = "v1", Kind = "Namespace", Metadata = new ObjectMeta { Name = namespaceName } }; var createdNamespace = await this.fixture.ApiClient.CreateNamespace(@namespace).ConfigureAwait(false); Assert.NotNull(createdNamespace); // // Create a CRD for our GrainFunction // var baseName = this.fixture.GetTemporaryObjectName("crd"); var group = "kubeleans.io"; var customResourceDefinitionName = $"{baseName}s.{group}"; var customResourceDefinition = new CustomResourceDefinition { ApiVersion = "apiextensions.k8s.io/v1beta1", Kind = "CustomResourceDefinition", Metadata = new ObjectMeta { Name = customResourceDefinitionName, Labels = new Dictionary <string, string>() { { "component", "kubeleans" } } }, Spec = new CustomResourceDefinitionSpec { Group = group, Version = "v1", Scope = "Namespaced", Names = new CustomResourceDefinitionNames { Plural = baseName + "s", Singular = baseName, Kind = this.fixture.GetKind(baseName), ShortNames = new List <string> { this.fixture.GetShortName(baseName) } } } }; var createdCustomResourceDefinition = await this.fixture.ApiClient.CreateCustomResourceDefinition(customResourceDefinition).ConfigureAwait(false); Assert.NotNull(createdCustomResourceDefinition); var isCreated = await PollWithPredicate( async() => { var polledCustomResourceDefinition = await this.fixture.ApiClient.GetCustomResourceDefinition(customResourceDefinitionName).ConfigureAwait(false); return(polledCustomResourceDefinition.Status.Conditions.FirstOrDefault(c => string.Compare(c.Status, "true", StringComparison.InvariantCultureIgnoreCase) == 0) != null); }).ConfigureAwait(false); Assert.True(isCreated); // // Create a GrainFunction Custom Object // var grainFunctionName = this.fixture.GetTemporaryObjectName("gf"); var grainFunction = new GrainFunction { ApiVersion = "kubeleans.io/v1", Kind = customResourceDefinition.Spec.Names.Kind, Metadata = new ObjectMeta { Name = grainFunctionName }, Spec = new GrainFunctionSpec { Binding = "http", Image = "httpgrains", Version = "v1" } }; var createdCustomObject = await this.fixture.ApiClient.CreateCustomObject(customResourceDefinition.Spec.Names.Plural, grainFunction).ConfigureAwait(false); // // List CustomObjects // var customObjects = await this.fixture.ApiClient.ListCustomObjects <GrainFunctionList>(customResourceDefinition.Spec.Names.Plural).ConfigureAwait(false); Assert.NotNull(customObjects); Assert.NotNull(customObjects.Items.FirstOrDefault(n => n.Metadata.Name == grainFunctionName)); // // Replace GrainFunction with an updated definition // grainFunction = await this.fixture.ApiClient.GetCustomObject <GrainFunction>(customResourceDefinition.Spec.Names.Plural, grainFunctionName).ConfigureAwait(false); grainFunction.Metadata.Labels = new Dictionary <string, string> { ["L1"] = "Test" }; var replacedGrainFunction = await this.fixture.ApiClient.ReplaceCustomObject(customResourceDefinition.Spec.Names.Plural, grainFunction).ConfigureAwait(false); // // Watch GrainFunction changes // var cts = new CancellationTokenSource(); var reset = new ManualResetEventSlim(false); var watchException = default(Exception); var watcher = new KubernetesWatcher <GrainFunction>((value) => { // // Look for the deleted customResourceDefinition in the list of changes // if (value.Type == WatchTypes.Deleted && value.Object.Metadata.Name == grainFunctionName) { reset.Set(); } return(Task.CompletedTask); }, (exception) => { watchException = exception; reset.Set(); return(Task.CompletedTask); }); var task = Task.Run(async() => await this.fixture.ApiClient.WatchCustomObjectChanges(customResourceDefinition.Spec.Names.Plural, watcher, cancellationToken: cts.Token).ConfigureAwait(false)); // // Delete GrainFunction // // Need to wait with the delete operation to make sure watcher catches up and gets the changes await Task.Delay(ApiDeleteDelayMilliseconds).ConfigureAwait(false); var deletedGrainFunction = await this.fixture.ApiClient.DeleteCustomObject <GrainFunction>(customResourceDefinition.Spec.Names.Plural, grainFunctionName).ConfigureAwait(false); Assert.NotNull(deletedGrainFunction); // Wait for watchers to be called and event signaled or time out Assert.True(reset.Wait(ApiWatcherTimeoutMilliseconds)); cts.Cancel(); await task.ConfigureAwait(false); // Verify that we had no exception during watch Assert.Null(watchException); // // Tead down created base objects // var deletedCustomResourceDefinition = await this.fixture.ApiClient.DeleteCustomResourceDefinition(customResourceDefinitionName).ConfigureAwait(false); Assert.NotNull(deletedCustomResourceDefinition); var deletedNamespace = await this.fixture.ApiClient.DeleteNamespace(namespaceName).ConfigureAwait(false); Assert.NotNull(deletedNamespace); }
public async Task <HttpOperationResponse <object> > PatchNamespacedCustomObjectWithHttpMessagesAsync <T>(CustomResourceDefinition crd, T item, string name) { Guard.Against.Null(name, nameof(name)); Guard.Against.Null(crd, nameof(crd)); Guard.Against.NullOrWhiteSpace(crd.ApiVersion, "crd.ApiVersion"); Guard.Against.NullOrWhiteSpace(crd.Namespace, "crd.Namespace"); Guard.Against.NullOrWhiteSpace(crd.PluralName, "crd.PluralName"); Guard.Against.Null(item, nameof(item)); return(await _client.PatchNamespacedCustomObjectWithHttpMessagesAsync( body : item, group : crd.ApiVersion.Split('/')[0], version : crd.ApiVersion.Split('/')[1], namespaceParameter : crd.Namespace, plural : crd.PluralName, name : name) .ConfigureAwait(false)); }
public void SerializeObject_ForCustomResource_RemovesEmptySets() { var template = new CustomResourceDefinition { metadata = new ObjectMeta { name = "test.crd" }, spec = new CustomResourceDefinitionSpec { group = "crd", names = new CustomResourceDefinitionNames { kind = "test", plural = "tests" }, scope = "Namespaced", subresources = new CustomResourceSubresources { status = new CustomResourceSubresourceStatus { } }, validation = new CustomResourceValidation { openAPIV3Schema = new Microsoft.OpenApi.Models.OpenApiSchema { Properties = new Dictionary <string, Microsoft.OpenApi.Models.OpenApiSchema> { { "apiVersion", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" } }, { "kind", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" } }, { "metadata", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "object" } }, { "spec", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "object", Properties = new Dictionary <string, Microsoft.OpenApi.Models.OpenApiSchema> { { "rules", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "array", Items = new Microsoft.OpenApi.Models.OpenApiSchema { Type = "object", Properties = new Dictionary <string, Microsoft.OpenApi.Models.OpenApiSchema> { { "backend", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "object", Properties = new Dictionary <string, Microsoft.OpenApi.Models.OpenApiSchema> { { "serviceName", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" } }, { "servicePort", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "integer", Format = "int32" } } } } }, { "host", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" } }, { "port", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "integer", Format = "int32" } } } } } }, { "tls", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "array", Items = new Microsoft.OpenApi.Models.OpenApiSchema { Type = "object", Properties = new Dictionary <string, Microsoft.OpenApi.Models.OpenApiSchema> { { "hosts", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "array", Items = new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" } } }, { "secretName", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "string" } } } } } } } } }, { "status", new Microsoft.OpenApi.Models.OpenApiSchema { Type = "object" } } } } }, version = "v1beta1" }, status = new CustomResourceDefinitionStatus { acceptedNames = new CustomResourceDefinitionNames { kind = "", plural = "" } } }; string yaml = YamlConverter.SerializeObject(template); Assert.IsFalse(yaml.Contains("allOf: []")); }