Skip to content

PackageRevision controller architecture docs (draft)#971

Draft
efiacor wants to merge 1 commit into
kptdev:mainfrom
Nordix:pr_ctr_docs_draft
Draft

PackageRevision controller architecture docs (draft)#971
efiacor wants to merge 1 commit into
kptdev:mainfrom
Nordix:pr_ctr_docs_draft

Conversation

@efiacor
Copy link
Copy Markdown
Collaborator

@efiacor efiacor commented May 6, 2026

Title

PackageRevision controller architecture docs (draft)


Description

  • What changed:
    Add v1alpha2 architecture documentation for the PackageRevision controller as Hugo draft pages. These render in deploy previews but are hidden from the production build.

New pages:

  • deployment-modes.md: v1alpha1 vs v1alpha2 architecture comparison
  • packagerevision-controller/_index.md: controller overview
  • packagerevision-controller/design.md: internal design
  • packagerevision-controller/interactions.md: component interactions

Also adds docs/config/development.toml (buildDrafts=true) and docs/config/production.toml so the existing Netlify build command picks up environment-specific settings.

  • Why it’s needed: New docs
  • How it works: See above

Related Issue(s)

  • Closes/Fixes #

Type of Change

  • Bug fix
  • New feature
  • Enhancement
  • Refactor
  • Documentation
  • Tests
  • Other: ________

Checklist

  • Code follows project style guidelines
  • Self-reviewed changes
  • Tests added/updated
  • Documentation added/updated
  • All tests and gating checks pass

Testing Instructions (Optional)


Additional Notes (Optional)

  • Known issues:
  • Further improvements:
  • Review notes:

AI Disclosure

[X] I have used AI in the creation of this PR.

Add v1alpha2 architecture documentation for the PackageRevision controller
as Hugo draft pages. These render in deploy previews but are hidden from
the production build.

New pages:
- deployment-modes.md: v1alpha1 vs v1alpha2 architecture comparison
- packagerevision-controller/_index.md: controller overview
- packagerevision-controller/design.md: internal design
- packagerevision-controller/interactions.md: component interactions

Also adds docs/config/development.toml (buildDrafts=true) and
docs/config/production.toml so the existing Netlify build command
picks up environment-specific settings.
@efiacor efiacor self-assigned this May 6, 2026
@efiacor efiacor added the documentation Improvements or additions to documentation label May 6, 2026
@netlify
Copy link
Copy Markdown

netlify Bot commented May 6, 2026

Deploy Preview for porch ready!

Name Link
🔨 Latest commit 4098450
🔍 Latest deploy log https://app.netlify.com/projects/porch/deploys/69fb9322db155d000835451e
😎 Deploy Preview https://deploy-preview-971--porch.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.
🤖 Make changes Run an agent on this branch

To edit notification comments on pull requests, go to your Netlify project configuration.


## Overview

The PackageRevision Controller manages the full lifecycle of package revisions as native Kubernetes CRDs. In the v1alpha1 architecture, the Porch API Server and Engine handle all operations synchronously within the request path. The PR controller takes a different approach — it watches `PackageRevision` CRDs in etcd and reconciles their desired state against Git asynchronously, following standard Kubernetes controller patterns.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think we should try and separate ourselves from the infamous AI "—" where we can. i know its a valid grammar but in practice noone ever uses often it apart form AI. gives the impression that it wasn't ran by people at the end

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just maybe ask it to reword those sentences without usage of that notorious character

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its one thing when its just once or twice but its all over the section


## Reconciliation Pipeline

Each reconcile executes three phases in sequence. If any phase produces an error or requires a requeue, subsequent phases are skipped.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

likely my misunderstanding but above we discuss how this new controller is asynchronous then down here we are mentioning doing 3 phases in sequence


Each reconcile executes three phases in sequence. If any phase produces an error or requires a requeue, subsequent phases are skipped.

**Source execution** handles one-time package creation. When a user creates a PackageRevision with `spec.source` set (init, clone, copy, or upgrade), the controller executes that source operation to produce the initial package content in Git. Once `status.creationSource` is populated, this phase becomes a no-op on future reconciles.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it not "in Git" only if its not draft? init by default on DB cache only does work in the cache no?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/ this shared cache


