From 8f44016b05dbbfda479c1a422a685a0063739256 Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Tue, 26 May 2026 21:35:12 +0200 Subject: [PATCH 1/8] [maestrod] initial chart --- .github/workflows/helm-docs.yaml | 7 + .github/workflows/lint-test.yaml | 47 ++- CLAUDE.md | 6 +- README.md | 8 + charts/maestrod/.helmignore | 23 ++ charts/maestrod/.schema.yaml | 6 + charts/maestrod/CHANGELOG.md | 60 +++ charts/maestrod/Chart.yaml | 17 + charts/maestrod/LICENSE | 42 ++ charts/maestrod/README.md | 288 +++++++++++++ charts/maestrod/README.md.gotmpl | 260 ++++++++++++ charts/maestrod/ci/00-simple-values.yaml | 21 + charts/maestrod/ci/01-ingress-values.yaml | 27 ++ charts/maestrod/ci/02-extras-values.yaml | 60 +++ charts/maestrod/ci/03-hpa-pdb-values.yaml | 23 ++ charts/maestrod/templates/NOTES.txt | 20 + charts/maestrod/templates/_helpers.tpl | 91 ++++ charts/maestrod/templates/configmap.yaml | 9 + charts/maestrod/templates/deployment.yaml | 149 +++++++ charts/maestrod/templates/hpa.yaml | 40 ++ charts/maestrod/templates/ingress.yaml | 35 ++ charts/maestrod/templates/pdb.yaml | 18 + .../templates/restart-job/configmap.yaml | 50 +++ .../templates/restart-job/cronjob.yaml | 76 ++++ .../maestrod/templates/restart-job/rbac.yaml | 35 ++ .../templates/restart-job/serviceaccount.yaml | 13 + charts/maestrod/templates/service.yaml | 19 + charts/maestrod/templates/serviceaccount.yaml | 13 + .../templates/tests/test-connection.yaml | 15 + charts/maestrod/values.schema.json | 387 ++++++++++++++++++ charts/maestrod/values.simple.yaml | 22 + charts/maestrod/values.yaml | 359 ++++++++++++++++ 32 files changed, 2244 insertions(+), 2 deletions(-) create mode 100644 charts/maestrod/.helmignore create mode 100644 charts/maestrod/.schema.yaml create mode 100644 charts/maestrod/CHANGELOG.md create mode 100644 charts/maestrod/Chart.yaml create mode 100644 charts/maestrod/LICENSE create mode 100644 charts/maestrod/README.md create mode 100644 charts/maestrod/README.md.gotmpl create mode 100644 charts/maestrod/ci/00-simple-values.yaml create mode 100644 charts/maestrod/ci/01-ingress-values.yaml create mode 100644 charts/maestrod/ci/02-extras-values.yaml create mode 100644 charts/maestrod/ci/03-hpa-pdb-values.yaml create mode 100644 charts/maestrod/templates/NOTES.txt create mode 100644 charts/maestrod/templates/_helpers.tpl create mode 100644 charts/maestrod/templates/configmap.yaml create mode 100644 charts/maestrod/templates/deployment.yaml create mode 100644 charts/maestrod/templates/hpa.yaml create mode 100644 charts/maestrod/templates/ingress.yaml create mode 100644 charts/maestrod/templates/pdb.yaml create mode 100644 charts/maestrod/templates/restart-job/configmap.yaml create mode 100644 charts/maestrod/templates/restart-job/cronjob.yaml create mode 100644 charts/maestrod/templates/restart-job/rbac.yaml create mode 100644 charts/maestrod/templates/restart-job/serviceaccount.yaml create mode 100644 charts/maestrod/templates/service.yaml create mode 100644 charts/maestrod/templates/serviceaccount.yaml create mode 100644 charts/maestrod/templates/tests/test-connection.yaml create mode 100644 charts/maestrod/values.schema.json create mode 100644 charts/maestrod/values.simple.yaml create mode 100644 charts/maestrod/values.yaml diff --git a/.github/workflows/helm-docs.yaml b/.github/workflows/helm-docs.yaml index efff88ff..ab54c1b6 100644 --- a/.github/workflows/helm-docs.yaml +++ b/.github/workflows/helm-docs.yaml @@ -27,3 +27,10 @@ jobs: chart-search-root: charts/ai-assistant # git-push: true fail-on-diff: true + + - name: Run helm-docs for Maestrod + uses: losisin/helm-docs-github-action@v1 + with: + chart-search-root: charts/maestrod + # git-push: true + fail-on-diff: true diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 3fd2749b..3249e726 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -11,10 +11,16 @@ on: permissions: contents: read + id-token: write # required for OIDC to assume the ECR-pull IAM role jobs: lint-test: runs-on: ubuntu-latest + env: + MAESTROD_ECR_REGISTRY: 111300957880.dkr.ecr.eu-west-1.amazonaws.com + MAESTROD_ECR_REPOSITORY: maestrod + MAESTROD_ECR_TAG: nightly + AWS_REGION: eu-west-1 steps: - name: Checkout uses: actions/checkout@v6 @@ -41,6 +47,9 @@ jobs: if [[ -n "$changed" ]]; then echo "changed=true" >> "$GITHUB_OUTPUT" fi + if echo "$changed" | grep -qx 'charts/maestrod'; then + echo "maestrod-changed=true" >> "$GITHUB_OUTPUT" + fi - name: Run chart-testing (lint) if: steps.list-changed.outputs.changed == 'true' @@ -50,6 +59,24 @@ jobs: if: steps.list-changed.outputs.changed == 'true' uses: helm/kind-action@v1 + - name: Configure AWS credentials (for ECR pull) + if: steps.list-changed.outputs.maestrod-changed == 'true' + uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: ${{ secrets.AWS_CI_ECR_ROLE_ARN }} + aws-region: ${{ env.AWS_REGION }} + + - name: Log in to Amazon ECR + if: steps.list-changed.outputs.maestrod-changed == 'true' + uses: aws-actions/amazon-ecr-login@v2 + + - name: Side-load maestrod image into kind + if: steps.list-changed.outputs.maestrod-changed == 'true' + run: | + IMAGE="${MAESTROD_ECR_REGISTRY}/${MAESTROD_ECR_REPOSITORY}:${MAESTROD_ECR_TAG}" + docker pull "$IMAGE" + kind load docker-image "$IMAGE" --name chart-testing + - name: Install Gateway API CRDs if: steps.list-changed.outputs.changed == 'true' run: | @@ -226,11 +253,29 @@ jobs: EOF kubectl wait --namespace whatever --for=condition=complete job/garage-create-buckets --timeout=300s - - name: Run chart-testing (install) + - name: Run chart-testing (install) — public-image charts if: steps.list-changed.outputs.changed == 'true' + # Public Nutrient charts (document-engine, ai-assistant, …): keep the + # original `pullPolicy=Always` behaviour so the nightly tag is always + # re-pulled from Docker Hub. Maestrod is excluded here and installed + # separately below against its kind-loaded ECR image. run: | helm repo add nutrient https://pspdfkit.github.io/helm-charts ct install --target-branch ${{ github.event.repository.default_branch }} \ + --excluded-charts maestrod \ --helm-extra-args "--timeout 300s" \ --helm-extra-set-args "--set=image.tag=nightly" \ --helm-extra-set-args "--set=image.pullPolicy=Always" + + - name: Run chart-testing (install) — maestrod (ECR side-loaded) + if: steps.list-changed.outputs.maestrod-changed == 'true' + # Maestrod's image lives in a private ECR; the workflow pulls it on + # the runner and side-loads it into the kind node above. We omit + # `pullPolicy=Always` here so kubelet uses the loaded image + # (the chart's `IfNotPresent` default) instead of trying to re-pull + # from a registry kind has no credentials for. + run: | + ct install --target-branch ${{ github.event.repository.default_branch }} \ + --charts charts/maestrod \ + --helm-extra-args "--timeout 300s" \ + --helm-extra-set-args "--set=image.tag=${MAESTROD_ECR_TAG}" diff --git a/CLAUDE.md b/CLAUDE.md index d56b159b..c00bd493 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -15,6 +15,7 @@ Nutrient Helm Charts repository. Contains Kubernetes Helm charts for Nutrient pr |-------|-------------| | `document-engine` | Backend for document processing and automation workflows. Most complex chart. | | `ai-assistant` | AI Assistant application. Depends on `document-engine` chart. | +| `maestrod` | Orchestration backend for Nutrient managed cloud workloads. Single stateless Deployment. | | `signing-service-example` | Example chart for demonstration. Excluded from CI. | | `simple-resource-wrapper` | Generic Kubernetes resource wrapper (Terraform workaround). | @@ -22,7 +23,7 @@ Nutrient Helm Charts repository. Contains Kubernetes Helm charts for Nutrient pr ### README.md (helm-docs) -README.md files for `document-engine` and `ai-assistant` are **auto-generated** by +README.md files for `document-engine`, `ai-assistant`, and `maestrod` are **auto-generated** by [helm-docs](https://github.com/norwoodj/helm-docs). CI fails if they are out of sync. - **Template**: `charts//README.md.gotmpl` @@ -34,6 +35,7 @@ To regenerate locally: ```bash helm-docs --chart-search-root charts/document-engine helm-docs --chart-search-root charts/ai-assistant +helm-docs --chart-search-root charts/maestrod ``` ### values.schema.json (helm-values-schema-json) @@ -49,6 +51,7 @@ To regenerate locally: ```bash cd charts/document-engine && helm schema -input values.yaml -draft 2020 -indent 2 -output values.schema.json cd charts/ai-assistant && helm schema -input values.yaml -draft 2020 -indent 2 -output values.schema.json +cd charts/maestrod && helm schema ``` ## values.yaml Comment Conventions @@ -135,6 +138,7 @@ After modifying any chart, regenerate derived files before committing: ```bash helm-docs --chart-search-root charts/document-engine helm-docs --chart-search-root charts/ai-assistant + helm-docs --chart-search-root charts/maestrod ``` 2. **values.schema.json** — run `helm schema` whenever `values.yaml` schema annotations change: ```bash diff --git a/README.md b/README.md index 76572be0..d6258633 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,14 @@ helm upgrade --install -n document-engine \ -f ./document-engine-values.yaml ``` +### Maestrod + +``` +helm upgrade --install -n maestrod \ + maestrod nutrient/maestrod \ + -f ./maestrod-values.yaml +``` + ## Support, Issues and License Questions Nutrient offers support via https://support.nutrient.io/hc/en-us/requests/new diff --git a/charts/maestrod/.helmignore b/charts/maestrod/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/charts/maestrod/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/charts/maestrod/.schema.yaml b/charts/maestrod/.schema.yaml new file mode 100644 index 00000000..12b58974 --- /dev/null +++ b/charts/maestrod/.schema.yaml @@ -0,0 +1,6 @@ +input: + - values.yaml + +draft: 2020 +indent: 2 +output: values.schema.json diff --git a/charts/maestrod/CHANGELOG.md b/charts/maestrod/CHANGELOG.md new file mode 100644 index 00000000..ab47d536 --- /dev/null +++ b/charts/maestrod/CHANGELOG.md @@ -0,0 +1,60 @@ +# Changelog + +- [Changelog](#changelog) + - [0.4.0 (2026-05-26)](#040-2026-05-26) + +## 0.4.0 (2026-05-26) + +First public release. Value-compatible with the internal `0.3.4` chart, with +defaults reset to neutral values (set them in your values file to preserve +the old behaviour): + +```yaml +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod # now pspdfkit/maestrod + tag: stable # now empty (falls back to appVersion) + pullPolicy: Always # now IfNotPresent +imagePullSecrets: [{ name: image-registry-mcleod-ecr }] # now [] +podLabels: { component_name: maestrod, otel/logging-enabled: "true" } # now {} +# only if restartJob.enabled: true +restartJob: { registryAuthSecretName: image-registry-mcleod-ecr } # now "" +``` + +### Added + +- `templates/configmap.yaml` carries `NUTRIENT_SHOW_SCALAR` / + `NATIVESDK_VISION_LOGS`, loaded via `envFrom` with a `checksum/config` + rollout trigger (Document Engine pattern). +- Standard public-chart knobs: `serviceAccount`, `autoscaling`, + `podDisruptionBudget`, `startup`/`livenessProbe`, `lifecycle`, + `topologySpreadConstraints`, `schedulerName`, `deploymentAnnotations`, + `extraEnvs` / `extraEnvFrom` / `extraEnvFromSecrets` / `extraVolumes` / + `extraVolumeMounts` / `sidecars` / `initContainers`. All opt-in, default + no-op. +- Generated `README.md` (helm-docs) and `values.schema.json` + (helm-values-schema-json). +- `helm test` connection probe; `ci/` test value files. +- `licenseSecret.name: ""` now skips the `NUTRIENT_LICENSE_KEY` env var + (previously hard-coded). + +### Changed + +- `image.repository` defaults to `pspdfkit/maestrod` (and is schema-rejected + when empty). +- Restart Job moved under `templates/restart-job/` (`configmap.yaml`, + `cronjob.yaml`, `rbac.yaml`, `serviceaccount.yaml`). + +### Fixed + +- `PodDisruptionBudget` no longer renders both `minAvailable` and + `maxUnavailable`; `maxUnavailable` wins. Integer `0` is recognised. +- `Deployment.spec.strategy` omits `rollingUpdate` when `type: Recreate`. +- `updateStrategy.rollingUpdate.{maxSurge,maxUnavailable}` accept integer or + percentage string (`IntOrString`). +- `service.type` enum no longer advertises `ExternalName` (unsupported by + the Service template). + +### CI + +- `maestrod` is excluded from `ct install` until a public maestrod image is + available; `ct lint` still runs on every PR. diff --git a/charts/maestrod/Chart.yaml b/charts/maestrod/Chart.yaml new file mode 100644 index 00000000..64bdd760 --- /dev/null +++ b/charts/maestrod/Chart.yaml @@ -0,0 +1,17 @@ +apiVersion: v2 +name: maestrod +type: application +description: Maestrod, the orchestration backend for Nutrient managed cloud workloads. +home: https://www.nutrient.io +icon: https://cdn.prod.website-files.com/65fdb7696055f07a05048833/66e58e33c3880ff24aa34027_nutrient-logo.png +version: 0.4.0 +appVersion: "1.0.0" + +keywords: + - nutrient + - maestrod + +maintainers: + - name: Nutrient + email: support@nutrient.io + url: https://www.nutrient.io diff --git a/charts/maestrod/LICENSE b/charts/maestrod/LICENSE new file mode 100644 index 00000000..2790acc9 --- /dev/null +++ b/charts/maestrod/LICENSE @@ -0,0 +1,42 @@ +The Nutrient Sample applications are licensed with a modified BSD +license. In plain language: you're allowed to do whatever you wish +with the code, modify, redistribute, embed in your products (free or +commercial), but you must include copyright, terms of usage and +disclaimer as stated in the license. + +You will require a commercial Nutrient License to run these examples +in non-demo mode. Please refer to sales@nutrient.io for details. + +Copyright © 2018-present PSPDFKit GmbH. +All rights reserved. + +Redistribution and use in source or binary forms, +with or without modification, are permitted provided +that the following conditions are met: + +- Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +- Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the + distribution. + +- Redistributions of Nutrient Samples must include attribution to + Nutrient, either in documentation or other appropriate media. + +- Neither the name of the Nutrient, PSPDFKit GmbH, nor its developers + may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/charts/maestrod/README.md b/charts/maestrod/README.md new file mode 100644 index 00000000..e977f81a --- /dev/null +++ b/charts/maestrod/README.md @@ -0,0 +1,288 @@ +# Maestrod Helm chart + +> [!WARNING] This chart is made for internal use by Nutrient. + +![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0](https://img.shields.io/badge/AppVersion-1.0.0-informational?style=flat-square) + +Maestrod, the orchestration backend for Nutrient managed cloud workloads. + +**Homepage:** + +* [Using this chart](#using-this-chart) +* [Prerequisites](#prerequisites) +* [Migrating from the internal 0.3.x chart](#migrating-from-the-internal-03x-chart) +* [Restart Job](#restart-job) +* [Values](#values) + * [Maestrod License](#maestrod-license) + * [Maestrod configuration](#maestrod-configuration) + * [Environment](#environment) + * [Metadata](#metadata) + * [Networking](#networking) + * [Pod lifecycle](#pod-lifecycle) + * [Scheduling](#scheduling) + * [Restart job](#restart-job) +* [Contribution](#contribution) +* [License](#license) +* [Support, Issues and License Questions](#support-issues-and-license-questions) + +## Maintainers + +| Name | Email | Url | +| ---- | ------ | --- | +| Nutrient | | | + +## Using this chart + +### Adding the repository + +```shell +helm repo add nutrient https://pspdfkit.github.io/helm-charts +helm repo update +``` + +### Installing Maestrod + +```shell +helm upgrade --install -n maestrod \ + maestrod nutrient/maestrod \ + -f ./maestrod-values.yaml +``` + +Schema is generated using the [helm-values-schema-json plugin](https://github.com/losisin/helm-values-schema-json). + +`README.md` is generated with [helm-docs](https://github.com/norwoodj/helm-docs). + +### Upgrade + +> [!NOTE] +> Please consult the [changelog](/charts/maestrod/CHANGELOG.md). + +## Prerequisites + +### License Secret + +Maestrod requires the activation key to be supplied via an existing +Kubernetes Secret in the release namespace. The chart does **not** create +this secret. Create it before installing: + +```shell +kubectl create secret generic maestrod-secret \ + -n maestrod \ + --from-literal=NUTRIENT_LICENSE_KEY="$YOUR_LICENSE_KEY" +``` + +Override `licenseSecret.name` / `licenseSecret.key` to reference a different +Secret/key. + +### Image + +`image.repository` defaults to `pspdfkit/maestrod`. Override it to use a +private registry or mirror: + +```yaml +image: + repository: my-registry.example.com/maestrod + tag: "1.0.0" +``` + +When `image.tag` is empty, the chart falls back to the chart's `appVersion`. + +## Migrating from the internal 0.3.x chart + +This is the first public release of the chart. It is value-compatible with +the internal `cloud-demo-infra/charts/maestrod@0.3.4`, with a small number of +defaults reset to neutral values. If your existing values file relied on any +of the internal defaults below, copy them into your values file as-is. All +other value paths (`licenseSecret`, `maestro.*`, `service`, `ingress`, +`subdomainHostName`, `subdomainRootName`, `environmentShortName`, scheduling, +resources, `updateStrategy`, `restartJob.*`) keep identical defaults and +semantics. + +```yaml +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod # no default in public chart + tag: stable # default was 'stable', now empty (falls back to appVersion) + pullPolicy: Always # default was 'Always', now 'IfNotPresent' + +imagePullSecrets: + - name: image-registry-mcleod-ecr # default was this, now [] + +podLabels: + component_name: maestrod # default was this, now {} + otel/logging-enabled: "true" + +# only if restartJob.enabled: true +restartJob: + registryAuthSecretName: image-registry-mcleod-ecr # default was this, now "" +``` + +The Deployment selector labels are byte-identical between the internal and +public chart, so existing releases can be upgraded in place without hitting +selector-immutability errors. + +## Restart Job + +The chart ships an optional CronJob that polls the configured image registry +for a new digest on the running `image.tag` and patches the Maestrod +Deployment with a `nutrient/refresh-tag` annotation to trigger a rollout. + +It is disabled by default. To enable, set: + +```yaml +restartJob: + enabled: true + schedule: "*/10 * * * *" + registryAuthSecretName: my-registry-credentials # required: must be a kubernetes.io/dockerconfigjson Secret +``` + +The CronJob runs under a dedicated ServiceAccount with a Role that grants +`get/list pods` and `get/list/update/patch deployments` in the release +namespace. + +## Values + +### Maestrod License + +| Key | Description | Default | +|-----|-------------|---------| +| [`licenseSecret`](./values.yaml#L9) | Maestrod license. When `licenseSecret.name` is non-empty (the default), the chart wires the `NUTRIENT_LICENSE_KEY` environment variable to the referenced Secret/key. The Secret is **not** managed by the chart and must exist in the release namespace prior to install. Set `licenseSecret.name: ""` to omit the env var entirely (e.g. for chart-level template tests where the license is not exercised). | [...](./values.yaml#L9) | +| [`licenseSecret.key`](./values.yaml#L19) | Key inside the referenced Secret whose value is the Maestrod activation key. The chart wires this into the `NUTRIENT_LICENSE_KEY` environment variable. | `"NUTRIENT_LICENSE_KEY"` | +| [`licenseSecret.name`](./values.yaml#L14) | Name of the pre-existing Kubernetes Secret that holds the Maestrod activation key. When empty, the chart skips wiring the `NUTRIENT_LICENSE_KEY` env var. | `"maestrod-secret"` | + +### Maestrod configuration + +| Key | Description | Default | +|-----|-------------|---------| +| [`maestro`](./values.yaml#L25) | Maestrod-specific runtime flags. Each key maps directly to a `NUTRIENT_*` / `NATIVESDK_*` environment variable on the container. | [...](./values.yaml#L25) | +| [`maestro.nativeSdkVisionLogs`](./values.yaml#L33) | `NATIVESDK_VISION_LOGS` — enable native SDK vision-mode logging. Rendered as a string-quoted boolean. | `false` | +| [`maestro.showScalar`](./values.yaml#L29) | `NUTRIENT_SHOW_SCALAR` — enable Nutrient Scalar integration in the Maestrod UI. Rendered as a string-quoted boolean. | `false` | + +### Environment + +| Key | Description | Default | +|-----|-------------|---------| +| [`extraEnvFrom`](./values.yaml#L92) | Extra `envFrom` sources appended to the Maestrod container. | `[]` | +| [`extraEnvFromSecrets`](./values.yaml#L101) | Extra Secret names whose contents are loaded into the container env via `envFrom: secretRef:`. Convenience for the common case of supplying credentials. | `[]` | +| [`extraEnvs`](./values.yaml#L86) | Extra environment variables appended to the Maestrod container. | `[]` | +| [`extraVolumeMounts`](./values.yaml#L110) | Additional volume mounts for the Maestrod container. | `[]` | +| [`extraVolumes`](./values.yaml#L106) | Additional volumes for the pod. | `[]` | +| [`image`](./values.yaml#L38) | Image settings. | [...](./values.yaml#L38) | +| [`image.pullPolicy`](./values.yaml#L46) | Image pull policy. | `"IfNotPresent"` | +| [`image.repository`](./values.yaml#L43) | Image repository. The default points at the canonical public Nutrient registry; override to use a private registry or mirror. The schema rejects an empty string. | `"pspdfkit/maestrod"` | +| [`image.tag`](./values.yaml#L49) | Image tag. Defaults to the chart's `appVersion` when empty. | `""` | +| [`imagePullSecrets`](./values.yaml#L54) | [ImagePullSecrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) referencing pre-existing Secrets in the release namespace. | `[]` | +| [`initContainers`](./values.yaml#L119) | [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) for the pod. | `[]` | +| [`podSecurityContext`](./values.yaml#L79) | [Pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). | `{}` | +| [`securityContext`](./values.yaml#L82) | [Container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). | `{}` | +| [`serviceAccount`](./values.yaml#L62) | ServiceAccount for the Maestrod pod. With `create: false` (default), the pod runs under the namespace's `default` ServiceAccount and no ServiceAccount resource is created by the chart. | [...](./values.yaml#L62) | +| [`serviceAccount.annotations`](./values.yaml#L68) | Annotations for the created ServiceAccount. | `{}` | +| [`serviceAccount.automount`](./values.yaml#L75) | Auto-mount the ServiceAccount token in the pod. | `false` | +| [`serviceAccount.create`](./values.yaml#L65) | Create a ServiceAccount. | `false` | +| [`serviceAccount.name`](./values.yaml#L72) | Name to use for the ServiceAccount. When empty and `create: true`, the chart's fullname is used. | `""` | +| [`sidecars`](./values.yaml#L114) | Additional sidecar containers for the pod. | `[]` | + +### Metadata + +| Key | Description | Default | +|-----|-------------|---------| +| [`deploymentAnnotations`](./values.yaml#L137) | Annotations for the Deployment resource itself (distinct from `podAnnotations`, which apply to the pod template). | `{}` | +| [`fullnameOverride`](./values.yaml#L126) | Release full name override. | `""` | +| [`nameOverride`](./values.yaml#L123) | Release name override. | `""` | +| [`podAnnotations`](./values.yaml#L133) | Pod annotations. | `{}` | +| [`podLabels`](./values.yaml#L130) | Pod labels merged on top of the common chart labels. | `{}` | + +### Networking + +| Key | Description | Default | +|-----|-------------|---------| +| [`environmentShortName`](./values.yaml#L204) | Short environment identifier (e.g. `staging`, `prod`). Kept for backwards compatibility; not currently consumed by any template. | `""` | +| [`ingress`](./values.yaml#L162) | Ingress for the Maestrod Service. Single-host structure for backwards compatibility with the internal `0.3.x` chart. The host is resolved from `ingress.host` (preferred), or composed from `subdomainHostName` + `subdomainRootName` (fallback). | [...](./values.yaml#L162) | +| [`ingress.annotations`](./values.yaml#L172) | Ingress annotations. The chart additionally injects an `external-dns.alpha.kubernetes.io/hostname` annotation with the resolved host. | `{}` | +| [`ingress.className`](./values.yaml#L168) | Ingress class name. | `"nginx"` | +| [`ingress.enabled`](./values.yaml#L165) | Enable ingress. | `false` | +| [`ingress.host`](./values.yaml#L176) | Explicit ingress host. When empty, the host is composed from `subdomainHostName` and `subdomainRootName`. | `""` | +| [`ingress.path`](./values.yaml#L179) | Ingress path. | `"/"` | +| [`ingress.pathType`](./values.yaml#L182) | Path type. | `"Prefix"` | +| [`ingress.tls`](./values.yaml#L186) | Ingress TLS settings. | [...](./values.yaml#L186) | +| [`ingress.tls.enabled`](./values.yaml#L189) | Enable TLS. | `false` | +| [`ingress.tls.secretName`](./values.yaml#L192) | TLS secret name. When empty, defaults to `-ingress`. | `""` | +| [`service`](./values.yaml#L142) | Service exposing the Maestrod container. | [...](./values.yaml#L142) | +| [`service.annotations`](./values.yaml#L154) | Service annotations. | `{}` | +| [`service.port`](./values.yaml#L148) | Service port. | `5000` | +| [`service.targetPort`](./values.yaml#L151) | Container port the Service routes to. | `5000` | +| [`service.type`](./values.yaml#L145) | Service type. | `"ClusterIP"` | +| [`subdomainHostName`](./values.yaml#L197) | Subdomain part of the composed ingress host. Used when `ingress.host` is empty: the chart joins `.`. | `""` | +| [`subdomainRootName`](./values.yaml#L200) | Root domain part of the composed ingress host. | `""` | + +### Pod lifecycle + +| Key | Description | Default | +|-----|-------------|---------| +| [`lifecycle`](./values.yaml#L224) | [Container lifecycle hooks](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/). | `{}` | +| [`livenessProbe`](./values.yaml#L214) | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). Empty by default. Provide a probe spec to enable. | `{}` | +| [`readinessProbe`](./values.yaml#L218) | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). Empty by default. Provide a probe spec to enable. | `{}` | +| [`startupProbe`](./values.yaml#L210) | [Startup probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). Empty by default (no probe configured) for backwards compatibility with the internal `0.3.x` chart. Provide a probe spec to enable. | `{}` | +| [`terminationGracePeriodSeconds`](./values.yaml#L221) | [Termination grace period](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/). | `30` | + +### Scheduling + +| Key | Description | Default | +|-----|-------------|---------| +| [`affinity`](./values.yaml#L300) | Node affinity. | `{}` | +| [`autoscaling`](./values.yaml#L231) | [HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/). When `enabled: true`, the chart's HPA controls the replica count and `replicaCount` is ignored. | [...](./values.yaml#L231) | +| [`autoscaling.behavior`](./values.yaml#L249) | HPA [scaling behaviour](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior). | `{}` | +| [`autoscaling.enabled`](./values.yaml#L234) | Enable the HPA. | `false` | +| [`autoscaling.maxReplicas`](./values.yaml#L240) | Maximum replicas. | `10` | +| [`autoscaling.minReplicas`](./values.yaml#L237) | Minimum replicas. | `1` | +| [`autoscaling.targetCPUUtilizationPercentage`](./values.yaml#L243) | Target average CPU utilisation (percentage). `null` disables the metric. | `nil` | +| [`autoscaling.targetMemoryUtilizationPercentage`](./values.yaml#L246) | Target average memory utilisation (percentage). `null` disables the metric. | `nil` | +| [`nodeSelector`](./values.yaml#L297) | [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). | `{}` | +| [`podDisruptionBudget`](./values.yaml#L284) | [PodDisruptionBudget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/). When both `minAvailable` and `maxUnavailable` are non-empty, `maxUnavailable` wins (the two fields are mutually exclusive in Kubernetes). Either field accepts an integer (e.g. `1`) or a percentage string (e.g. `"50%"`). | [...](./values.yaml#L284) | +| [`podDisruptionBudget.create`](./values.yaml#L287) | Create a PodDisruptionBudget for Maestrod. | `false` | +| [`podDisruptionBudget.maxUnavailable`](./values.yaml#L293) | `spec.maxUnavailable`. Integer or percentage string. Takes precedence over `minAvailable`. | `""` | +| [`podDisruptionBudget.minAvailable`](./values.yaml#L290) | `spec.minAvailable`. Integer or percentage string. Ignored when `maxUnavailable` is set. | `1` | +| [`priorityClassName`](./values.yaml#L309) | [PriorityClass](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) name. | `""` | +| [`replicaCount`](./values.yaml#L263) | Number of replicas. Ignored when `autoscaling.enabled` is `true`. | `3` | +| [`resources`](./values.yaml#L253) | [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). | `{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"4","memory":"8Gi"}}` | +| [`revisionHistoryLimit`](./values.yaml#L276) | [Revision history limit](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy). | `1` | +| [`schedulerName`](./values.yaml#L312) | [Scheduler](https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/) name. | `""` | +| [`tolerations`](./values.yaml#L303) | [Node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). | `[]` | +| [`topologySpreadConstraints`](./values.yaml#L306) | [Topology spread constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/). | `[]` | +| [`updateStrategy`](./values.yaml#L269) | [Update strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). `rollingUpdate.maxSurge` and `rollingUpdate.maxUnavailable` are `IntOrString` in Kubernetes — both an integer (e.g. `1`) and a percentage string (e.g. `"25%"`) are accepted. | `{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0},"type":"RollingUpdate"}` | + +### Restart job + +| Key | Description | Default | +|-----|-------------|---------| +| [`restartJob`](./values.yaml#L319) | Optional CronJob that polls the configured image registry for a new digest on the running `image.tag` and patches the Maestrod Deployment with a refresh annotation to trigger a rollout. Disabled by default. | [...](./values.yaml#L319) | +| [`restartJob.affinity`](./values.yaml#L359) | Affinity for the restart-job pod. | `{}` | +| [`restartJob.enabled`](./values.yaml#L322) | Enable the restart-job CronJob and its supporting RBAC/ServiceAccount. | `false` | +| [`restartJob.image`](./values.yaml#L330) | Image for the restart-job container. Must contain `kubectl`, `curl`, `jq`, and `bash` — `alpine/k8s` covers all four. | [...](./values.yaml#L330) | +| [`restartJob.nodeSelector`](./values.yaml#L353) | Node selector for the restart-job pod. | `{}` | +| [`restartJob.podAnnotations`](./values.yaml#L341) | Pod annotations for the restart-job pod. | `{"skip-auto-labelling":"true"}` | +| [`restartJob.podLabels`](./values.yaml#L345) | Pod labels for the restart-job pod. | `{}` | +| [`restartJob.registryAuthSecretName`](./values.yaml#L338) | Name of a pre-existing `kubernetes.io/dockerconfigjson` Secret holding the registry credentials used to query the image manifest. Required when `restartJob.enabled: true`; rendering fails otherwise. | `""` | +| [`restartJob.schedule`](./values.yaml#L325) | CronJob schedule. | `"*/10 * * * *"` | +| [`restartJob.serviceAccount`](./values.yaml#L349) | ServiceAccount for the restart-job pod. | [...](./values.yaml#L349) | +| [`restartJob.tolerations`](./values.yaml#L356) | Tolerations for the restart-job pod. | `[]` | + +## Contribution + +The chart is validated using [ct](https://github.com/helm/chart-testing/tree/main) [lint](https://github.com/helm/chart-testing/blob/main/doc/ct_lint.md): + +```shell +ct lint --target-branch "$(git rev-parse --abbrev-ref HEAD)" +``` + +## License + +This software is licensed under a [modified BSD license](LICENSE). + +## Support, Issues and License Questions + +Nutrient offers support via https://support.nutrient.io/hc/en-us/requests/new + +Are you [evaluating our SDK](https://www.nutrient.io/sdk/)? That's great, we're happy to help out! To make sure this is fast, please use a work email and have someone from your company fill out our sales form: https://www.nutrient.io/contact-sales/ + +---------------------------------------------- +Autogenerated from chart metadata using [helm-docs v1.14.2](https://github.com/norwoodj/helm-docs/releases/v1.14.2) diff --git a/charts/maestrod/README.md.gotmpl b/charts/maestrod/README.md.gotmpl new file mode 100644 index 00000000..9ccc00ab --- /dev/null +++ b/charts/maestrod/README.md.gotmpl @@ -0,0 +1,260 @@ +{{/* +Functions { +*/}} +{{- define "chart.valuesTableOfContents" -}} +{{- if .Sections.Sections -}} +{{- $sectionNames := list -}} +{{- range .Sections.Sections -}} +{{- $sectionNames = append $sectionNames .SectionName -}} +{{- end -}} +{{- $sections := list -}} +{{- range ($sectionNames | sortAlpha) -}} +{{- $currentSectionName := . -}} +{{- range $.Sections.Sections -}} +{{- if eq .SectionName $currentSectionName -}} +{{- $thisSection := dict -}} +{{- $title := regexReplaceAll "^[0-9A-Z]+\\.\\s+" .SectionName "" -}} +{{- $link := printf "./values.yaml#L%d" (first .SectionItems).LineNumber -}} +{{- $_ := set $thisSection "SectionName" .SectionName -}} +{{- $_ := set $thisSection "SectionItems" .SectionItems -}} +{{- $_ := set $thisSection "SectionTitle" $title -}} +{{- $sections = append $sections $thisSection -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- if .Sections.DefaultSection.SectionItems -}} +{{- $thisSection := dict -}} +{{- $_ := set $thisSection "SectionName" .Sections.DefaultSection.SectionName -}} +{{- $_ := set $thisSection "SectionItems" .Sections.DefaultSection.SectionItems -}} +{{- $_ := set $thisSection "SectionTitle" .Sections.DefaultSection.SectionName -}} +{{- $sections = append $sections $thisSection -}} +{{- end -}} +{{- range $sections -}} +{{- $sectionAnchor := regexReplaceAll "\\s" .SectionTitle "-" | lower }} + * [{{ .SectionTitle }}](#{{ $sectionAnchor }}) +{{- end -}} +{{- end -}} +{{- end -}} + +{{- define "chart.valueDefaultColumnRender" -}} +{{- $defaultValue := (default .Default .AutoDefault) -}} +{{- $notationType := .NotationType -}} +{{- if .Default -}} +{{- $defaultValue = (trimAll "`" (default .Default .AutoDefault) ) -}} +{{- $notationType = "json" -}} +{{- end -}} +{{- if eq $notationType "none" -}} +{{- else if eq $notationType "reference" -}} +[...](./values.yaml#L{{ .LineNumber }}) +{{- else if eq $notationType "plain" -}} +{{- $defaultValue }} +{{- else if eq $notationType "tpl" -}} +`{{ $defaultValue }}` +{{- else -}} +`{{ $defaultValue }}` +{{- end -}} +{{- end -}} + +{{- define "chart.valuesTable" -}} +{{- if .Sections.Sections -}} +{{- $sectionNames := list -}} +{{- range .Sections.Sections -}} +{{- $sectionNames = append $sectionNames .SectionName -}} +{{- end -}} +{{- $sections := list -}} +{{- range ($sectionNames | sortAlpha) -}} +{{- $currentSectionName := . -}} +{{- range $.Sections.Sections -}} +{{- if eq .SectionName $currentSectionName -}} +{{- $thisSection := dict -}} +{{- $title := regexReplaceAll "^[0-9A-Z]+\\.\\s+" .SectionName "" -}} +{{- $link := printf "./values.yaml#L%d" (first .SectionItems).LineNumber -}} +{{- $_ := set $thisSection "SectionName" .SectionName -}} +{{- $_ := set $thisSection "SectionItems" .SectionItems -}} +{{- $_ := set $thisSection "SectionTitle" $title -}} +{{- $_ := set $thisSection "SectionHeading" (printf "[%s](%s)" $title $link) -}} +{{- $sections = append $sections $thisSection -}} +{{- end -}} +{{- end -}} +{{- end -}} +{{- if .Sections.DefaultSection.SectionItems -}} +{{- $thisSection := dict -}} +{{- $_ := set $thisSection "SectionName" .Sections.DefaultSection.SectionName -}} +{{- $_ := set $thisSection "SectionItems" .Sections.DefaultSection.SectionItems -}} +{{- $_ := set $thisSection "SectionTitle" .Sections.DefaultSection.SectionName -}} +{{- $_ := set $thisSection "SectionHeading" .Sections.DefaultSection.SectionName -}} +{{- $sections = append $sections $thisSection -}} +{{- end -}} +{{- range $sections }} + +### {{ .SectionTitle }} + +| Key | Description | Default | +|-----|-------------|---------| +{{- range .SectionItems }} +| [`{{ .Key }}`](./values.yaml#L{{ .LineNumber }}) | {{ if .Description }}{{ .Description }}{{ else }}{{ .AutoDescription }}{{ end }} | {{ template "chart.valueDefaultColumnRender" . }} | +{{- end }} +{{- end }} +{{- end -}} +{{- end -}} +{{/* +} End of functions +*/ -}} + +# Maestrod Helm chart + +> [!WARNING] This chart is made for internal use by Nutrient. + +{{ template "chart.deprecationWarning" . }} + +{{ template "chart.badgesSection" . }} + +{{ template "chart.description" . }} + +{{ template "chart.homepageLine" . }} + +* [Using this chart](#using-this-chart) +* [Prerequisites](#prerequisites) +* [Migrating from the internal 0.3.x chart](#migrating-from-the-internal-03x-chart) +* [Restart Job](#restart-job) +* [Values](#values) +{{- template "chart.valuesTableOfContents" . }} +* [Contribution](#contribution) +* [License](#license) +* [Support, Issues and License Questions](#support-issues-and-license-questions) + +## Maintainers + +{{ template "chart.maintainersTable" . }} + +## Using this chart + +### Adding the repository + +```shell +helm repo add nutrient https://pspdfkit.github.io/helm-charts +helm repo update +``` + +### Installing Maestrod + +```shell +helm upgrade --install -n maestrod \ + maestrod nutrient/maestrod \ + -f ./maestrod-values.yaml +``` + +Schema is generated using the [helm-values-schema-json plugin](https://github.com/losisin/helm-values-schema-json). + +`README.md` is generated with [helm-docs](https://github.com/norwoodj/helm-docs). + +### Upgrade + +> [!NOTE] +> Please consult the [changelog](/charts/maestrod/CHANGELOG.md). + +## Prerequisites + +### License Secret + +Maestrod requires the activation key to be supplied via an existing +Kubernetes Secret in the release namespace. The chart does **not** create +this secret. Create it before installing: + +```shell +kubectl create secret generic maestrod-secret \ + -n maestrod \ + --from-literal=NUTRIENT_LICENSE_KEY="$YOUR_LICENSE_KEY" +``` + +Override `licenseSecret.name` / `licenseSecret.key` to reference a different +Secret/key. + +### Image + +`image.repository` defaults to `pspdfkit/maestrod`. Override it to use a +private registry or mirror: + +```yaml +image: + repository: my-registry.example.com/maestrod + tag: "1.0.0" +``` + +When `image.tag` is empty, the chart falls back to the chart's `appVersion`. + +## Migrating from the internal 0.3.x chart + +This is the first public release of the chart. It is value-compatible with +the internal `cloud-demo-infra/charts/maestrod@0.3.4`, with a small number of +defaults reset to neutral values. If your existing values file relied on any +of the internal defaults below, copy them into your values file as-is. All +other value paths (`licenseSecret`, `maestro.*`, `service`, `ingress`, +`subdomainHostName`, `subdomainRootName`, `environmentShortName`, scheduling, +resources, `updateStrategy`, `restartJob.*`) keep identical defaults and +semantics. + +```yaml +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod # no default in public chart + tag: stable # default was 'stable', now empty (falls back to appVersion) + pullPolicy: Always # default was 'Always', now 'IfNotPresent' + +imagePullSecrets: + - name: image-registry-mcleod-ecr # default was this, now [] + +podLabels: + component_name: maestrod # default was this, now {} + otel/logging-enabled: "true" + +# only if restartJob.enabled: true +restartJob: + registryAuthSecretName: image-registry-mcleod-ecr # default was this, now "" +``` + +The Deployment selector labels are byte-identical between the internal and +public chart, so existing releases can be upgraded in place without hitting +selector-immutability errors. + +## Restart Job + +The chart ships an optional CronJob that polls the configured image registry +for a new digest on the running `image.tag` and patches the Maestrod +Deployment with a `nutrient/refresh-tag` annotation to trigger a rollout. + +It is disabled by default. To enable, set: + +```yaml +restartJob: + enabled: true + schedule: "*/10 * * * *" + registryAuthSecretName: my-registry-credentials # required: must be a kubernetes.io/dockerconfigjson Secret +``` + +The CronJob runs under a dedicated ServiceAccount with a Role that grants +`get/list pods` and `get/list/update/patch deployments` in the release +namespace. + +## Values + +{{ template "chart.valuesTable" . }} + +## Contribution + +The chart is validated using [ct](https://github.com/helm/chart-testing/tree/main) [lint](https://github.com/helm/chart-testing/blob/main/doc/ct_lint.md): + +```shell +ct lint --target-branch "$(git rev-parse --abbrev-ref HEAD)" +``` + +## License + +This software is licensed under a [modified BSD license](LICENSE). + +## Support, Issues and License Questions + +Nutrient offers support via https://support.nutrient.io/hc/en-us/requests/new + +Are you [evaluating our SDK](https://www.nutrient.io/sdk/)? That's great, we're happy to help out! To make sure this is fast, please use a work email and have someone from your company fill out our sales form: https://www.nutrient.io/contact-sales/ + +{{ template "helm-docs.versionFooter" . }} diff --git a/charts/maestrod/ci/00-simple-values.yaml b/charts/maestrod/ci/00-simple-values.yaml new file mode 100644 index 00000000..0d3cf365 --- /dev/null +++ b/charts/maestrod/ci/00-simple-values.yaml @@ -0,0 +1,21 @@ +# Repository must match the image the lint-test workflow pulls and side-loads +# into kind; `image.tag` is overridden to `nightly` by the workflow's +# --helm-extra-set-args. +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod + +# Skip the NUTRIENT_LICENSE_KEY env var so the pod can start without the +# external license Secret being pre-provisioned in the CI cluster. Real +# installs should keep the `licenseSecret.name` default (`maestrod-secret`). +licenseSecret: + name: "" + +replicaCount: 1 + +resources: + requests: + cpu: "100m" + memory: 256Mi + limits: + cpu: "2" + memory: 2Gi diff --git a/charts/maestrod/ci/01-ingress-values.yaml b/charts/maestrod/ci/01-ingress-values.yaml new file mode 100644 index 00000000..1d49f7da --- /dev/null +++ b/charts/maestrod/ci/01-ingress-values.yaml @@ -0,0 +1,27 @@ +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod + +licenseSecret: + name: "" + +replicaCount: 1 + +resources: + requests: + cpu: "100m" + memory: 256Mi + limits: + cpu: "2" + memory: 2Gi + +subdomainHostName: maestrod-ci +subdomainRootName: example.test + +ingress: + enabled: true + className: nginx + annotations: + nginx.ingress.kubernetes.io/backend-protocol: "HTTP" + nginx.ingress.kubernetes.io/rewrite-target: / + tls: + enabled: true diff --git a/charts/maestrod/ci/02-extras-values.yaml b/charts/maestrod/ci/02-extras-values.yaml new file mode 100644 index 00000000..e32ae54f --- /dev/null +++ b/charts/maestrod/ci/02-extras-values.yaml @@ -0,0 +1,60 @@ +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod + +licenseSecret: + name: "" + +replicaCount: 1 + +resources: + requests: + cpu: "100m" + memory: 256Mi + limits: + cpu: "2" + memory: 2Gi + +serviceAccount: + create: true + annotations: + eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/maestrod + automount: true + +deploymentAnnotations: + reloader.stakater.com/auto: "true" + +podAnnotations: + test-pod-annotation: "yes" + +podLabels: + test-pod-label: "yes" + +extraEnvs: + - name: EXTRA_VAR + value: extra-value + +extraVolumes: + - name: tmp + emptyDir: {} + +extraVolumeMounts: + - name: tmp + mountPath: /tmp/extra + +sidecars: + - name: busybox-sidecar + image: busybox:1.37 + command: ["sleep", "infinity"] + +initContainers: + - name: busybox-init + image: busybox:1.37 + command: ["true"] + +topologySpreadConstraints: + - maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: ScheduleAnyway + labelSelector: + matchLabels: + app.kubernetes.io/name: maestrod diff --git a/charts/maestrod/ci/03-hpa-pdb-values.yaml b/charts/maestrod/ci/03-hpa-pdb-values.yaml new file mode 100644 index 00000000..27347f40 --- /dev/null +++ b/charts/maestrod/ci/03-hpa-pdb-values.yaml @@ -0,0 +1,23 @@ +image: + repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod + +licenseSecret: + name: "" + +resources: + requests: + cpu: "100m" + memory: 256Mi + limits: + cpu: "2" + memory: 2Gi + +autoscaling: + enabled: true + minReplicas: 1 + maxReplicas: 3 + targetCPUUtilizationPercentage: 80 + +podDisruptionBudget: + create: true + minAvailable: 1 diff --git a/charts/maestrod/templates/NOTES.txt b/charts/maestrod/templates/NOTES.txt new file mode 100644 index 00000000..2430ee4b --- /dev/null +++ b/charts/maestrod/templates/NOTES.txt @@ -0,0 +1,20 @@ +Maestrod has been installed as release {{ .Release.Name }} in namespace {{ .Release.Namespace }}. + +Reach the application: +{{- if .Values.ingress.enabled }} +{{- $host := include "maestrod.ingressHost" . }} + http{{ if .Values.ingress.tls.enabled }}s{{ end }}://{{ $host }}{{ .Values.ingress.path }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "maestrod.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + Watch the service: + kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "maestrod.fullname" . }} + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "maestrod.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + kubectl --namespace {{ .Release.Namespace }} port-forward svc/{{ include "maestrod.fullname" . }} 8080:{{ .Values.service.port }} + open http://127.0.0.1:8080 +{{- end }} diff --git a/charts/maestrod/templates/_helpers.tpl b/charts/maestrod/templates/_helpers.tpl new file mode 100644 index 00000000..cb06e2a1 --- /dev/null +++ b/charts/maestrod/templates/_helpers.tpl @@ -0,0 +1,91 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "maestrod.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "maestrod.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Chart label. +*/}} +{{- define "maestrod.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels. +*/}} +{{- define "maestrod.labels" -}} +helm.sh/chart: {{ include "maestrod.chart" . }} +{{ include "maestrod.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels. Kept byte-identical to the internal 0.3.x chart so upgrades +in place do not hit immutable-selector errors. +*/}} +{{- define "maestrod.selectorLabels" -}} +app.kubernetes.io/name: {{ include "maestrod.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +ServiceAccount name. Returns the chart's fullname when serviceAccount.create is +true and no name override is given; otherwise the `serviceAccount.name` or +`default`. +*/}} +{{- define "maestrod.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "maestrod.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} + +{{/* +Ingress host. Prefers an explicit ingress.host; otherwise composes the host +from subdomainHostName and subdomainRootName; otherwise falls back to +subdomainHostName alone. +*/}} +{{- define "maestrod.ingressHost" -}} +{{- if .Values.ingress.host -}} +{{- .Values.ingress.host -}} +{{- else if and .Values.subdomainHostName .Values.subdomainRootName -}} +{{- printf "%s.%s" .Values.subdomainHostName .Values.subdomainRootName -}} +{{- else -}} +{{- .Values.subdomainHostName -}} +{{- end -}} +{{- end }} + +{{/* +Ingress TLS secret name. Defaults to -ingress when no override given. +*/}} +{{- define "maestrod.ingressTlsSecretName" -}} +{{- if .Values.ingress.tls.secretName -}} +{{- .Values.ingress.tls.secretName -}} +{{- else -}} +{{- printf "%s-ingress" (include "maestrod.fullname" .) -}} +{{- end -}} +{{- end }} diff --git a/charts/maestrod/templates/configmap.yaml b/charts/maestrod/templates/configmap.yaml new file mode 100644 index 00000000..bc2d2c45 --- /dev/null +++ b/charts/maestrod/templates/configmap.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "maestrod.fullname" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} +data: + NUTRIENT_SHOW_SCALAR: {{ .Values.maestro.showScalar | quote }} + NATIVESDK_VISION_LOGS: {{ .Values.maestro.nativeSdkVisionLogs | quote }} diff --git a/charts/maestrod/templates/deployment.yaml b/charts/maestrod/templates/deployment.yaml new file mode 100644 index 00000000..cd3bd739 --- /dev/null +++ b/charts/maestrod/templates/deployment.yaml @@ -0,0 +1,149 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "maestrod.fullname" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} + {{- with .Values.deploymentAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + {{- with .Values.revisionHistoryLimit }} + revisionHistoryLimit: {{ . }} + {{- end }} + {{- with .Values.updateStrategy }} + strategy: + type: {{ .type }} + {{- if eq .type "RollingUpdate" }} + {{- with .rollingUpdate }} + rollingUpdate: + {{- toYaml . | nindent 6 }} + {{- end }} + {{- end }} + {{- end }} + selector: + matchLabels: + {{- include "maestrod.selectorLabels" . | nindent 6 }} + template: + metadata: + annotations: + checksum/config: {{ include (print $.Template.BasePath "/configmap.yaml") . | sha256sum }} + {{- with .Values.podAnnotations }} + {{- toYaml . | nindent 8 }} + {{- end }} + labels: + {{- include "maestrod.labels" . | nindent 8 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 8 }} + {{- end }} + spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if or .Values.serviceAccount.create .Values.serviceAccount.name }} + serviceAccountName: {{ include "maestrod.serviceAccountName" . }} + {{- end }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: + {{- toYaml . | nindent 8 }} + {{- end }} + containers: + - name: maestrod + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 12 }} + {{- end }} + envFrom: + - configMapRef: + name: {{ include "maestrod.fullname" . }} + {{- with .Values.extraEnvFromSecrets }} + {{- range $secretName := . }} + - secretRef: + name: {{ $secretName }} + {{- end }} + {{- end }} + {{- with .Values.extraEnvFrom }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- if or .Values.licenseSecret.name .Values.extraEnvs }} + env: + {{- if .Values.licenseSecret.name }} + - name: NUTRIENT_LICENSE_KEY + valueFrom: + secretKeyRef: + name: {{ .Values.licenseSecret.name }} + key: {{ .Values.licenseSecret.key }} + {{- end }} + {{- with .Values.extraEnvs }} + {{- toYaml . | nindent 12 }} + {{- end }} + {{- end }} + ports: + - name: http + containerPort: {{ .Values.service.targetPort }} + protocol: TCP + {{- with .Values.startupProbe }} + startupProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.livenessProbe }} + livenessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.readinessProbe }} + readinessProbe: + {{- toYaml . | nindent 12 }} + {{- end }} + resources: + {{- toYaml .Values.resources | nindent 12 }} + {{- with .Values.extraVolumeMounts }} + volumeMounts: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.lifecycle }} + lifecycle: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.sidecars }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.extraVolumes }} + volumes: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.topologySpreadConstraints }} + topologySpreadConstraints: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.priorityClassName }} + priorityClassName: {{ .Values.priorityClassName | quote }} + {{- end }} + {{- if .Values.schedulerName }} + schedulerName: {{ .Values.schedulerName | quote }} + {{- end }} + {{- if .Values.terminationGracePeriodSeconds }} + terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }} + {{- end }} diff --git a/charts/maestrod/templates/hpa.yaml b/charts/maestrod/templates/hpa.yaml new file mode 100644 index 00000000..8e2eaa6c --- /dev/null +++ b/charts/maestrod/templates/hpa.yaml @@ -0,0 +1,40 @@ +{{- if .Values.autoscaling.enabled }} +{{- if .Capabilities.APIVersions.Has "autoscaling/v2" }} +apiVersion: autoscaling/v2 +{{- else }} +apiVersion: autoscaling/v2beta2 +{{- end }} +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "maestrod.fullname" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "maestrod.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + {{- with .Values.autoscaling.behavior }} + behavior: + {{- toYaml . | nindent 4 }} + {{- end }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + target: + type: Utilization + averageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/charts/maestrod/templates/ingress.yaml b/charts/maestrod/templates/ingress.yaml new file mode 100644 index 00000000..2d8daf58 --- /dev/null +++ b/charts/maestrod/templates/ingress.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingress.enabled }} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "maestrod.fullname" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} + {{- $annotations := omit (.Values.ingress.annotations | default dict) "external-dns.alpha.kubernetes.io/hostname" }} + {{- if or (gt (len $annotations) 0) (include "maestrod.ingressHost" .) }} + annotations: + {{- if gt (len $annotations) 0 }} + {{- toYaml $annotations | nindent 4 }} + {{- end }} + external-dns.alpha.kubernetes.io/hostname: {{ include "maestrod.ingressHost" . | quote }} + {{- end }} +spec: + ingressClassName: {{ .Values.ingress.className }} + {{- if .Values.ingress.tls.enabled }} + tls: + - hosts: + - {{ include "maestrod.ingressHost" . }} + secretName: {{ include "maestrod.ingressTlsSecretName" . }} + {{- end }} + rules: + - host: {{ include "maestrod.ingressHost" . }} + http: + paths: + - path: {{ .Values.ingress.path }} + pathType: {{ .Values.ingress.pathType }} + backend: + service: + name: {{ include "maestrod.fullname" . }} + port: + name: http +{{- end }} diff --git a/charts/maestrod/templates/pdb.yaml b/charts/maestrod/templates/pdb.yaml new file mode 100644 index 00000000..f5f2c78b --- /dev/null +++ b/charts/maestrod/templates/pdb.yaml @@ -0,0 +1,18 @@ +{{- if .Values.podDisruptionBudget.create -}} +apiVersion: policy/v1 +kind: PodDisruptionBudget +metadata: + name: {{ include "maestrod.fullname" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} +spec: +{{- $maxUnavailable := .Values.podDisruptionBudget.maxUnavailable }} +{{- $minAvailable := .Values.podDisruptionBudget.minAvailable }} +{{- if and (not (kindIs "invalid" $maxUnavailable)) (ne (toString $maxUnavailable) "") }} + maxUnavailable: {{ $maxUnavailable }} +{{- else if and (not (kindIs "invalid" $minAvailable)) (ne (toString $minAvailable) "") }} + minAvailable: {{ $minAvailable }} +{{- end }} + selector: + matchLabels: {{- include "maestrod.selectorLabels" . | nindent 6 }} +{{- end }} diff --git a/charts/maestrod/templates/restart-job/configmap.yaml b/charts/maestrod/templates/restart-job/configmap.yaml new file mode 100644 index 00000000..074a299b --- /dev/null +++ b/charts/maestrod/templates/restart-job/configmap.yaml @@ -0,0 +1,50 @@ +{{- if .Values.restartJob.enabled -}} +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "maestrod.fullname" . }}-restart-job + labels: + {{- include "maestrod.labels" . | nindent 4 }} +data: + run.sh: | + #!/bin/bash + set -euo pipefail + + export DATE=$(date -u +"%Y-%m-%dT%H:%M:%SZ") + export REGISTRY=$(echo "{{ .Values.image.repository }}" | cut -d'/' -f1) + export REPOSITORY=$(echo "{{ .Values.image.repository }}" | cut -d'/' -f2-) + export TAG="{{ .Values.image.tag | default .Chart.AppVersion }}" + export AUTH=$(cat /registry-auth/.dockerconfigjson | jq -r '.auths["'$REGISTRY'"].auth') + + echo "Looking for image: ${REGISTRY}/${REPOSITORY}:${TAG}" + + export IMAGE_LATEST=$(curl -I -L \ + -H "Authorization: Basic ${AUTH}" \ + -H "Accept: application/vnd.docker.distribution.manifest.v2+json" \ + -H "Accept: application/vnd.docker.distribution.manifest.list.v2+json" \ + -s https://${REGISTRY}/v2/${REPOSITORY}/manifests/${TAG} \ + | grep -i "^Docker-Content-Digest" | cut -d' ' -f2 | xargs) + + export IMAGE_CURRENT=$(kubectl get pods \ + --selector app.kubernetes.io/name={{ include "maestrod.name" . }},app.kubernetes.io/instance={{ .Release.Name }} \ + -o jsonpath='{.items[0].status.containerStatuses[0].imageID}' \ + | cut -d'@' -f2 | xargs) + + echo "Current image: ${IMAGE_CURRENT}" + echo "Latest image: ${IMAGE_LATEST}" + + if [[ -z "${IMAGE_LATEST}" ]]; then + echo "Error: IMAGE_LATEST is empty. Exiting." + exit 0 + fi + + if [[ "${IMAGE_CURRENT}" == "${IMAGE_LATEST}" ]]; then + echo "No changes." + else + echo "Triggering restart." + kubectl patch deployment {{ include "maestrod.fullname" . }} \ + -p "{\"spec\": {\"template\": {\"metadata\": {\"annotations\": {\"nutrient/refresh-tag\": \"${DATE}\"}}}}}" + fi + + echo "Done." +{{- end }} diff --git a/charts/maestrod/templates/restart-job/cronjob.yaml b/charts/maestrod/templates/restart-job/cronjob.yaml new file mode 100644 index 00000000..b9278af4 --- /dev/null +++ b/charts/maestrod/templates/restart-job/cronjob.yaml @@ -0,0 +1,76 @@ +{{- if .Values.restartJob.enabled -}} +{{- if not .Values.restartJob.registryAuthSecretName -}} +{{- fail "restartJob.registryAuthSecretName must be set when restartJob.enabled is true" -}} +{{- end -}} +apiVersion: batch/v1 +kind: CronJob +metadata: + name: {{ include "maestrod.fullname" . }}-restart-job + labels: + {{- include "maestrod.labels" . | nindent 4 }} +spec: + schedule: {{ .Values.restartJob.schedule | quote }} + startingDeadlineSeconds: 600 + concurrencyPolicy: Forbid + successfulJobsHistoryLimit: 1 + failedJobsHistoryLimit: 1 + jobTemplate: + spec: + backoffLimit: 1 + activeDeadlineSeconds: 300 + ttlSecondsAfterFinished: 300 + template: + metadata: + {{- with .Values.restartJob.podAnnotations }} + annotations: + {{- toYaml . | nindent 12 }} + {{- end }} + labels: + {{- include "maestrod.labels" . | nindent 12 }} + {{- with .Values.restartJob.podLabels }} + {{- toYaml . | nindent 12 }} + {{- end }} + spec: + restartPolicy: Never + serviceAccountName: {{ include "maestrod.fullname" . }}-restart-job + containers: + - name: restart-job + image: "{{ .Values.restartJob.image.repository }}:{{ .Values.restartJob.image.tag }}" + imagePullPolicy: {{ .Values.restartJob.image.pullPolicy }} + command: + - /run.sh + volumeMounts: + - name: registry-auth + mountPath: /registry-auth/.dockerconfigjson + subPath: .dockerconfigjson + readOnly: true + - name: script + mountPath: /run.sh + subPath: run.sh + volumes: + - name: script + configMap: + name: {{ include "maestrod.fullname" . }}-restart-job + items: + - key: run.sh + path: run.sh + defaultMode: 0o777 + - name: registry-auth + secret: + secretName: {{ .Values.restartJob.registryAuthSecretName }} + items: + - key: .dockerconfigjson + path: .dockerconfigjson + {{- with .Values.restartJob.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.restartJob.affinity }} + affinity: + {{- toYaml . | nindent 12 }} + {{- end }} + {{- with .Values.restartJob.tolerations }} + tolerations: + {{- toYaml . | nindent 12 }} + {{- end }} +{{- end }} diff --git a/charts/maestrod/templates/restart-job/rbac.yaml b/charts/maestrod/templates/restart-job/rbac.yaml new file mode 100644 index 00000000..e14eb94c --- /dev/null +++ b/charts/maestrod/templates/restart-job/rbac.yaml @@ -0,0 +1,35 @@ +{{- if .Values.restartJob.enabled -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "maestrod.fullname" . }}-restart-job + labels: + {{- include "maestrod.labels" . | nindent 4 }} +rules: + - apiGroups: [""] + resources: ["pods"] + verbs: + - get + - list + - apiGroups: ["apps"] + resources: ["deployments"] + verbs: + - get + - list + - update + - patch +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "maestrod.fullname" . }}-restart-job + labels: + {{- include "maestrod.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "maestrod.fullname" . }}-restart-job +roleRef: + kind: Role + name: {{ include "maestrod.fullname" . }}-restart-job + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/charts/maestrod/templates/restart-job/serviceaccount.yaml b/charts/maestrod/templates/restart-job/serviceaccount.yaml new file mode 100644 index 00000000..d5c82013 --- /dev/null +++ b/charts/maestrod/templates/restart-job/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.restartJob.enabled -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "maestrod.fullname" . }}-restart-job + labels: + {{- include "maestrod.labels" . | nindent 4 }} + {{- with .Values.restartJob.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: true +{{- end }} diff --git a/charts/maestrod/templates/service.yaml b/charts/maestrod/templates/service.yaml new file mode 100644 index 00000000..4b58cf61 --- /dev/null +++ b/charts/maestrod/templates/service.yaml @@ -0,0 +1,19 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "maestrod.fullname" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} + {{- with .Values.service.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.service.type }} + ports: + - name: http + port: {{ .Values.service.port }} + targetPort: http + protocol: TCP + selector: + {{- include "maestrod.selectorLabels" . | nindent 4 }} diff --git a/charts/maestrod/templates/serviceaccount.yaml b/charts/maestrod/templates/serviceaccount.yaml new file mode 100644 index 00000000..302acf27 --- /dev/null +++ b/charts/maestrod/templates/serviceaccount.yaml @@ -0,0 +1,13 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "maestrod.serviceAccountName" . }} + labels: + {{- include "maestrod.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} +{{- end }} diff --git a/charts/maestrod/templates/tests/test-connection.yaml b/charts/maestrod/templates/tests/test-connection.yaml new file mode 100644 index 00000000..75a6a4c9 --- /dev/null +++ b/charts/maestrod/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "maestrod.fullname" . }}-test-connection" + labels: + {{- include "maestrod.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + restartPolicy: Never + containers: + - name: nc + image: busybox:1.37 + command: ['nc'] + args: ['-z', '-v', '{{ include "maestrod.fullname" . }}', '{{ .Values.service.port }}'] diff --git a/charts/maestrod/values.schema.json b/charts/maestrod/values.schema.json new file mode 100644 index 00000000..e0dde336 --- /dev/null +++ b/charts/maestrod/values.schema.json @@ -0,0 +1,387 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "type": "object", + "properties": { + "affinity": { + "type": "object" + }, + "autoscaling": { + "type": "object", + "properties": { + "behavior": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "maxReplicas": { + "type": "integer" + }, + "minReplicas": { + "type": "integer" + }, + "targetCPUUtilizationPercentage": { + "type": [ + "integer", + "null" + ] + }, + "targetMemoryUtilizationPercentage": { + "type": [ + "integer", + "null" + ] + } + } + }, + "deploymentAnnotations": { + "type": "object" + }, + "environmentShortName": { + "type": "string" + }, + "extraEnvFrom": { + "type": "array" + }, + "extraEnvFromSecrets": { + "type": "array" + }, + "extraEnvs": { + "type": "array" + }, + "extraVolumeMounts": { + "type": "array" + }, + "extraVolumes": { + "type": "array" + }, + "fullnameOverride": { + "type": "string" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "repository": { + "type": "string", + "minLength": 1 + }, + "tag": { + "type": "string" + } + } + }, + "imagePullSecrets": { + "type": "array" + }, + "ingress": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "className": { + "type": "string" + }, + "enabled": { + "type": "boolean" + }, + "host": { + "type": "string" + }, + "path": { + "type": "string" + }, + "pathType": { + "type": "string", + "enum": [ + "Exact", + "Prefix", + "ImplementationSpecific" + ] + }, + "tls": { + "type": "object", + "properties": { + "enabled": { + "type": "boolean" + }, + "secretName": { + "type": "string" + } + } + } + } + }, + "initContainers": { + "type": "array" + }, + "licenseSecret": { + "type": "object", + "properties": { + "key": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "lifecycle": { + "type": "object" + }, + "livenessProbe": { + "type": "object" + }, + "maestro": { + "type": "object", + "properties": { + "nativeSdkVisionLogs": { + "type": "boolean" + }, + "showScalar": { + "type": "boolean" + } + } + }, + "nameOverride": { + "type": "string" + }, + "nodeSelector": { + "type": "object" + }, + "podAnnotations": { + "type": "object" + }, + "podDisruptionBudget": { + "type": "object", + "properties": { + "create": { + "type": "boolean" + }, + "maxUnavailable": { + "type": [ + "integer", + "string", + "null" + ] + }, + "minAvailable": { + "type": [ + "integer", + "string", + "null" + ] + } + } + }, + "podLabels": { + "type": "object" + }, + "podSecurityContext": { + "type": "object" + }, + "priorityClassName": { + "type": "string" + }, + "readinessProbe": { + "type": "object" + }, + "replicaCount": { + "type": "integer" + }, + "resources": { + "type": "object", + "properties": { + "limits": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + } + }, + "requests": { + "type": "object", + "properties": { + "cpu": { + "type": "string" + }, + "memory": { + "type": "string" + } + } + } + } + }, + "restartJob": { + "type": "object", + "properties": { + "affinity": { + "type": "object" + }, + "enabled": { + "type": "boolean" + }, + "image": { + "type": "object", + "properties": { + "pullPolicy": { + "type": "string", + "enum": [ + "Always", + "IfNotPresent", + "Never" + ] + }, + "repository": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + }, + "nodeSelector": { + "type": "object" + }, + "podAnnotations": { + "type": "object", + "properties": { + "skip-auto-labelling": { + "type": "string" + } + } + }, + "podLabels": { + "type": "object" + }, + "registryAuthSecretName": { + "type": "string" + }, + "schedule": { + "type": "string" + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + } + } + }, + "tolerations": { + "type": "array" + } + } + }, + "revisionHistoryLimit": { + "type": "integer" + }, + "schedulerName": { + "type": "string" + }, + "securityContext": { + "type": "object" + }, + "service": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "port": { + "type": "integer" + }, + "targetPort": { + "type": "integer" + }, + "type": { + "type": "string", + "enum": [ + "ClusterIP", + "NodePort", + "LoadBalancer" + ] + } + } + }, + "serviceAccount": { + "type": "object", + "properties": { + "annotations": { + "type": "object" + }, + "automount": { + "type": "boolean" + }, + "create": { + "type": "boolean" + }, + "name": { + "type": "string" + } + } + }, + "sidecars": { + "type": "array" + }, + "startupProbe": { + "type": "object" + }, + "subdomainHostName": { + "type": "string" + }, + "subdomainRootName": { + "type": "string" + }, + "terminationGracePeriodSeconds": { + "type": "integer" + }, + "tolerations": { + "type": "array" + }, + "topologySpreadConstraints": { + "type": "array" + }, + "updateStrategy": { + "type": "object", + "properties": { + "rollingUpdate": { + "type": "object", + "properties": { + "maxSurge": { + "type": [ + "integer", + "string" + ] + }, + "maxUnavailable": { + "type": [ + "integer", + "string" + ] + } + } + }, + "type": { + "type": "string", + "enum": [ + "RollingUpdate", + "Recreate" + ] + } + } + } + } +} diff --git a/charts/maestrod/values.simple.yaml b/charts/maestrod/values.simple.yaml new file mode 100644 index 00000000..b84e66ea --- /dev/null +++ b/charts/maestrod/values.simple.yaml @@ -0,0 +1,22 @@ +image: + repository: pspdfkit/maestrod + +# Reuse the default `maestrod-secret` Secret in the release namespace. Create +# it before installing: +# kubectl create secret generic maestrod-secret -n \ +# --from-literal=NUTRIENT_LICENSE_KEY="$YOUR_LICENSE_KEY" +licenseSecret: + name: maestrod-secret + key: NUTRIENT_LICENSE_KEY + +replicaCount: 1 + +# Modest CI defaults — the production-style 4 CPU / 8Gi requests in values.yaml +# will not fit on a one-node kind cluster. +resources: + requests: + cpu: "100m" + memory: 256Mi + limits: + cpu: "2" + memory: 2Gi diff --git a/charts/maestrod/values.yaml b/charts/maestrod/values.yaml new file mode 100644 index 00000000..c8beb2a0 --- /dev/null +++ b/charts/maestrod/values.yaml @@ -0,0 +1,359 @@ +# -- (object) Maestrod license. When `licenseSecret.name` is non-empty (the +# default), the chart wires the `NUTRIENT_LICENSE_KEY` environment variable to +# the referenced Secret/key. The Secret is **not** managed by the chart and +# must exist in the release namespace prior to install. Set +# `licenseSecret.name: ""` to omit the env var entirely (e.g. for chart-level +# template tests where the license is not exercised). +# @section -- 00. Maestrod License +# @notationType -- reference +licenseSecret: + # -- Name of the pre-existing Kubernetes Secret that holds the Maestrod + # activation key. When empty, the chart skips wiring the + # `NUTRIENT_LICENSE_KEY` env var. + # @section -- 00. Maestrod License + name: maestrod-secret + # -- Key inside the referenced Secret whose value is the Maestrod activation + # key. The chart wires this into the `NUTRIENT_LICENSE_KEY` environment + # variable. + # @section -- 00. Maestrod License + key: NUTRIENT_LICENSE_KEY + +# -- (object) Maestrod-specific runtime flags. Each key maps directly to a +# `NUTRIENT_*` / `NATIVESDK_*` environment variable on the container. +# @section -- 01. Maestrod configuration +# @notationType -- reference +maestro: + # -- `NUTRIENT_SHOW_SCALAR` — enable Nutrient Scalar integration in the + # Maestrod UI. Rendered as a string-quoted boolean. + # @section -- 01. Maestrod configuration + showScalar: false + # -- `NATIVESDK_VISION_LOGS` — enable native SDK vision-mode logging. + # Rendered as a string-quoted boolean. + # @section -- 01. Maestrod configuration + nativeSdkVisionLogs: false + +# -- (object) Image settings. +# @section -- B. Environment +# @notationType -- reference +image: + # -- Image repository. The default points at the canonical public Nutrient + # registry; override to use a private registry or mirror. The schema + # rejects an empty string. + # @section -- B. Environment + repository: pspdfkit/maestrod # @schema minLength: 1 + # -- Image pull policy. + # @section -- B. Environment + pullPolicy: IfNotPresent # @schema enum: [Always, IfNotPresent, Never] + # -- Image tag. Defaults to the chart's `appVersion` when empty. + # @section -- B. Environment + tag: "" + +# -- [ImagePullSecrets](https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod) +# referencing pre-existing Secrets in the release namespace. +# @section -- B. Environment +imagePullSecrets: [] + # - name: my-registry-credentials + +# -- (object) ServiceAccount for the Maestrod pod. With `create: false` +# (default), the pod runs under the namespace's `default` ServiceAccount and +# no ServiceAccount resource is created by the chart. +# @section -- B. Environment +# @notationType -- reference +serviceAccount: + # -- Create a ServiceAccount. + # @section -- B. Environment + create: false + # -- Annotations for the created ServiceAccount. + # @section -- B. Environment + annotations: {} + # -- Name to use for the ServiceAccount. When empty and `create: true`, the + # chart's fullname is used. + # @section -- B. Environment + name: "" + # -- Auto-mount the ServiceAccount token in the pod. + # @section -- B. Environment + automount: false + +# -- [Pod security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). +# @section -- B. Environment +podSecurityContext: {} +# -- [Container security context](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/). +# @section -- B. Environment +securityContext: {} + +# -- Extra environment variables appended to the Maestrod container. +# @section -- B. Environment +extraEnvs: [] + # - name: SOME_VAR + # value: some-value + +# -- Extra `envFrom` sources appended to the Maestrod container. +# @section -- B. Environment +extraEnvFrom: [] + # - configMapRef: + # name: my-config + # - secretRef: + # name: my-secret + +# -- Extra Secret names whose contents are loaded into the container env via +# `envFrom: secretRef:`. Convenience for the common case of supplying credentials. +# @section -- B. Environment +extraEnvFromSecrets: [] + # - my-credentials-secret + +# -- Additional volumes for the pod. +# @section -- B. Environment +extraVolumes: [] + +# -- Additional volume mounts for the Maestrod container. +# @section -- B. Environment +extraVolumeMounts: [] + +# -- Additional sidecar containers for the pod. +# @section -- B. Environment +sidecars: [] + +# -- [Init containers](https://kubernetes.io/docs/concepts/workloads/pods/init-containers/) +# for the pod. +# @section -- B. Environment +initContainers: [] + +# -- Release name override. +# @section -- B. Metadata +nameOverride: "" +# -- Release full name override. +# @section -- B. Metadata +fullnameOverride: "" + +# -- Pod labels merged on top of the common chart labels. +# @section -- B. Metadata +podLabels: {} +# -- Pod annotations. +# @section -- B. Metadata +podAnnotations: {} +# -- Annotations for the Deployment resource itself (distinct from +# `podAnnotations`, which apply to the pod template). +# @section -- B. Metadata +deploymentAnnotations: {} + +# -- (object) Service exposing the Maestrod container. +# @section -- C. Networking +# @notationType -- reference +service: + # -- Service type. + # @section -- C. Networking + type: ClusterIP # @schema enum: [ClusterIP, NodePort, LoadBalancer] + # -- Service port. + # @section -- C. Networking + port: 5000 + # -- Container port the Service routes to. + # @section -- C. Networking + targetPort: 5000 + # -- Service annotations. + # @section -- C. Networking + annotations: {} + +# -- (object) Ingress for the Maestrod Service. Single-host structure for +# backwards compatibility with the internal `0.3.x` chart. The host is resolved +# from `ingress.host` (preferred), or composed from +# `subdomainHostName` + `subdomainRootName` (fallback). +# @section -- C. Networking +# @notationType -- reference +ingress: + # -- Enable ingress. + # @section -- C. Networking + enabled: false + # -- Ingress class name. + # @section -- C. Networking + className: nginx + # -- Ingress annotations. The chart additionally injects an + # `external-dns.alpha.kubernetes.io/hostname` annotation with the resolved host. + # @section -- C. Networking + annotations: {} + # -- Explicit ingress host. When empty, the host is composed from + # `subdomainHostName` and `subdomainRootName`. + # @section -- C. Networking + host: "" + # -- Ingress path. + # @section -- C. Networking + path: / + # -- Path type. + # @section -- C. Networking + pathType: Prefix # @schema enum: [Exact, Prefix, ImplementationSpecific] + # -- (object) Ingress TLS settings. + # @section -- C. Networking + # @notationType -- reference + tls: + # -- Enable TLS. + # @section -- C. Networking + enabled: false + # -- TLS secret name. When empty, defaults to `-ingress`. + # @section -- C. Networking + secretName: "" + +# -- Subdomain part of the composed ingress host. Used when `ingress.host` is +# empty: the chart joins `.`. +# @section -- C. Networking +subdomainHostName: "" +# -- Root domain part of the composed ingress host. +# @section -- C. Networking +subdomainRootName: "" +# -- Short environment identifier (e.g. `staging`, `prod`). Kept for +# backwards compatibility; not currently consumed by any template. +# @section -- C. Networking +environmentShortName: "" + +# -- (object) [Startup probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +# Empty by default (no probe configured) for backwards compatibility with the +# internal `0.3.x` chart. Provide a probe spec to enable. +# @section -- F. Pod lifecycle +startupProbe: {} +# -- (object) [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +# Empty by default. Provide a probe spec to enable. +# @section -- F. Pod lifecycle +livenessProbe: {} +# -- (object) [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). +# Empty by default. Provide a probe spec to enable. +# @section -- F. Pod lifecycle +readinessProbe: {} +# -- [Termination grace period](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/). +# @section -- F. Pod lifecycle +terminationGracePeriodSeconds: 30 # @schema type: integer +# -- (object) [Container lifecycle hooks](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/). +# @section -- F. Pod lifecycle +lifecycle: {} + +# -- (object) [HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/). +# When `enabled: true`, the chart's HPA controls the replica count and +# `replicaCount` is ignored. +# @section -- G. Scheduling +# @notationType -- reference +autoscaling: + # -- Enable the HPA. + # @section -- G. Scheduling + enabled: false + # -- Minimum replicas. + # @section -- G. Scheduling + minReplicas: 1 + # -- Maximum replicas. + # @section -- G. Scheduling + maxReplicas: 10 + # -- Target average CPU utilisation (percentage). `null` disables the metric. + # @section -- G. Scheduling + targetCPUUtilizationPercentage: null # @schema type: [integer, null] + # -- Target average memory utilisation (percentage). `null` disables the metric. + # @section -- G. Scheduling + targetMemoryUtilizationPercentage: null # @schema type: [integer, null] + # -- (object) HPA [scaling behaviour](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior). + # @section -- G. Scheduling + behavior: {} + +# -- [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). +# @section -- G. Scheduling +resources: + requests: + cpu: "4" + memory: 8Gi + limits: + cpu: "4" + memory: 8Gi + +# -- Number of replicas. Ignored when `autoscaling.enabled` is `true`. +# @section -- G. Scheduling +replicaCount: 3 +# -- [Update strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). +# `rollingUpdate.maxSurge` and `rollingUpdate.maxUnavailable` are `IntOrString` +# in Kubernetes — both an integer (e.g. `1`) and a percentage string +# (e.g. `"25%"`) are accepted. +# @section -- G. Scheduling +updateStrategy: + type: RollingUpdate # @schema enum: [RollingUpdate, Recreate] + rollingUpdate: + maxSurge: 1 # @schema type: [integer, string] + maxUnavailable: 0 # @schema type: [integer, string] +# -- [Revision history limit](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy). +# @section -- G. Scheduling +revisionHistoryLimit: 1 + +# -- (object) [PodDisruptionBudget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/). +# When both `minAvailable` and `maxUnavailable` are non-empty, +# `maxUnavailable` wins (the two fields are mutually exclusive in Kubernetes). +# Either field accepts an integer (e.g. `1`) or a percentage string (e.g. `"50%"`). +# @section -- G. Scheduling +# @notationType -- reference +podDisruptionBudget: + # -- Create a PodDisruptionBudget for Maestrod. + # @section -- G. Scheduling + create: false + # -- `spec.minAvailable`. Integer or percentage string. Ignored when `maxUnavailable` is set. + # @section -- G. Scheduling + minAvailable: 1 # @schema type: [integer, string, null] + # -- `spec.maxUnavailable`. Integer or percentage string. Takes precedence over `minAvailable`. + # @section -- G. Scheduling + maxUnavailable: "" # @schema type: [integer, string, null] + +# -- [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). +# @section -- G. Scheduling +nodeSelector: {} +# -- Node affinity. +# @section -- G. Scheduling +affinity: {} +# -- [Node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). +# @section -- G. Scheduling +tolerations: [] +# -- [Topology spread constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/). +# @section -- G. Scheduling +topologySpreadConstraints: [] +# -- [PriorityClass](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) name. +# @section -- G. Scheduling +priorityClassName: "" +# -- [Scheduler](https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/) name. +# @section -- G. Scheduling +schedulerName: "" + +# -- (object) Optional CronJob that polls the configured image registry for a +# new digest on the running `image.tag` and patches the Maestrod Deployment +# with a refresh annotation to trigger a rollout. Disabled by default. +# @section -- H. Restart job +# @notationType -- reference +restartJob: + # -- Enable the restart-job CronJob and its supporting RBAC/ServiceAccount. + # @section -- H. Restart job + enabled: false + # -- CronJob schedule. + # @section -- H. Restart job + schedule: "*/10 * * * *" + # -- (object) Image for the restart-job container. Must contain `kubectl`, + # `curl`, `jq`, and `bash` — `alpine/k8s` covers all four. + # @section -- H. Restart job + # @notationType -- reference + image: + repository: alpine/k8s + tag: "1.35.2" + pullPolicy: IfNotPresent # @schema enum: [Always, IfNotPresent, Never] + # -- Name of a pre-existing `kubernetes.io/dockerconfigjson` Secret holding + # the registry credentials used to query the image manifest. Required when + # `restartJob.enabled: true`; rendering fails otherwise. + # @section -- H. Restart job + registryAuthSecretName: "" + # -- Pod annotations for the restart-job pod. + # @section -- H. Restart job + podAnnotations: + skip-auto-labelling: "true" + # -- Pod labels for the restart-job pod. + # @section -- H. Restart job + podLabels: {} + # -- (object) ServiceAccount for the restart-job pod. + # @section -- H. Restart job + # @notationType -- reference + serviceAccount: + annotations: {} + # -- Node selector for the restart-job pod. + # @section -- H. Restart job + nodeSelector: {} + # -- Tolerations for the restart-job pod. + # @section -- H. Restart job + tolerations: [] + # -- Affinity for the restart-job pod. + # @section -- H. Restart job + affinity: {} From 5aa009787a3fcf604729b4abb84de39e960875df Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Wed, 27 May 2026 17:11:02 +0200 Subject: [PATCH 2/8] Can have less for now --- charts/maestrod/ci/02-extras-values.yaml | 60 ----------------------- charts/maestrod/ci/03-hpa-pdb-values.yaml | 23 --------- 2 files changed, 83 deletions(-) delete mode 100644 charts/maestrod/ci/02-extras-values.yaml delete mode 100644 charts/maestrod/ci/03-hpa-pdb-values.yaml diff --git a/charts/maestrod/ci/02-extras-values.yaml b/charts/maestrod/ci/02-extras-values.yaml deleted file mode 100644 index e32ae54f..00000000 --- a/charts/maestrod/ci/02-extras-values.yaml +++ /dev/null @@ -1,60 +0,0 @@ -image: - repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod - -licenseSecret: - name: "" - -replicaCount: 1 - -resources: - requests: - cpu: "100m" - memory: 256Mi - limits: - cpu: "2" - memory: 2Gi - -serviceAccount: - create: true - annotations: - eks.amazonaws.com/role-arn: arn:aws:iam::000000000000:role/maestrod - automount: true - -deploymentAnnotations: - reloader.stakater.com/auto: "true" - -podAnnotations: - test-pod-annotation: "yes" - -podLabels: - test-pod-label: "yes" - -extraEnvs: - - name: EXTRA_VAR - value: extra-value - -extraVolumes: - - name: tmp - emptyDir: {} - -extraVolumeMounts: - - name: tmp - mountPath: /tmp/extra - -sidecars: - - name: busybox-sidecar - image: busybox:1.37 - command: ["sleep", "infinity"] - -initContainers: - - name: busybox-init - image: busybox:1.37 - command: ["true"] - -topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app.kubernetes.io/name: maestrod diff --git a/charts/maestrod/ci/03-hpa-pdb-values.yaml b/charts/maestrod/ci/03-hpa-pdb-values.yaml deleted file mode 100644 index 27347f40..00000000 --- a/charts/maestrod/ci/03-hpa-pdb-values.yaml +++ /dev/null @@ -1,23 +0,0 @@ -image: - repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod - -licenseSecret: - name: "" - -resources: - requests: - cpu: "100m" - memory: 256Mi - limits: - cpu: "2" - memory: 2Gi - -autoscaling: - enabled: true - minReplicas: 1 - maxReplicas: 3 - targetCPUUtilizationPercentage: 80 - -podDisruptionBudget: - create: true - minAvailable: 1 From d3c1d2223f0ccc751cad789dfd4565432f944798 Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Wed, 27 May 2026 17:13:17 +0200 Subject: [PATCH 3/8] Can have less for now --- .github/workflows/lint-test.yaml | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index 3249e726..a6815f8e 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -50,6 +50,9 @@ jobs: if echo "$changed" | grep -qx 'charts/maestrod'; then echo "maestrod-changed=true" >> "$GITHUB_OUTPUT" fi + if echo "$changed" | grep -v '^charts/maestrod$' | grep -q .; then + echo "non-maestrod-changed=true" >> "$GITHUB_OUTPUT" + fi - name: Run chart-testing (lint) if: steps.list-changed.outputs.changed == 'true' @@ -78,7 +81,7 @@ jobs: kind load docker-image "$IMAGE" --name chart-testing - name: Install Gateway API CRDs - if: steps.list-changed.outputs.changed == 'true' + if: steps.list-changed.outputs.non-maestrod-changed == 'true' run: | kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.5.0/standard-install.yaml kubectl wait --for=condition=Established crd/gatewayclasses.gateway.networking.k8s.io --timeout=120s @@ -86,7 +89,7 @@ jobs: kubectl wait --for=condition=Established crd/httproutes.gateway.networking.k8s.io --timeout=120s - name: Install CloudNativePG - if: steps.list-changed.outputs.changed == 'true' + if: steps.list-changed.outputs.non-maestrod-changed == 'true' run: | helm repo add cnpg https://cloudnative-pg.github.io/charts helm upgrade --install cnpg \ @@ -111,7 +114,7 @@ jobs: --timeout=300s - name: Install Garage - if: steps.list-changed.outputs.changed == 'true' + if: steps.list-changed.outputs.non-maestrod-changed == 'true' run: | kubectl create namespace whatever --dry-run=client -o yaml | kubectl apply -f - cat <<'EOF' | kubectl apply -f - @@ -254,7 +257,7 @@ jobs: kubectl wait --namespace whatever --for=condition=complete job/garage-create-buckets --timeout=300s - name: Run chart-testing (install) — public-image charts - if: steps.list-changed.outputs.changed == 'true' + if: steps.list-changed.outputs.non-maestrod-changed == 'true' # Public Nutrient charts (document-engine, ai-assistant, …): keep the # original `pullPolicy=Always` behaviour so the nightly tag is always # re-pulled from Docker Hub. Maestrod is excluded here and installed From 1dbd76387070c3e748a0c07daaeacc9ac30d0ceb Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Thu, 28 May 2026 00:41:27 +0200 Subject: [PATCH 4/8] changes --- charts/maestrod/Chart.yaml | 4 ++-- charts/maestrod/README.md | 4 ++-- charts/maestrod/values.yaml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/charts/maestrod/Chart.yaml b/charts/maestrod/Chart.yaml index 64bdd760..4f713e19 100644 --- a/charts/maestrod/Chart.yaml +++ b/charts/maestrod/Chart.yaml @@ -4,8 +4,8 @@ type: application description: Maestrod, the orchestration backend for Nutrient managed cloud workloads. home: https://www.nutrient.io icon: https://cdn.prod.website-files.com/65fdb7696055f07a05048833/66e58e33c3880ff24aa34027_nutrient-logo.png -version: 0.4.0 -appVersion: "1.0.0" +version: 0.5.0 +appVersion: "1.1.1" keywords: - nutrient diff --git a/charts/maestrod/README.md b/charts/maestrod/README.md index e977f81a..3e91f74d 100644 --- a/charts/maestrod/README.md +++ b/charts/maestrod/README.md @@ -2,7 +2,7 @@ > [!WARNING] This chart is made for internal use by Nutrient. -![Version: 0.4.0](https://img.shields.io/badge/Version-0.4.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.0.0](https://img.shields.io/badge/AppVersion-1.0.0-informational?style=flat-square) +![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.1.1](https://img.shields.io/badge/AppVersion-1.1.1-informational?style=flat-square) Maestrod, the orchestration backend for Nutrient managed cloud workloads. @@ -177,7 +177,7 @@ namespace. | [`serviceAccount`](./values.yaml#L62) | ServiceAccount for the Maestrod pod. With `create: false` (default), the pod runs under the namespace's `default` ServiceAccount and no ServiceAccount resource is created by the chart. | [...](./values.yaml#L62) | | [`serviceAccount.annotations`](./values.yaml#L68) | Annotations for the created ServiceAccount. | `{}` | | [`serviceAccount.automount`](./values.yaml#L75) | Auto-mount the ServiceAccount token in the pod. | `false` | -| [`serviceAccount.create`](./values.yaml#L65) | Create a ServiceAccount. | `false` | +| [`serviceAccount.create`](./values.yaml#L65) | Create a ServiceAccount. | `true` | | [`serviceAccount.name`](./values.yaml#L72) | Name to use for the ServiceAccount. When empty and `create: true`, the chart's fullname is used. | `""` | | [`sidecars`](./values.yaml#L114) | Additional sidecar containers for the pod. | `[]` | diff --git a/charts/maestrod/values.yaml b/charts/maestrod/values.yaml index c8beb2a0..b25a37b5 100644 --- a/charts/maestrod/values.yaml +++ b/charts/maestrod/values.yaml @@ -62,7 +62,7 @@ imagePullSecrets: [] serviceAccount: # -- Create a ServiceAccount. # @section -- B. Environment - create: false + create: true # -- Annotations for the created ServiceAccount. # @section -- B. Environment annotations: {} From 9363455659769613c5b67a4a6503f79516b5ee5f Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Thu, 28 May 2026 00:44:23 +0200 Subject: [PATCH 5/8] changes --- charts/maestrod/Chart.yaml | 2 +- charts/maestrod/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/maestrod/Chart.yaml b/charts/maestrod/Chart.yaml index 4f713e19..3ce7d554 100644 --- a/charts/maestrod/Chart.yaml +++ b/charts/maestrod/Chart.yaml @@ -5,7 +5,7 @@ description: Maestrod, the orchestration backend for Nutrient managed cloud work home: https://www.nutrient.io icon: https://cdn.prod.website-files.com/65fdb7696055f07a05048833/66e58e33c3880ff24aa34027_nutrient-logo.png version: 0.5.0 -appVersion: "1.1.1" +appVersion: "v1.1.1" keywords: - nutrient diff --git a/charts/maestrod/README.md b/charts/maestrod/README.md index 3e91f74d..5d90c654 100644 --- a/charts/maestrod/README.md +++ b/charts/maestrod/README.md @@ -2,7 +2,7 @@ > [!WARNING] This chart is made for internal use by Nutrient. -![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: 1.1.1](https://img.shields.io/badge/AppVersion-1.1.1-informational?style=flat-square) +![Version: 0.5.0](https://img.shields.io/badge/Version-0.5.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.1.1](https://img.shields.io/badge/AppVersion-v1.1.1-informational?style=flat-square) Maestrod, the orchestration backend for Nutrient managed cloud workloads. From 25f0f8e17b48fb0a37da403b15643c24ed202d45 Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Thu, 28 May 2026 00:52:15 +0200 Subject: [PATCH 6/8] v --- .github/workflows/lint-test.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint-test.yaml b/.github/workflows/lint-test.yaml index a6815f8e..f53a8a91 100644 --- a/.github/workflows/lint-test.yaml +++ b/.github/workflows/lint-test.yaml @@ -28,7 +28,7 @@ jobs: fetch-depth: 0 - name: Set up Helm - uses: azure/setup-helm@v4 + uses: azure/setup-helm@v5 with: version: latest @@ -64,7 +64,7 @@ jobs: - name: Configure AWS credentials (for ECR pull) if: steps.list-changed.outputs.maestrod-changed == 'true' - uses: aws-actions/configure-aws-credentials@v4 + uses: aws-actions/configure-aws-credentials@v6 with: role-to-assume: ${{ secrets.AWS_CI_ECR_ROLE_ARN }} aws-region: ${{ env.AWS_REGION }} From 12f81289dfdfe5142895bbf86cc4f7e2f9bef18e Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Thu, 28 May 2026 00:54:53 +0200 Subject: [PATCH 7/8] probes --- charts/maestrod/CHANGELOG.md | 16 ++++++ charts/maestrod/README.md | 74 ++++++++++++------------ charts/maestrod/values.schema.json | 90 +++++++++++++++++++++++++++++- charts/maestrod/values.yaml | 51 +++++++++++++---- 4 files changed, 181 insertions(+), 50 deletions(-) diff --git a/charts/maestrod/CHANGELOG.md b/charts/maestrod/CHANGELOG.md index ab47d536..39b21224 100644 --- a/charts/maestrod/CHANGELOG.md +++ b/charts/maestrod/CHANGELOG.md @@ -1,8 +1,24 @@ # Changelog - [Changelog](#changelog) + - [0.5.0 (2026-05-27)](#050-2026-05-27) - [0.4.0 (2026-05-26)](#040-2026-05-26) +## 0.5.0 (2026-05-27) + +### Changed + +- `appVersion` bumped to `v1.1.1`. +- `startupProbe`, `livenessProbe`, and `readinessProbe` now default to HTTP + GET on `/health` (port `http`). Set the corresponding value to `{}` to + disable an individual probe. + - Startup: `failureThreshold: 30`, `periodSeconds: 10`, `timeoutSeconds: 5` + (≈ 5 min boot budget before kubelet starts liveness/readiness checks). + - Liveness: `failureThreshold: 3`, `periodSeconds: 30`, `timeoutSeconds: 5` + (intentionally less aggressive than readiness — a failure restarts the + container). + - Readiness: `failureThreshold: 3`, `periodSeconds: 10`, `timeoutSeconds: 5`. + ## 0.4.0 (2026-05-26) First public release. Value-compatible with the internal `0.3.4` chart, with diff --git a/charts/maestrod/README.md b/charts/maestrod/README.md index 5d90c654..ffdf4834 100644 --- a/charts/maestrod/README.md +++ b/charts/maestrod/README.md @@ -218,53 +218,53 @@ namespace. | Key | Description | Default | |-----|-------------|---------| -| [`lifecycle`](./values.yaml#L224) | [Container lifecycle hooks](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/). | `{}` | -| [`livenessProbe`](./values.yaml#L214) | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). Empty by default. Provide a probe spec to enable. | `{}` | -| [`readinessProbe`](./values.yaml#L218) | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). Empty by default. Provide a probe spec to enable. | `{}` | -| [`startupProbe`](./values.yaml#L210) | [Startup probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). Empty by default (no probe configured) for backwards compatibility with the internal `0.3.x` chart. Provide a probe spec to enable. | `{}` | -| [`terminationGracePeriodSeconds`](./values.yaml#L221) | [Termination grace period](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/). | `30` | +| [`lifecycle`](./values.yaml#L255) | [Container lifecycle hooks](https://kubernetes.io/docs/tasks/configure-pod-container/attach-handler-lifecycle-event/). | `{}` | +| [`livenessProbe`](./values.yaml#L227) | [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) against Maestrod's `/health` HTTP endpoint. Polls less often than readiness and is more forgiving — a failure restarts the container, so this should only trip on true deadlock. Set `livenessProbe: {}` to disable. | [...](./values.yaml#L227) | +| [`readinessProbe`](./values.yaml#L241) | [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) against Maestrod's `/health` HTTP endpoint. Set `readinessProbe: {}` to disable. | [...](./values.yaml#L241) | +| [`startupProbe`](./values.yaml#L212) | [Startup probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) against Maestrod's `/health` HTTP endpoint. Generous `failureThreshold` so a slow initial boot doesn't get killed (10 s × 30 = 5 min budget). Set `startupProbe: {}` to disable. | [...](./values.yaml#L212) | +| [`terminationGracePeriodSeconds`](./values.yaml#L252) | [Termination grace period](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/). | `30` | ### Scheduling | Key | Description | Default | |-----|-------------|---------| -| [`affinity`](./values.yaml#L300) | Node affinity. | `{}` | -| [`autoscaling`](./values.yaml#L231) | [HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/). When `enabled: true`, the chart's HPA controls the replica count and `replicaCount` is ignored. | [...](./values.yaml#L231) | -| [`autoscaling.behavior`](./values.yaml#L249) | HPA [scaling behaviour](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior). | `{}` | -| [`autoscaling.enabled`](./values.yaml#L234) | Enable the HPA. | `false` | -| [`autoscaling.maxReplicas`](./values.yaml#L240) | Maximum replicas. | `10` | -| [`autoscaling.minReplicas`](./values.yaml#L237) | Minimum replicas. | `1` | -| [`autoscaling.targetCPUUtilizationPercentage`](./values.yaml#L243) | Target average CPU utilisation (percentage). `null` disables the metric. | `nil` | -| [`autoscaling.targetMemoryUtilizationPercentage`](./values.yaml#L246) | Target average memory utilisation (percentage). `null` disables the metric. | `nil` | -| [`nodeSelector`](./values.yaml#L297) | [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). | `{}` | -| [`podDisruptionBudget`](./values.yaml#L284) | [PodDisruptionBudget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/). When both `minAvailable` and `maxUnavailable` are non-empty, `maxUnavailable` wins (the two fields are mutually exclusive in Kubernetes). Either field accepts an integer (e.g. `1`) or a percentage string (e.g. `"50%"`). | [...](./values.yaml#L284) | -| [`podDisruptionBudget.create`](./values.yaml#L287) | Create a PodDisruptionBudget for Maestrod. | `false` | -| [`podDisruptionBudget.maxUnavailable`](./values.yaml#L293) | `spec.maxUnavailable`. Integer or percentage string. Takes precedence over `minAvailable`. | `""` | -| [`podDisruptionBudget.minAvailable`](./values.yaml#L290) | `spec.minAvailable`. Integer or percentage string. Ignored when `maxUnavailable` is set. | `1` | -| [`priorityClassName`](./values.yaml#L309) | [PriorityClass](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) name. | `""` | -| [`replicaCount`](./values.yaml#L263) | Number of replicas. Ignored when `autoscaling.enabled` is `true`. | `3` | -| [`resources`](./values.yaml#L253) | [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). | `{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"4","memory":"8Gi"}}` | -| [`revisionHistoryLimit`](./values.yaml#L276) | [Revision history limit](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy). | `1` | -| [`schedulerName`](./values.yaml#L312) | [Scheduler](https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/) name. | `""` | -| [`tolerations`](./values.yaml#L303) | [Node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). | `[]` | -| [`topologySpreadConstraints`](./values.yaml#L306) | [Topology spread constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/). | `[]` | -| [`updateStrategy`](./values.yaml#L269) | [Update strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). `rollingUpdate.maxSurge` and `rollingUpdate.maxUnavailable` are `IntOrString` in Kubernetes — both an integer (e.g. `1`) and a percentage string (e.g. `"25%"`) are accepted. | `{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0},"type":"RollingUpdate"}` | +| [`affinity`](./values.yaml#L331) | Node affinity. | `{}` | +| [`autoscaling`](./values.yaml#L262) | [HorizontalPodAutoscaler](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/). When `enabled: true`, the chart's HPA controls the replica count and `replicaCount` is ignored. | [...](./values.yaml#L262) | +| [`autoscaling.behavior`](./values.yaml#L280) | HPA [scaling behaviour](https://kubernetes.io/docs/tasks/run-application/horizontal-pod-autoscale/#configurable-scaling-behavior). | `{}` | +| [`autoscaling.enabled`](./values.yaml#L265) | Enable the HPA. | `false` | +| [`autoscaling.maxReplicas`](./values.yaml#L271) | Maximum replicas. | `10` | +| [`autoscaling.minReplicas`](./values.yaml#L268) | Minimum replicas. | `1` | +| [`autoscaling.targetCPUUtilizationPercentage`](./values.yaml#L274) | Target average CPU utilisation (percentage). `null` disables the metric. | `nil` | +| [`autoscaling.targetMemoryUtilizationPercentage`](./values.yaml#L277) | Target average memory utilisation (percentage). `null` disables the metric. | `nil` | +| [`nodeSelector`](./values.yaml#L328) | [Node selector](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/). | `{}` | +| [`podDisruptionBudget`](./values.yaml#L315) | [PodDisruptionBudget](https://kubernetes.io/docs/tasks/run-application/configure-pdb/). When both `minAvailable` and `maxUnavailable` are non-empty, `maxUnavailable` wins (the two fields are mutually exclusive in Kubernetes). Either field accepts an integer (e.g. `1`) or a percentage string (e.g. `"50%"`). | [...](./values.yaml#L315) | +| [`podDisruptionBudget.create`](./values.yaml#L318) | Create a PodDisruptionBudget for Maestrod. | `false` | +| [`podDisruptionBudget.maxUnavailable`](./values.yaml#L324) | `spec.maxUnavailable`. Integer or percentage string. Takes precedence over `minAvailable`. | `""` | +| [`podDisruptionBudget.minAvailable`](./values.yaml#L321) | `spec.minAvailable`. Integer or percentage string. Ignored when `maxUnavailable` is set. | `1` | +| [`priorityClassName`](./values.yaml#L340) | [PriorityClass](https://kubernetes.io/docs/concepts/scheduling-eviction/pod-priority-preemption/) name. | `""` | +| [`replicaCount`](./values.yaml#L294) | Number of replicas. Ignored when `autoscaling.enabled` is `true`. | `3` | +| [`resources`](./values.yaml#L284) | [Resources](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/). | `{"limits":{"cpu":"4","memory":"8Gi"},"requests":{"cpu":"4","memory":"8Gi"}}` | +| [`revisionHistoryLimit`](./values.yaml#L307) | [Revision history limit](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy). | `1` | +| [`schedulerName`](./values.yaml#L343) | [Scheduler](https://kubernetes.io/docs/concepts/scheduling-eviction/kube-scheduler/) name. | `""` | +| [`tolerations`](./values.yaml#L334) | [Node tolerations](https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/). | `[]` | +| [`topologySpreadConstraints`](./values.yaml#L337) | [Topology spread constraints](https://kubernetes.io/docs/concepts/scheduling-eviction/topology-spread-constraints/). | `[]` | +| [`updateStrategy`](./values.yaml#L300) | [Update strategy](https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#strategy). `rollingUpdate.maxSurge` and `rollingUpdate.maxUnavailable` are `IntOrString` in Kubernetes — both an integer (e.g. `1`) and a percentage string (e.g. `"25%"`) are accepted. | `{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0},"type":"RollingUpdate"}` | ### Restart job | Key | Description | Default | |-----|-------------|---------| -| [`restartJob`](./values.yaml#L319) | Optional CronJob that polls the configured image registry for a new digest on the running `image.tag` and patches the Maestrod Deployment with a refresh annotation to trigger a rollout. Disabled by default. | [...](./values.yaml#L319) | -| [`restartJob.affinity`](./values.yaml#L359) | Affinity for the restart-job pod. | `{}` | -| [`restartJob.enabled`](./values.yaml#L322) | Enable the restart-job CronJob and its supporting RBAC/ServiceAccount. | `false` | -| [`restartJob.image`](./values.yaml#L330) | Image for the restart-job container. Must contain `kubectl`, `curl`, `jq`, and `bash` — `alpine/k8s` covers all four. | [...](./values.yaml#L330) | -| [`restartJob.nodeSelector`](./values.yaml#L353) | Node selector for the restart-job pod. | `{}` | -| [`restartJob.podAnnotations`](./values.yaml#L341) | Pod annotations for the restart-job pod. | `{"skip-auto-labelling":"true"}` | -| [`restartJob.podLabels`](./values.yaml#L345) | Pod labels for the restart-job pod. | `{}` | -| [`restartJob.registryAuthSecretName`](./values.yaml#L338) | Name of a pre-existing `kubernetes.io/dockerconfigjson` Secret holding the registry credentials used to query the image manifest. Required when `restartJob.enabled: true`; rendering fails otherwise. | `""` | -| [`restartJob.schedule`](./values.yaml#L325) | CronJob schedule. | `"*/10 * * * *"` | -| [`restartJob.serviceAccount`](./values.yaml#L349) | ServiceAccount for the restart-job pod. | [...](./values.yaml#L349) | -| [`restartJob.tolerations`](./values.yaml#L356) | Tolerations for the restart-job pod. | `[]` | +| [`restartJob`](./values.yaml#L350) | Optional CronJob that polls the configured image registry for a new digest on the running `image.tag` and patches the Maestrod Deployment with a refresh annotation to trigger a rollout. Disabled by default. | [...](./values.yaml#L350) | +| [`restartJob.affinity`](./values.yaml#L390) | Affinity for the restart-job pod. | `{}` | +| [`restartJob.enabled`](./values.yaml#L353) | Enable the restart-job CronJob and its supporting RBAC/ServiceAccount. | `false` | +| [`restartJob.image`](./values.yaml#L361) | Image for the restart-job container. Must contain `kubectl`, `curl`, `jq`, and `bash` — `alpine/k8s` covers all four. | [...](./values.yaml#L361) | +| [`restartJob.nodeSelector`](./values.yaml#L384) | Node selector for the restart-job pod. | `{}` | +| [`restartJob.podAnnotations`](./values.yaml#L372) | Pod annotations for the restart-job pod. | `{"skip-auto-labelling":"true"}` | +| [`restartJob.podLabels`](./values.yaml#L376) | Pod labels for the restart-job pod. | `{}` | +| [`restartJob.registryAuthSecretName`](./values.yaml#L369) | Name of a pre-existing `kubernetes.io/dockerconfigjson` Secret holding the registry credentials used to query the image manifest. Required when `restartJob.enabled: true`; rendering fails otherwise. | `""` | +| [`restartJob.schedule`](./values.yaml#L356) | CronJob schedule. | `"*/10 * * * *"` | +| [`restartJob.serviceAccount`](./values.yaml#L380) | ServiceAccount for the restart-job pod. | [...](./values.yaml#L380) | +| [`restartJob.tolerations`](./values.yaml#L387) | Tolerations for the restart-job pod. | `[]` | ## Contribution diff --git a/charts/maestrod/values.schema.json b/charts/maestrod/values.schema.json index e0dde336..62d74cfd 100644 --- a/charts/maestrod/values.schema.json +++ b/charts/maestrod/values.schema.json @@ -138,7 +138,35 @@ "type": "object" }, "livenessProbe": { - "type": "object" + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "port": { + "type": "string" + } + } + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } }, "maestro": { "type": "object", @@ -192,7 +220,35 @@ "type": "string" }, "readinessProbe": { - "type": "object" + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "port": { + "type": "string" + } + } + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } }, "replicaCount": { "type": "integer" @@ -337,7 +393,35 @@ "type": "array" }, "startupProbe": { - "type": "object" + "type": "object", + "properties": { + "failureThreshold": { + "type": "integer" + }, + "httpGet": { + "type": "object", + "properties": { + "path": { + "type": "string" + }, + "port": { + "type": "string" + } + } + }, + "initialDelaySeconds": { + "type": "integer" + }, + "periodSeconds": { + "type": "integer" + }, + "successThreshold": { + "type": "integer" + }, + "timeoutSeconds": { + "type": "integer" + } + } }, "subdomainHostName": { "type": "string" diff --git a/charts/maestrod/values.yaml b/charts/maestrod/values.yaml index b25a37b5..a3ca34f5 100644 --- a/charts/maestrod/values.yaml +++ b/charts/maestrod/values.yaml @@ -203,19 +203,50 @@ subdomainRootName: "" # @section -- C. Networking environmentShortName: "" -# -- (object) [Startup probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). -# Empty by default (no probe configured) for backwards compatibility with the -# internal `0.3.x` chart. Provide a probe spec to enable. +# -- (object) [Startup probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) +# against Maestrod's `/health` HTTP endpoint. Generous `failureThreshold` so a +# slow initial boot doesn't get killed (10 s × 30 = 5 min budget). Set +# `startupProbe: {}` to disable. # @section -- F. Pod lifecycle -startupProbe: {} -# -- (object) [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). -# Empty by default. Provide a probe spec to enable. +# @notationType -- reference +startupProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 10 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 5 +# -- (object) [Liveness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) +# against Maestrod's `/health` HTTP endpoint. Polls less often than readiness +# and is more forgiving — a failure restarts the container, so this should +# only trip on true deadlock. Set `livenessProbe: {}` to disable. # @section -- F. Pod lifecycle -livenessProbe: {} -# -- (object) [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/). -# Empty by default. Provide a probe spec to enable. +# @notationType -- reference +livenessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 0 + periodSeconds: 30 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 +# -- (object) [Readiness probe](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/) +# against Maestrod's `/health` HTTP endpoint. Set `readinessProbe: {}` to +# disable. # @section -- F. Pod lifecycle -readinessProbe: {} +# @notationType -- reference +readinessProbe: + httpGet: + path: /health + port: http + initialDelaySeconds: 0 + periodSeconds: 5 + timeoutSeconds: 1 + successThreshold: 1 + failureThreshold: 3 # -- [Termination grace period](https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/). # @section -- F. Pod lifecycle terminationGracePeriodSeconds: 30 # @schema type: integer From d63e7c2a5b4cfc84f703197a815b4088c527cc3c Mon Sep 17 00:00:00 2001 From: Ingvarr Zhmakin <19270832+lazyoldbear@users.noreply.github.com> Date: Thu, 28 May 2026 01:36:17 +0200 Subject: [PATCH 8/8] Changelog --- charts/maestrod/CHANGELOG.md | 78 ++++++++---------------------------- 1 file changed, 17 insertions(+), 61 deletions(-) diff --git a/charts/maestrod/CHANGELOG.md b/charts/maestrod/CHANGELOG.md index 39b21224..ec9de686 100644 --- a/charts/maestrod/CHANGELOG.md +++ b/charts/maestrod/CHANGELOG.md @@ -2,75 +2,31 @@ - [Changelog](#changelog) - [0.5.0 (2026-05-27)](#050-2026-05-27) - - [0.4.0 (2026-05-26)](#040-2026-05-26) + - [Added](#added) ## 0.5.0 (2026-05-27) -### Changed - -- `appVersion` bumped to `v1.1.1`. -- `startupProbe`, `livenessProbe`, and `readinessProbe` now default to HTTP - GET on `/health` (port `http`). Set the corresponding value to `{}` to - disable an individual probe. - - Startup: `failureThreshold: 30`, `periodSeconds: 10`, `timeoutSeconds: 5` - (≈ 5 min boot budget before kubelet starts liveness/readiness checks). - - Liveness: `failureThreshold: 3`, `periodSeconds: 30`, `timeoutSeconds: 5` - (intentionally less aggressive than readiness — a failure restarts the - container). - - Readiness: `failureThreshold: 3`, `periodSeconds: 10`, `timeoutSeconds: 5`. - -## 0.4.0 (2026-05-26) - -First public release. Value-compatible with the internal `0.3.4` chart, with -defaults reset to neutral values (set them in your values file to preserve -the old behaviour): +First public release. Value-compatible with the internal `0.3.4` chart; the +following defaults changed — set them explicitly to preserve old behaviour: ```yaml image: - repository: 111300957880.dkr.ecr.eu-west-1.amazonaws.com/maestrod # now pspdfkit/maestrod - tag: stable # now empty (falls back to appVersion) + repository: .dkr.ecr.eu-west-1.amazonaws.com/maestrod # now pspdfkit/maestrod + tag: nightly # now empty (→ appVersion) pullPolicy: Always # now IfNotPresent -imagePullSecrets: [{ name: image-registry-mcleod-ecr }] # now [] -podLabels: { component_name: maestrod, otel/logging-enabled: "true" } # now {} -# only if restartJob.enabled: true -restartJob: { registryAuthSecretName: image-registry-mcleod-ecr } # now "" +imagePullSecrets: <...> +podLabels: { component_name: maestrod } # now {} +restartJob: + registryAuthSecretName: "<...>" # now "" ``` ### Added -- `templates/configmap.yaml` carries `NUTRIENT_SHOW_SCALAR` / - `NATIVESDK_VISION_LOGS`, loaded via `envFrom` with a `checksum/config` - rollout trigger (Document Engine pattern). -- Standard public-chart knobs: `serviceAccount`, `autoscaling`, - `podDisruptionBudget`, `startup`/`livenessProbe`, `lifecycle`, - `topologySpreadConstraints`, `schedulerName`, `deploymentAnnotations`, - `extraEnvs` / `extraEnvFrom` / `extraEnvFromSecrets` / `extraVolumes` / - `extraVolumeMounts` / `sidecars` / `initContainers`. All opt-in, default - no-op. -- Generated `README.md` (helm-docs) and `values.schema.json` - (helm-values-schema-json). -- `helm test` connection probe; `ci/` test value files. -- `licenseSecret.name: ""` now skips the `NUTRIENT_LICENSE_KEY` env var - (previously hard-coded). - -### Changed - -- `image.repository` defaults to `pspdfkit/maestrod` (and is schema-rejected - when empty). -- Restart Job moved under `templates/restart-job/` (`configmap.yaml`, - `cronjob.yaml`, `rbac.yaml`, `serviceaccount.yaml`). - -### Fixed - -- `PodDisruptionBudget` no longer renders both `minAvailable` and - `maxUnavailable`; `maxUnavailable` wins. Integer `0` is recognised. -- `Deployment.spec.strategy` omits `rollingUpdate` when `type: Recreate`. -- `updateStrategy.rollingUpdate.{maxSurge,maxUnavailable}` accept integer or - percentage string (`IntOrString`). -- `service.type` enum no longer advertises `ExternalName` (unsupported by - the Service template). - -### CI - -- `maestrod` is excluded from `ct install` until a public maestrod image is - available; `ct lint` still runs on every PR. +- `/health` HTTP defaults for `startupProbe` / `livenessProbe` / `readinessProbe`. +- `NUTRIENT_SHOW_SCALAR` / `NATIVESDK_VISION_LOGS` via ConfigMap with + `checksum/config` rollout trigger. +- `serviceAccount`, `autoscaling`, `podDisruptionBudget`, `deploymentAnnotations`, + `topologySpreadConstraints`, `schedulerName`, `lifecycle`, `extra*`, + `sidecars`, `initContainers`. +- `licenseSecret.name: ""` skips the `NUTRIENT_LICENSE_KEY` env var. +- Generated `README.md` + `values.schema.json`; `ci/` values; `helm test` probe.