Coding Standards
This page documents the coding standards and patterns used throughout ORC.
Package structure
Each controller lives in its own package under internal/controllers/<resource>/:
| File | Purpose |
|---|---|
controller.go | Controller setup, name, and SetupWithManager |
actuator.go | OpenStack CRUD operations |
status.go | Status writer implementation |
zz_generated.*.go | Generated code (do not edit) |
Package-scoped variables for the controller name, finalizers, and dependencies are defined in controller.go or zz_generated.controller.go.
Type aliases
Use type aliases at the top of actuator.go to improve readability:
// OpenStack resource types
type (
osResourceT = flavors.Flavor
createResourceActuator = generic.CreateResourceActuator[orcObjectPT, orcObjectT, filterT, osResourceT]
deleteResourceActuator = generic.DeleteResourceActuator[orcObjectPT, orcObjectT, osResourceT]
helperFactory = generic.ResourceHelperFactory[orcObjectPT, orcObjectT, resourceSpecT, filterT, osResourceT]
)
Interface compliance
Use compile-time interface assertions to verify actuators implement required interfaces:
var _ createResourceActuator = flavorActuator{}
var _ deleteResourceActuator = flavorActuator{}
Naming conventions
Controller names
Controller names must be:
- Unique among all controllers
- Contain only lowercase letters and hyphens
const controllerName = "flavor"
OpenStack resource names
By default, use the ORC object name as the OpenStack resource name. Provide a helper function:
func getResourceName(orcObject *orcv1alpha1.Flavor) string {
if orcObject.Spec.Resource.Name != nil {
return *orcObject.Spec.Resource.Name
}
return orcObject.Name
}
Error handling
For detailed information on error handling patterns, including transient vs terminal errors and ReconcileStatus, see Controller Implementation.
Logging
ORC uses four logging levels defined in internal/logging/levels.go:
Status level
Always shown. Reserved for operational logs about the service itself:
- Startup and shutdown messages
- Runtime conditions indicating service state
log.V(logging.Status).Info("Failed to reach API server")
Info level
Default for most deployments. Log principal actions:
- Resource creation and deletion
- Reconcile completion (Progressing=False)
log.V(logging.Info).Info("OpenStack resource created", "id", resource.ID)
Verbose level
Additional context for administrators. Logs on every reconcile:
log.V(logging.Verbose).Info("web-download is not supported", "reason", reason)
Debug level
Very verbose, for development and debugging:
log.V(logging.Debug).Info("Got resource", "resource", resource)
Import ordering
Organize imports in three groups, separated by blank lines:
- Standard library
- Third-party packages
- Internal packages
import (
"context"
"errors"
"github.com/gophercloud/gophercloud/v2/openstack/compute/v2/flavors"
ctrl "sigs.k8s.io/controller-runtime"
orcv1alpha1 "github.com/k-orc/openstack-resource-controller/v2/api/v1alpha1"
"github.com/k-orc/openstack-resource-controller/v2/internal/controllers/generic/interfaces"
)
RBAC markers
Define RBAC requirements using kubebuilder markers in controller.go:
// +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
Pointer handling
Use k8s.io/utils/ptr for pointer operations:
import "k8s.io/utils/ptr"
// Dereference with default
value := ptr.Deref(optionalField, defaultValue)
// Create pointer to value
ptr.To(someValue)
Testing
Test file naming
- Unit tests:
*_test.goalongside source files - API validation tests:
test/apivalidations/<resource>_test.go
Mocking
Use generated mocks from internal/osclients/mock/ for unit tests. Mocks are generated via //go:generate directives.
Generated code
Files matching these patterns are generated and should not be edited:
zz_generated.*.go- Files in
pkg/clients/
Always run make generate after modifying API types.