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));
        }
Example #4
0
 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);
        }
Example #7
0
    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...");
            }
        }
Example #9
0
        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);
        }
Example #10
0
        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);
        }
Example #11
0
        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);
        }
Example #12
0
        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));
        }
Example #13
0
        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: []"));
        }