Skip to content

Commit

Permalink
Merge pull request #161 from shiftstack/actuator
Browse files Browse the repository at this point in the history
Implement a generic controller
  • Loading branch information
mandre authored Jan 15, 2025
2 parents b3d0eb1 + f5dd120 commit b900957
Show file tree
Hide file tree
Showing 44 changed files with 1,771 additions and 2,993 deletions.
118 changes: 74 additions & 44 deletions internal/controllers/flavor/actuator.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,95 +27,92 @@ import (
orcv1alpha1 "github.com/k-orc/openstack-resource-controller/api/v1alpha1"
"github.com/k-orc/openstack-resource-controller/internal/controllers/generic"
osclients "github.com/k-orc/openstack-resource-controller/internal/osclients"
"github.com/k-orc/openstack-resource-controller/internal/scope"
orcerrors "github.com/k-orc/openstack-resource-controller/internal/util/errors"
)

type osResourcePT = *flavors.Flavor
type orcObjectPT = *orcv1alpha1.Flavor

type flavorActuator struct {
*orcv1alpha1.Flavor
osClient osclients.ComputeClient
obj *orcv1alpha1.Flavor
osClient osclients.ComputeClient
controller generic.ResourceController
}

func newActuator(ctx context.Context, k8sClient client.Client, scopeFactory scope.Factory, orcObject *orcv1alpha1.Flavor) (flavorActuator, error) {
log := ctrl.LoggerFrom(ctx)

clientScope, err := scopeFactory.NewClientScopeFromObject(ctx, k8sClient, log, orcObject)
if err != nil {
return flavorActuator{}, err
}
osClient, err := clientScope.NewComputeClient()
if err != nil {
return flavorActuator{}, err
}
var _ generic.CreateResourceActuator[osResourcePT] = flavorActuator{}
var _ generic.DeleteResourceActuator[osResourcePT] = flavorActuator{}

return flavorActuator{
Flavor: orcObject,
osClient: osClient,
}, nil
func (actuator flavorActuator) GetObject() client.Object {
return actuator.obj
}

var _ generic.CreateResourceActuator[*flavors.Flavor] = flavorActuator{}
var _ generic.DeleteResourceActuator[*flavors.Flavor] = flavorActuator{}
func (actuator flavorActuator) GetController() generic.ResourceController {
return actuator.controller
}

func (obj flavorActuator) GetManagementPolicy() orcv1alpha1.ManagementPolicy {
return obj.Spec.ManagementPolicy
func (actuator flavorActuator) GetManagementPolicy() orcv1alpha1.ManagementPolicy {
return actuator.obj.Spec.ManagementPolicy
}

func (obj flavorActuator) GetManagedOptions() *orcv1alpha1.ManagedOptions {
return obj.Spec.ManagedOptions
func (actuator flavorActuator) GetManagedOptions() *orcv1alpha1.ManagedOptions {
return actuator.obj.Spec.ManagedOptions
}

func (flavorActuator) GetResourceID(osResource *flavors.Flavor) string {
return osResource.ID
}

func (obj flavorActuator) GetOSResourceByStatusID(ctx context.Context) (bool, *flavors.Flavor, error) {
if obj.Status.ID == nil {
func (actuator flavorActuator) GetStatusID() *string {
return actuator.obj.Status.ID
}

func (actuator flavorActuator) GetOSResourceByStatusID(ctx context.Context) (bool, *flavors.Flavor, error) {
if actuator.obj.Status.ID == nil {
return false, nil, nil
}
flavor, err := obj.osClient.GetFlavor(ctx, *obj.Status.ID)
flavor, err := actuator.osClient.GetFlavor(ctx, *actuator.obj.Status.ID)
return true, flavor, err
}

func (obj flavorActuator) GetOSResourceBySpec(ctx context.Context) (*flavors.Flavor, error) {
if obj.Spec.Resource == nil {
func (actuator flavorActuator) GetOSResourceBySpec(ctx context.Context) (*flavors.Flavor, error) {
if actuator.obj.Spec.Resource == nil {
return nil, nil
}
return GetByFilter(ctx, obj.osClient, specToFilter(*obj.Spec.Resource))
return GetByFilter(ctx, actuator.osClient, specToFilter(*actuator.obj.Spec.Resource))
}

func (obj flavorActuator) GetOSResourceByImportID(ctx context.Context) (bool, *flavors.Flavor, error) {
if obj.Spec.Import == nil {
func (actuator flavorActuator) GetOSResourceByImportID(ctx context.Context) (bool, *flavors.Flavor, error) {
if actuator.obj.Spec.Import == nil {
return false, nil, nil
}
if obj.Spec.Import.ID == nil {
if actuator.obj.Spec.Import.ID == nil {
return false, nil, nil
}
flavor, err := obj.osClient.GetFlavor(ctx, *obj.Spec.Import.ID)
flavor, err := actuator.osClient.GetFlavor(ctx, *actuator.obj.Spec.Import.ID)
return true, flavor, err
}

func (obj flavorActuator) GetOSResourceByImportFilter(ctx context.Context) (bool, *flavors.Flavor, error) {
if obj.Spec.Import == nil {
func (actuator flavorActuator) GetOSResourceByImportFilter(ctx context.Context) (bool, *flavors.Flavor, error) {
if actuator.obj.Spec.Import == nil {
return false, nil, nil
}
if obj.Spec.Import.Filter == nil {
if actuator.obj.Spec.Import.Filter == nil {
return false, nil, nil
}
flavor, err := GetByFilter(ctx, obj.osClient, *obj.Spec.Import.Filter)
flavor, err := GetByFilter(ctx, actuator.osClient, *actuator.obj.Spec.Import.Filter)
return true, flavor, err
}

func (obj flavorActuator) CreateResource(ctx context.Context) ([]generic.WaitingOnEvent, *flavors.Flavor, error) {
resource := obj.Spec.Resource
func (actuator flavorActuator) CreateResource(ctx context.Context) ([]generic.WaitingOnEvent, *flavors.Flavor, error) {
resource := actuator.obj.Spec.Resource

if resource == nil {
// Should have been caught by API validation
return nil, nil, orcerrors.Terminal(orcv1alpha1.ConditionReasonInvalidConfiguration, "Creation requested, but spec.resource is not set")
}

createOpts := flavors.CreateOpts{
Name: string(getResourceName(obj.Flavor)),
Name: string(getResourceName(actuator.obj)),
RAM: int(resource.RAM),
VCPUs: int(resource.Vcpus),
Disk: ptr.To(int(resource.Disk)),
Expand All @@ -125,7 +122,7 @@ func (obj flavorActuator) CreateResource(ctx context.Context) ([]generic.Waiting
Description: string(ptr.Deref(resource.Description, "")),
}

osResource, err := obj.osClient.CreateFlavor(ctx, createOpts)
osResource, err := actuator.osClient.CreateFlavor(ctx, createOpts)
if err != nil {
// We should require the spec to be updated before retrying a create which returned a conflict
if orcerrors.IsConflict(err) {
Expand All @@ -137,8 +134,8 @@ func (obj flavorActuator) CreateResource(ctx context.Context) ([]generic.Waiting
return nil, osResource, nil
}

func (obj flavorActuator) DeleteResource(ctx context.Context, flavor *flavors.Flavor) ([]generic.WaitingOnEvent, error) {
return nil, obj.osClient.DeleteFlavor(ctx, flavor.ID)
func (actuator flavorActuator) DeleteResource(ctx context.Context, flavor *flavors.Flavor) ([]generic.WaitingOnEvent, error) {
return nil, actuator.osClient.DeleteFlavor(ctx, flavor.ID)
}

// getResourceName returns the name of the OpenStack resource we should use.
Expand All @@ -148,3 +145,36 @@ func getResourceName(orcObject *orcv1alpha1.Flavor) orcv1alpha1.OpenStackName {
}
return orcv1alpha1.OpenStackName(orcObject.Name)
}

type flavorActuatorFactory struct{}

var _ generic.ActuatorFactory[orcObjectPT, osResourcePT] = flavorActuatorFactory{}

func newActuator(ctx context.Context, orcObject *orcv1alpha1.Flavor, controller generic.ResourceController) (flavorActuator, error) {
log := ctrl.LoggerFrom(ctx)

clientScope, err := controller.GetScopeFactory().NewClientScopeFromObject(ctx, controller.GetK8sClient(), log, orcObject)
if err != nil {
return flavorActuator{}, err
}
osClient, err := clientScope.NewComputeClient()
if err != nil {
return flavorActuator{}, err
}

return flavorActuator{
obj: orcObject,
osClient: osClient,
controller: controller,
}, nil
}

func (flavorActuatorFactory) NewCreateActuator(ctx context.Context, orcObject orcObjectPT, controller generic.ResourceController) ([]generic.WaitingOnEvent, generic.CreateResourceActuator[osResourcePT], error) {
actuator, err := newActuator(ctx, orcObject, controller)
return nil, actuator, err
}

func (flavorActuatorFactory) NewDeleteActuator(ctx context.Context, orcObject orcObjectPT, controller generic.ResourceController) ([]generic.WaitingOnEvent, generic.DeleteResourceActuator[osResourcePT], error) {
actuator, err := newActuator(ctx, orcObject, controller)
return nil, actuator, err
}
34 changes: 4 additions & 30 deletions internal/controllers/flavor/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,18 @@ package flavor
import (
"context"

"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"

orcv1alpha1 "github.com/k-orc/openstack-resource-controller/api/v1alpha1"

ctrlexport "github.com/k-orc/openstack-resource-controller/internal/controllers/export"
"github.com/k-orc/openstack-resource-controller/internal/controllers/generic"
"github.com/k-orc/openstack-resource-controller/internal/scope"
)

const (
Finalizer = "openstack.k-orc.cloud/flavor"

FieldOwner = "openstack.k-orc.cloud/flavorcontroller"
// Field owner of the object finalizer.
SSAFinalizerTxn = "finalizer"
// Field owner of transient status.
SSAStatusTxn = "status"
// Field owner of persistent id field.
SSAIDTxn = "id"
)

// ssaFieldOwner returns the field owner for a specific named SSA transaction.
func ssaFieldOwner(txn string) client.FieldOwner {
return client.FieldOwner(FieldOwner + "/" + txn)
}
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=flavors,verbs=get;list;watch;create;update;patch;delete
// +kubebuilder:rbac:groups=openstack.k-orc.cloud,resources=flavors/status,verbs=get;update;patch

type flavorReconcilerConstructor struct {
scopeFactory scope.Factory
Expand All @@ -59,20 +44,9 @@ func (flavorReconcilerConstructor) GetName() string {
return "flavor"
}

// orcFlavorReconciler reconciles an ORC Flavor.
type orcFlavorReconciler struct {
client client.Client
recorder record.EventRecorder
scopeFactory scope.Factory
}

// SetupWithManager sets up the controller with the Manager.
func (c flavorReconcilerConstructor) SetupWithManager(ctx context.Context, mgr ctrl.Manager, options controller.Options) error {
reconciler := orcFlavorReconciler{
client: mgr.GetClient(),
recorder: mgr.GetEventRecorderFor("orc-flavor-controller"),
scopeFactory: c.scopeFactory,
}
reconciler := generic.NewController(c.GetName(), mgr.GetClient(), c.scopeFactory, flavorActuatorFactory{}, flavorStatusWriter{})

return ctrl.NewControllerManagedBy(mgr).
For(&orcv1alpha1.Flavor{}).
Expand Down
Loading

0 comments on commit b900957

Please sign in to comment.