Skip to content

Commit 5e47270

Browse files
committed
feat(deploy): add Helm chart for in-cluster ORB REST server
Helm chart under deploy/helm/orb: Deployment running the ORB REST server with /health liveness+readiness probes, ClusterIP Service, ConfigMap-mounted config, ServiceAccount, and namespaced RBAC (Role+RoleBinding for pods/deployments/statefulsets/jobs; optional ClusterRole for node-watch or cluster-scoped namespaces). values.yaml covers image, replicas, resources, provider selection, auth, and RBAC toggles. README documents install, values, RBAC scope, and the per-process /metrics multi-worker caveat.
1 parent 64cf3ca commit 5e47270

9 files changed

Lines changed: 661 additions & 0 deletions

File tree

deploy/helm/orb/Chart.yaml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
apiVersion: v2
2+
name: orb
3+
description: Open Resource Broker (ORB) — dynamic cloud resource provisioning via REST API
4+
type: application
5+
version: 0.1.0
6+
appVersion: "1.7.0"
7+
keywords:
8+
- resource-broker
9+
- cloud
10+
- kubernetes
11+
- provisioning
12+
home: https://github.com/finos/open-resource-broker
13+
sources:
14+
- https://github.com/finos/open-resource-broker
15+
maintainers:
16+
- name: Open Resource Broker Maintainers
17+
email: open-resource-broker-maintainers@lists.finos.org

