Skip to content

Coding Standards

This page documents the coding standards and patterns used throughout ORC.

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:

  1. Standard library
  2. Third-party packages
  3. 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.go alongside 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.