The PR controller sits alongside the Repository Controller in the controllers deployment. It depends on the shared cache that the Repository Controller creates and populates — this is enforced at startup by initializing the repo reconciler first and injecting its cache into the PR reconciler.

The Porch API Server and Engine continue to serve `PackageRevisionResources` for content access. When a user pushes content through PRR, the API Server writes to Git via the Engine and then patches the render-request annotation on the PackageRevision CRD. This annotation change triggers the PR controller to pick up the new content and render it.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

again has this changed? api server writes to git through the engine if its not draft by default right? or has this changed?

Comment on lines +55 to +75
## Enabling the Controller

The PR controller is enabled via the `--reconcilers` flag on the controllers deployment:

```
--reconcilers=packagerevisions
```

It requires the Repository Controller to be running (for the shared cache), the `PackageRevision` CRD to be installed, and the `FUNCTION_RUNNER_ADDRESS` environment variable to be set if external function evaluation is needed.

## Configuration

The controller exposes flags for tuning concurrency and retry behavior:

| Flag | Default | Description |
|------|---------|-------------|
| `packagerevisions.max-concurrent-reconciles` | 50 | Maximum parallel reconciles |
| `packagerevisions.max-concurrent-renders` | 20 | Maximum parallel render operations |
| `packagerevisions.render-requeue-delay` | 2s | Delay before requeue when render limit reached |
| `packagerevisions.repo-operation-retry-attempts` | 3 | Retry count for git operations |
| `packagerevisions.max-grpc-message-size` | 6MB | Max gRPC message size for fn-runner |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is "Configuration and Deployment" information and should be in its relevant section 6 no? can have a link to it from here though

Comment on lines +34 to +42
┌──────────────┐ ┌──────────────────┐
│ kubectl │────>│ etcd (CRD) │
│ │<────│ PackageRevision │
└──────────────┘ └────────┬─────────┘
│ watch
┌──────────────────┐ ┌───────────────┐ ┌─────┐
│ PR Controller │────>│ Shared Cache │────>│ Git │
└──────────────────┘ └───────────────┘ └─────┘
Copy link
Copy Markdown
Contributor

@Catalin-Stratulat-Ericsson Catalin-Stratulat-Ericsson May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

diagram is crooked + what is the second box? engine? CRD? if thats meant to be the packagerevision CRD then that should be at the top of that box + in brackets mention (ectd) e.g. "PackageRevision CRD (etcd)"

Comment on lines +18 to +23
```
┌──────────────┐ ┌──────────────────┐ ┌─────────┐ ┌─────┐
│ kubectl │────>│ Porch API Server │────>│ Engine │────>│ Git │
│ │<────│ (aggregated API) │<────│ │<────│ │
└──────────────┘ └──────────────────┘ └─────────┘ └─────┘
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small nitpick but its a bit weird how this diagram (aka the before) doesn't include any mention of the cache (CR or DB) but the after diagram below does have the Shared one? this gives the impression that we didn't interact with the cache before for this which we did im pretty sure.

Comment on lines +49 to +53
```
┌──────────────┐ ┌──────────────────┐ ┌─────────┐ ┌─────┐
│ kubectl │────>│ Porch API Server │────>│ Engine │────>│ Git │
└──────────────┘ └──────────────────┘ └─────────┘ └─────┘
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dont know if having this diagram a second time is of any value? can just point to the section/diagram above.

Comment on lines +57 to +67
## Key Differences

**Storage model.** In v1alpha1, PackageRevision exists only in Git — the API Server synthesizes it on the fly. In v1alpha2, PackageRevision lives in etcd as a real CRD, with Git as the backing store for content. This gives you native Kubernetes features for free: field selectors, server-side filtering, standard watches, SSA, and standard RBAC without custom authorization logic.

**Execution model.** v1alpha1 is synchronous — a create request blocks until the package is written to Git and rendered. v1alpha2 is asynchronous — the CRD is created immediately in etcd, and the controller reconciles it in the background. Status conditions (Ready, Rendered) tell you when the work is done.