deploy/helm/orb/README.md

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
# ORB Helm Chart
2+
3+
Deploys Open Resource Broker (ORB) as a REST server in a Kubernetes cluster.
4+
5+
## Install
6+
7+
```bash
8+
# Default install (k8s provider, auth off)
9+
helm install orb ./deploy/helm/orb --namespace orb --create-namespace
10+
11+
# With custom values
12+
helm install orb ./deploy/helm/orb \
13+
--namespace orb --create-namespace \
14+
-f my-values.yaml
15+
16+
# Upgrade
17+
helm upgrade orb ./deploy/helm/orb --namespace orb -f my-values.yaml
18+
```
19+
20+
## Key Values
21+
22+
| Value | Default | Description |
23+
|---|---|---|
24+
| `image.repository` | `ghcr.io/finos/open-resource-broker` | Container image |
25+
| `image.tag` | chart `appVersion` | Image tag |
26+
| `replicaCount` | `1` | Pod replicas (see /metrics caveat) |
27+
| `provider.type` | `k8s` | Active provider (`k8s` or `aws`) |
28+
| `server.port` | `8000` | REST API port |
29+
| `server.workers` | `1` | uvicorn worker count |
30+
| `auth.enabled` | `false` | Enable authentication |
31+
| `auth.strategy` | `none` | Auth strategy |
32+
| `auth.existingSecret` | `""` | Secret name for bearer token |
33+
| `rbac.create` | `true` | Create namespaced Role/RoleBinding |
34+
| `rbac.clusterScoped` | `false` | Also create ClusterRole/ClusterRoleBinding |
35+
36+
## Authentication
37+
38+
Auth defaults to **off** (matching the ORB `AuthConfig` schema default). To enable bearer token auth:
39+
40+
```yaml
41+
auth:
42+
enabled: true
43+
strategy: bearer_token
44+
existingSecret: orb-auth # kubectl create secret generic orb-auth --from-literal=bearer-token=<token>
45+
```
46+
47+
For IAM auth on AWS set `auth.strategy: iam` and annotate the ServiceAccount with an IRSA role ARN via `serviceAccount.annotations`.
48+
49+
## RBAC
50+
51+
By default a namespaced `Role` + `RoleBinding` is created granting the k8s provider access to pods, deployments, statefulsets, jobs, and events within the release namespace.
52+
53+
For **cluster-scoped watch** (`provider.k8s.namespaces: ["*"]`) or **node watch** (`provider.k8s.nodeWatchEnabled: true`), enable the ClusterRole:
54+
55+
```yaml
56+
rbac:
57+
clusterScoped: true
58+
provider:
59+
k8s:
60+
namespaces: ["*"]
61+
nodeWatchEnabled: true
62+
```
63+
64+
This grants nodes `get/list/watch` cluster-wide. Review with your security team before applying in shared clusters.
65+
66+
## /metrics Caveat (Multi-Worker)
67+
68+
Each uvicorn worker process maintains its own in-process Prometheus registry. With `server.workers > 1`, scraping `/metrics` returns only one worker's counters depending on which process the load balancer routes to. For accurate metrics in multi-worker mode, use a shared metrics backend (Redis, Prometheus push gateway) or keep `server.workers: 1` and scale via `replicaCount` instead (each pod is fully scraped).
69+
70+
## Server Entrypoint
71+
72+
The container runs `docker-entrypoint.sh serve`, which translates `HF_*` environment variables into:
73+
74+
```
75+
orb server start --foreground --host HOST --port PORT [--workers N]
76+
```
77+
78+
The config file is mounted from the generated ConfigMap at `/etc/orb/config.json` and passed via `HF_CONFIG_FILE`.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
{{/*
2+
Expand the name of the chart.
3+
*/}}
4+
{{- define "orb.name" -}}
5+
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
6+
{{- end }}
7+
8+
{{/*
9+
Create a default fully-qualified app name.
10+
Truncate to 63 chars because Kubernetes DNS naming constraints.
11+
*/}}
12+
{{- define "orb.fullname" -}}
13+
{{- if .Values.fullnameOverride }}
14+
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
15+
{{- else }}
16+
{{- $name := default .Chart.Name .Values.nameOverride }}
17+
{{- if contains $name .Release.Name }}
18+
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
19+
{{- else }}
20+
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
21+
{{- end }}
22+
{{- end }}
23+
{{- end }}
24+
25+
{{/*
26+
Create chart label value (name-version).
27+
*/}}
28+
{{- define "orb.chart" -}}
29+
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
30+
{{- end }}
31+
32+
{{/*
33+
Common labels applied to all resources.
34+
*/}}
35+
{{- define "orb.labels" -}}
36+
helm.sh/chart: {{ include "orb.chart" . }}
37+
{{ include "orb.selectorLabels" . }}
38+
{{- if .Chart.AppVersion }}
39+
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
40+
{{- end }}
41+
app.kubernetes.io/managed-by: {{ .Release.Service }}
42+
{{- end }}
43+
44+
{{/*
45+
Selector labels used by the Deployment and Service.
46+
*/}}
47+
{{- define "orb.selectorLabels" -}}
48+
app.kubernetes.io/name: {{ include "orb.name" . }}
49+
app.kubernetes.io/instance: {{ .Release.Name }}
50+
{{- end }}
51+
52+
{{/*
53+
Resolve the ServiceAccount name.
54+
*/}}
55+
{{- define "orb.serviceAccountName" -}}
56+
{{- if .Values.serviceAccount.create }}
57+
{{- default (include "orb.fullname" .) .Values.serviceAccount.name }}
58+
{{- else }}
59+
{{- default "default" .Values.serviceAccount.name }}
60+
{{- end }}
61+
{{- end }}
62+
63+
{{/*
64+
Resolve the container image (repo:tag).
65+
*/}}
66+
{{- define "orb.image" -}}
67+
{{- $tag := .Values.image.tag | default .Chart.AppVersion }}
68+
{{- printf "%s:%s" .Values.image.repository $tag }}
69+
{{- end }}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ include "orb.fullname" . }}-config
5+
namespace: {{ .Release.Namespace }}
6+
labels:
7+
{{- include "orb.labels" . | nindent 4 }}
8+
data:
9+
config.json: |
10+
{
11+
"version": "2.0.0",
12+
"scheduler": {
13+
"type": "default"
14+
},
15+
"provider": {
16+
"providers": [
17+
{
18+
"name": "{{ .Values.provider.type }}-default",
19+
"type": {{ .Values.provider.type | quote }},
20+
"enabled": true,
21+
"weight": 100,
22+
"config": {
23+
{{- if eq .Values.provider.type "k8s" }}
24+
"namespaces": {{ .Values.provider.k8s.namespaces | toJson }},
25+
"node_watch_enabled": {{ .Values.provider.k8s.nodeWatchEnabled }},
26+
"default_handler": {{ .Values.provider.k8s.defaultHandler | quote }}
27+
{{- else if eq .Values.provider.type "aws" }}
28+
"region": {{ .Values.provider.aws.region | quote }}
29+
{{- if .Values.provider.aws.profile }},
30+
"profile": {{ .Values.provider.aws.profile | quote }}
31+
{{- end }}
32+
{{- end }}
33+
}
34+
}
35+
],
36+
"selection_policy": "FIRST_AVAILABLE",
37+
"default_provider_type": {{ .Values.provider.type | quote }}
38+
},
39+
"storage": {
40+
"type": {{ .Values.storage.type | quote }},
41+
"base_path": {{ .Values.storage.basePath | quote }}
42+
},
43+
"logging": {
44+
"level": {{ .Values.server.logLevel | quote }}
45+
},
46+
"server": {
47+
"enabled": true,
48+
"host": {{ .Values.server.host | quote }},
49+
"port": {{ .Values.server.port }},
50+
"workers": {{ .Values.server.workers }},
51+
"auth": {
52+
"enabled": {{ .Values.auth.enabled }},
53+
"strategy": {{ .Values.auth.strategy | quote }}
54+
}
55+
}
56+
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
apiVersion: apps/v1
2+
kind: Deployment
3+
metadata:
4+
name: {{ include "orb.fullname" . }}
5+
namespace: {{ .Release.Namespace }}
6+
labels:
7+
{{- include "orb.labels" . | nindent 4 }}
8+
spec:
9+
replicas: {{ .Values.replicaCount }}
10+
selector:
11+
matchLabels:
12+
{{- include "orb.selectorLabels" . | nindent 6 }}
13+
template:
14+
metadata:
15+
annotations:
16+
# Force pod rollout when ConfigMap content changes
17+
checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }}
18+
{{- with .Values.podAnnotations }}
19+
{{- toYaml . | nindent 8 }}
20+
{{- end }}
21+
labels:
22+
{{- include "orb.selectorLabels" . | nindent 8 }}
23+
{{- with .Values.podLabels }}
24+
{{- toYaml . | nindent 8 }}
25+
{{- end }}
26+
spec:
27+
{{- with .Values.imagePullSecrets }}
28+
imagePullSecrets:
29+
{{- toYaml . | nindent 8 }}
30+
{{- end }}
31+
serviceAccountName: {{ include "orb.serviceAccountName" . }}
32+
securityContext:
33+
{{- toYaml .Values.podSecurityContext | nindent 8 }}
34+
containers:
35+
- name: orb
36+
securityContext:
37+
{{- toYaml .Values.securityContext | nindent 12 }}
38+
image: {{ include "orb.image" . }}
39+
imagePullPolicy: {{ .Values.image.pullPolicy }}
40+
# Entrypoint: docker-entrypoint.sh serve
41+
# -> orb server start --foreground --host HOST --port PORT [--workers N]
42+
# The entrypoint reads HF_* env vars and invokes `orb server start --foreground`.
43+
command: ["./docker-entrypoint.sh", "serve"]
44+
ports:
45+
- name: http
46+
containerPort: {{ .Values.server.port }}
47+
protocol: TCP
48+
env:
49+
# Server
50+
- name: HF_SERVER_ENABLED
51+
value: "true"
52+
- name: HF_SERVER_HOST
53+
value: {{ .Values.server.host | quote }}
54+
- name: HF_SERVER_PORT
55+
value: {{ .Values.server.port | quote }}
56+
- name: HF_SERVER_WORKERS
57+
value: {{ .Values.server.workers | quote }}
58+
- name: HF_SERVER_LOG_LEVEL
59+
value: {{ .Values.server.serverLogLevel | quote }}
60+
- name: HF_LOGGING_LEVEL
61+
value: {{ .Values.server.logLevel | quote }}
62+
# Config file path (mounted from ConfigMap)
63+
- name: HF_CONFIG_FILE
64+
value: "/etc/orb/config.json"
65+
# Auth
66+
- name: HF_AUTH_ENABLED
67+
value: {{ .Values.auth.enabled | quote }}
68+
- name: HF_AUTH_STRATEGY
69+
value: {{ .Values.auth.strategy | quote }}
70+
{{- if .Values.auth.existingSecret }}
71+
- name: HF_AUTH_BEARER_TOKEN
72+
valueFrom:
73+
secretKeyRef:
74+
name: {{ .Values.auth.existingSecret }}
75+
key: bearer-token
76+
{{- end }}
77+
# Provider
78+
- name: HF_PROVIDER_TYPE
79+
value: {{ .Values.provider.type | quote }}
80+
{{- if eq .Values.provider.type "aws" }}
81+
- name: HF_PROVIDER_AWS_REGION
82+
value: {{ .Values.provider.aws.region | quote }}
83+
{{- end }}
84+
# Storage
85+
- name: HF_STORAGE_STRATEGY
86+
value: {{ .Values.storage.type | quote }}
87+
- name: HF_STORAGE_BASE_PATH
88+
value: {{ .Values.storage.basePath | quote }}
89+
{{- with .Values.extraEnv }}
90+
{{- toYaml . | nindent 12 }}
91+
{{- end }}
92+
volumeMounts:
93+
- name: orb-config
94+
mountPath: /etc/orb
95+
readOnly: true
96+
- name: orb-data
97+
mountPath: /app/data
98+
- name: orb-logs
99+
mountPath: /app/logs
100+
- name: orb-tmp
101+
mountPath: /app/tmp
102+
{{- with .Values.extraVolumeMounts }}
103+
{{- toYaml . | nindent 12 }}
104+
{{- end }}
105+
livenessProbe:
106+
httpGet:
107+
path: /health
108+
port: http
109+
initialDelaySeconds: 15
110+
periodSeconds: 30
111+
timeoutSeconds: 10
112+
failureThreshold: 3
113+
readinessProbe:
114+
httpGet:
115+
path: /health
116+
port: http
117+
initialDelaySeconds: 10
118+
periodSeconds: 10
119+
timeoutSeconds: 5
120+
failureThreshold: 3
121+
resources:
122+
{{- toYaml .Values.resources | nindent 12 }}
123+
volumes:
124+
- name: orb-config
125+
configMap:
126+
name: {{ include "orb.fullname" . }}-config
127+
- name: orb-data
128+
emptyDir: {}
129+
- name: orb-logs
130+
emptyDir: {}
131+
- name: orb-tmp
132+
emptyDir: {}
133+
{{- with .Values.extraVolumes }}
134+
{{- toYaml . | nindent 8 }}
135+
{{- end }}
136+
{{- with .Values.nodeSelector }}
137+
nodeSelector:
138+
{{- toYaml . | nindent 8 }}
139+
{{- end }}
140+
{{- with .Values.affinity }}
141+
affinity:
142+
{{- toYaml . | nindent 8 }}
143+
{{- end }}
144+
{{- with .Values.tolerations }}
145+
tolerations:
146+
{{- toYaml . | nindent 8 }}
147+
{{- end }}

0 commit comments

Comments
 (0)