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
10 changes: 7 additions & 3 deletions cmd/cli/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -607,10 +607,14 @@ func roleNameList(srcs []orktypes.RoleTemplateSource) []string {
return out
}

func clusterRoleNameList(srcs []orktypes.PlaceholderSource) []string {
func clusterRoleNameList(srcs []orktypes.ClusterRoleTemplateSource) []string {
out := make([]string, len(srcs))
for i := range srcs {
out[i] = fmt.Sprintf("<clusterrole-%d>", i+1)
for i, s := range srcs {
if s.Name != "" {
out[i] = s.Name
} else {
out[i] = fmt.Sprintf("<clusterrole-%d>", i+1)
}
}
return out
}
Expand Down
87 changes: 87 additions & 0 deletions documentation/concepts/profiles/08-resourcequota-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# ResourceQuota Profile

A ResourceQuota profile is a named preset that expands into a complete Kubernetes `ResourceQuota` hard limits block at reconcile time.

You write a profile name. Orkestra writes the pod counts, CPU, and memory limits for the namespace.

---

## Profiles

| Profile | Pods | CPU | Memory | CPU Request | Memory Request | Use case |
|---------|------|-----|--------|-------------|----------------|----------|
| `small` | 10 | 2 | 4Gi | 1 | 2Gi | Development namespaces, small teams |
| `medium` | 20 | 4 | 8Gi | 2 | 4Gi | Standard team namespaces |
| `large` | 50 | 8 | 16Gi | 4 | 8Gi | Production namespaces, larger services |
| `xlarge` | 100 | 16 | 32Gi | 8 | 16Gi | High-scale production or platform namespaces |

Each profile sets `pods`, `cpu`, `memory`, `requests.cpu`, `requests.memory`, `limits.cpu`, and `limits.memory`.

---

## Usage

Set `profile` on any `resourceQuotas` entry:

```yaml
onCreate:
resourceQuotas:
- name: "{{ .metadata.name }}-quota"
profile: medium
reconcile: true
```

Or dynamically from the CR spec:

```yaml
resourceQuotas:
- name: "{{ .metadata.name }}-quota"
profile: '{{ .spec.size | default "small" }}'
reconcile: true
```

This pattern is common in namespace provisioning operators where the CR carries a `size` field that controls how much of the cluster the namespace receives.

---

## Rules

**Profile or explicit — not both.**

`profile` and `hard` cannot coexist on the same ResourceQuota entry. Orkestra ignores the profile if `hard` is also set and logs a warning.

```yaml
# Valid
resourceQuotas:
- name: ns-quota
profile: large

# Valid
resourceQuotas:
- name: ns-quota
hard:
pods: "30"
cpu: "6"
memory: 12Gi

# Invalid — profile is ignored, hard wins
resourceQuotas:
- name: ns-quota
profile: large
hard:
pods: "30"
```

**Unknown profiles log a warning and skip the resource.** A typo does not cause a reconcile failure — Orkestra skips that ResourceQuota and logs the unknown profile name.

---

## Choosing a profile

| Situation | Profile |
|-----------|---------|
| Developer sandbox, local testing | `small` |
| Team namespace, feature environment | `medium` |
| Production service namespace | `large` |
| Platform or high-traffic production | `xlarge` |
| Fine-grained control needed | Omit profile, use `hard` directly |
104 changes: 104 additions & 0 deletions documentation/concepts/profiles/09-networkpolicy-profile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# NetworkPolicy Profile

A NetworkPolicy profile is a named preset that expands into a complete set of ingress/egress rules and policy types at reconcile time.

You write a profile name. Orkestra writes the rules.

---

## Profiles

| Profile | Ingress | Egress | Use case |
|---------|---------|--------|----------|
| `deny-all` | Blocked | Blocked | Baseline isolation — no traffic in or out |
| `deny-all-ingress` | Blocked | Unmanaged | Block all inbound, leave egress open |
| `deny-all-egress` | Unmanaged | Blocked | Block all outbound, leave ingress open |
| `allow-same-namespace` | Same namespace only | Unmanaged | Allow pods within the namespace to talk to each other |
| `allow-dns-egress` | Unmanaged | UDP/TCP port 53 only | Allow DNS resolution (combine with other policies) |

---

## Usage

Set `profile` on any `networkPolicies` entry:

```yaml
onCreate:
networkPolicies:
- name: "{{ .metadata.name }}-baseline"
podSelector: {}
profile: deny-all
reconcile: true
```

Profiles compose by declaring multiple policies:

```yaml
onCreate:
networkPolicies:
- name: "{{ .metadata.name }}-deny-all"
podSelector: {}
profile: deny-all
reconcile: true
- name: "{{ .metadata.name }}-allow-dns"
podSelector: {}
profile: allow-dns-egress
reconcile: true
```

This is the standard layered approach: start with `deny-all`, then add back only what the workload needs.

---

## How `deny-all` works

Kubernetes interprets an empty ingress or egress slice as "block all". The `deny-all` profile sets:

```yaml
policyTypes:
- Ingress
- Egress
ingress: []
egress: []
```

The explicit `policyTypes` declaration is required — without it, an empty `egress` slice does not block egress traffic.

---

## Rules

**Profile or explicit — not both.**

`profile` and explicit `ingress`/`egress`/`policyTypes` fields cannot coexist on the same NetworkPolicy entry. If both are set, the profile takes precedence and the explicit fields are ignored.

**`podSelector` is independent of profile.**

The profile only sets the traffic rules. You still control which pods the policy applies to via `podSelector`. An empty map (`{}`) selects all pods in the namespace.

```yaml
networkPolicies:
- name: deny-all
podSelector: {} # applies to all pods
profile: deny-all

- name: deny-worker-egress
podSelector:
role: worker # applies only to pods with role=worker
profile: deny-all-egress
```

**Unknown profiles log a warning and skip the resource.** A typo does not cause a reconcile failure.

---

## Choosing a profile

| Situation | Profile |
|-----------|---------|
| New namespace — start locked down | `deny-all` |
| Block inbound only, egress already managed | `deny-all-ingress` |
| Block outbound only | `deny-all-egress` |
| Services in the same namespace need to talk | `allow-same-namespace` |
| Workload needs DNS (after deny-all) | `allow-dns-egress` |
| Custom traffic rules | Omit profile, use `ingress`/`egress` directly |
4 changes: 3 additions & 1 deletion documentation/concepts/profiles/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ Static names are validated at load time. Template expressions are validated when

---

## The seven profile families
## Profile families

| Family | What it controls | Applied to |
|--------|-----------------|------------|
Expand All @@ -73,6 +73,8 @@ Static names are validated at load time. Template expressions are validated when
| [HPA Behavior](./05-hpa-behavior-profile.md) | Kubernetes HPA scale-up/down policies | `hpa[*].behavior` |
| [PDB Behavior](./06-pdb-profile.md) | PodDisruptionBudget disruption limits | `pdb[*].behavior` |
| [Rolling Update](./07-rolling-update-profile.md) | Deployment/StatefulSet/ReplicaSet rollout strategy | `deployments[*].rollingUpdate`, `statefulSets[*].rollingUpdate`, `replicaSets[*].rollingUpdate` |
| [ResourceQuota](./08-resourcequota-profile.md) | Namespace resource limits (pods, CPU, memory) | `resourceQuotas[*]` |
| [NetworkPolicy](./09-networkpolicy-profile.md) | Traffic allow/deny rules for pods | `networkPolicies[*]` |

---

Expand Down
16 changes: 6 additions & 10 deletions documentation/contributing/contributing-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,30 @@ The registry is the library of built-in Kubernetes resource handlers Orkestra kn
| `namespaces/` | `v1 Namespace` | Implemented — cleanup on delete not yet working |
| `roles/` | `rbac/v1 Role` | Implemented — needs tests |
| `rolebindings/` | `rbac/v1 RoleBinding` | Implemented — needs tests |
| `clusterroles/` | `rbac/v1 ClusterRole` | Implemented — needs tests |
| `clusterrolebindings/` | `rbac/v1 ClusterRoleBinding` | Implemented — needs tests |
| `serviceaccounts/` | `v1 ServiceAccount` | Implemented — needs tests |
| `networkpolicies/` | `networking.k8s.io/v1 NetworkPolicy` | Implemented — needs tests |
| `resourcequotas/` | `v1 ResourceQuota` | Implemented — needs tests |
| `limitranges/` | `v1 LimitRange` | Implemented — needs tests |
| `customresources/` | Dynamic CR via `dynamic` client | Implemented |
| `pods/` | `v1 Pod` | Implemented |

---

## 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.
The following resource types are declared in `pkg/types/types_hook_templates.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`
Expand Down
111 changes: 111 additions & 0 deletions documentation/reference/schema/02-katalog/16-resource-types.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Resource types

Every `onCreate`, `onReconcile`, and `onDelete` block accepts the resource types listed on this page. Resources are applied in the order declared within each type slice.

---

## Supported

These resource types are fully implemented — Orkestra creates, updates, and deletes them on every reconcile.

### Workloads

| Field | Kubernetes kind | API version |
|-------|----------------|-------------|
| `deployments` | `Deployment` | `apps/v1` |
| `replicaSets` | `ReplicaSet` | `apps/v1` |
| `statefulSets` | `StatefulSet` | `apps/v1` |
| `pods` | `Pod` | `v1` |
| `jobs` | `Job` | `batch/v1` |
| `cronJobs` | `CronJob` | `batch/v1` |

### Networking

| Field | Kubernetes kind | API version |
|-------|----------------|-------------|
| `services` | `Service` | `v1` |
| `ingresses` | `Ingress` | `networking.k8s.io/v1` |
| `networkPolicies` | `NetworkPolicy` | `networking.k8s.io/v1` |

### Configuration

| Field | Kubernetes kind | API version |
|-------|----------------|-------------|
| `configMaps` | `ConfigMap` | `v1` |
| `secrets` | `Secret` | `v1` |
| `namespaces` | `Namespace` | `v1` |

### Storage

| Field | Kubernetes kind | API version |
|-------|----------------|-------------|
| `persistentVolumeClaims` | `PersistentVolumeClaim` | `v1` |
| `persistentVolumes` | `PersistentVolume` | `v1` |

### Identity

| Field | Kubernetes kind | API version |
|-------|----------------|-------------|
| `serviceAccounts` | `ServiceAccount` | `v1` |
| `roles` | `Role` | `rbac.authorization.k8s.io/v1` |
| `roleBindings` | `RoleBinding` | `rbac.authorization.k8s.io/v1` |
| `clusterRoles` | `ClusterRole` | `rbac.authorization.k8s.io/v1` |
| `clusterRoleBindings` | `ClusterRoleBinding` | `rbac.authorization.k8s.io/v1` |

### Policy

| Field | Kubernetes kind | API version |
|-------|----------------|-------------|
| `hpa` | `HorizontalPodAutoscaler` | `autoscaling/v2` |
| `pdb` | `PodDisruptionBudget` | `policy/v1` |
| `resourceQuotas` | `ResourceQuota` | `v1` |
| `limitRanges` | `LimitRange` | `v1` |

### Custom

| Field | Description |
|-------|-------------|
| `custom` | Any CRD — applied via the dynamic client. Accepts any YAML structure. |

---

## Not yet supported

The following fields are accepted in the YAML and parsed without error, but Orkestra does not act on them at reconcile time. They are placeholders — declaring them does nothing.

| Field | Kubernetes kind | Notes |
|-------|----------------|-------|
| `daemonSets` | `DaemonSet` | Planned |
| `podTemplates` | `PodTemplate` | Planned |
| `storageClasses` | `StorageClass` | Planned |
| `storageLocations` | Velero `BackupStorageLocation` | Planned |
| `storagePools` | Rook `CephBlockPool` | Planned |
| `storageBackups` | Velero `Backup` | Planned |
| `storageSnapshots` | `VolumeSnapshot` | Planned |
| `storageVolumes` | Longhorn `Volume` | Planned |
| `serviceMonitors` | Prometheus Operator `ServiceMonitor` | Planned — use `custom` in the meantime |
| `priorityClasses` | `PriorityClass` | Planned |
| `priorityLevelConfigurations` | `PriorityLevelConfiguration` | Planned |
| `runtimeClasses` | `RuntimeClass` | Planned |
| `podSecurityPolicies` | `PodSecurityPolicy` (deprecated) | No plan — PSP is removed in Kubernetes 1.25+ |
| `volumes` | Pod volume definitions | Future — injected into pod specs |
| `volumeMounts` | Container volume mounts | Future — injected into container specs |

!!! tip "Using an unsupported type now"
For any resource type not in the supported list, use `custom:` with the full YAML structure. Orkestra applies it via the dynamic client against the cluster API. See [13-external.md](13-external.md) for details on combining external API calls with custom resources.

---

## ClusterRole and ClusterRoleBinding notes

`clusterRoles` and `clusterRoleBindings` are cluster-scoped. Kubernetes does not allow a namespace-scoped CR to own a cluster-scoped resource via `OwnerReferences` — setting one causes the garbage collector to treat the resource as orphaned and delete it immediately.

Orkestra tracks ownership via the `orkestra.io/owner` label instead. This means cluster-scoped resources are **not automatically deleted** when the CR is deleted. Declare explicit cleanup in `onDelete` if needed:

```yaml
onDelete:
clusterRoles:
- name: "{{ .metadata.name }}-cluster-role"
clusterRoleBindings:
- name: "{{ .metadata.name }}-crb"
```
1 change: 1 addition & 0 deletions documentation/reference/schema/02-katalog/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ This scaffolds the simplest Katalog — a single CRD that creates a Deployment a
| [11-katalog-notification.md](11-katalog-notification.md) | `notification` block |
| [12-katalog-providers.md](12-katalog-providers.md) | `providers` block |
| [15-enrich.md](15-enrich.md) | `enrich` — post-reconcile enrichment |
| [16-resource-types.md](16-resource-types.md) | Supported resource types and placeholder fields |

---

Expand Down
Loading
Loading