**Observability.** In v1alpha1, debugging requires reading API Server logs to understand what happened. In v1alpha2, the CRD's status conditions, events, and standard `kubectl describe` output show the current state and any errors. The controller's reconcile loop is visible through standard controller-runtime metrics.

**Scalability.** The v1alpha1 API Server is a single process handling all operations. In v1alpha2, the controller scales independently — you can tune concurrency, and the async model naturally handles bursts by queuing work rather than blocking requests.

**Engine role.** In v1alpha1, the Engine handles everything: lifecycle, tasks, rendering, content access, validation. In v1alpha2, the Engine handles only content access for PackageRevisionResources. Lifecycle, source execution, and rendering move to the PR controller.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be in a table format. of mode vs topic with the differences in each mode in the table content

Comment on lines +75 to +77
## Coexistence

Both modes can run in the same cluster. The v1alpha1 aggregated API and v1alpha2 CRD operate on different API resources and don't conflict. However, they manage separate sets of packages — a package created via v1alpha1 is not automatically visible as a v1alpha2 CRD. Migration tooling exists to move packages between modes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but no mention so far of where this tooling is and how to use it?

@Catalin-Stratulat-Ericsson
Copy link
Copy Markdown
Contributor

also no functionality section for this component like all the others even other controllers? was this intentional or just missed?


**Source execution** handles one-time package creation. When a user creates a PackageRevision with `spec.source` set (init, clone, copy, or upgrade), the controller executes that source operation to produce the initial package content in Git. Once `status.creationSource` is populated, this phase becomes a no-op on future reconciles.

**Rendering** runs the KRM function pipeline defined in the package's Kptfile. Two events trigger rendering: a content push via the PRR handler (signalled by the `porch.kpt.dev/render-request` annotation), or the completion of source execution. The controller reads resources from the cache, invokes kpt render through the function runner, and writes the results back.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Rendering** runs the KRM function pipeline defined in the package's Kptfile. Two events trigger rendering: a content push via the PRR handler (signalled by the `porch.kpt.dev/render-request` annotation), or the completion of source execution. The controller reads resources from the cache, invokes kpt render through the function runner, and writes the results back.
**Rendering** runs the KRM function pipeline defined in the package's Kptfile. Two events can trigger the rendering: a content push via the PRR handler (signalled by the `porch.kpt.dev/render-request` annotation), or the completion of source execution. The controller reads resources from the cache, invokes kpt render through the function runner, and writes the results back.


The PR controller sits alongside the Repository Controller in the controllers deployment. It depends on the shared cache that the Repository Controller creates and populates — this is enforced at startup by initializing the repo reconciler first and injecting its cache into the PR reconciler.

The Porch API Server and Engine continue to serve `PackageRevisionResources` for content access. When a user pushes content through PRR, the API Server writes to Git via the Engine and then patches the render-request annotation on the PackageRevision CRD. This annotation change triggers the PR controller to pick up the new content and render it.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The Porch API Server and Engine continue to serve `PackageRevisionResources` for content access. When a user pushes content through PRR, the API Server writes to Git via the Engine and then patches the render-request annotation on the PackageRevision CRD. This annotation change triggers the PR controller to pick up the new content and render it.
The Porch API Server and Engine continue to serve `PackageRevisionResources` (PRR) for content access. When a user pushes content through PRR, the API Server writes to Git via the Engine and then patches the render-request annotation on the PackageRevision CRD. This annotation change triggers the PR controller to pick up the new content and render it.


## Overview

The PackageRevision Controller manages the full lifecycle of package revisions as native Kubernetes CRDs. In the v1alpha1 architecture, the Porch API Server and Engine handle all operations synchronously within the request path. The PR controller takes a different approach — it watches `PackageRevision` CRDs in etcd and reconciles their desired state against Git asynchronously, following standard Kubernetes controller patterns.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The PackageRevision Controller manages the full lifecycle of package revisions as native Kubernetes CRDs. In the v1alpha1 architecture, the Porch API Server and Engine handle all operations synchronously within the request path. The PR controller takes a different approach — it watches `PackageRevision` CRDs in etcd and reconciles their desired state against Git asynchronously, following standard Kubernetes controller patterns.
The PackageRevision (PR) Controller manages the full lifecycle of package revisions as native Kubernetes CRDs. In the v1alpha1 architecture, the Porch API Server and Engine handle all operations synchronously within the request path. The PR controller takes a different approach — it watches `PackageRevision` CRDs in etcd and reconciles their desired state against Git asynchronously, following standard Kubernetes controller patterns.

