Skip to content

Implementing resource-specific functionality

The majority of a resource controller's functionality is implemented in a few interfaces which are passed to the generic controller. Current controllers implement these in actuator.go in the controller's package directory, but feel free to split them up as makes most sense for your controller.

The required interfaces are described in detail by the godoc of the interfaces packages. Specific interfaces are linked below.

ResourceHelperFactory

The controller's entry point to these interfaces is ResourceHelperFactory, which is passed as an argument to reconciler.NewController in SetupWithManager. This interface is simply a set of constructors returning implementations of the other required interfaces. It is not expected to have any state, so is expected to be implemented as methods on an empty struct.

APIObjectAdapter

The APIObjectAdapter is an adapter interface which allows the generic controller to access fields of an API object which are common to all API types. For example, all API types have a status field which in turn has an ID field. However, the generic controller is not able to directly reference this concrete field as it is defined separately for every API type.

As the APIObjectAdapter covers only API fields which are automatically generated, it is itself automatically generated by the code generator and written to zz_generated.adapter.go in the controller's package directory. ResourceHelperFactory's NewAPIObjectAdapter method should simply return an instance of this struct.

Actuator

The actuator implements the majority of the resource-specific functionality. It is split into several interfaces:

CreateResourceActuator and DeleteResourceActuator are separated because they may have different initialisation requirements. The delete flow should minimise initialisation requirements: users often try to fix problems by deleting resources, and we should not refuse deletion because a creation dependency is unhealthy. In practice, both may be implemented on the same struct.

Idempotency rules for CreateResource and DeleteResource

CreateResource and DeleteResource MUST NOT perform any action after the OpenStack Create or Delete API call. The generic controller calls CreateResource only when the resource does not exist, and DeleteResource only when it does. Any action after the API call would not be retried if it fails.

For example, Neutron tags cannot be set during resource creation. This must be done by a resource reconciler, not in CreateResource.

Both methods may be called multiple times until they succeed. Any actions before the API call must therefore be idempotent.

ReconcileResourceActuator

ReconcileResourceActuator is an optional interface which may be implemented by the object returned by NewCreateActuator. If implemented, its methods will be called every time the object is reconciled when the resource exists. This enables:

  • Post-creation initialisation (e.g. setting Neutron tags)
  • Object mutability (responding to spec changes after creation)

GetResourceReconcilers returns a list of ResourceReconciler functions. Each reconciler receives the orcObject and osResource and returns a ReconcileStatus. Key points:

  • All returned reconcilers are executed in order, even if earlier ones fail. Errors and statuses are aggregated.
  • If a reconciler makes any change to the OpenStack resource, it MUST return progress.NeedsRefresh() to force a status refresh.

ResourceStatusWriter

The Available and Progressing conditions are critical components of the ORC API. To ensure they are implemented consistently by all controllers they are largely generated by code called by the generic controller. The ResourceStatusWriter interface provides the resource-specific methods required to populate:

  • The Available condition
  • The Progressing condition
  • Resource-specific state in status.resource

GetApplyConfig

This is a simple wrapper around the constructor for the relevant apply configuration. This apply configuration and its constructor will have been automatically generated by make generate. For example, in the Flavor controller this is a wrapper round pkg/clients/applyconfiguration/api/v1alpha1.Flavor.

ResourceAvailableStatus

This sets the value of the Available condition. This should not return true unless the resource is completely ready to be used.

ApplyResourceStatus

Writes the observed resource status to an apply configuration.

Note that object status is written in a single server-side apply 'transaction'. Meaning if, during a reconcile, the controller is not able to fetch the resource from OpenStack, it will not be able to add the resource status to the transaction and therefore status.resource will be unset. This is intentional behaviour, and you should not attempt to work around this or save the previous state. The state will be populated again when the controller is able to fetch the resource.