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>()
}
});
}