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:
BaseResourceActuator: methods required for all actuatorsCreateResourceActuator: methods required for creating or importing a resourceDeleteResourceActuator: methods required for deleting a resourceReconcileResourceActuator: methods required for reconciling a resource after creation
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
Availablecondition - The
Progressingcondition - 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.