Coding Standards
This page documents the coding standards and patterns used throughout ORC.
Copyright headers
Every source file must carry the Apache 2.0 copyright header from hack/boilerplate.go.txt. Do not include a year in the copyright line.
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{}
Error handling
For detailed information on error handling patterns, including transient vs terminal errors and ReconcileStatus, see Controller Implementation.
Resource reconcilers
Each resource reconciler returned by GetResourceReconcilers must target exactly one OpenStack API. Operations that use a separate API from the resource's Update API (e.g. Neutron tags, security group rules, flavor extra specs) must be implemented as their own reconciler, not embedded in updateResource. See ReconcileResourceActuator for details.
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/v3/api/v1alpha1"
"github.com/k-orc/openstack-resource-controller/v3/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.