Contributing to `pkg/orkestra-registry`

3 min read

The registry is the library of built-in Kubernetes resource handlers Orkestra knows how to create, update, and delete on behalf of an operator. Each resource type lives in its own subdirectory.


What exists

DirectoryResourceStatus
deployments/apps/v1 DeploymentComplete
services/v1 ServiceComplete (minor TODOs in edge cases)
configmaps/v1 ConfigMapComplete
secrets/v1 SecretComplete
statefulsets/apps/v1 StatefulSetComplete
jobs/batch/v1 JobComplete
cronjobs/batch/v1 CronJobComplete
replicasets/apps/v1 ReplicaSetComplete
ingresses/networking.k8s.io/v1 IngressImplemented — needs tests
hpas/autoscaling/v2 HPAImplemented — needs tests
pdbs/policy/v1 PodDisruptionBudgetImplemented — needs tests
pvcs/v1 PersistentVolumeClaimImplemented — needs tests
pvs/v1 PersistentVolumeImplemented — needs tests
namespaces/v1 NamespaceImplemented — cleanup on delete not yet working
roles/rbac/v1 RoleImplemented — needs tests
rolebindings/rbac/v1 RoleBindingImplemented — needs tests
serviceaccounts/v1 ServiceAccountImplemented — needs tests
customresources/Dynamic CR via dynamic clientImplemented
pods/v1 PodImplemented

What needs implementing

The following resource types are declared in pkg/types/types.go as PlaceholderSource — they are accepted in YAML but not yet executed. Implementing one means building the full handler package.

Workloads:

  • DaemonSets
  • PodTemplates

RBAC:

  • ClusterRoles
  • ClusterRoleBindings

Scheduling:

  • PriorityClasses
  • PriorityLevelConfigurations
  • LimitRanges
  • ResourceQuotas
  • RuntimeClasses

Networking:

  • NetworkPolicies

Storage:

  • StorageClasses
  • StorageLocations
  • StoragePools
  • StorageBackups
  • StorageSnapshots
  • StorageVolumes

Monitoring:

  • ServiceMonitors (Prometheus Operator CRD)

Other:

  • Volumes (injected into pod specs)
  • VolumeMounts (injected into pod specs)
  • PodSecurityPolicies

When you implement one, remove it from the PlaceholderSource block in pkg/types/types.go and replace it with a proper typed slice.


How to add a resource type

Follow the pattern of any complete resource (e.g., deployments/):

1. Create the directory

pkg/orkestra-registry/<resourcename>/
  <resourcename>.go   — Create, Update, Delete, Resolve functions
  types.go            — ResolvedSpec struct

2. Implement four functions

func Create(ctx context.Context, kube kubeclient.KubeClient, owner domain.Object, spec ResolvedSpec) error
func Update(ctx context.Context, kube kubeclient.KubeClient, owner domain.Object, spec ResolvedSpec) error
func Delete(ctx context.Context, kube kubeclient.KubeClient, owner domain.Object, spec ResolvedSpec) error

All three are idempotent. Create does nothing if the resource exists. Delete does nothing if it does not.

3. Add a resolver in template/resolver.go

func (r *Resolver) ResolveMyResourceTemplate(src *orktypes.MyResourceSource) (*myresource.ResolvedSpec, error)

Use r.Resolve(expr) for any field that may contain a {{ .spec.something }} expression.

4. Wire the runner in pkg/reconciler/generic.go

Find runResources and add your resource type alongside the existing ones.

5. Write tests

Every resource needs tests covering:

  • Create — resource created; already exists (no-op)
  • Update — drift detected and applied; no drift (no-op)
  • Delete — resource exists (deleted); does not exist (no-op)

Use the pkg/simulate harness and a fake clientset (kubeclient.NewFakeClientset()).


Code conventions

  • Use kubeclient.KubeClient for all API calls — never raw client-go.
  • Return wrapped errors: fmt.Errorf("creating daemonset: %w", err).
  • Owner references are set via common.SetOwnerReference(owner, resource).
  • Labels are merged with labels.ManagedLabels(owner).
  • Namespace resolution uses common.ResolveNamespace(owner, spec.Namespace).