Skip to main content

Resource Controllers

Introduction

Resource Controllers are called when Kubernetes resources are created, modified, or deleted. They are configured to watch specific resource types, have methods for responding to such updates.

Reconciler

Reconcilers are responsible for bringing the actual state of a resource to the desired state, which is expressed in the Custom Resource object specification.

When a reconcile event is triggered, it is always passed the current state of the resource to be reconciled.

danger

A reconciler must be idempotent

A function is said to be idempotent if it can be applied multiple times without changing the result beyond the initial application.

Triggers

Reconcile is triggered by the following events:

  • A resource has been created.
  • A resource has been updated.
  • A resource failed reconciliation and was requeued.
  • An object with an ownerReference to the resource.

Example

In its simplest form, this is what the ReconcileAsync method looks like:

using Neon.Operator;
using Neon.Operator.Controllers;

public class ExampleController : ResourceControllerBase<V1ExampleEntity>
{
public override async Task<ResourceControllerResult> ReconcileAsync(V1ExampleEntity resource, CancellationToken cancellationToken = default)
{
// TODO: apply logic

return ResourceControllerResult.Ok();
}
}

Status Updates

StatusModifiedAsync is called when the status of a resource has changed.

public override async Task StatusModifiedAsync(V1ExampleEntity resource, CancellationToken cancellationToken = default)
{
// react to status update
}

Requeuing

When a reconcile event throws an exception, it will be requeued.

Global defaults

Global defaults can be configured by setting ResourceManagerOptions in ConfigureOperator.

var k8s = KubernetesOperatorHost
.CreateDefaultBuilder()
.ConfigureOperator(configure =>
{
configure.ResourceManagerOptions = new ResourceManagerOptions()
{
ErrorMaxRequeueInterval = TimeSpan.FromMinutes(1),
ErrorMaxRetryCount = 10,
ErrorMinRequeueInterval = TimeSpan.FromSeconds(1)
};
})
.UseStartup<Startup>()
.Build();

await = k8s.RunAsync();

Controller specific

Controllers are configurable by passing ResourceManagerOptions to your controller when calling AddController in Startup.cs.

note

To do this manual configuration, automatic configuration should be disabled by adding the [Controller(Ignore = true)] attribute to the controller.

To do this manual configuration, automatic configuration should be disabled by adding the [Controller(Ignore = true)] attribute to the controller. :::

using Neon.Operator;
using Neon.Operator.ResourceManager;

public void ConfigureServices(IServiceCollection services)
{
services.AddKubernetesOperator()
.AddController<ExampleController>(
options: new ResourceManagerOptions()
{
ErrorMaxRequeueInterval = TimeSpan.FromMinutes(1),
ErrorMaxRetryCount = 10,
ErrorMinRequeueInterval = TimeSpan.FromSeconds(1)
});
}

Manual requeue

public override async Task<ResourceControllerResult> ReconcileAsync(V1ExampleEntity resource, CancellationToken cancellationToken = default)
{
return ResourceControllerResult.RequeueEvent(TimeSpan.FromSeconds(10));
}

Leader election

Events

The following methods are provided for reacting to leadership events.

public override async Task OnPromotionAsync(CancellationToken cancellationToken = default)
{
// Controller was promoted to leader.
}

public override async Task OnDemotionAsync(CancellationToken cancellationToken = default)
{
// Controller is no longer leader.
}

public override async Task OnNewLeaderAsyncc(string identity, CancellationToken cancellationToken = default)
{
// There is a new leader. The identity of the new leader is given.
}

Configuring

Leader election can be configured by setting LeaderElectionConfig when adding a controller via AddController in Startup.cs.

note

To do this manual configuration, automatic configuration should be disabled by adding the [Controller(Ignore = true)] attribute to the controller.

using Neon.Operator;

public void ConfigureServices(IServiceCollection services)
{
services.AddKubernetesOperator()
.AddController<ExampleController>(
leaderConfig: new LeaderElectionConfig(
k8s: K8s,
@namespace: "default",
leaseName: $"example.controller",
identity: Pod.Name))
}

Filtering

Filtering resources can be achieved by applying field selectors and/or label selectors. Both label selectors and field selectors are comma-separated key=value strings.

Field Selectors

Field selectors can be set either by the Controller attribute, or by the ResourceManagerOptions.FieldSelector property.

[Controller(FieldSelector = "metadata.name=my-resource")]
public class ExampleController : ResourceControllerBase<ExampleResource>

Label Selectors

Field selectors can be set either by the Controller attribute, or by the ResourceManagerOptions.FieldSelector property.

[Controller(LabelSelector = "neonkube.io/managed-by=my-operator,neonkube.io/controlled-by=example-controller")]
public class ExampleController : ResourceControllerBase<ExampleResource>

Dependent Resources

In many cases, an operator creates a bunch of Kubernetes resources in the cluster, as a result of reconciling a Resource. For instance, the etcd-operator creates two services and a number of pods for a single EtcdCluster CR. In this case, all the Kubernetes resources created by the operator for a CR is defined as dependent resources. The etcd-operator may want to watch the pods that it created in case it needs to reconcile again.

DependentResources can be defined by adding an annotation to the Controller.

using Neon.Operator;
using Neon.Operator.Controllers;

[DependentResource<V1Pod>]
[DependentResource<V1Service>]
public class EtcdController : ResourceControllerBase<V1EtcdCluster>
{
// Controller implementation.
}

Controllers are configurable by setting DependentResources in ResourceManagerOptions when adding a controller via AddController in Startup.cs.

note

To do this manual configuration, automatic configuration should be disabled by adding the [Controller(Ignore = true)] attribute to the controller.

using Neon.Operator;
using Neon.Operator.ResourceManager;

public void ConfigureServices(IServiceCollection services)
{
services.AddKubernetesOperator()
.AddController<EtcdController>(
options: new ResourceManagerOptions()
{
DependentResources = new List<IDependentResource>()
{
new DependentResource<V1Pod>(),
new DependentResource<V1Service>()
}
});
}