--reconcilers=packagerevisions
```

It requires the Repository Controller to be running (for the shared cache), the `PackageRevision` CRD to be installed, and the `FUNCTION_RUNNER_ADDRESS` environment variable to be set if external function evaluation is needed.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
It requires the Repository Controller to be running (for the shared cache), the `PackageRevision` CRD to be installed, and the `FUNCTION_RUNNER_ADDRESS` environment variable to be set if external function evaluation is needed.
Make sure the Repository Controller is running (for the shared cache), the `PackageRevision` CRD is installed, and the `FUNCTION_RUNNER_ADDRESS` environment variable is set if external function evaluation is needed.


## Overview

The PackageRevision Controller manages the full lifecycle of package revisions as native Kubernetes CRDs. In the v1alpha1 architecture, the Porch API Server and Engine handle all operations synchronously within the request path. The PR controller takes a different approach — it watches `PackageRevision` CRDs in etcd and reconciles their desired state against Git asynchronously, following standard Kubernetes controller patterns.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should it be PR controller or PR Controller?


The fundamental design decision is the separation of intent from content. The `PackageRevision` CRD in etcd is the source of truth for **what the user wants** — which lifecycle state the package should be in, how it was created, whether rendering is requested. Git is the source of truth for **what the package contains** — the actual KRM resource files.

The controller bridges these two stores. A user sets `spec.lifecycle: Published` on the CRD; the controller transitions the package in Git to published state and updates `status` to reflect the result. This is standard Kubernetes controller semantics — spec is desired state, status is observed state.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The controller bridges these two stores. A user sets `spec.lifecycle: Published` on the CRD; the controller transitions the package in Git to published state and updates `status` to reflect the result. This is standard Kubernetes controller semantics — spec is desired state, status is observed state.
The controller bridges these two stores. When you set `spec.lifecycle: Published` on the CRD; the controller transitions the package in Git to published state and updates `status` to reflect the result. This is standard Kubernetes controller semantics — spec is desired state, status is observed state.

3. The PR controller's predicate filter detects the annotation change and triggers a reconcile.
4. The controller reads the updated content from the cache, renders it, and writes the results back.

This handoff means the API Server doesn't need to know how rendering works — it just signals that new content is available. The PR controller doesn't need to know how content was written — it just reads whatever is in the cache.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This handoff means the API Server doesn't need to know how rendering works — it just signals that new content is available. The PR controller doesn't need to know how content was written — it just reads whatever is in the cache.
This handoff means the API Server doesn't need to know how rendering works — it just signals that new content is available. Plus, the PR controller doesn't need to know how content was written — it just reads whatever is in the cache.


## Function Runner

The PR controller calls the function runner during the render phase. The function runner is a standalone gRPC service that executes KRM functions — both builtin Go functions compiled into the binary and external functions running in containers.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The PR controller calls the function runner during the render phase. The function runner is a standalone gRPC service that executes KRM functions — both builtin Go functions compiled into the binary and external functions running in containers.
The PR controller calls the function runner during the render phase. The function runner is a standalone gRPC service that executes KRM functions — both built-in Go functions compiled into the binary and external functions running in containers.


Concurrency is bounded by the `max-concurrent-renders` setting. If the function runner is unavailable, renders fail and the Rendered condition is set to False with the error message. The controller does not retry failed renders automatically — it waits for the next trigger (annotation change or manual requeue).

If `FUNCTION_RUNNER_ADDRESS` is not set, only builtin Go functions are available. External container-based functions will fail.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
If `FUNCTION_RUNNER_ADDRESS` is not set, only builtin Go functions are available. External container-based functions will fail.
If `FUNCTION_RUNNER_ADDRESS` is not set, only built-in Go functions are available. External container-based functions will fail.

@efiacor efiacor marked this pull request as draft May 15, 2026 07:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants