Skip to content

feat(target-operatorbox): the experiment lands — phase 1 - #261

Merged
iAlexeze merged 1 commit into
mainfrom
feat/per-target-operatorbox
Aug 16, 2026
Merged

feat(target-operatorbox): the experiment lands — phase 1#261
iAlexeze merged 1 commit into
mainfrom
feat/per-target-operatorbox

Conversation

@iAlexeze

@iAlexeze iAlexeze commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator

Summary

The experiment from #260 reaches phase 1.

Each serve target can now carry its own operatorBox. The reconciler selects
the box from the CR's serve-alias / serve-target annotations — different
resources, different lifecycle hooks, different preReconcile gates per surface.

operatorBox:                     # CRD-level fallback
  reconciler:
    hooks:
      location: github.com/myorg/myoperator/hooks
      function: AppHooks
      args:
        featureEnabled: '{{ .external.flags.body }}'
        inBusinessHours: '{{ inBusinessHours }}'

serve:
  target:
    v2-enabled:
      primary: true
      operatorBox:               # hooks — args forced, gate active
        preReconcile:
          enqueueGate:
            when:
              - field: '{{ inBusinessHours }}'
                equals: "true"
        reconciler:
          hooks:
            args:
              featureEnabled: "true"
              inBusinessHours: '{{ inBusinessHours }}'

    regional:
      operatorBox:               # declarative — forEach over regions
        preReconcile:
          reconcileGate:
            when:
              - field: "{{ len .spec.regions }}"
                notEquals: "0"
        onCreate:
          deployments:
            - name: "{{ .metadata.name }}-{{ .item }}"
              image: "{{ .spec.image }}"
              forEach:
                field: spec.regions
                as: item
          namespaces:
            - name: "{{ .metadata.name }}-{{ .item }}"
              forEach:
                field: spec.regions
                as: item

When a CR switches target, the previous surface's resources are cleaned up via
a label-selector sweep on orkestra-owner=<name>.<prevTarget>.
keepPreviousSurface: true skips the sweep.

Documentation updated.

Test plan

  • Target switch cleans up previous surface resources (namespaced + cluster-scoped)
  • forEach resources swept correctly when spec fields are cleared before cleanup
  • preReconcile gates (enqueueGate / reconcileGate) fire per target
  • keepPreviousSurface retains old resources across target switch
  • 409 on target switch without ?override=true
  • make unit-test
  • make ork

This is still an ongoing experiment of what the target surface could mean for the runtime.

Each serve target can declare its own operatorBox — resources, lifecycle
hooks, and preReconcile gates (enqueueGate / reconcileGate). The reconciler
selects the active box from serve-alias / serve-target annotations.

Surface switches are detected via orkestra.orkspace.io/last-surface and
cleaned up with a label-selector sweep (SweepOwnedNamespaced/ClusterScoped)
rather than template expansion, which is immune to spec fields being cleared
before cleanup runs. keepPreviousSurface skips the sweep when set.

EffectiveOwnerKey stamps and checks ownership as <name>.<alias> for
target-mode CRs, allowing per-surface resource isolation. Routing conflict
detection uses ServeTargetForFieldSelector (no primary fallback) so
switching targets without ?override=true fires a 409.

Docs: 26-serve-target-operatorbox.md, updated 02-target-mode.md,
10-multi-cluster-routing.md (read path ?cluster param), 20-serve.md.
@iAlexeze
iAlexeze merged commit 14e42f4 into main Aug 16, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant