Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions docs/concepts/developer-abstractions.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,18 @@ The project boundary also defines the scope for internal communication and share
can communicate freely with each other. This locality principle reduces complexity for
developers while maintaining security and isolation between different application domains.

Every Project references a **ProjectType** (or cluster-scoped **ClusterProjectType**) through `spec.type`. The
project type is the platform engineer's infrastructure template for the project: it defines the data-plane namespace
and the shared resources (network policies, quotas, baseline RBAC) materialized in every environment the project is
deployed to. Projects supply `parameters` that conform to the type's schema, mirroring how Components supply
parameters to their ComponentType. The reference is immutable after creation, and projects created through the API or
the Backstage UI default to the platform's `default` ClusterProjectType when no type is chosen.

Projects follow the same release lifecycle as components. The Project controller cuts an immutable **ProjectRelease**
whenever the referenced type or the parameter values change, and one **ProjectReleaseBinding** per environment pins a
release to that environment. Promotion at the project level means advancing a binding's pin to a newer release; the
release itself never changes.

## Component

A **Component** represents a deployable unit of software - the fundamental building block of applications in OpenChoreo.
Expand Down
44 changes: 44 additions & 0 deletions docs/concepts/platform-abstractions.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,24 @@ When a ResourceReleaseBinding is created, the binding controller renders the Res
Resource parameters and environment overrides, produces a RenderedRelease that is applied to the target plane, and
resolves the declared outputs so consuming workloads can read them.

## ProjectReleaseBinding

A **ProjectReleaseBinding** is the project-level member of the binding family. It pins a **ProjectRelease** (an
immutable snapshot of a Project and its referenced ProjectType) to an Environment, and it is the explicit owner of the
project's data-plane namespace for that environment. The binding controller creates the namespace, applies the
namespace-scoped resources rendered from the project type (NetworkPolicies, ResourceQuotas, baseline RBAC), and
reports readiness. Components and resources belonging to the project deploy into that namespace.

Bindings are authored by clients: the Backstage UI and the `occ` CLI create one binding per pipeline environment at
project-creation time, and GitOps setups commit them to Git. The controller never creates bindings on its own, so a
project only deploys to the environments that have bindings. A binding created without a release pin is seeded once
with the project's latest release; after that, advancing the pin is always an explicit promotion step.

Like the other bindings, a ProjectReleaseBinding carries per-environment configuration. Values supplied through its
`environmentConfigs` are validated against the project type schema inlined in the pinned ProjectRelease snapshot, not
the live ProjectType, so validation stays fixed for an existing binding even if the type is edited later. This lets the
same release run with different quotas or policies in each environment while the snapshot itself remains unchanged.

## Component Types

A **ComponentType** is a platform engineer-defined template that governs how components are deployed and managed in
Expand Down Expand Up @@ -262,6 +280,32 @@ The **`retainPolicy`** field on a ResourceType sets the default retention behavi
default is `Delete`. Platform engineers opt into `Retain` for templates that emit non-recoverable state (production
databases, persistent volumes), and individual environments can still override the policy through the binding.

## Project Types

A **ProjectType** is a platform engineer-defined template for project-scoped infrastructure. Where ComponentTypes
describe how a code component is deployed and ResourceTypes describe how managed infrastructure is provisioned,
ProjectTypes describe what every project of that type gets in each environment: the data-plane namespace itself and
the shared resources seeded into it, such as default-deny NetworkPolicies, ResourceQuotas, baseline RBAC, and
ImagePullSecrets. This gives infrastructure that is scoped to a project and environment pair a proper home, instead of
being carried by whichever component's template happened to include it.

OpenChoreo also provides **ClusterProjectType**, a cluster-scoped variant available to projects in all namespaces. The
platform ships a minimal `default` ClusterProjectType that provisions only the namespace; projects created through the
API or the Backstage UI reference it unless another type is chosen.

ProjectTypes use the same schema-driven approach as ComponentTypes and ResourceTypes. **Parameters** capture
project-level values supplied on the Project and frozen into each ProjectRelease snapshot. **EnvironmentConfigs**
capture per-environment values supplied through ProjectReleaseBinding, so the same release can apply different quotas
or policies in each environment. CEL-based **validation rules** enforce invariants across both during rendering.

Every ProjectType must include a `v1/Namespace` entry in its resource templates whose name is the platform-computed
namespace placeholder. This mandate is what makes namespace ownership explicit: the namespace exists because the
project was deployed to the environment, with a lifecycle independent of any individual component.

When a Project references a ProjectType, the Project controller cuts an immutable **ProjectRelease** capturing the
type's spec and the project's parameters at that point in time. Later edits to the ProjectType never affect releases
that were already cut; they produce a new release that environments adopt through promotion.

## Workflow and ClusterWorkflow

A **Workflow** is a platform engineer-defined template for running automation tasks in OpenChoreo. Workflows provide
Expand Down
38 changes: 34 additions & 4 deletions docs/concepts/resource-relationships.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,35 @@ Component chain. The Resource controller never authors bindings on its own; prom
is always an explicit action by a platform engineer or GitOps tooling. This isolation keeps developer-facing Resource
spec edits from triggering automatic redeploys against stateful infrastructure.

### Project and ProjectType

**ProjectTypes** define platform-level templates for project-scoped infrastructure: the data-plane namespace and the
shared resources seeded into it in every environment. The cluster-scoped variant **ClusterProjectType** is available
across all namespaces, and the platform ships a minimal `default` ClusterProjectType.

**Projects** reference a ProjectType (or ClusterProjectType) through `spec.type` and supply parameter values that
conform to the type's schema. The reference is required and immutable, mirroring how Components reference
ComponentTypes: every project is governed by an infrastructure template the platform team has approved, and a project
cannot be re-targeted to a different template after creation.

### ProjectRelease and ProjectReleaseBinding

A **ProjectRelease** is an immutable snapshot of a Project and its referenced ProjectType at a specific point in time.
The Project controller cuts a new release whenever the type's spec or the project's parameters change; existing
releases remain unchanged so the same snapshot can be promoted across environments without drift. This mirrors how
ComponentRelease and ResourceRelease work for their domains.

A **ProjectReleaseBinding** pins a ProjectRelease to an Environment and carries per-environment configuration
overrides. It is also the explicit owner of the project's data-plane namespace for that environment: the binding
controller creates the namespace, applies the resources rendered from the inlined project type, and produces a
**RenderedRelease** applied to the target data plane. Component and resource deployments into an environment depend on
this namespace existing, so the project binding converges first.

The chain (Project → ProjectRelease → ProjectReleaseBinding → RenderedRelease) completes the pattern at the project
level. Bindings are authored by clients (the Backstage UI, the `occ` CLI, or GitOps commits), not by the controller. A
binding created without a release pin is seeded once with the project's latest release; advancing the pin afterwards
is always an explicit promotion step.

## Network Relationships

Network relationships in OpenChoreo define how components communicate and expose functionality. These relationships
Expand Down Expand Up @@ -201,10 +230,11 @@ template. Since ComponentReleases are immutable snapshots, existing deployments
### Deletion Cascades

Resource relationships define deletion behavior. When a project is deleted, all its components and Resources are
removed. When a Component is deleted, its WorkflowRuns, ComponentReleases, ReleaseBindings, and RenderedReleases are
cleaned up. When a Resource is deleted, deletion is blocked while any ResourceReleaseBinding still references it; once
all bindings are gone, the owned ResourceReleases are removed. These cascading relationships ensure that resources are
properly cleaned up without leaving orphaned objects.
removed, and the project's cleanup finalizer cascades deletion to its ProjectReleaseBindings, tearing down the
data-plane namespaces the bindings own. When a Component is deleted, its WorkflowRuns, ComponentReleases,
ReleaseBindings, and RenderedReleases are cleaned up. When a Resource is deleted, deletion is blocked while any
ResourceReleaseBinding still references it; once all bindings are gone, the owned ResourceReleases are removed. These
cascading relationships ensure that resources are properly cleaned up without leaving orphaned objects.

The retention of the underlying data-plane state on a binding deletion is governed by the binding's retention policy.
Production environments can preserve volumes and databases on deletion while dev and staging dispose of them, the same
Expand Down
7 changes: 7 additions & 0 deletions docs/concepts/runtime-model.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,13 @@ its own namespace, network policies, and security boundaries. Components within
cluster-local networking, but all communication across cell boundaries must flow through well-defined gateways. This
architecture ensures that failures, security breaches, or performance issues in one cell cannot directly impact others.

The cell's namespace and its baseline resources have an explicit owner: the **ProjectReleaseBinding** for that project
and environment. The binding renders the project's ProjectType (which must declare the namespace itself as a template
entry), creates the namespace on the data plane, and seeds it with the shared infrastructure the type defines, such as
default-deny network policies and resource quotas. Component and resource deployments into the environment wait for
this namespace to exist, so the cell's boundary is established before anything runs inside it, and its lifecycle is
tied to the project and environment rather than to whichever component deployed first.

The cell model aligns with microservices best practices and Domain-Driven Design principles. By mapping bounded contexts
to isolated runtime units, OpenChoreo ensures that architectural boundaries are enforced by infrastructure. This
alignment reduces the cognitive load on developers - the same mental model used for designing applications applies to
Expand Down
167 changes: 167 additions & 0 deletions docs/developer-guide/deploying-applications/project-releases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
---
title: Project Releases
description: How project releases are cut, bound to environments, and promoted
---

# Project Releases

Components are not the only thing a project deploys. Every project also carries project-scoped infrastructure (its data-plane namespace, network policies, quotas) defined by the [ProjectType](../../platform-engineer-guide/project-types.md) it references. This page covers how that infrastructure is versioned and promoted through project releases.

Two resources drive the lifecycle:

- **ProjectRelease**: an immutable snapshot of your project's type and parameter values at a point in time. Cut automatically; you never edit one.
- **ProjectReleaseBinding**: one per environment. It pins a ProjectRelease to that environment, owns the project's data-plane namespace there, and carries per-environment configuration.

```text
Project + ProjectType → ProjectRelease → ProjectReleaseBinding (per environment) → data plane
```

This is the same pattern components follow with ComponentRelease and ReleaseBinding, applied at the project level.

## Setting the Project Type

Every Project references a ProjectType or ClusterProjectType through `spec.type`, and optionally supplies `parameters` that conform to the type's schema:

```yaml
apiVersion: openchoreo.dev/v1alpha1
kind: Project
metadata:
name: online-store
namespace: default
spec:
deploymentPipelineRef:
name: default
type:
kind: ClusterProjectType
name: standard-project
parameters:
tier: premium
```

`spec.type` is immutable after creation. When you create a project through the Backstage UI or the OpenChoreo API without picking a type, it defaults to the platform's `default` ClusterProjectType, which provisions only the namespace. Projects applied directly with `kubectl` must set `spec.type` explicitly.

To generate a Project manifest from a type's schema, with required parameters as placeholders and optional ones as commented examples, use the scaffolder:

```bash
occ project scaffold online-store --clusterprojecttype standard-project
```

The scaffold also emits one ProjectReleaseBinding per environment in the deployment pipeline (`--no-bindings` opts out).

## How Releases Are Cut

You do not create project releases by hand. The Project controller watches the project and its referenced type, and cuts a new ProjectRelease named `<project>-<hash>` whenever either changes:

- The platform engineer updates the ProjectType (a new policy, a changed template)
- You change `spec.parameters` on the Project

The newest release is recorded on the project's status:

```bash
kubectl get project online-store -o jsonpath='{.status.latestRelease.name}'
# online-store-a1b2c3d4
```

Existing releases are never modified. Environments that pin an older release keep running it until you promote.

## Bindings and Where They Come From

Each environment gets exactly one ProjectReleaseBinding. When you create a project through the Backstage UI or scaffold it with `occ`, one binding per pipeline environment is created for you. In GitOps setups you commit the bindings yourself; the controller never creates them behind your back, so a project only deploys to the environments you have bindings for.

A binding created with an empty `spec.projectRelease` is seeded once by the Project controller with the project's latest release. After that, no controller touches the pin. Advancing it is always an explicit action: that is the promotion model.

## Deploy and Promote

### Via CLI

`occ project deploy` follows the same shape as `occ component deploy`, one verb for both deploy and promote:

```bash
# Deploy: ensure a binding for the first pipeline environment.
# The pin is left unset so the controller seeds it with the latest release.
occ project deploy online-store

# Promote: advance the staging binding to the release development runs
occ project deploy online-store --to staging

# Promote to production, setting per-environment values on the way
occ project deploy online-store --to production --set cpuQuota=16
```

`--set key=value` merges into the binding's `environmentConfigs`.

### Via Backstage UI

1. Open your project in the Backstage console
2. Go to the **Releases** view to see the releases and what each environment currently runs
3. Click **Promote** on the target environment and confirm; the UI shows a diff against what the environment runs today

### Via kubectl or GitOps

Promotion is a one-field patch on the target environment's binding:

```bash
kubectl patch projectreleasebinding online-store-staging \
--type merge -p '{"spec":{"projectRelease":"online-store-a1b2c3d4"}}'
```

In GitOps mode, commit the same change to the binding manifest in Git. Manifests that omit `projectRelease` never conflict with the controller's one-time seeding under server-side apply, so both styles coexist: leave the field out to let the platform seed the first environment, and manage promotion pins explicitly where you want Git to be authoritative.

## Environment-Specific Configuration

The ProjectType declares an `environmentConfigs` schema; the binding supplies the values for its environment:

```yaml
apiVersion: openchoreo.dev/v1alpha1
kind: ProjectReleaseBinding
metadata:
name: online-store-production
namespace: default
spec:
owner:
projectName: online-store
environment: production
projectRelease: online-store-a1b2c3d4
environmentConfigs:
cpuQuota: "16"
memoryQuota: "32Gi"
```

The same release deployed to development and production can run with different quotas or policies while the snapshot itself stays identical. Values are validated against the schema on the pinned release; a value the schema rejects surfaces on the binding's conditions rather than reaching the data plane.

## Rolling Back

Because releases are immutable and kept, rollback is the same operation as promotion: point the binding at an older release.

```bash
occ projectrelease list --project online-store
kubectl patch projectreleasebinding online-store-production \
--type merge -p '{"spec":{"projectRelease":"online-store-9f8e7d6c"}}'
```

## What Happens on the Data Plane

The binding owns the project's data-plane namespace for its environment (named `dp-{namespace}-{project}-{environment}-{hash}` and surfaced on `status.namespace`). It creates the namespace, applies the resources rendered from the project type, and reports readiness through conditions:

| Condition | Meaning |
| ---------------- | ----------------------------------------------------- |
| `Synced` | The pinned release resolved and rendered successfully |
| `NamespaceReady` | The data-plane namespace exists and is active |
| `ResourcesReady` | The rendered project-type resources report healthy |
| `Ready` | Aggregate over the three above |

Your components and resources deploy into that namespace. Until the project binding for an environment has converged, component and resource deployments to that environment wait for the namespace to exist.

```bash
kubectl get prb -n default
# NAME PROJECT ENVIRONMENT RELEASE READY AGE
# online-store-development online-store development online-store-a1b2c3d4 True 2d
# online-store-staging online-store staging online-store-a1b2c3d4 True 1d
```

## What's Next

- [Deploy and Promote](./deploy-and-promote.md): the component-level deployment workflow
- [Environment Overrides](./environment-overrides.md): per-environment configuration for components
- [Authoring ProjectTypes](../../platform-engineer-guide/project-types.md): the platform engineer side of this lifecycle
- [ProjectRelease API Reference](../../reference/api/runtime/projectrelease.md) and [ProjectReleaseBinding API Reference](../../reference/api/platform/projectreleasebinding.md)
Loading