mirror of
https://github.com/cozystack/cozystack.git
synced 2026-11-03 18:47:04 +09:00
The `OIDCUser` typedef in `packages/extra/monitoring/values.yaml`
declared only `@field {string} email`; `cozyvalues-gen` regenerated
`values.schema.json`, `README.md`, and the mirrored `openAPISchema`
on the `Monitoring` ApplicationDefinition WITHOUT a `role` field.
Consequences:
- The `@enum OIDCRole` declaration was orphan — referenced from
nowhere, so the schema's items object had `required: ["email"]`
and one property.
- A dashboard-driven CR would offer a form without any role picker;
a schema-conformant `{email}` submission would render into the
users-Job with `role: null`, `jq --raw-output` yields the string
`"null"`, the `POST /api/orgs/1/users` body carries
`"role":"null"`, Grafana returns HTTP 400, and the post-install
hook exhausts `backoffLimit: 6` — HelmRelease never becomes Ready.
- Unit + e2e tests all hardcoded `role: Admin` / `role: Viewer`, so
the contract mismatch never surfaced in CI.
Fix:
- Add `@field {OIDCRole} role - ...` to the `OIDCUser` typedef in
`packages/extra/monitoring/values.yaml` so cozyvalues-gen materialises
the field with enum + required.
- Regenerate `values.schema.json`, `README.md`, and mirrored
`openAPISchema` via `make generate`.
- Add `monitoring.oidc.assertUsersRoleShape` template-level guard
(same shape as `assertUsersEmailShape`) so a stale-schema CR or a
direct-helm invocation fails at render time with a legible error
instead of at hook execution with a Grafana 400.
- Consolidate all `spec.oidc.users` guards into a single
`monitoring.oidc.assertUsersPrologue` include and call it once from
`oidc-users-job.yaml`, ordering the incompat check
(`assertCustomSecretRefUsersEmpty`) before the per-entry role /
email regexes so operators see the more actionable message first.
Drop the duplicate include from `grafana.yaml` (Job template renders
in every mode anyway).
- Two new helm-unittest cases lock the regression:
`oidc.users entry missing role fails the render` and
`oidc.users entry with unknown role fails the render`.
- Point the previous `secretRef + users` incompat unit test at the
users-Job template since that's where the prologue now fires.
- `docs/oidc-grafana.md`: correct the failed-hook post-mortem window
claim — with `ttlSecondsAfterFinished: 3600` the Kubernetes TTL
controller reaps the Job + Pod 1h after the terminal condition,
not "until the next helm-upgrade".
Signed-off-by: IvanHunters <xorokhotnikov@gmail.com>
672 lines
24 KiB
JSON
672 lines
24 KiB
JSON
{
|
|
"title": "Chart Values",
|
|
"type": "object",
|
|
"properties": {
|
|
"host": {
|
|
"description": "The hostname used to access Grafana externally (defaults to 'grafana' subdomain for the tenant host).",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"metricsStorages": {
|
|
"description": "Configuration of metrics storage instances.",
|
|
"type": "array",
|
|
"default": [
|
|
{
|
|
"deduplicationInterval": "15s",
|
|
"name": "shortterm",
|
|
"retentionPeriod": "3d",
|
|
"storage": "10Gi",
|
|
"storageClassName": ""
|
|
},
|
|
{
|
|
"deduplicationInterval": "5m",
|
|
"name": "longterm",
|
|
"retentionPeriod": "14d",
|
|
"storage": "10Gi",
|
|
"storageClassName": ""
|
|
}
|
|
],
|
|
"items": {
|
|
"type": "object",
|
|
"required": [
|
|
"deduplicationInterval",
|
|
"name",
|
|
"retentionPeriod",
|
|
"storage"
|
|
],
|
|
"properties": {
|
|
"deduplicationInterval": {
|
|
"description": "Deduplication interval for metrics.",
|
|
"type": "string"
|
|
},
|
|
"name": {
|
|
"description": "Name of the storage instance.",
|
|
"type": "string"
|
|
},
|
|
"retentionPeriod": {
|
|
"description": "Retention period for metrics.",
|
|
"type": "string"
|
|
},
|
|
"storage": {
|
|
"description": "Persistent volume size.",
|
|
"type": "string",
|
|
"default": "10Gi"
|
|
},
|
|
"storageClassName": {
|
|
"description": "StorageClass used for the data.",
|
|
"type": "string"
|
|
},
|
|
"vminsert": {
|
|
"description": "Configuration for vminsert.",
|
|
"type": "object",
|
|
"properties": {
|
|
"maxAllowed": {
|
|
"description": "Maximum allowed resources.",
|
|
"type": "object",
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU limit.",
|
|
"default": 1,
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory limit.",
|
|
"default": "1Gi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
},
|
|
"minAllowed": {
|
|
"description": "Minimum guaranteed resources.",
|
|
"type": "object",
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU request.",
|
|
"default": "100m",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory request.",
|
|
"default": "256Mi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"vmselect": {
|
|
"description": "Configuration for vmselect.",
|
|
"type": "object",
|
|
"properties": {
|
|
"maxAllowed": {
|
|
"description": "Maximum allowed resources.",
|
|
"type": "object",
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU limit.",
|
|
"default": 1,
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory limit.",
|
|
"default": "1Gi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
},
|
|
"minAllowed": {
|
|
"description": "Minimum guaranteed resources.",
|
|
"type": "object",
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU request.",
|
|
"default": "100m",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory request.",
|
|
"default": "256Mi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"vmstorage": {
|
|
"description": "Configuration for vmstorage.",
|
|
"type": "object",
|
|
"properties": {
|
|
"maxAllowed": {
|
|
"description": "Maximum allowed resources.",
|
|
"type": "object",
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU limit.",
|
|
"default": 1,
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory limit.",
|
|
"default": "1Gi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
},
|
|
"minAllowed": {
|
|
"description": "Minimum guaranteed resources.",
|
|
"type": "object",
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU request.",
|
|
"default": "100m",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory request.",
|
|
"default": "256Mi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"logsStorages": {
|
|
"description": "Configuration of logs storage instances.",
|
|
"type": "array",
|
|
"default": [
|
|
{
|
|
"name": "generic",
|
|
"retentionPeriod": "1",
|
|
"storage": "10Gi",
|
|
"storageClassName": "replicated"
|
|
}
|
|
],
|
|
"items": {
|
|
"type": "object",
|
|
"required": [
|
|
"name",
|
|
"retentionPeriod",
|
|
"storage",
|
|
"storageClassName"
|
|
],
|
|
"properties": {
|
|
"name": {
|
|
"description": "Name of the storage instance.",
|
|
"type": "string"
|
|
},
|
|
"retentionPeriod": {
|
|
"description": "Retention period for logs.",
|
|
"type": "string",
|
|
"default": "1"
|
|
},
|
|
"storage": {
|
|
"description": "Persistent volume size.",
|
|
"type": "string",
|
|
"default": "10Gi"
|
|
},
|
|
"storageClassName": {
|
|
"description": "StorageClass used to store the data.",
|
|
"type": "string",
|
|
"default": "replicated"
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"alerta": {
|
|
"description": "Configuration for the Alerta service.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"alerts": {
|
|
"description": "Alert routing configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"slack": {
|
|
"description": "Configuration for Slack alerts.",
|
|
"type": "object",
|
|
"default": {},
|
|
"required": [
|
|
"url"
|
|
],
|
|
"properties": {
|
|
"disabledSeverity": {
|
|
"description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).",
|
|
"type": "array",
|
|
"default": [],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"url": {
|
|
"description": "Configuration uri for Slack alerts.",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
},
|
|
"telegram": {
|
|
"description": "Configuration for Telegram alerts.",
|
|
"type": "object",
|
|
"default": {},
|
|
"required": [
|
|
"chatID",
|
|
"token"
|
|
],
|
|
"properties": {
|
|
"chatID": {
|
|
"description": "Telegram chat ID(s), separated by commas.",
|
|
"type": "string",
|
|
"default": ""
|
|
},
|
|
"disabledSeverity": {
|
|
"description": "List of severities without alerts (e.g. [\"informational\",\"warning\"]).",
|
|
"type": "array",
|
|
"default": [],
|
|
"items": {
|
|
"type": "string"
|
|
}
|
|
},
|
|
"token": {
|
|
"description": "Telegram bot token.",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"resources": {
|
|
"description": "Resource configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"limits": {
|
|
"description": "Resource limits.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU limit.",
|
|
"default": 1,
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory limit.",
|
|
"default": "1Gi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
},
|
|
"requests": {
|
|
"description": "Resource requests.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU request.",
|
|
"default": "100m",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory request.",
|
|
"default": "256Mi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"storage": {
|
|
"description": "Persistent volume size for the database.",
|
|
"type": "string",
|
|
"default": "10Gi"
|
|
},
|
|
"storageClassName": {
|
|
"description": "StorageClass used for the database.",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
},
|
|
"grafana": {
|
|
"description": "Configuration for Grafana.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"db": {
|
|
"description": "Database configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"size": {
|
|
"description": "Persistent volume size for the database.",
|
|
"type": "string",
|
|
"default": "10Gi"
|
|
}
|
|
}
|
|
},
|
|
"resources": {
|
|
"description": "Resource configuration.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"limits": {
|
|
"description": "Resource limits.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU limit.",
|
|
"default": 1,
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory limit.",
|
|
"default": "1Gi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
},
|
|
"requests": {
|
|
"description": "Resource requests.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"cpu": {
|
|
"description": "CPU request.",
|
|
"default": "100m",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
},
|
|
"memory": {
|
|
"description": "Memory request.",
|
|
"default": "256Mi",
|
|
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
|
|
"anyOf": [
|
|
{
|
|
"type": "integer"
|
|
},
|
|
{
|
|
"type": "string"
|
|
}
|
|
],
|
|
"x-kubernetes-int-or-string": true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"vmagent": {
|
|
"description": "Configuration for VictoriaMetrics Agent.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"externalLabels": {
|
|
"description": "External labels applied to all metrics.",
|
|
"type": "object",
|
|
"default": {
|
|
"cluster": "cozystack"
|
|
}
|
|
},
|
|
"remoteWrite": {
|
|
"description": "Remote write configuration.",
|
|
"type": "object",
|
|
"default": {
|
|
"urls": [
|
|
"http://vminsert-shortterm:8480/insert/0/prometheus",
|
|
"http://vminsert-longterm:8480/insert/0/prometheus"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"oidc": {
|
|
"description": "OIDC authentication for the Grafana instance. See docs/oidc-grafana.md for the operator guide.",
|
|
"type": "object",
|
|
"default": {},
|
|
"required": [
|
|
"mode"
|
|
],
|
|
"properties": {
|
|
"customConfig": {
|
|
"description": "Tenant-supplied `[auth.generic_oauth]` payload; consumed only when `mode: CustomConfig`.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"config": {
|
|
"description": "Inline map of `[auth.generic_oauth]` keys. Written verbatim into the Grafana CR `spec.config`. Mutually exclusive with `secretRef.name`.",
|
|
"type": "object",
|
|
"default": {}
|
|
},
|
|
"secretRef": {
|
|
"description": "Reference to a Secret holding an `auth.ini` fragment; mounted under `/etc/grafana/oidc` and loaded via `GF_PATHS_CUSTOM_INI`.",
|
|
"type": "object",
|
|
"default": {},
|
|
"properties": {
|
|
"name": {
|
|
"description": "Name of an existing Secret in the release namespace whose `auth.ini` key holds a ready-made `[auth.generic_oauth]` block. Mutually exclusive with `customConfig.config`.",
|
|
"type": "string",
|
|
"default": ""
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
"mode": {
|
|
"description": "Identity mode. `None`: no OIDC, only the `grafana-admin-password` Secret works. `System`: trust the platform `cozy` realm via a per-instance confidential client and audience binding; zero-config default. `CustomConfig`: trust a tenant-supplied issuer directly (BYO); `cozy` is not in the path.",
|
|
"type": "string",
|
|
"default": "None",
|
|
"enum": [
|
|
"None",
|
|
"System",
|
|
"CustomConfig"
|
|
]
|
|
},
|
|
"users": {
|
|
"description": "Users granted access to this Grafana instance's org. Each entry is reconciled into Grafana org membership + role by a chart-owned post-install/post-upgrade Job. Works for both `System` and `CustomConfig` modes; ignored when `mode: None`. Users not listed here get nothing (no default org role from group mapping).",
|
|
"type": "array",
|
|
"default": [],
|
|
"items": {
|
|
"type": "object",
|
|
"required": [
|
|
"email",
|
|
"role"
|
|
],
|
|
"properties": {
|
|
"email": {
|
|
"description": "Email address matched against the `email` claim from the issuer. The chart pre-provisions a Grafana account with this email so the users-Job can set the org role before the operator's first login (`oauth_allow_insecure_email_lookup = true` glues the two together). Must match a conservative pattern: local part is `[A-Za-z0-9._%+-]+`, domain is `[A-Za-z0-9.-]+\\.[A-Za-z]{2,}`. Whitespace, quoted literals, and unusual URL-reserved characters are rejected on schema level so the reconcile-Job's shell handling never has to defend against malformed input.",
|
|
"type": "string"
|
|
},
|
|
"role": {
|
|
"description": "Grafana org role to bind: `Admin`, `Editor`, or `Viewer`. Applied to the release's Grafana org (`Main Org.`); server-level `GrafanaAdmin` promotion is out of scope for Phase 1. Required — the users-Job POSTs `{loginOrEmail, role}` to `/api/orgs/1/users` and PATCHes `{role}` to converge across re-runs; omitting the field would render `role: \"null\"` into the body and fail with HTTP 400.",
|
|
"type": "string",
|
|
"enum": [
|
|
"Admin",
|
|
"Editor",
|
|
"Viewer"
|
|
]
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|